AWS 환경에서 CCM(MSP 있을때) 설정 방법

1. Datadog 공식 문서

[상세 항목 정리]
Pasted image 20260723104527.png

[완료까지 전체 흐름 - 아래 PDF 내용 정리한 것임]
A. Management Account (MSP가 수행)

  1. Pricing Rule 생성 — Scope Global, Type Discount, 0% (DatadogCcmPricingRule1)
  2. Pricing Plan 생성 — 위 Rule 선택 (DatadogCcmPricingPlan1)
  3. Billing Group 생성 — Plan 선택, 대상 Member Account 포함, 그중 CUR 만들 계정을 Primary로 1개 지정 (DatadogCcmBillingGroup1)
  4. Member 계정의 CF 실행 Role에 권한 부여 — ccm-cur-cloudformation-policy.json (s3 생성/정책, cur:Put/Describe/Delete, iam:PutRolePolicy 등). 이미 있는 권한은 중복 부여 불필요

B. Member Account (고객이 수행 )

  1. CloudFormation Stack 실행 — 반드시 us-east-1, 첨부된 CCM Cloudformation for MSP.yaml 업로드
    • 파라미터 입력: CloudCostBucketName, CloudCostBucketRegion, CloudCostReportPrefix, CloudCostReportName, CreateCloudCostBucket=true, CreateCloudCostReport=true
    • DatadogIntegrationRole = 기존 AWS 통합에서 쓰던 Role (Datadog → Integration → AWS → 해당 계정 → AWS Role Name에서 확인)
    • 이 스택이 S3 버킷 + CUR 정의(Hourly/Parquet/RESOURCES) + Role 인라인 정책을 생성
    • 마지막 페이지의 파라미터 값을 그대로 기록
  2. Datadog에서 등록 — Cloud Cost > Settings > Add Account → Use connected AWS account → Manually → 5번 파라미터 값 입력 → Add Account

2. 설정 방법 (PDF)

1. MSP 설정 (AWS Billing Conductor & Pro Forma CUR)

2. 고객사 수행

  1. CloudFormation
    ![[CCM Cloudformation for MSP - 고객사 수행.yaml]]
# version: 0.0.1
AWSTemplateFormatVersion: 2010-09-09
Description: Datadog Cloud Cost Management Setup
Parameters:
  CloudCostBucketName:
    Type: String
    Description: The name of the S3 bucket for storing the cost and usage reports.
    MinLength: 1
  CloudCostBucketRegion:
    Type: String
    Description: The AWS region where the S3 bucket is located.
    MinLength: 1
  CloudCostReportPrefix:
    Type: String
    Description: The prefix to be added to the cost and usage report files in the S3 bucket.
    MinLength: 1
  CloudCostReportName:
    Type: String
    Description: The name of the cost and usage report.
    MinLength: 1
  DatadogIntegrationRole:
    Type: String
    Description: The name of the IAM role that has been integrated with Datadog.
    MinLength: 1
  CreateCloudCostReport:
    Type: String
    Default: true
    AllowedValues:
      - true
      - false
    Description: Whether a new Cost and Usage Report is created, or an existing one is used.
  CreateCloudCostBucket:
    Type: String
    Default: true
    AllowedValues:
      - true
      - false
    Description: Whether the S3 bucket for storing the Cost and Usage Report is created, or an existing one is used. Ignored if CreateCloudCostReport is false.

Conditions:
  ShouldCreateCloudCostReport:
    Fn::Equals:
      - Ref: CreateCloudCostReport
      - true
  ShouldCreateCloudCostBucket:
    Fn::And:
      - Fn::Equals:
          - Ref: CreateCloudCostBucket
          - true
      - !Condition ShouldCreateCloudCostReport # If CUR already exists, we don't need to create the bucket

Resources:
  DatadogCCMIAMPolicy:
    Type: AWS::IAM::RolePolicy
    Properties:
      PolicyName: !Sub "datadog-ccm-iam-policy--${CloudCostBucketName}-${CloudCostReportPrefix}-${CloudCostReportName}"
      RoleName: !Ref DatadogIntegrationRole
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: ReadCloudCostBucket
            Effect: Allow
            Action:
              - s3:ListBucket
            Resource: !Sub "arn:${AWS::Partition}:s3:::${CloudCostBucketName}"
          - Sid: GetBill
            Effect: Allow
            Action:
              - s3:GetObject
            Resource: !Sub "arn:${AWS::Partition}:s3:::${CloudCostBucketName}/${CloudCostReportPrefix}/${CloudCostReportName}/*"
          - Sid: CheckAccuracy
            Effect: Allow
            Action:
              - ce:Get*
            Resource: "*"
          - Sid: ListCURs
            Effect: Allow
            Action:
              - cur:DescribeReportDefinitions
            Resource: "*"
          - Sid: ListOrganizations
            Effect: Allow
            Action:
              - organizations:Describe*
              - organizations:List*
            Resource: "*"

  DDCloudCostBucket:
    Condition: ShouldCreateCloudCostBucket
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref CloudCostBucketName

  DDCloudCostBucketAccessPolicy:
    Condition: ShouldCreateCloudCostBucket
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref DDCloudCostBucket
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - billingreports.amazonaws.com
                - bcm-data-exports.amazonaws.com
            Action:
              - s3:PutObject
              - s3:GetBucketPolicy
            Resource:
              - !Sub ${DDCloudCostBucket.Arn}
              - !Sub ${DDCloudCostBucket.Arn}/*
            Condition:
              StringLike:
                aws:SourceArn:
                  - !Sub arn:*:cur:*:${AWS::AccountId}:definition/*
                  - !Sub arn:*:bcm-data-exports:*:${AWS::AccountId}:export/*
                aws:SourceAccount: !Sub ${AWS::AccountId}

  # DDCURReportDefinition can only be done after the bucket is created
  # and has the right policies. The bucket is only created if ShouldCreateCloudCostBucket
  # is true. DependsOn does not work with Conditions, so we use a WaitCondition
  # There are 2 wait handles,
  # - BucketWaitHandle: for the bucket if bucket should be created
  # - EmptyWaitHandle: for empty wait condition if bucket should not be created
  # BucketWaitCondition to join them and be used as a dependency for DDCURReportDefinition
  # More info here - https://garbe.io/blog/2017/07/17/cloudformation-hacks/
  BucketWaitHandle:
    Condition: ShouldCreateCloudCostBucket
    DependsOn: DDCloudCostBucketAccessPolicy
    Type: "AWS::CloudFormation::WaitConditionHandle"

  EmptyWaitHandle:
    Type: "AWS::CloudFormation::WaitConditionHandle"

  BucketWaitCondition:
    Type: "AWS::CloudFormation::WaitCondition"
    Properties:
      Handle:
        !If [
          ShouldCreateCloudCostBucket,
          !Ref BucketWaitHandle,
          !Ref EmptyWaitHandle,
        ]
      Timeout: "1"
      Count: 0

  DDCURReportDefinition:
    Condition: ShouldCreateCloudCostReport
    DependsOn: BucketWaitCondition
    Type: AWS::CUR::ReportDefinition
    Properties:
      AdditionalSchemaElements:
        - RESOURCES
      Compression: Parquet
      Format: Parquet
      RefreshClosedReports: true
      ReportName: !Ref CloudCostReportName
      ReportVersioning: CREATE_NEW_REPORT
      S3Bucket: !Ref CloudCostBucketName
      S3Prefix: !Ref CloudCostReportPrefix
      S3Region: !Ref CloudCostBucketRegion
      TimeUnit: HOURLY
  1. CCM연동 가이드 - 고객사 수행