We are going to integrate Azure AD as a federated identity provider (IdP) in AWS Cognito user pool that provides a single sign-on (SSO) option for our Grafana users.
The Problem
We have multiple instances of Grafana deployed for different projects. We want to grant various business users permissions to access Grafana, but we don’t want to use its basic auth as that would be too much overhead considering that applications are deployed in different AWS accounts/regions.
AWS Cognito vs Azure AD
We use AWS for everything. You want a managed Kubernetes cluster? Here you go, EKS.
What’s that? You want to run containers without Kubernetes? No worries, ECS.
You’re not sure if your Java app runs in a Docker container, but you still want to deploy and scale it like those other people on Stack Overflow? Elastic Beanstalk, you’re welcome.
Oh, you just want a “simple server” where you can SSH into it and deploy your app by hand? The good old EC2 will serve you well then.
Tired of deploying your build artifacts manually you say? Look no further, CodePipeline.
You need to centrally manage your business users to grant them SSO permissions to various applications hosted on Amazon? AWS Cognito. Err, well, no. Azure AD.
If there is one thing that Microsoft does well is its user management (don’t quote me on that please). Azure AD user management is decent.
We want to use Azure AD because we manage business users there already, and we want to use it as a federated identity provider in AWS Cognito. Could we not configure Grafana to use Azure AD OAuth2 authentication and skip the Cognito part you ask? We could. But this way we would need to add and configure an enterprise application in Azure AD for every instance of Grafana (we are talking dozens here). From a maintenance point of view it is easier to configure a single enterprise application for AWS Cognito, and then create multiple app clients for Grafana with Terraform.
A high level flow with federated authentication that we aim to achieve is illustrated below.
- On a Grafana login screen, user selects Azure AD to authenticate.
- User gets redirected to Azure AD for login.
- On successful authentication, the IdP posts back a SAML assertion/token containing user’s identity details to an AWS Cognito user pool.
- AWS Cognito user pool issues a set of tokens to Grafana application.
- Grafana application can use the token issued by the AWS Cognito user pool for authorised access.
Pre-requisites
- A Microsoft account with administrative access to Azure Active Directory (or whatever it’s called at the time of reading this).
- An AWS account with administrative CLI access configured.
- A working instance of Grafana. Note that installation of Grafana is beyond the scope of this article.
The Plan
We are going to do the following:
- Create an AWS Cognito user pool.
- Add AWS Cognito as an enterprise application in Azure AD.
- Create App Roles in Azure AD.
- Add Users to Azure AD Enterprise Application and Assign App Roles.
- Add Azure AD as SAML identity provider in AWS Cognito.
- Create an app client for Grafana and use the newly created SAML IdP for Azure AD.
- Configure Grafana to use generic OAuth2 authentication.
Step 1: Create an AWS Cognito User Pool
Create a user pool called azure-ad-grafana in eu-west-1 region (change the pool name and region as required).
$ aws --region eu-west-1 \ cognito-idp create-user-pool \ --pool-name azure-ad-grafana
The output should contain a user pool ID. Copy the value of the user pool ID.
"UserPool": { "Id": "eu-west-1_QUipJzcj3", "Name": "azure-ad-grafana",
Add a domain name to the user pool. In this case the domain name is lisenet (change as required).
$ aws --region eu-west-1 \ cognito-idp create-user-pool-domain \ --user-pool-id eu-west-1_QUipJzcj3 \ --domain lisenet
Do note that your SAML Identifier (Entity ID) will be in a format of urn:amazon:cognito:sp:yourUserPoolID
.
SAML Reply URL format is https://yourDomainPrefix.auth.awsRegion.amazoncognito.com/saml2/idpresponse
.
Step 2: Add AWS Cognito as Enterprise Application in Azure AD
We are going to add an AWS Cognito user pool that we have just created as an application in Azure AD in order to establish a trust relationship between them.
Log in to the Azure Portal.
In the Azure Services section, choose Azure Active Directory (or Microsoft Entra ID).
In the left sidebar, choose Enterprise applications.
Choose Create your own application.
Enter a name for your application and select Integrate any other application you don’t find in the gallery (Non-gallery), as shown below. Choose Create.
On the Getting Started page, in the Set up single sign on tile, choose Get started, as shown below.
On the next screen, select SAML.
In the middle pane under Set up Single Sign-On with SAML, in the Basic SAML Configuration section, choose the Edit icon.
In the right pane under Basic SAML Configuration, replace the default Identifier ID (Entity ID) with this:
urn:amazon:cognito:sp:eu-west-1_QUipJzcj3
In the Reply URL (Assertion Consumer Service URL) field, enter this:
https://lisenet.auth.eu-west-1.amazoncognito.com/saml2/idpresponse
See the image below for more info.
In the middle pane under Set up Single Sign-On with SAML, in the Attributes & Claims section, choose Edit.
Choose Add new claim.
Enter the name email_verified, select Source as Attribute, and set Source Attribute as “true”. Choose Save. See the image below for more info.
Scroll down to the SAML Certificates section and copy the App Federation Metadata Url. We will need it shortly.
Step 3: Create App Roles in Azure AD
We are now going to create Azure AD app roles so that we can manage Grafana permissions using SAML claims. Note that this does not require Azure AD user groups.
Log in to the Azure Portal.
In the Azure Services section, choose Azure Active Directory (or Microsoft Entra ID).
In the left sidebar, choose App registrations.
Choose All applications. There should be our aws-cognito app displayed on the page. Select it.
In the left sidebar, choose App roles.
Choose Create app role.
Enter “Grafana Admin” as a display name for your app role. Select Users/Groups for allowed member types. Enter “admin” as a value that will be passed as a SAML claim. See the image below for more info.
Create two more app roles using the same process: Grafana Editor and Grafana Viewer. See the image below for more info.
Step 4: Add Users to Azure AD Enterprise Application and Assign App Roles
We are going to add users to our aws-cognito enterprise application so that they can log in using SSO. If you don’t have any Azure AD users, then before proceeding, create three users with the following names: Grafana Admin, Grafana Editor, Grafana Viewer. Make sure that all users have their email address attributes populated.
Log in to the Azure Portal.
In the Azure Services section, choose Azure Active Directory (or Microsoft Entra ID).
In the left sidebar, choose Enterprise applications.
There should be our aws-cognito application displayed on the page. Select it.
In the left sidebar, choose Users and groups.
Choose Add user/group.
For a user Grafana Admin, select a matching role of Grafana Admin. Do the same for Grafana Editor and Grafana Viewer users. See the image below for more info.
Step 5: Add Azure AD as SAML IDP in AWS Cognito
We need to add an attribute in the AWS Cognito user pool where group membership details from Azure AD can be received. We also need to add Azure AD as an identity provider.
Add a custom attribute grafana_role
to the user pool.
$ aws --region eu-west-1 \ cognito-idp add-custom-attributes \ --user-pool-id eu-west-1_QUipJzcj3 \ --custom-attributes Name=grafana_group,AttributeDataType="String"
Do note that you will not see any output.
Add Azure AD as an IdP.
$ aws --region eu-west-1 \ cognito-idp create-identity-provider \ --user-pool-id eu-west-1_QUipJzcj3 \ --provider-name=AzureAD \ --provider-type SAML \ --provider-details MetadataURL=https://login.microsoftonline.com/35d7624c-7811-4d1f-8287-0e918502d7c4/federationmetadata/2007-06/federationmetadata.xml?appid=e24744de-06ba-4241-8035-837e97df85ef \ --attribute-mapping email=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress,custom:grafana_group=http://schemas.microsoft.com/ws/2008/06/identity/claims/role,name=http://schemas.microsoft.com/identity/claims/displayname,email_verified=email_verified
The output should look something like this:
{ "IdentityProvider": { "UserPoolId": "eu-west-1_QUipJzcj3", "ProviderName": "AzureAD", "ProviderType": "SAML", "ProviderDetails": { "MetadataURL": "https://login.microsoftonline.com/35d7624c-7811-4d1f-8287-0e918502d7c4/federationmetadata/2007-06/federationmetadata.xml?appid=e24744de-06ba-4241-8035-837e97df85ef" }, "AttributeMapping": { "custom:grafana_group": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", "email_verified": "email_verified", "name": "http://schemas.microsoft.com/identity/claims/displayname" }, "LastModifiedDate": "2023-11-19T19:40:23.093000+00:00", "CreationDate": "2023-11-19T19:40:23.093000+00:00" } }
Step 6: Create an App Client in AWS Cognito
Before using AWS Cognito in Grafana, we need to register Grafana with Cognito as an app client. An app client is an entity within a user pool that has permission to call unauthenticated API operations (e.g. to register or sign in).
Create an app client. Make sure to change the callback-urls
to match your Grafana URL.
$ aws --region eu-west-1 \ cognito-idp create-user-pool-client \ --user-pool-id eu-west-1_QUipJzcj3 \ --client-name Grafana \ --generate-secret \ --callback-urls https://grafana.apps.lisenet.com//login/generic_oauth \ --explicit-auth-flows "ALLOW_ADMIN_USER_PASSWORD_AUTH" "ALLOW_REFRESH_TOKEN_AUTH" "ALLOW_USER_PASSWORD_AUTH" \ --allowed-o-auth-flows code \ --allowed-o-auth-scopes openid email profile\ --allowed-o-auth-flows-user-pool-client \ --supported-identity-providers AzureAD
Have a look at the output and note the ClientId and ClientSecret (see example below).
{ "UserPoolClient": { "UserPoolId": "eu-west-1_QUipJzcj3", "ClientName": "Grafana", "ClientId": "2abai6nl458ftveulta9pg1r39", "ClientSecret": "6pgpa3ips59ch0v1caung2ua4h1981bjdg32oajkppkvjvjsu65", [...] }
We will need to reference these details in Grafana configuration in the next step.
Step 7: Configure Grafana to use AWS Cognito
Edit grafana.ini
file and configure generic OAuth2.
We set role_attribute_path
to grant user permissions based on their Azure AD app role. Also, because of role_attribute_strict = true
, if a user does not have a Grafana role assigned, no login is allowed. This is considerably more secure than granting the Viewer role by default.
[auth.generic_oauth] allow_assign_grafana_admin = true api_url = https://lisenet.auth.eu-west-1.amazoncognito.com/oauth2/userInfo auth_url = https://lisenet.auth.eu-west-1.amazoncognito.com/oauth2/authorize client_id = 2abai6nl458ftveulta9pg1r39 client_secret = 6pgpa3ips59ch0v1caung2ua4h1981bjdg32oajkppkvjvjsu65 enabled = true name = AzureAD role_attribute_path = ("custom:grafana_group" == 'admin' && 'GrafanaAdmin' || "custom:grafana_group" == 'editor' && 'Editor' || "custom:grafana_group" == 'viewer' && 'Viewer') role_attribute_strict = true scopes = openid profile email token_url = https://lisenet.auth.eu-west-1.amazoncognito.com/oauth2/token
Test Grafana Login
Open Grafana UI and click on Sign in with AzureAD button.
Provide Azure AD user credentials and log in.
Grafana users should look like this:
AWS Cognito users should look like this:
This concludes our configuration.
Disclaimer
Sensitive details (such Entity ID, Client ID or Client Secret) provided in this article are not these of the real application for security reasons. They are however perfectly valid to serve as reference points.
The level of detail in this one is incredible, ta.
Very good thank you.