AWS CDK を使用して GitHub と統合した CodePipeline の作成例

公開日 2025年1月15日
更新日 2025年1月17日
20 分で読める
AWS

イントロダクション

本記事では、AWS CDK を使用して AWS CodePipeline を作成する方法について説明します。この CodePipeline には、GitHub とのソースコード統合、CodeBuild を使用したアプリケーションのビルド、および CloudFormation によるリソースのデプロイが含まれます。また、CodePipeline のビルドアーティファクトを格納するためのカスタム S3 バケットも設定します。


前提条件

開始する前に、以下を準備してください:

  1. AWS CDK がインストールされ、設定されていること。
  2. アプリケーションコードと buildspec.yml ファイルを含む GitHub リポジトリを所有していること。
  3. GitHub OAuth トークンが AWS Secrets Manager に保存されていること。
  4. CodePipeline と CodeBuild 用に適切な IAM ロールが設定されていること。

主要な部分

この CodePipeline は以下の3つのステージで構成されます:

  1. ソースステージ: GitHub リポジトリからコードを取得します。
  2. ビルドステージ: CodeBuild を使用してアプリケーションをビルドします。
  3. デプロイステージ: 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 を活用することで、デプロイプロセスを簡素化し、アプリケーションの新機能開発に集中できます。

概要

技術的洞察、経験、思考を共有する個人ブログ

クイックリンク

お問い合わせ

  • Email: hushukang_blog@proton.me
  • GitHub

© 2025 CODE赤兎. 無断転載禁止