Querying AWS CloudWatch from Grafana
Grafana can query CloudWatch metrics and logs from your team's AWS account. This uses cross-account IAM role assumption — Grafana assumes a role you create in your account to access your CloudWatch data.
Prerequisites
Before you begin, you need administrator rights in your Grafana organization. This allows you to create and configure data sources.
Contact the Platon observability team to request administrator rights for your organization. A separate Entra ID group will be created for your organization's admins. Once you're added to this group, you'll have the Editor or Admin role in your Grafana organization.
Step 1: Create an IAM Role in Your AWS Account
Create an IAM role in your AWS account that Grafana can assume. The role needs two things:
- A trust policy allowing the Grafana service role to assume it
- Permissions for the AWS services you want to query
Trust Policy
The role must trust the Grafana service role in the mgmt1 account (225881135767). Use the following trust policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Principal": {
"AWS": "arn:aws:iam::225881135767:role/Grafana-assume-role-mgmt1"
}
}
]
}
The trust policy above only verifies that the request comes from the shared Grafana service role — it does not restrict which Grafana user assumes the role. Since role naming conventions are documented here and AWS account numbers are not secret, another team could configure a data source in Grafana pointing to your role if they know or guess your account number.
If the resources you expose (e.g. logs) contain sensitive data, add an ExternalId condition to your trust policy. See Protecting Sensitive Resources with ExternalId below.
Permissions
Attach a policy with the permissions Grafana needs. For CloudWatch logs and metrics, a minimal policy looks like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:DescribeLogGroups",
"logs:GetLogGroupFields",
"logs:StartQuery",
"logs:StopQuery",
"logs:GetQueryResults",
"logs:GetLogEvents",
"logs:FilterLogEvents"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"cloudwatch:DescribeAlarmsForMetric",
"cloudwatch:DescribeAlarmHistory",
"cloudwatch:DescribeAlarms",
"cloudwatch:ListMetrics",
"cloudwatch:GetMetricData",
"cloudwatch:GetInsightRuleReport"
],
"Resource": "*"
}
]
}
The example above grants read access to all log groups and metrics in your account. Restrict the Resource field if you want to limit which log groups or namespaces Grafana can access.
Terraform Example
If you use Terraform, here's a complete example:
resource "aws_iam_role" "grafana" {
name = "GrafanaIAMRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = "sts:AssumeRole"
Principal = {
AWS = "arn:aws:iam::225881135767:role/Grafana-assume-role-mgmt1"
}
}
]
})
}
resource "aws_iam_role_policy" "grafana_cloudwatch" {
name = "GrafanaCloudWatchAccess"
role = aws_iam_role.grafana.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"logs:DescribeLogGroups",
"logs:GetLogGroupFields",
"logs:StartQuery",
"logs:StopQuery",
"logs:GetQueryResults",
"logs:GetLogEvents",
"logs:FilterLogEvents",
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"cloudwatch:DescribeAlarmsForMetric",
"cloudwatch:DescribeAlarmHistory",
"cloudwatch:DescribeAlarms",
"cloudwatch:ListMetrics",
"cloudwatch:GetMetricData",
"cloudwatch:GetInsightRuleReport",
]
Resource = "*"
}
]
})
}
Note the ARN of the role you created — you'll need it when configuring the data source in Grafana.
Step 2: Add a CloudWatch Data Source in Grafana
-
Log in to Grafana and switch to your organization
-
Go to Connections → Data sources → Add data source
-
Search for CloudWatch and select it
-
Configure the data source:
Setting Value Authentication Provider AWS SDK Default Assume Role ARN The ARN of the role you created (e.g. arn:aws:iam::<your-account-id>:role/GrafanaIAMRole)Default Region The AWS region where your resources are (e.g. eu-north-1) -
Click Save & test to verify the connection
If "Save & test" fails, double-check that:
- The trust policy on your role references
arn:aws:iam::225881135767:role/Grafana-assume-role-mgmt1exactly - Your role has the necessary CloudWatch/Logs permissions
- The region is correct
Protecting Sensitive Resources with ExternalId
By default, any Grafana organization admin who knows your AWS account number can set up a data source that assumes your role. If the resources you expose through this role contain sensitive data (e.g. application logs with personal information), you should add an ExternalId condition to your trust policy.
The ExternalId acts as a shared secret between your IAM role and the Grafana data source. Only someone who knows the ExternalId can successfully assume the role.
1. Add the Condition to Your Trust Policy
Generate a random UUID (e.g. with uuidgen) and add a Condition block to your trust policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Principal": {
"AWS": "arn:aws:iam::225881135767:role/Grafana-assume-role-mgmt1"
},
"Condition": {
"StringEquals": {
"sts:ExternalId": "your-random-uuid-here"
}
}
}
]
}
If you use Terraform, add the condition to the assume_role_policy:
resource "aws_iam_role" "grafana" {
name = "GrafanaIAMRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = "sts:AssumeRole"
Principal = {
AWS = "arn:aws:iam::225881135767:role/Grafana-assume-role-mgmt1"
}
Condition = {
StringEquals = {
"sts:ExternalId" = var.grafana_external_id
}
}
}
]
})
}
2. Configure the ExternalId in Grafana
When configuring your CloudWatch data source in Grafana, enter the same UUID in the External ID field:
| Setting | Value |
|---|---|
| Authentication Provider | AWS SDK Default |
| Assume Role ARN | The ARN of your role |
| External ID | The same UUID you used in the trust policy |
| Default Region | Your AWS region |
The ExternalId should be treated like a password. Do not commit it to version control or share it outside your team. If you use Terraform, store it in a variable and manage it through your secrets pipeline.
How It Works
The authentication chain is:
- Grafana runs as a Kubernetes pod with an EKS service account
- The service account is linked to the IAM role
Grafana-assume-role-mgmt1via IRSA (IAM Roles for Service Accounts) - When you query a CloudWatch data source, Grafana uses its service role to call
sts:AssumeRoleon your team's role - Your role returns temporary credentials scoped to your account
- Grafana uses those credentials to query CloudWatch in your account
Beyond CloudWatch
The same pattern works for any AWS service that Grafana supports as a data source. If you want Grafana to access other AWS services in your account (e.g. X-Ray, Athena, Timestream), add the relevant permissions to your IAM role and configure the appropriate data source in Grafana with the same Assume Role ARN.