🙄

Projen Pipelines に入門してみた

に公開

はじめに

こんばんは。Penetrator プロダクト開発部で主にインフラを担当している遠藤です。

みなさん元気に CI/CD やっていますか??
先日の AWS Summit Japan 2025 のセッションで Projen Pipelines なるものが紹介されていたので、今回こちらに入門してみました。
パッと調べた限りあまり情報がなさそうでしたので (特に日本語のものは)、興味がある方はぜひ参考にしてみてください!

Projen Pipelines とは?

Projen Pipelinesは、AWS CDK の開発者によって作られたプロジェクト設定ツール「Projen」を利用し、CI/CD パイプラインの自動生成を実現するオープンソースプロジェクトです。アプリケーション向けの継続的デリバリー (CD) パイプラインを高レベルで抽象化して定義できます。

  • 特徴
    • CI/CD パイプラインのコード生成を自動化
    • GitHub Actions や GitLab CI など複数の CI/CD プラットフォームをサポート
    • パイプライン設定における実績のあるデフォルト設定を標準搭載
    • パイプライン設定を書き換えずにプラットフォーム間の切り替えが容易
    • 複雑なデプロイシナリオを少ないコードで扱える
  • 導入メリット
    • パイプライン設定の記述や保守における反復作業を削減
    • 実績のあるデフォルト設定によりプロジェクト間での一貫性を確保
    • パイプライン定義の抽象化により、GitHub から GitLab などプラットフォーム移行を円滑化

これにより、開発チームは CI/CD パイプラインの構築・管理を効率的かつ標準化された形で実施できるようになります。

※ 公式 GitHub Repository
https://github.com/open-constructs/projen-pipelines

CI/CD パイプラインの作成・実行手順

