Open9

CDK Pipelinesの調査メモ

https://docs.aws.amazon.com/cdk/api/latest/docs/pipelines-readme.html
  • original APIとmodern APIがある
    • original API: CdkPipelineコンストラクタ
    • modern API: CodePipelineコンストラクタ
  • modern APIのほうが、デフォルトがいい感じ、拡張性に優れている、並列デプロイのサポート、複数のsynth inputsをサポート、CodeBuildをより制御できる
  • なるべくmodern APIに移行してね、とのこと

At a glance

  • アプリのCDを開始するには、まずStageのサブクラスを作る
    • コンストラクタ内でアプリケーションのStackをインスタンス化する
  • CodePipelineのインスタンスを作って、作りたい環境の分だけpipeline.addStage(stage)する
  • self-mutating
    • StackやStageを書き換えると自身(Pipeline)を書き換えて、変更を反映する
    • (そりゃそうやろと思ったが、self-mutatingみたいな名前つけるほどのことなのかよく理解できていない)

CDK Versioning

prereleaseの機能を使うので、以下の設定をcdk.jsonに追記する

{
  // ...
  "context": {
    "@aws-cdk/core:newStyleStackSynthesis": true
  }
}

Provisioning the pipeline

  • cdk deployの前に必ずgit commit & git pushすること
  • cdk deploy PipelineStack
    • 1度目だけ管理者権限が必要。それ以降は不要なので、すぐ管理者権限を取り除いたほうがいい

Working on the pipeline

  • self-mutation機能はパイプライン開発の邪魔になることもあるので、selfMutation: falseでself-mutatingを無効化できるので、開発時に使ってね、とのこと。

Defining the pipeline

Synth and sources

  • CodePipelineコンストラクタは、synthパラメタを引数に取る
    • cdk synth後のcdk.outディレクトリに吐かれるコンテンツを生成するためのもの
  • ShellStepコンストラクタ
    • commandsに出力するためのコマンドを配列で書く
    • cdkのディレクトリがリポジトリの直下にない場合は、commandscd ディレクトリしつつ、primaryOutputDirectorycdk.outのパスを別途指定する

CodePipeline Sources

  • ソースの置き場所を指定。CodePipelineクラスのファクトリメソッドを使う
// connectionを使う例(推奨)
// AWSコンソールで認証する
CodePipelineSource.connection('org/repo', 'branch', {
  connectionArn: 'arn:aws:codestar-connections:us-east-1:222222222222:connection/7d2469ff-514a-4e4f-9003-5ca4a43cdc41',
});

// Githubで発行したトークンを使う
// トークンはrepo, admin:repo_hookスコープを持っている必要がある
CodePipelineSource.gitHub('org/repo', 'branch', {
  // This is optional
  authentication: SecretValue.secretsManager('my-token'),
});

Additional inputs

  • additionalInputsに追加の入力を指定できる
    • ShellStep or CodePipelineSource
  • (CDKのコードと、アプリケーションのコードを分離する、みたいなことができちゃう感じ?)

CDK application deployments

  • Stageの追加
    • Stage: ターゲットとなる環境にデプロイされる対象
  • pipeline.addState(Stage)で追加できる
// Do this as many times as necessary with any account and region
// Account and region may different from the pipeline's.
pipeline.addStage(new MyApplicationStage(this, 'Prod', {
  env: {
    account: '123456789012',
    region: 'eu-west-1',
  }
}));
  • デプロイ先環境が別アカウントの場合、そのアカウントのbootstrapが事前に必要

Deploying in parallel

  • デフォルトでは各Stageは直列にデプロイされる
  • 並列でデプロイしたい場合は、addWaveを使う
const europeWave = pipeline.addWave('Europe');
europeWave.addStage(new MyApplicationStage(this, 'Ireland', {
  env: { region: 'eu-west-1' }
}));
europeWave.addStage(new MyApplicationStage(this, 'Germany', {
  env: { region: 'eu-central-1' }
}));

Deploying to other accounts / encrypting the Artifact Bucket

  • 別リージョン・アカウントへの透過的に実行するけど、CodePipelineコンストラクタにcrossAccountKeys: trueを付ける必要がある
    • artifact bucketの暗号化を有効にする設定
  • (鍵の生成等は勝手にやってくれる感じ?)

Validation

  • addStage, addWavepre, postオプション
    • stage, waveの前後で任意のアクションを実行できる
  • Validationを追加するのに使うといい
  • ManualApprovalSteppreに置くことで、手動による承認ステップを挟むことができる

Using CloudFormation Stack Outputs in approvals

  • CloudFormation Stackの出力を承認時に利用可能
  • ShellStepのenvFromCfnOutputsで指定できる
class MyApplicationStage extends Stage {
  public readonly loadBalancerAddress: CfnOutput;
  // ...
}

const lbApp = new MyApplicationStage(this, 'MyApp', { /* ... */ });
pipeline.addStage(lbApp, {
  post: [
    new ShellStep('HitEndpoint', {
      envFromCfnOutputs: {
        // Make the load balancer address available as $URL inside the commands
        URL: lbApp.loadBalancerAddress,
      },
      commands: ['curl -Ssf $URL'],
    });
  ],
});

