📑

AWS Cost and Usage Report(CUR)をTerraformで作成する

2024/04/14に公開

AWS Cost and Usage Report(AWSのコストと使用状況レポート、以下CUR)は、AWSのコストやリソースの使用状況をS3やRedshiftにアップロードしてくれる機能です。EC2インスタンスIDなどのリソース単位を含めることができるため、コストの追跡が容易になります。

Cost Explorerと異なり、EC2以外のAWSサービスのリソースも含まれます。1日1回以上、S3にアップロードされるので、自分たちの使い慣れたBIツールなどで分析・可視化することができます。

AWSマネジメントコンソールからCURを設定することが多いのですが、今回はTerraform(一部CloudFormation)で作成する方法を紹介します。

プロバイダの設定

今回は、CURのデータを保存するS3バケットを東京リージョン(ap-northeast-1)に作成します。
後述しますが、CURを作成するためには、us-east-1リージョンにもプロバイダを設定する必要があるため、エイリアスを使用します。

provider "aws" {
  region  = "ap-northeast-1"
}

# For using AWS CUR
provider "aws" {
  region  = "us-east-1"
  alias   = "us-east-1"
}

S3バケットの作成

CURのデータを保存するS3バケット(example-billing-report)を作成します。

resource "aws_s3_bucket" "cur" {
  bucket        = "example-billing-report"
  force_destroy = false
}

resource "aws_s3_bucket_policy" "cur" {
  bucket = aws_s3_bucket.cur.bucket
  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Sid    = "EnableAWSDataExportsToWriteToS3AndCheckPolicy",
        Effect = "Allow",
        Principal = {
          Service = [
            "billingreports.amazonaws.com",
            "bcm-data-exports.amazonaws.com",
          ],
        },
        Action = [
          "s3:GetBucketPolicy",
          "s3:PutObject",
        ],
        Resource = [
          aws_s3_bucket.cur.arn,
          "${aws_s3_bucket.cur.arn}/*",
        ],
        Condition = {
          StringEquals = {
            "aws:SourceArn" = [
              "arn:aws:cur:us-east-1:${data.aws_caller_identity.self.account_id}:definition/*",
              "arn:aws:bcm-data-exports:us-east-1:${data.aws_caller_identity.self.account_id}:export/*",
            ],
          },
          StringLike = {
            "aws:SourceAccount" = data.aws_caller_identity.self.account_id
          }
        },
      },
    ],
  })
}

S3バケットポリシーは、従来のCURだとこちらのドキュメントになりますが、AWS Data Exports(AWS CUR 2.0)の利用も見据えて、上記のようにしています。

CURの作成

aws_cur_report_definitionリソースで定義しますが、us-east-1リージョンで作成する必要があるのが注意です。
Amazon AthenaでCURのデータをクエリすることを想定しているため、Parquet形式で出力するようにしています。

locals {
  # CURでParquet形式で出力する場合は、S3パスが必須なため指定する
  s3_prefix = "hoge"
}
resource "aws_cur_report_definition" "cur" {
  provider                   = aws.us-east-1
  report_name                = "${var.app}-report"
  time_unit                  = "DAILY"
  format                     = "Parquet"
  compression                = "Parquet"
  additional_schema_elements = ["RESOURCES"]
  s3_bucket                  = aws_s3_bucket.cur.bucket
  s3_region                  = var.region
  s3_prefix                  = local.s3_prefix
  additional_artifacts       = ["ATHENA"]
  refresh_closed_reports     = false
  report_versioning          = "OVERWRITE_REPORT"
}

この状態でterraform applyを実行すると、S3バケットとCURが作成されます。CURが有効になるまで最大24時間かかります。

CloudFormationスタックの実行

CURが有効になった後、Amazon AthenaでCURデータをクエリできるようにするため、AWSから提供されているCloudFormationスタックを実行します。このCloudFormationテンプレートはS3バケットに配信されており、以下のリソースを作成します。

  • AWS Lambda関数
  • AWS Glue Crawler
  • AWS Glue Database

S3にCURデータ(Parquetファイル)が配信されると、AWS Lambda関数がトリガーされ、AWS Glue Crawlerによって、Parquetファイルを参照し、Glue Data Catalogが更新され、Athenaでクエリできるようになる仕組みです。

次のようにCloudFormationスタックを定義し、terraform applyを実行します。

resource "aws_cloudformation_stack" "cur_athena" {
  name         = "cost-and-usage-report-athena"
  template_url = "https://${aws_s3_bucket.cur.bucket}.s3.amazonaws.com/${local.s3_prefix}/${aws_cur_report_definition.cur.report_name}/crawler-cfn.yml"
  capabilities = ["CAPABILITY_IAM"]
}

まとめ

次回はAWS Data Exports(CUR 2.0)について紹介したいと思います。

参考

Discussion