1. はじめに
こんにちは!株式会社Definerのライターチームです!
今回は、AWS CodePipelineとCodeCommitを使ってクロスアカウントにCI/CDパイプラインをデプロイする方法について気になりますよね。
実際の画面や、資源を見ながら詳しく解説していきましょう。
2. 目的・ユースケース
この記事では、AWSアカウントを跨いだCI/CD構築という目的に向けて、CodePipelineという技術を活用します。
ITの現場で、AWS CodePipelineとCodeCommitを使ってクロスアカウントにCI/CDパイプラインをデプロイしたいときに、参考になる情報やプラクティスをまとめています。
3. セットアップ1. KMSとS3
<アカウントA>CodeCommitへのPushをトリガーに、<アカウントB>CodePipelineでデプロイを行う機構を実装します。
今回は、クロスアカウントの権限周りを中心に説明していきます。
前提として、デプロイされるECSサービスやパイプラインの設定ファイルを保存するS3、CodeCommitは既に存在するものとします。
この辺りの構築については、「Codepipelineを使ってECSの自動デプロイ(CD)を実装する」を参考にしてください。
①<アカウントB>S3のバケットポリシー設定
以下のポリシーをS3に適用しました。
{
"Version": "2012-10-17",
"Id": "S3Policy",
"Statement": [
{
"Sid": "CrossAccountS3GetPutPolicy",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${アカウントA}:root"
},
"Action": [
"s3:Get*",
"s3:Put*"
],
"Resource": "arn:aws:s3:::test-codepipeline-account-b/*"
},
{
"Sid": "CrossAccountS3ListPolicy",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${アカウントA}:root"
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::test-codepipeline-account-b"
}
]
}
②<アカウントB>KMSのポリシー設定
KMSを作成し、以下のキーポリシーを適用しました。
{
"Version": "2012-10-17",
"Id": "key-consolepolicy-3",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${自分のアカウント(B)}:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow access for Key Administrators",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${自分のアカウント(B)}:user/${ユーザ名}"
},
"Action": [
"kms:Create*",
"kms:Describe*",
"kms:Enable*",
"kms:List*",
"kms:Put*",
"kms:Update*",
"kms:Revoke*",
"kms:Disable*",
"kms:Get*",
"kms:Delete*",
"kms:TagResource",
"kms:UntagResource",
"kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion"
],
"Resource": "*"
},
{
"Sid": "Allow use of the key",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${自分のアカウント(B)}:user/${ユーザ名}"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
},
{
"Sid": "Allow use of the key",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::${アカウントA}:root",
"arn:aws:iam::${自分のアカウント(B)}:role/service-role/AWSCodePipelineServiceRole-ap-northeast-1-test-cicd"
]
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
},
{
"Sid": "Allow attachment of persistent resources",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::${アカウントA}:root",
"arn:aws:iam::${自分のアカウント(B)}:role/service-role/AWSCodePipelineServiceRole-ap-northeast-1-test-cicd"
]
},
"Action": [
"kms:CreateGrant",
"kms:ListGrants",
"kms:RevokeGrant"
],
"Resource": "*",
"Condition": {
"Bool": {
"kms:GrantIsForAWSResource": "true"
}
}
},
{
"Sid": "Allow attachment of persistent resources",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${自分のアカウント()}:user/${ユーザ名}"
},
"Action": [
"kms:CreateGrant",
"kms:ListGrants",
"kms:RevokeGrant"
],
"Resource": "*",
"Condition": {
"Bool": {
"kms:GrantIsForAWSResource": "true"
}
}
}
]
}
4. セットアップ2. IAMロール
続いて、IAMを設定していきます。
①<アカウントB>CodePipelineのIAMロールの設定
以下のポリシーを使用しました。
{
"Statement": [
{
"Action": [
"iam:PassRole"
],
"Resource": "*",
"Effect": "Allow",
"Condition": {
"StringEqualsIfExists": {
"iam:PassedToService": [
"cloudformation.amazonaws.com",
"elasticbeanstalk.amazonaws.com",
"ec2.amazonaws.com",
"ecs-tasks.amazonaws.com"
]
}
}
},
{
"Action": [
"codecommit:CancelUploadArchive",
"codecommit:GetBranch",
"codecommit:GetCommit",
"codecommit:GetRepository",
"codecommit:GetUploadArchiveStatus",
"codecommit:UploadArchive"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Sid": "AssumeRolePolicy",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": [
"arn:aws:iam::201920823605:role/*"
]
},
{
"Action": [
"codedeploy:CreateDeployment",
"codedeploy:GetApplication",
"codedeploy:GetApplicationRevision",
"codedeploy:GetDeployment",
"codedeploy:GetDeploymentConfig",
"codedeploy:RegisterApplicationRevision"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"codestar-connections:UseConnection"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"elasticbeanstalk:*",
"ec2:*",
"elasticloadbalancing:*",
"autoscaling:*",
"cloudwatch:*",
"s3:*",
"sns:*",
"cloudformation:*",
"rds:*",
"sqs:*",
"ecs:*"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"lambda:InvokeFunction",
"lambda:ListFunctions"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"opsworks:CreateDeployment",
"opsworks:DescribeApps",
"opsworks:DescribeCommands",
"opsworks:DescribeDeployments",
"opsworks:DescribeInstances",
"opsworks:DescribeStacks",
"opsworks:UpdateApp",
"opsworks:UpdateStack"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"cloudformation:CreateStack",
"cloudformation:DeleteStack",
"cloudformation:DescribeStacks",
"cloudformation:UpdateStack",
"cloudformation:CreateChangeSet",
"cloudformation:DeleteChangeSet",
"cloudformation:DescribeChangeSet",
"cloudformation:ExecuteChangeSet",
"cloudformation:SetStackPolicy",
"cloudformation:ValidateTemplate"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"codebuild:BatchGetBuilds",
"codebuild:StartBuild",
"codebuild:BatchGetBuildBatches",
"codebuild:StartBuildBatch"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Effect": "Allow",
"Action": [
"devicefarm:ListProjects",
"devicefarm:ListDevicePools",
"devicefarm:GetRun",
"devicefarm:GetUpload",
"devicefarm:CreateUpload",
"devicefarm:ScheduleRun"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"servicecatalog:ListProvisioningArtifacts",
"servicecatalog:CreateProvisioningArtifact",
"servicecatalog:DescribeProvisioningArtifact",
"servicecatalog:DeleteProvisioningArtifact",
"servicecatalog:UpdateProduct"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"cloudformation:ValidateTemplate"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecr:DescribeImages"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"states:DescribeExecution",
"states:DescribeStateMachine",
"states:StartExecution"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"appconfig:StartDeployment",
"appconfig:StopDeployment",
"appconfig:GetDeployment"
],
"Resource": "*"
}
],
"Version": "2012-10-17"
}
②<アカウントA>CodeCommitのIAMロールの作成
以下のポリシーを設定しました。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "UploadArtifactPolicy",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::test-codepipeline-account-b/*"
]
},
{
"Sid": "KMSAccessPolicy",
"Effect": "Allow",
"Action": [
"kms:DescribeKey",
"kms:GenerateDataKey*",
"kms:Encrypt",
"kms:ReEncrypt*",
"kms:Decrypt"
],
"Resource": [
"arn:aws:kms:ap-northeast-1:${アカウントB}:key/${KMSのID}"
]
},
{
"Sid": "CodeCommitAccessPolicy",
"Effect": "Allow",
"Action": [
"codecommit:GetBranch",
"codecommit:GetCommit",
"codecommit:UploadArchive",
"codecommit:GetUploadArchiveStatus",
"codecommit:CancelUploadArchive"
],
"Resource": [
"*"
]
}
]
}
5. パイプライン構築
続いて、パイプラインを構築していきます。
①<アカウントB>CodePipeline構築
クロスアカウントはコンソールからは設定できないようなので、CLIで作成します。
インプットとして、「input.son」ファイルを用意します。
パイプライン構築後、実際にアカウントBのCodeCommitにPushすると、アカウントAのパイプラインが動くことが確認できました!
## CodePipeline作成コマンド
aws codepipeline update-pipeline --cli-input-json file://input.json
input.jsonファイル
{
"pipeline": {
"name": "test-cicd-cross-account-b",
"roleArn": "arn:aws:iam::${アカウントB}:role/service-role/AWSCodePipelineServiceRole-ap-northeast-1-test-cicd",
"artifactStore": {
"type": "S3",
"location": "test-codepipeline-account-b",
"encryptionKey": {
"id": "arn:aws:kms:ap-northeast-1:${アカウントB}:alias/test-codepipeline",
"type": "KMS"
}
},
"stages": [
{
"name": "Source",
"actions": [
{
"name": "Source",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "CodeCommit",
"version": "1"
},
"runOrder": 1,
"configuration": {
"RepositoryName": "test-account-a",
"BranchName": "main"
},
"outputArtifacts": [
{
"name": "SourceArtifact"
}
],
"inputArtifacts": [],
"roleArn": "arn:aws:iam::${アカウントA}:role/codecommit-account-a"
},
{
"name": "s3",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "S3",
"version": "1"
},
"runOrder": 1,
"configuration": {
"PollForSourceChanges": "false",
"S3Bucket": "test-codepipeline-account-b",
"S3ObjectKey": "imagedefinitions.json.zip"
},
"outputArtifacts": [
{
"name": "s3output"
}
],
"inputArtifacts": [],
"region": "ap-northeast-1"
}
]
},
{
"name": "Deploy",
"actions": [
{
"name": "Deploy",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "ECS",
"version": "1"
},
"runOrder": 1,
"configuration": {
"ClusterName": "default",
"ServiceName": "test-nginx"
},
"outputArtifacts": [],
"inputArtifacts": [
{
"name": "s3output"
}
],
"region": "ap-northeast-1",
"namespace": "DeployVariables"
}
]
}
],
"version": 2
}
}
6. 引用・参考記事
7. 独自ソリューション「PrismScaler」について
PrismScalerは、開発・運用を要さずにたった3ステップで、AWSやAzure、GCPなどのマルチクラウド基盤構築を実現するWebサービスです。
エンジニアの大変な作業を肩代わり
・自動構築
・自動監視
・構成可視化
クラウド基盤に関わる作業を以上のように効率化します。
SRE/DevOpsエンジニアが行う大変な作業を肩代わりします。
高品質な汎用クラウド基盤の実現
・クラウド基盤構築/クラウド移行
・クラウドの保守運用・コスト最適化
など幅広い利用シーンを想定しています。IaaSやPaaSを適切に組み合わせた数百を超える高品質な汎用クラウド基盤を容易に実現できます。
興味を持たれた方には、無料で資料を提供しております。
お気軽にご相談ください。
8. お問合せ
株式会社Definerでは、
・ITの上流から下流まで一気通貫のワンストップソリューションをご提供。
・AIやクラウドのITインフラなど、先進的なIT技術のコンサルティングから要件定義 / 設計開発 / 実装、保守運用に至るまでの統合的な支援にコミット。
・少ないエンジニアで事業が成長する仕組みづくりの実現。
・エンジニアが喜ぶ、採用しやすい環境づくりの実現。
・高速なアジャイル開発環境の実現。
・自社プロダクトとしてPrismScalerを展開。
上記事業内容を進行しております。
※「開発者ブログ」では、エンジニアの入門編として有益な情報を無料公開しています。
ご相談やお問い合わせは「株式会社Definer」へ。
9. Definerに関して。
・ Definer Incは、ITの上流から下流まで一気通貫のワンストップソリューションをご提供しております。
・ AIやクラウドのITインフラなど、先進的なIT技術のコンサルティングから要件定義 / 設計開発 / 実装、保守運用に至るまでの統合的な支援にコミットしています。
・ DevOpsとCI/CDコンサルティングにより「少ないエンジニアで事業が成長する仕組みづくり」「エンジニアが喜ぶ、採用しやすい環境づくり」「高速なアジャイル開発環境」を実現しています。
・ また、自社プロダクトとしてPrismScalerを展開しております。PrismScalerは、AWS、Azure、GCPなどのマルチクラウド / ITインフラの高品質かつ迅速な、「自動構築」「自動監視」「問題検知」「構成可視化」を実現します。