Datadog 複数AWSアカウント統合の導入手順
目的
1つのDatadog組織で複数のAWSアカウントを統合管理する実装手順を提供する
前提条件
Datadog組織とAWSアカウント、Terraformディレクトリの対応関係
datadog/docs/BasicDesign.mdによると、各AWSアカウントは以下のDatadog組織に対応しています。 同じDatadog組織に属する複数のAWSアカウントを、対応するTerraformディレクトリで管理します。
| AWSアカウントID | 用途 | Datadog組織 | Terraformディレクトリ |
|---|---|---|---|
| 853790572692 | インフラ開発用 | FD - インフラ | datadog/common/infra-dev/ |
| 900176301532 | 開発環境 | FD - 開発 | datadog/common/develop/ |
| 301608970378 | ステージング環境 | FD - ステージング | datadog/common/staging/ |
| 967691968827 | プラットフォーム本番 | FD - 本番 | datadog/common/production/ |
| 913831226605 | AmazonConnect+FDシステム本番 | FD - 本番 | datadog/common/production/ |
重要: 導入対象のAWSアカウントを、対応するDatadog組織のTerraformディレクトリに定義してください。
例:Amazon Connect (913831226605) を追加する場合
- Datadog組織: FD - 本番
- 追加先ディレクトリ:
datadog/common/production/
実装手順
フェーズ1: Lambda Forwarderの作成(PR#1)
注意: Datadog IntegrationモジュールはLambda Forwarderが存在することを前提としているため、最初にLambda Forwarderを作成します。
実装先: fastdoctor-template/common/amazon-connect/
ステップ1: ローカル環境の準備
cd fastdoctor-template/common/amazon-connect
./download-tfvar.shステップ2: 既存Lambda Forwarderの確認
Amazon Connect環境では、Lambda Forwarderの配置状況は以下の通りです:
| リージョン | Lambda Forwarder | 作成方法 | 今回の対応 |
|---|---|---|---|
| バージニア (us-east-1) | 既に存在 | platformモジュール経由で作成済み | 作成不要 |
| 東京 (ap-northeast-1) | 未作成 | - | 今回作成が必要 |
ステップ3: Datadog API Keyの確認
terraform.tfvarsでDatadog API Keyが設定されていることを確認:
注意: このAPI Keyは、フェーズ2で設定するDatadog組織(FD - 本番)のAPI Keyと同じである必要があります。
ステップ4: Lambda Forwarderモジュールの追加(東京リージョンのみ)
重要な設計判断:
platformモジュール全体を呼び出すと、VPC、踏み台サーバー、ECRなど不要なリソースが作成されてしまう- そのため、東京リージョンでは
datadog_lambda_forwarderサブモジュールを直接呼び出す - これにより、Lambda Forwarderのみを作成し、他の不要なインフラストラクチャリソースの作成を回避する
main.tfに以下を追加(東京リージョンのみ):
# Datadog Lambda Forwarder - 東京リージョン
# 注意: バージニアリージョンは既にplatformモジュール経由で作成済みのため、東京のみ追加
# platformモジュール全体を呼び出すとVPC、踏み台、ECR等の不要なリソースが作成されるため、
# datadog_lambda_forwarderサブモジュールを直接呼び出す
module "datadog-lambda-forwarder-tokyo" {
source = "../../template_modules/common/platform/datadog_lambda_forwarder"
datadog_api_key = var.datadog_api_key
dd_site = "ap1.datadoghq.com"
}設計ノート:
- バージニアリージョン:
module "platform"経由で既に作成済み(追加不要) - 東京リージョン:
datadog_lambda_forwarderサブモジュールを直接呼び出して作成 - 東京リージョンはデフォルトプロバイダー(ap-northeast-1)を使用
provider.tfでマルチリージョンプロバイダーが定義されていることを前提としています
ステップ5: Terraform適用
terraform init
terraform plan -var-file=terraform.tfvars
terraform apply -var-file=terraform.tfvars確認ポイント:
- 東京リージョンのLambda Forwarderのみが新規作成される計画になっている
- バージニアリージョンのリソースには変更がないこと
ステップ6: Lambda ARNの確認
デプロイが完了したら、Lambda ARNを確認します:
# バージニアリージョン
terraform output -raw datadog_forwarder_arn_virginia
# 東京リージョン
terraform output -raw datadog_forwarder_arn_tokyoまたは、AWS CLIで確認:
# バージニアリージョン
aws lambda get-function \
--function-name datadog-forwarder \
--query 'Configuration.FunctionArn' \
--output text \
--region us-east-1
# 東京リージョン
aws lambda get-function \
--function-name datadog-forwarder \
--query 'Configuration.FunctionArn' \
--output text \
--region ap-northeast-1期待される出力:
arn:aws:lambda:us-east-1:913831226605:function:datadog-forwarder
arn:aws:lambda:ap-northeast-1:913831226605:function:datadog-forwarderステップ7: terraform.tfvarsをS3にアップロード
./upload-tfvar.shステップ8: PRの作成とマージ
- 変更内容をコミット
- PRを作成
- レビュー・承認後にマージ
フェーズ2: Datadog側の設定(PR#2)
注意: フェーズ1が完了し、Lambda Forwarderが作成された後に実施
ステップ1: ローカル環境の準備
cd datadog/common/production
./download-tfvar.shステップ2: Terraform設定ファイルの修正
2-1. main.tfの修正
まず、既存のモジュール名を確認します:
terraform state list | grep "module.integration"シナリオA: 既存モジュール名が integration の場合
既存のモジュール名を integration-${env} に変更する必要があります。 ステップ3のTerraform State移行で terraform state mv を実行してから、main.tfを修正します。
例:integration → integration-production にリネーム後、新しいアカウントを追加
# Production AWS Account Integration (967691968827)
module "integration-production" {
source = "../../common_modules/integration"
datadog_api_key = var.datadog_api_key
datadog_app_key = var.datadog_app_key
datadog_api_url = var.datadog_api_url
account_id = var.production_account_id
slack_channels = var.production_slack_channels
excluded_regions = var.excluded_regions
}
# Amazon Connect AWS Account Integration (913831226605)
module "integration-amazon-connect" {
source = "../../common_modules/integration"
datadog_api_key = var.datadog_api_key
datadog_app_key = var.datadog_app_key
datadog_api_url = var.datadog_api_url
account_id = var.amazon_connect_account_id
slack_channels = var.amazon_connect_slack_channels
excluded_regions = var.excluded_regions
}シナリオB: 既存モジュール名がすでに integration-${env} の場合
既にモジュール名が環境名付きの場合は、同じ命名規則で新しいアカウントを追加します。 ステップ3のTerraform State移行は不要です。
例:既存が integration-production の場合、integration-amazon-connect を追加
2-2. variable.tfの修正 アカウント別の変数を定義:
# Production AWS Account
variable "production_account_id" {
type = string
description = "Production AWS Account ID"
}
variable "production_slack_channels" {
type = map(string)
description = "Map of Slack channel names for Production environment"
}
# Amazon Connect AWS Account
variable "amazon_connect_account_id" {
type = string
description = "Amazon Connect AWS Account ID"
}
variable "amazon_connect_slack_channels" {
type = map(string)
description = "Map of Slack channel names for Amazon Connect environment"
}2-3. output.tfの修正 各アカウントのExternal IDを個別に出力:
output "production_external_id" {
value = module.integration-production.external_id
description = "External ID for Production AWS Account Datadog integration"
sensitive = true # セキュリティ上の理由でsensitiveに設定
}
output "amazon_connect_external_id" {
value = module.integration-amazon-connect.external_id
description = "External ID for Amazon Connect AWS Account Datadog integration"
sensitive = true # セキュリティ上の理由でsensitiveに設定
}
# 後方互換性のために残す(既存のAWS側の設定が参照している可能性がある)
output "external_id" {
value = module.integration-production.external_id
description = "External ID for Production AWS Account (backward compatibility)"
sensitive = true # セキュリティ上の理由でsensitiveに設定
}2-4. terraform.tfvarsの修正 アカウント別の設定値を記載:
# Amazon Connect AWS Account
amazon_connect_account_id = "913831226605"
# DatadogのSlack通知チャンネルの設定(Amazon Connect)
amazon_connect_slack_channels = {
"channel-1" = "#aws-amazon-connect-alerts"
}ステップ3: Terraform State移行(シナリオAの場合のみ)
注意: このステップはシナリオA(既存モジュール名が integration の場合)のみ実施してください。 シナリオB(既にモジュール名が integration-${env} の場合)は、このステップをスキップしてステップ4に進んでください。
シナリオAのみ実施:既存のmodule.integrationをmodule.integration-productionにリネームします。
# 現在のステートを確認
terraform state list
# module.integrationをmodule.integration-productionに移行
terraform state mv 'module.integration' 'module.integration-production'
# 移行後のステートを確認
terraform state list期待される結果:
module.integration-production.datadog_integration_aws.this
module.integration-production.datadog_integration_aws_lambda_arn.this[...]
module.integration-production.datadog_integration_aws_log_collection.this[...]
...ステップ4: Terraform Plan実行
terraform init
terraform plan -var-file=terraform.tfvars確認ポイント(シナリオA):
module.integration-productionが既存リソースを変更せず維持(state移行により名前のみ変更)module.integration-amazon-connectが新規リソースとして作成される計画になっている- 削除されるリソースがないこと
確認ポイント(シナリオB):
- 既存モジュール(例:
module.integration-production)に変更がないこと module.integration-amazon-connectが新規リソースとして作成される計画になっている- 削除されるリソースがないこと
ステップ5: Terraform Apply実行
terraform apply -var-file=terraform.tfvarsステップ6: External IDの取得
# sensitiveに設定しているため、-rawフラグが必要
terraform output -raw production_external_id
terraform output -raw amazon_connect_external_idこれらのExternal IDを控えておく(AWS側のIAMロール設定で使用)
ステップ7: terraform.tfvarsをS3にアップロード
./upload-tfvar.shステップ8: PRの作成とマージ
- 変更内容をコミット
- PRを作成
- レビュー・承認後にマージ
フェーズ3: AWS側のIAM設定(PR#3)
注意: フェーズ2が完了し、External IDが取得できた後に実施
ステップ1: 変数の追加
実装先: fastdoctor-template/common/amazon-connect/globals/iam/
variable.tfに以下を追加:
variable "datadog_external_id" {
type = string
description = "External ID for Datadog AWS Integration"
}terraform.tfvarsに以下を追加(フェーズ1で取得したExternal IDを設定):
datadog_external_id = "xxxxxxxx11111111" # フェーズ1のステップ6で取得した値ステップ2: IAM Policy Documentの作成
policy.tfに以下を追加:
クリックして展開:IAM Policy Documentのコード
# Datadog統合用Assume Role Policy
data "aws_iam_policy_document" "datadog_aws_integration_assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = ["arn:aws:iam::464622532012:root"] # Datadog AWS Account
}
condition {
test = "StringEquals"
variable = "sts:ExternalId"
values = [var.datadog_external_id]
}
}
}
# Datadog統合用IAMポリシー
# 完全なポリシーは datadog/common_modules/integration/main.tf を参照
data "aws_iam_policy_document" "datadog_integration" {
statement {
sid = "DatadogAWSIntegrationPolicy"
actions = [
"apigateway:GET",
"autoscaling:Describe*",
"backup:List*",
"budgets:ViewBudget",
"cloudfront:GetDistributionConfig",
"cloudfront:ListDistributions",
"cloudtrail:DescribeTrails",
"cloudtrail:GetTrailStatus",
"cloudtrail:LookupEvents",
"cloudwatch:Describe*",
"cloudwatch:Get*",
"cloudwatch:List*",
"codedeploy:List*",
"codedeploy:BatchGet*",
"directconnect:Describe*",
"dynamodb:List*",
"dynamodb:Describe*",
"ec2:Describe*",
"ecs:Describe*",
"ecs:List*",
"elasticache:Describe*",
"elasticache:List*",
"elasticfilesystem:DescribeFileSystems",
"elasticfilesystem:DescribeTags",
"elasticfilesystem:DescribeAccessPoints",
"elasticloadbalancing:Describe*",
"elasticmapreduce:List*",
"elasticmapreduce:Describe*",
"es:ListTags",
"es:ListDomainNames",
"es:DescribeElasticsearchDomains",
"events:CreateEventBus",
"fsx:DescribeFileSystems",
"fsx:ListTagsForResource",
"health:DescribeEvents",
"health:DescribeEventDetails",
"health:DescribeAffectedEntities",
"kinesis:List*",
"kinesis:Describe*",
"lambda:GetPolicy",
"lambda:List*",
"logs:DeleteSubscriptionFilter",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:DescribeSubscriptionFilters",
"logs:FilterLogEvents",
"logs:PutSubscriptionFilter",
"logs:TestMetricFilter",
"organizations:Describe*",
"organizations:List*",
"rds:Describe*",
"rds:List*",
"redshift:DescribeClusters",
"redshift:DescribeLoggingStatus",
"route53:List*",
"s3:GetBucketLogging",
"s3:GetBucketLocation",
"s3:GetBucketNotification",
"s3:GetBucketTagging",
"s3:ListAllMyBuckets",
"s3:PutBucketNotification",
"ses:Get*",
"sns:List*",
"sns:Publish",
"sqs:ListQueues",
"states:ListStateMachines",
"states:DescribeStateMachine",
"support:DescribeTrustedAdvisor*",
"support:RefreshTrustedAdvisorCheck",
"tag:GetResources",
"tag:GetTagKeys",
"tag:GetTagValues",
"xray:BatchGetTraces",
"xray:GetTraceSummaries"
]
resources = ["*"]
}
}
module "policy-allow-datadog-integration" {
source = "../../../../modules/iam/policy"
name = "DatadogAWSIntegrationPolicy"
description = "Policy for Datadog AWS Integration"
policy_json = data.aws_iam_policy_document.datadog_integration.json
}ステップ3: IAM Roleの作成
role.tfに以下を追加:
# Datadog統合用IAMロール
module "allow-datadog-integration-role" {
source = "../../../../modules/iam/role"
name = "datadog-integration-role"
assume_role_policy = data.aws_iam_policy_document.datadog_aws_integration_assume_role.json
role_policies = {
"policy_01" = { policy_arn = module.policy-allow-datadog-integration.policy_arn }
}
}ステップ4: Terraform適用
cd fastdoctor-template/common/amazon-connect/globals/iam
./download-tfvar.sh
terraform init
terraform plan -var-file=terraform.tfvars
terraform apply -var-file=terraform.tfvars
./upload-tfvar.shステップ5: Datadog UIで統合確認
- Datadog → Integrations → AWS を開く
- Amazon Connect (913831226605) が表示されることを確認
- メトリクス収集が開始されることを確認(1-2時間かかる場合があります)
検証項目
Datadog側の確認
- [ ] 2つのAWSアカウント統合が表示される
- [ ] Production (967691968827)
- [ ] Amazon Connect (913831226605)
- [ ] 各アカウントの異なるExternal IDが生成されている
- [ ] Slack通知チャンネルがアカウントごとに正しく設定されている
AWS側の確認
- [ ] Amazon Connect環境にIAMロール
datadog-integration-roleが作成されている - [ ] IAMロールのTrust Policyに正しいExternal IDが設定されている
- [ ] 必要なIAMポリシーがアタッチされている
メトリクス収集の確認
- [ ] Datadog Metrics Explorer でAmazon Connectのメトリクスが表示される
- [ ] 適切なタグ付けがされている(例:
aws_account:913831226605) - [ ] 除外リージョン(
excluded_regions)が正しく適用されている
External ID管理方針
採用方針:External IDはtfvarsに直接記載
AWS側のIAMロール設定で必要となるExternal IDは、terraform.tfvarsに直接記載する方針とします。
代替案として検討したCross-accountのremote state参照を採用しない理由
Cross-accountのremote state参照(AssumeRoleによる他アカウントのTerraform stateの参照)も技術的には可能ですが、以下の理由により採用しません:
デメリット
アカウント間の連携が複雑になる
- IAMロール作成が必要
- 信頼ポリシーの設定が必要
- S3バケットポリシーまたはAssumeRole用のIAMポリシー設定が必要
S3の参照によるセキュリティリスク
- 他アカウントのS3バケットへのアクセス権限を付与する必要がある
- 最小権限の原則から逸脱する可能性
- 監査ログの管理が複雑になる
初期構築時しか利用しない
- External IDはDatadog統合の初期設定時に一度取得するのみ
- その後は変更されることがほぼない
- 頻繁に参照する必要がないため、複雑な仕組みを導入するメリットが小さい
メンテナンス負荷の増加
- IAMロール・ポリシーの追加管理が必要
- トラブルシューティングが複雑化
- ドキュメント化・教育コストの増加
単一障害点の増加
- AssumeRoleの権限問題でTerraform実行が失敗する可能性
- 依存関係が増えることで障害の影響範囲が拡大
メリット(採用しない理由に比べて小さい)
- External IDの自動同期
- コードの変更時に手動でのIDコピーが不要
External IDのセキュリティ扱い
External IDはsensitive属性で保護します:
理由:
- terraform planの出力が安全: CI/CDログやコンソール出力に表示されなくなる
- 最小権限の原則: 不必要に公開しない方が安全
- 取得は可能: 必要な時は
terraform output -raw production_external_idで取得可能 - terraform.tfvarsへの記載は問題なし: S3で暗号化保存されるため
取得方法:
# sensitiveに設定しているため、-rawフラグが必要
terraform output -raw production_external_id
terraform output -raw amazon_connect_external_id結論
External IDは初期設定時に一度だけ必要な値であり、頻繁に変更されることもないため、シンプルさとセキュリティのバランスを考慮して、terraform.tfvarsに直接記載する方針とします。また、Terraform outputではsensitive属性を設定し、CI/CDログへの露出を防ぎます。
トラブルシューティング
ステート移行エラー
症状: terraform state mv でエラーが発生 対処:
terraform init -reconfigure
terraform state list
# 正確なリソース名を確認してから再実行External IDが取得できない
症状: terraform output でExternal IDが表示されない 対処:
# apply が成功していることを確認
terraform show | grep external_id
# または直接Datadog UIから確認IAMロールのAssumeRole失敗
症状: Datadog UI で「AssumeRole failed」エラー 原因: External IDの不一致 対処:
- Datadog側のExternal IDを再確認:
terraform output amazon_connect_external_id - AWS側のIAMロールのTrust Policyを確認
- External IDが完全一致しているか確認(前後の空白などに注意)
メトリクスが収集されない
症状: Datadog UIでメトリクスが表示されない 確認項目:
- IAMロールのポリシーが正しくアタッチされているか
- Datadog Integration設定で該当アカウントが有効になっているか
- 収集開始から5-10分待つ(初回メトリクス収集には時間がかかる)
参考資料
- Datadog公式ドキュメント - AWS Integration
- BasicDesign.md(
datadog/docs/BasicDesign.md)- Datadog組織とAWSアカウントの対応関係
注意事項
- Slack通知チャンネルは手動管理
- Terraform で初回作成後、
lifecycle.ignore_changesにより Datadog UI での手動変更が優先される - チャンネル追加/削除は Datadog UI から行うこと
- Terraform で初回作成後、