diff --git a/tf-stage2/keycloak.tf b/tf-stage2/keycloak.tf
index 83d15fa5b51cd0afe150dc80e2299b42d36f98f3..68f8d60928964f2d4f64f8e19e2e99462e03f4ee 100644
--- a/tf-stage2/keycloak.tf
+++ b/tf-stage2/keycloak.tf
@@ -415,7 +415,8 @@ resource "keycloak_group_roles" "staff" {
   realm_id = "archlinux"
   group_id = keycloak_group.staff.id
   role_ids = [
-    keycloak_role.staff.id
+    keycloak_role.staff.id,
+    keycloak_role.grafana_archlinux_staff.id
   ]
 }
 
@@ -761,3 +762,26 @@ resource "keycloak_openid_user_realm_role_protocol_mapper" "user_realm_role_mapp
   add_to_id_token     = false
   add_to_access_token = false
 }
+
+// All of the below is to restrict access to Grafana to members in the Arch Linux Staff group.
+resource "keycloak_role" "grafana_archlinux_staff" {
+  realm_id    = "archlinux"
+  client_id   = keycloak_openid_client.grafana_openid_client.id
+  name        = "Staff"
+  description = "Arch Linux Staff Grafana"
+}
+
+resource "keycloak_generic_client_role_mapper" "grafana_archlinux_staff_to_email" {
+  realm_id        = "archlinux"
+  role_id         = keycloak_role.grafana_archlinux_staff.id
+  client_scope_id = keycloak_openid_client_scope.email.id
+}
+
+// This needs to be imported from the default client scopes created by Keycloak.
+resource "keycloak_openid_client_scope" "email" {
+  realm_id               = "archlinux"
+  name                   = "email"
+  description            = "OpenID Connect built-in scope: email"
+  include_in_token_scope = true
+  consent_screen_text    = "$${emailScopeConsentText}"
+}