Search code examples
amazon-web-servicesamazon-iamamazon-cloudwatch-metrics

Read only AWS CLI access to strictly CloudWatch billing metrics


I need to provide somebody with read only AWS CLI access to our CloudWatch billing metrics ONLY. I'm not sure how to do this since CloudWatch doesn't have any specific resources that one can control access to. This means there are no ARN's to specify in an IAM policy and as a result, any resource designation in the policy is "*". More info regarding CloudWatch ARN limitations can be found here. I looked into using namespaces but I believe the "aws-portal" namespace is for the console. Any direction or ideas are greatly appreciated.

With the current CloudWatch ARN limitations the IAM policy would look something like this.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "cloudwatch:DescribeMetricData",
        "cloudwatch:GetMetricData"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

Solution

  • As you say, you will not be able to achieve this within CloudWatch. According to the docs:

    CloudWatch doesn't have any specific resources for you to control access to... For example, you can't give a user access to CloudWatch data for only a specific set of EC2 instances or a specific load balancer. Permissions granted using IAM cover all the cloud resources you use or monitor with CloudWatch.

    An alternative option might be to:

    1. Use Scheduled events on a lambda function to periodically export relevant billing metrics from Cloudwatch to an S3 bucket. For example, using the Python SDK, the lambda might look something like this:

      import boto3
      from datetime import datetime, timedelta
      def lambda_handler(event, context):
          try:
              bucket_name = "so-billing-metrics"
              filename = '-'.join(['billing', datetime.now().strftime("%Y-%m-%d-%H")])
              region_name = "us-east-1"
              dimensions = {'Name': 'Currency', 'Value':'USD'}
              metric_name = 'EstimatedCharges'
              namespace = 'AWS/Billing'
              start_time = datetime.now() - timedelta(hours = 1)
              end_time = datetime.now()
      
              # Create CloudWatch client
              cloudwatch = boto3.client('cloudwatch', region_name=region_name)  
      
              # Get billing metrics for the last hour
              metrics = cloudwatch.get_metric_statistics(
                  Dimensions=[dimensions], 
                  MetricName=metric_name, 
                  Namespace=namespace,
                  StartTime=start_time,
                  EndTime=end_time,
                  Period=60,
                  Statistics=['Sum'])
      
              # Save data to temp file
              with open('/tmp/billingmetrics', 'wb') as f:
                  # Write header and data
                  f.write("Timestamp, Cost")
                  for entry in metrics['Datapoints']:
                      f.write(",".join([entry['Timestamp'].strftime('%Y-%m-%d %H:%M:%S'), str(entry['Sum']), entry['Unit']]))
      
              # Upload temp file to S3
              s3 = boto3.client('s3')
              with open('/tmp/billingmetrics', 'rb') as data:
                  s3.upload_fileobj(data, bucket_name, filename)
      
          except Exception as e:
              print str(e)
              return 0
          return 1
      

      Note: You will need to ensure that the Lambda function has the relevant permissions to write to S3 and read from cloudwatch.

    2. Restrict the IAM User/Role to read only access to the S3 bucket.