Running scripts compiled during the synth step

  • synthの出力をstageのpre/postで使う方法について
  • synthの出力だけじゃなくて、他のビルドステップの出力を利用できる
const synth = new ShellStep('Synth', { /* ... */ });
const pipeline = new CodePipeline(this, 'Pipeline', { synth });

pipeline.addStage(/* ... */, {
  post: [
    new ShellStep('Approve', {
      // Use the contents of the 'integ' directory from the synth step as the input
      input: synth.addOutputDirectory('integ'),
      commands: ['cd integ && ./run.sh'],
    }),
  ],
});

ちょっと長くなったので、この後は分割して投稿する

Customizing CodeBuild Projects

  • CDK pipelinesはShellStepごとにCodeBuildのプロジェクトを生成している
  • CdeBuildStepコンストラクタを使えば、より細かく設定できる
  • また、CodePipeinecodeBuildDefaultsにCodeBuildのデフォルト値を設定できる

CDK Environment Bootstrapping

  • environment(環境) = account + region
  • 最低2つの環境: Pipelineがプロビジョンされる環境、アプリケーションがデプロイされる環境
    • 2つの環境が同一ということもある
    • 分けるのが推奨
  • 環境は、bootstrapが必要
  • 2つの環境が別の場合、両方にbootstrapが必要 & trust releationshipの追加が必要
  • CDK PipelineはクロスアカウントのCI/CDに対応しているモダンなbootstrapが必要
    • cdk.json"@aws-cdk/core:newStyleStackSynthesis": trueがあり、cdk.jsonと同一ディレクトリでcdk bootstrapする場合は、自動的にモダンなものにスイッチする
    • そうでない場合、CDK_NEW_BOOTSTRAP=1をつけて、cdk bootstrapする
# アカウント = 111111111111, region = us-east-1のbootstrap
$ env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap \
    [--profile admin-profile-1] \
    --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \
    aws://111111111111/us-east-1

アプリケーションのデプロイ先の別の環境をbootstrapする場合、--trustにPipelineのアカウントを指定

# アカウント = 222222222222, region = us-east-1のbootstrap
# アカウント = 111111111111のPipelineからデプロイするので--trust 111111111111を付けている
$ env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap \
    [--profile admin-profile-2] \
    --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \
    --trust 11111111111 \
    aws://222222222222/us-east-2

Vpc.fromLookup()等のlookupしたい場合は、--trust-for-lookup Pipelineのアカウントを付ける

$ env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap \
    [--profile admin-profile-2] \
    --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \
    --trust-for-lookup 11111111111 \
    aws://222222222222/us-east-2
  • --trust付ける場合、Pipeline側のアカウントはデプロイ先のアカウントに対して--cloudformation-execution-policiesで指定した権限を持つことになるので、注意
  • 管理者権限はbootstrap時、最初のPipelineのデプロイ時だけ使用して、あとは使わない
  • AdministratorAccessの利用について
    • PipelineのAdministratorAccessはすべてのAWSリソースをデプロイ可能にするために付けている
    • CDKのコード全てが信頼できる状態じゃないと、変なリソースをデプロイされたりするので、依存関係はちゃんと管理する。また、適切なポリシーでAdministratorAccessを利用する
    • CDK実行ロールにパーミッションバウンダリーを実装すること

Context Lookups

  • 有効なAZの数、Route53 Hosted Zone IDなど、cdk synth時に自動で調べる
  • cdk bootstrap --trust-for-lookup=<account>

Security Considerations

Confirm permissions broadening

  • stageのデプロイ前にcdk diffで新たにIAM権限の付与等が行われていないか確認したい
  • ConfirmPermissionsBroadeningをstageのpreに挟む
    • cdk diffの結果、何も無ければ自動でOK
    • なにかあれば、マニュアルの承認が必要
const stage = new MyApplicationStage(this, 'MyApplication');
pipeline.addStage(stage, {
  pre: [
    new ConfirmPermissionsBroadening('Check', { stage }),
  ],
});

SNSトピックを付けることもできる

import * as sns from '@aws-cdk/aws-sns';
import * as subscriptions from '@aws-cdk/aws-sns-subscriptions';
import * as pipelines from '@aws-cdk/pipelines';

const topic = new sns.Topic(this, 'SecurityChangesTopic');
topic.addSubscription(new subscriptions.EmailSubscription('test@email.com'));

const stage = new MyApplicationStage(this, 'MyApplication');
pipeline.addStage(stage, {
  pre: [
    new ConfirmPermissionsBroadening('Check', {
      stage,
      notificationTopic: topic,
    }),
  ],
});

ここまでの疑問点

  • --trustを付けた場合の適切な--cloudformation-execution-policiesのポリシーはなに?
    • 結局管理者権限にならない?それかPowerUserみたいなやつをつければいいのかな?
  • Permission Boundaryってなに?
作成者以外のコメントは許可されていません