今回は AWS CDK 用の CI/CD パイプラインを作成し、実際に Deploy してみます。
※ 手順内の Construct ID 等の命名は適当です。最小権限の原則も気にせず進めています。あしからず。

  1. AWS CDK プロジェクトの作成
    まず、今回の検証を実施する AWS CDK プロジェクトを作成します。

    Terminal
    mkdir projen-pipelines && \
    cd projen-pipelines && \
    npx projen new awscdk-app-ts
    

    上記のコマンドを実行すると、下記のようなディレクトリやファイルが生成されます。

    projen-pipelines/
    ├── .github/
    │   ├── workflows/
    │   │   ├── build.yml
    │   │   ├── pull-request-lint.yml
    │   │   └── upgrade.yml
    │   └── pull_request_template.md
    ├── .projen/
    │   ├── deps.json
    │   ├── files.json
    │   └── tasks.json
    ├── node_modules/
    ├── src/
    │   └── main.ts
    ├── test/
    │   └── main.test.ts
    ├── .eslintrc.json
    ├── .gitattributes
    ├── .gitignore
    ├── .mergify.yml
    ├── .npmignore
    ├── .projenrc.ts
    ├── cdk.json
    ├── LICENSE
    ├── package.json
    ├── README.md
    ├── tsconfig.dev.json
    ├── tsconfig.json
    └── yarn.lock
    

    cdk init コマンドで AWS CDK プロジェクトを初期化する場合と少し構造が異なりますが、src/main.ts が CDK のエントリポイントです。

    src/main.ts
    import { App, Stack, StackProps } from 'aws-cdk-lib';
    import { Construct } from 'constructs';
    
    export class MyStack extends Stack {
      constructor(scope: Construct, id: string, props: StackProps = {}) {
        super(scope, id, props);
    
        // define resources here...
      }
    }
    
    // for development, use account/region from cdk cli
    const devEnv = {
      account: process.env.CDK_DEFAULT_ACCOUNT,
      region: process.env.CDK_DEFAULT_REGION,
    };
    
    const app = new App();
    
    new MyStack(app, 'projen-pipelines-dev', { env: devEnv });
    // new MyStack(app, 'projen-pipelines-prod', { env: prodEnv });
    
    app.synth();
    
  2. projen-pipelines のインストール
    続いて、projen-pipelines を依存関係に追加します。

    .projenrc.ts
    import { awscdk } from 'projen';
    const project = new awscdk.AwsCdkTypeScriptApp({
      cdkVersion: '2.1.0',
      defaultReleaseBranch: 'main',
      name: 'projen-pipelines',
      projenrcTs: true,
    
      devDeps: ['projen-pipelines'],  // 追加
    });
    project.synth();
    

    設定の変更を適用します。

    Terminal
    npx projen
    

    上記のコマンドを実行すると、ライブラリのインストールと同時に package.json や tasks.json 等の設定ファイルにも変更が適用されます。

  3. IAM Role の作成
    続いて、CI/CD パイプラインで使用する IAM Role を定義します。

    src/main.ts
    import { App, CfnOutput, RemovalPolicies, Stack, StackProps } from 'aws-cdk-lib';
    import { Construct } from 'constructs';
    import * as iam from 'aws-cdk-lib/aws-iam';
     
    export class MyStack extends Stack {
      constructor(scope: Construct, id: string, props: StackProps = {}) {
        super(scope, id, props);
     
        const githubRepository = '<Org>/<Repository>';  // 自身の環境に合わせて修正
     
        const githubProvider = new iam.OpenIdConnectProvider(this, 'GitHubProvider', {
          url: 'https://token.actions.githubusercontent.com',
          clientIds: ['sts.amazonaws.com'],
          thumbprints: ['6938fd4d98bab03faadb97b34396831e3780aea1'],
        });
     
        const githubActionsPolicy = new iam.ManagedPolicy(this, 'GitHubActionsPolicy', {
          statements: [
            new iam.PolicyStatement({
              actions: ['*'],
              resources: ['*'],
            }),
          ],
        });
     
        const githubActionsRole = new iam.Role(this, 'GitHubActionsRole', {
          assumedBy: new iam.FederatedPrincipal(
            githubProvider.openIdConnectProviderArn,
            {
              "StringEquals": {
                "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
              },
              "StringLike": {
                "token.actions.githubusercontent.com:sub": `repo:${githubRepository}:*`
              },
            },
            "sts:AssumeRoleWithWebIdentity",
          ),
          managedPolicies: [githubActionsPolicy],
        });
     
        new CfnOutput(this, 'GitHubActionsRoleArn', {
          value: githubActionsRole.roleArn,
          exportName: 'GitHubActionsRoleArn',
        });
      }
    }
     
    // for development, use account/region from cdk cli
    const devEnv = {
      account: process.env.CDK_DEFAULT_ACCOUNT,
      region: process.env.CDK_DEFAULT_REGION,
    };
     
    const app = new App();
     
    const myStack = new MyStack(app, 'MyStack', { env: devEnv });
    RemovalPolicies.of(myStack).destroy()
    // new MyStack(app, 'projen-pipelines-prod', { env: prodEnv });
     
    app.synth();
    

    こちらは手動で作成します。
    ※ profile の設定をしている場合は適宜オプションを使用して実行してください。

    Terminal
    cdk deploy MyStack
    
  4. CI/CD パイプラインの作成
    いよいよ、CI/CD パイプラインを作成していきます。
    ※ 今回は GitHub Actions を利用する想定で定義していきます。

    .projenrc.ts
    import { awscdk } from 'projen';
    import { GithubCDKPipeline } from 'projen-pipelines';
    
    const project = new awscdk.AwsCdkTypeScriptApp({
      cdkVersion: '2.1.0',
      defaultReleaseBranch: 'main',
      name: 'projen-pipelines',
      projenrcTs: true,
    
      devDeps: ['projen-pipelines'],
    });
    
    new GithubCDKPipeline(project, {
      iamRoleArns: {
        default: 'arn:aws:iam::<AccountId>:role/MyStack-GitHubActionsRolexxxxxxxxxxxxxxxx',  // 手順 3 で作成した Role の ARN
      },
      stages: [
        {
          name: 'dev',
          env: { account: '<AccountId>', region: 'ap-northeast-1' },  // dev 環境として Deploy したい AWS Account ID/Region
        },
      ],
    });
    
    project.synth();
    

    定義ができたら、再度変更を適用します。

    Terminal
    npx projen
    

    上記のコマンドを実行すると、GitHub Actions Workflow ファイルを含む各種ファイルが生成・更新されます。

    .github/workflows/deploy.yml
    # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen".
    
    name: deploy
    on:
      push:
        branches:
          - main
      workflow_dispatch: {}
    jobs:
      synth:
        name: Synth CDK application
        needs: []
        runs-on: ubuntu-latest
        permissions:
          contents: read
        env:
          CI: "true"
        steps:
          - uses: actions/setup-node@v4
            with:
              node-version: "20"
          - name: Checkout
            uses: actions/checkout@v4
          - run: npx projen install:ci
          - run: npx projen build
          - name: Upload Artifact
            uses: actions/upload-artifact@v4.3.6
            with:
              name: cloud-assembly
              path: cdk.out/
      assetUpload:
        name: Publish assets to AWS
        needs: synth
        runs-on: ubuntu-latest
        permissions:
          id-token: write
          contents: read
        env:
          CI: "true"
        steps:
          - uses: actions/setup-node@v4
            with:
              node-version: "20"
          - name: Checkout
            uses: actions/checkout@v4
            with:
              fetch-depth: 0
          - run: git config --global user.name "github-actions" && git config --global user.email "github-actions@github.com"
          - name: Download Artifact
            uses: actions/download-artifact@v4
            with:
              name: cloud-assembly
              path: cdk.out/
          - run: npx projen install:ci
          - name: AWS Credentials
            uses: aws-actions/configure-aws-credentials@v4
            with:
              role-to-assume: arn:aws:iam::<AccountId>:role/MyStack-GitHubActionsRolexxxxxxxxxxxxxxxx
              role-session-name: GitHubAction
              aws-region: us-east-1
          - run: npx projen publish:assets
      deploy-dev:
        name: Deploy stage dev to AWS
        needs: assetUpload
        runs-on: ubuntu-latest
        permissions:
          contents: read
          id-token: write
        concurrency:
          group: deploy-dev
          cancel-in-progress: false
        env:
          CI: "true"
        steps:
          - uses: actions/setup-node@v4
            with:
              node-version: "20"
          - name: Checkout
            uses: actions/checkout@v4
          - name: Download Artifact
            uses: actions/download-artifact@v4
            with:
              name: cloud-assembly
              path: cdk.out/
          - run: npx projen install:ci
          - name: AWS Credentials
            uses: aws-actions/configure-aws-credentials@v4
            with:
              role-to-assume: arn:aws:iam::<AccountId>:role/MyStack-GitHubActionsRolexxxxxxxxxxxxxxxx
              role-session-name: GitHubAction
              aws-region: ap-northeast-1
          - run: npx projen deploy:dev
          - name: Upload Artifact
            uses: actions/upload-artifact@v4.3.6
            with:
              name: cdk-outputs-dev
              path: cdk-outputs-dev.json
    
  5. サンプルリソースの作成
    ここでは、CI/CD パイプラインを通して Deploy するためのサンプルリソースを作成します。
    今回は簡単な S3 Bucket を定義します。

    src/stack.ts
    import { Stack, StackProps } from 'aws-cdk-lib';
    import { Construct } from 'constructs';
    import * as s3 from 'aws-cdk-lib/aws-s3';
    
    
    export class BackendStack extends Stack {
      constructor(scope: Construct, id: string, props: StackProps = {}) {
        super(scope, id, props);
    
        new s3.Bucket(this, 'MyBucket');
      }
    }
    
  6. Deploy 設定の適用と Deploy 実行
    次に、手順 5 で作成したサンプル Stack を CI/CD パイプラインに載せて Deploy するよう設定します。

    src/main.ts
    import { App, CfnOutput, RemovalPolicies, Stack, StackProps } from 'aws-cdk-lib';
    import { Construct } from 'constructs';
    import * as iam from 'aws-cdk-lib/aws-iam';
    
    import { PipelineApp } from './app'
    import { BackendStack } from './stack'
    
    
    export class MyStack extends Stack {
      constructor(scope: Construct, id: string, props: StackProps = {}) {
        super(scope, id, props);
    
        ... 省略 ...
    
        // for development, use account/region from cdk cli
        const devEnv = {
          account: process.env.CDK_DEFAULT_ACCOUNT,
          region: process.env.CDK_DEFAULT_REGION,
        };
    
        // Deploy 設定適用
        const app = new PipelineApp({
          provideDevStack: (scope, id, props) => {
            return new BackendStack(scope, id, props);
          },
        });
    
        new MyStack(app, 'MyStack', { env: devEnv });
    
        // app 全体に RemovalPolicy を設定
        RemovalPolicies.of(app).destroy()
    
    app.synth();
    

    最後に、これまでのコード変更を Commit し、GitHub に Push します。
    ※ Commit コマンドは記載していないので、各自実行してください。

    Terminal
    git remote add origin https://github.com/<Org>/<Repository>.git && \
    git push origin main
    

    以上で作業は完了です。

    今回は main branch への Push をトリガーに workflow が起動する定義になっているため、GitHub の Repositoy 画面で、deploy の Workflow が正常に実行され、AWS 環境に S3 Bucket が Deploy されている事を確認できれば成功です!

まとめ

本記事では、Projen Pipelines を利用した CI/CD パイプラインの構築・実行方法 を紹介しました。
かなり少ないコードで workflow を定義できて、使いこなせればかなり便利かもしれないと思います。
CI/CD パイプラインの構築方法に迷っている方、Projen Pipelines に興味がある方の参考になれば嬉しいです。
もっと良い方法があれば、ぜひコメント等で教えてください!

※ 今回の検証コード
https://github.com/yendoooo/projen-pipelines

最後に

株式会社 Penetrator はシリーズ A ラウンドにおいて総額 5.5 億円の資金調達を実施し、不動産テック業界における更なる成長を目指して、採用活動を一層強化しています。エンジニア、デザイナー、カスタマーサクセス、BizDev、営業、マーケティングなど、事業拡大を支える多様なポジションで共に挑戦していただける方を待っています!

▽ 会社のカルチャーを知りたい方はこちら

https://www.wantedly.com/companies/company_9924832

▽ 募集職種を知りたい方はこちら

https://hrmos.co/pages/where/jobs

Discussion