AWS CDK を使用して GitHub と統合した CodePipeline の作成例
イントロダクション
本記事では、AWS CDK を使用して AWS CodePipeline を作成する方法について説明します。この CodePipeline には、GitHub とのソースコード統合、CodeBuild を使用したアプリケーションのビルド、および CloudFormation によるリソースのデプロイが含まれます。また、CodePipeline のビルドアーティファクトを格納するためのカスタム S3 バケットも設定します。
前提条件
開始する前に、以下を準備してください:
- AWS CDK がインストールされ、設定されていること。
- アプリケーションコードと
buildspec.ymlファイルを含む GitHub リポジトリを所有していること。 - GitHub OAuth トークンが AWS Secrets Manager に保存されていること。
- CodePipeline と CodeBuild 用に適切な IAM ロールが設定されていること。
主要な部分
この CodePipeline は以下の3つのステージで構成されます:
- ソースステージ: GitHub リポジトリからコードを取得します。
- ビルドステージ: CodeBuild を使用してアプリケーションをビルドします。
- デプロイステージ: CloudFormation を使用してリソースをデプロイします。
コードの詳細解説
以下は、CodePipeline を作成するための AWS CDK コードの詳細な説明です。
1. リソースバケット
CodePipeline の実行中に使用されるアセットファイルを保存するために、S3 バケットを作成します。このバケットにはライフサイクルルールが設定されており、7日後にファイルが削除されます。
const assetBucket = new s3.Bucket(this, '<construct id>', {
bucketName: '<bucketName>',
lifecycleRules: [
{
id: 'DeleteAfter7Days',
enabled: true,
expiration: cdk.Duration.days(7),
},
],
});
2. IAM ロール
CodePipeline には既存の IAM ロールが ARN を使用して指定されます。
const codepipelineRole = iam.Role.fromRoleArn(this, '<construct id>', '<CODE_PIPELINE_ROLE_ARN>', {
mutable: false,
});
3. ビルドプロジェクト
CodeBuild プロジェクトを設定してアプリケーションをビルドします。このプロジェクトは Amazon Linux 2 をビルド環境として使用します。
const buildProject = new codebuild.PipelineProject(this, '<construct id>', {
projectName: '<projectName>',
role: codepipelineRole,
queuedTimeout: cdk.Duration.hours(0.5),
timeout: cdk.Duration.hours(0.5),
buildSpec: codebuild.BuildSpec.fromSourceFilename('buildspec.yml'),
environment: {
buildImage: codebuild.LinuxBuildImage.AMAZON_LINUX_2_5,
computeType: codebuild.ComputeType.SMALL,
environmentVariables: {
XX: {
type: codebuild.BuildEnvironmentVariableType.PLAINTEXT,
value: '<value>',
},
},
},
});
ここで buildspec.yml ファイルを指定します。このファイルにはビルドコマンドと CodeBuild プロジェクトの設定が記述されており、アプリケーションコードと共にバージョン管理が可能です。
4. パイプラインの定義
CodePipeline の3つのステージ(ソース、ビルド、デプロイ)を設定します。これには、前述の S3 バケットと IAM ロールを使用します。
const pipeline = new codepipeline.Pipeline(this, '<construct id>', {
pipelineName: '<pipelineName>',
artifactBucket: assetBucket,
role: codepipelineRole,
pipelineType: codepipeline.PipelineType.V1,
});
5. ソースステージ
GitHub リポジトリからコードを取得するため、Webhook を使用してソースステージを構成します。GitHub OAuth トークンは AWS Secrets Manager を介して安全に取得します。
const sourceAction = new codepipeline_actions.GitHubSourceAction({
actionName: 'GitHubSource',
owner: '<GitHub account user>',
repo: '<GitHub repository>',
branch: '<branch>',
oauthToken: cdk.SecretValue.secretsManager('github-token', {
jsonField: 'oauthToken',
}),
output: sourceOutput,
trigger: codepipeline_actions.GitHubTrigger.WEBHOOK,
runOrder: 1,
});
説明:
cdk.SecretValue.secretsManager: AWS Secrets Manager に保存されたトークンを安全に取得します。'github-token': Secrets Manager に保存された秘密情報の名前です。この情報には OAuth トークンが含まれています。jsonField: 'oauthToken': OAuth トークンが保存されている Secrets Manager のフィールド名です。
この方法を使用することで、OAuth トークンのような機密情報がアプリケーション内にハードコードされることを防ぎ、セキュリティを強化します。
6. ビルドステージ
ビルドステージでは、前述の CodeBuild プロジェクトを実行します。
const buildAction = new codepipeline_actions.CodeBuildAction({
actionName: 'Build',
project: buildProject,
input: sourceOutput,
outputs: [buildOutput],
role: codepipelineRole,
runOrder: 2,
});
7. デプロイステージ
デプロイステージでは、CloudFormation アクションを使用してスタックを作成または更新します。
const deployAction = new codepipeline_actions.CloudFormationCreateUpdateStackAction({
actionName: 'CloudFormation-CreateUpdateStack',
stackName: '<stackName>',
adminPermissions: true,
templatePath: buildOutput.atPath('cloudformation-template.yaml'),
deploymentRole: codepipelineRole,
replaceOnFailure: true,
role: codepipelineRole,
runOrder: 3,
});
この例では、cloudformation-template.yaml ファイルがビルドステージでcdk synthコマンドにより生成されたファイルです。
8.ステージをCodePipelineに追加
最後に、各ステージを CodePipeline に追加します。
pipeline.addStage({
stageName: 'Source',
actions: [sourceAction],
});
pipeline.addStage({
stageName: 'Build',
actions: [buildAction],
});
pipeline.addStage({
stageName: 'Deploy',
actions: [deployAction],
});
全体のソース
import * as cdk from 'aws-cdk-lib';
import * as codebuild from 'aws-cdk-lib/aws-codebuild';
import * as codepipeline from 'aws-cdk-lib/aws-codepipeline';
import * as codepipeline_actions from 'aws-cdk-lib/aws-codepipeline-actions';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as s3 from 'aws-cdk-lib/aws-s3';
import type { Construct } from 'constructs';
export class CodePipelineStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
/** asset bucket */
const assetBucket = new s3.Bucket(this, `<construct id>`, {
bucketName: `<bucketName>`,
lifecycleRules: [
{
id: 'DeleteAfter7Days',
enabled: true,
expiration: cdk.Duration.days(7),
},
],
});
const codepipelineRole = iam.Role.fromRoleArn(this, `<construct id>`, '<CODE_PIPELINE_ROLE_ARN>', {
mutable: false,
});
const buildProject = new codebuild.PipelineProject(this, `<construct id>`, {
projectName: `<projectName>`,
role: codepipelineRole,
queuedTimeout: cdk.Duration.hours(0.5),
timeout: cdk.Duration.hours(0.5),
buildSpec: codebuild.BuildSpec.fromSourceFilename('buildspec.yml'),
environment: {
buildImage: codebuild.LinuxBuildImage.AMAZON_LINUX_2_5,
computeType: codebuild.ComputeType.SMALL,
environmentVariables: {
XX: {
type: codebuild.BuildEnvironmentVariableType.PLAINTEXT,
value: '<value>',
},
},
},
});
const pipeline = new codepipeline.Pipeline(this, `<construct id>`, {
pipelineName: `<pipelineName>`,
artifactBucket: assetBucket,
role: codepipelineRole,
pipelineType: codepipeline.PipelineType.V1,
});
const sourceOutput = new codepipeline.Artifact();
const buildOutput = new codepipeline.Artifact();
const sourceAction = new codepipeline_actions.GitHubSourceAction({
actionName: 'GitHubSource',
owner: '<GitHub account user>',
repo: '<GitHub repository>',
branch: '<branch>',
oauthToken: cdk.SecretValue.secretsManager('github-token', {
jsonField: 'oauthToken',
}),
output: sourceOutput,
trigger: codepipeline_actions.GitHubTrigger.WEBHOOK,
runOrder: 1,
});
const buildAction = new codepipeline_actions.CodeBuildAction({
actionName: 'Build',
project: buildProject,
input: sourceOutput,
outputs: [buildOutput],
role: codepipelineRole,
runOrder: 2,
});
const deployAction = new codepipeline_actions.CloudFormationCreateUpdateStackAction({
actionName: 'CloudFormation-CreateUpdateStack',
stackName: `<stackName>`,
adminPermissions: true,
templatePath: buildOutput.atPath(`cloudformation-template.yaml`),
deploymentRole: codepipelineRole,
replaceOnFailure: true,
role: codepipelineRole,
runOrder: 3,
});
pipeline.addStage({
stageName: 'Source',
actions: [sourceAction],
});
pipeline.addStage({
stageName: 'Build',
actions: [buildAction],
});
pipeline.addStage({
stageName: 'Deploy',
actions: [deployAction],
});
}
}
結論
この例では、AWS CDK を使用して強力な CI/CD CodePipeline を構築する方法を示しました。GitHub との統合により、コミットごとに自動で CodePipeline をトリガーできます。さらに、CloudFormation によるデプロイにより、インフラが常に最新の状態で維持されます。
この CodePipeline を活用することで、デプロイプロセスを簡素化し、アプリケーションの新機能開発に集中できます。