

這是 AWS CDK v2 開發人員指南。較舊的 CDK v1 已於 2022 年 6 月 1 日進入維護，並於 2023 年 6 月 1 日結束支援。

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用 CDK 管道的持續整合和交付 (CI/CD)
<a name="cdk-pipeline"></a>

使用 AWS Construct Library 中的 [CDK 管道](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines-readme.html)模組來設定 AWS CDK 應用程式的持續交付。當您將 CDK 應用程式的原始碼遞交至 AWS CodeCommit、GitHub 或 AWS CodeStar 時，CDK 管道可以自動建置、測試和部署您的新版本。

CDK 管道正在自我更新。如果您新增應用程式階段或堆疊，管道會自動自行重新設定以部署這些新階段或堆疊。

**注意**  
CDK 管道支援兩個 APIs。一個是 CDK 管道開發人員預覽版中提供的原始 API。另一個是現代 API，納入預覽階段期間從 CDK 客戶收到的意見回饋。本主題中的範例使用現代 API。如需兩個支援APIs 之間的差異詳細資訊，請參閱 *aws-cdk GitHub 儲存庫*中的 [CDK Pipelines 原始 API](https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/pipelines/ORIGINAL_API.md)。

## 引導您的 AWS 環境
<a name="cdk-pipeline-bootstrap"></a>

您必須先引導要部署堆疊[的環境](environments.md) AWS ，才能使用 CDK 管道。

CDK 管道至少涉及兩個環境。第一個環境是佈建管道的位置。第二個環境是您想要部署應用程式堆疊或階段的位置 （階段是相關堆疊的群組）。這些環境可以相同，但最佳實務建議是在不同的環境中隔離階段。

**注意**  
如需[AWS 引導所建立資源類型的詳細資訊，以及如何自訂引導堆疊，請參閱 CDK](bootstrapping.md) 引導。

使用 CDK 管道的持續部署需要將下列項目包含在 CDK Toolkit 堆疊中：
+ Amazon Simple Storage Service (Amazon S3) 儲存貯體。
+ Amazon ECR 儲存庫。
+ IAM 角色為管道的各個部分提供所需的許可。

CDK Toolkit 將升級您現有的引導堆疊，或視需要建立新的引導堆疊。

若要引導可佈建 AWS CDK 管道的環境，請叫用 `cdk bootstrap`，如下列範例所示。如有需要，透過 `npx`命令叫用 AWS CDK Toolkit 會暫時安裝它。如果存在 Toolkit，它也會使用目前專案中安裝的 Toolkit 版本。

 `--cloudformation-execution-policies` 指定未來 CDK 管道部署將在其中執行的政策 ARN。預設`AdministratorAccess`政策可確保您的管道可以部署每種類型的 AWS 資源。如果您使用此政策，請確定您信任組成 AWS CDK 應用程式的所有程式碼和相依性。

大多數組織對自動化可以部署的資源類型要求更嚴格的控制。請洽詢組織內適當的部門，以判斷管道應使用的政策。

如果您的預設 AWS 設定檔包含必要的身分驗證組態和 AWS 區域，您可以省略 `--profile`選項。

**Example**  

```
npx cdk bootstrap aws://<ACCOUNT-NUMBER>/<REGION> --profile <ADMIN-PROFILE> \
    --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess
```

```
npx cdk bootstrap aws://<ACCOUNT-NUMBER></REGION> --profile< ADMIN-PROFILE> ^
    --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess
```

若要引導其他環境讓管道部署 AWS CDK 應用程式，請改用下列命令。`--trust` 選項指出哪些其他帳戶應具有將 AWS CDK 應用程式部署到此環境的許可。針對此選項，指定管道 AWS 的帳戶 ID。

同樣地，如果您的預設 AWS 設定檔包含必要的身分驗證組態和 AWS 區域，您可以省略 `--profile`選項。

**Example**  

```
npx cdk bootstrap aws://<ACCOUNT-NUMBER>/<REGION> --profile <ADMIN-PROFILE> \
    --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \
    --trust <PIPELINE-ACCOUNT-NUMBER>
```

```
npx cdk bootstrap aws://<ACCOUNT-NUMBER>/<REGION> --profile <ADMIN-PROFILE> ^
    --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess ^
    --trust <PIPELINE-ACCOUNT-NUMBER>
```

**提示**  
僅使用管理登入資料來引導和佈建初始管道。之後，請使用管道本身，而不是您的本機機器來部署變更。

如果您要升級舊式引導環境，則在建立新儲存貯體時，會孤立先前的 Amazon S3 儲存貯體。使用 Amazon S3 主控台手動刪除它。

### 保護您的引導堆疊免於刪除
<a name="cdk-pipeline-protect"></a>

如果刪除引導堆疊，原本在環境中佈建以支援 CDK 部署 AWS 的資源也會遭到刪除。這會導致管道停止運作。如果發生這種情況，就沒有一般的復原解決方案。

環境開機後，請勿刪除並重新建立環境的開機堆疊。反之，請再次執行 `cdk bootstrap`命令，嘗試將引導堆疊更新為新版本。

為了防止意外刪除您的引導堆疊，建議您使用 `cdk bootstrap`命令提供 `--termination-protection`選項，以啟用終止保護。您可以在新的或現有的引導堆疊上啟用終止保護。若要進一步了解此選項，請參閱 ` --termination-protection `。

啟用終止保護後，您可以使用 AWS CLI 或 CloudFormation 主控台進行驗證。

1. 執行下列命令，在新的或現有的引導堆疊上啟用終止保護：

   ```
   $ cdk bootstrap --termination-protection
   ```

1. 使用 AWS CLI 或 CloudFormation 主控台進行驗證。以下是使用 CLI AWS 的範例。如果您修改了引導堆疊名稱，請將 取代`CDKToolkit`為您的堆疊名稱：

   ```
   $ aws cloudformation describe-stacks --stack-name <CDKToolkit> --query "Stacks[0].EnableTerminationProtection"
   true
   ```

## 初始化專案
<a name="cdk-pipeline-init"></a>

建立新的空白 GitHub 專案，並將其複製到 `my-pipeline`目錄中的工作站。（本主題中的程式碼範例使用 GitHub。 您也可以使用 AWS CodeStar 或 AWS CodeCommit.)

```
git clone <GITHUB-CLONE-URL> my-pipeline
cd my-pipeline
```

**注意**  
您可以使用應用程式主目錄`my-pipeline`的 以外的名稱。不過，如果您這麼做，您稍後必須在本主題中調整檔案和類別名稱。這是因為 AWS CDK Toolkit 會根據主目錄的名稱來建立一些檔案和類別名稱。

複製後，照常初始化專案。

**Example**  

```
$ cdk init app --language typescript
```

```
$ cdk init app --language javascript
```

```
$ cdk init app --language python
```
建立應用程式之後，也請輸入下列兩個命令。這些會啟用應用程式的 Python 虛擬環境，並安裝 AWS CDK 核心相依性。  

```
$ source .venv/bin/activate # On Windows, run `.\venv\Scripts\activate` instead
$ python -m pip install -r requirements.txt
```

```
$ cdk init app --language java
```
如果您使用的是 IDE，您現在可以開啟或匯入專案。例如，在 Eclipse 中，選擇**檔案** > **匯入** > **Maven** > **現有 Maven 專案**。確定專案設定設定為使用 Java 8 (1.8)。

```
$ cdk init app --language csharp
```
如果您使用的是 Visual Studio，請在 `src`目錄中開啟解決方案檔案。

```
$ cdk init app --language go
```
建立應用程式之後，也請輸入下列命令來安裝應用程式所需的 AWS Construct Library 模組。  

```
$ go get
```

**重要**  
請務必將您的 `cdk.json`和 `cdk.context.json` 檔案遞交至來源控制。內容資訊 （例如從 AWS 您的帳戶擷取的功能旗標和快取值） 是專案狀態的一部分。在其他環境中，這些值可能不同，這可能會導致結果發生非預期的變更。如需詳細資訊，請參閱[內容值和 AWS CDK](context.md)。

## 定義管道
<a name="cdk-pipeline-define"></a>

您的 CDK 管道應用程式將包含至少兩個堆疊：一個代表管道本身，以及一或多個代表透過它部署之應用程式的堆疊。堆疊也可以分組為階段，您可以使用這些*階段*將基礎設施堆疊的副本部署到不同的環境。目前，我們將考慮管道，並在稍後深入探索將部署的應用程式。

建構` [CodePipeline](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines.CodePipeline.html) `是代表使用 AWS CodePipeline 做為其部署引擎之 CDK 管道的建構。當您在堆疊`CodePipeline`中執行個體化時，您可以定義管道的來源位置 （例如 GitHub 儲存庫）。您也可以定義建置應用程式的命令。

例如，以下定義管道，其來源存放在 GitHub 儲存庫中。它還包含 TypeScript CDK 應用程式的建置步驟。依指示填寫 GitHub 儲存庫的相關資訊。

**注意**  
根據預設，管道會使用儲存在 Secrets Manager 中的個人存取字符，以名稱 對 GitHub 進行身分驗證`github-token`。

您也需要更新管道堆疊的執行個體化，以指定 AWS 帳戶和區域。

**Example**  
在 `lib/my-pipeline-stack.ts`（如果您的專案資料夾未命名為 ，可能會有所不同`my-pipeline`)：  

```
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { CodePipeline, CodePipelineSource, ShellStep } from 'aws-cdk-lib/pipelines';

export class MyPipelineStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const pipeline = new CodePipeline(this, 'Pipeline', {
      pipelineName: 'MyPipeline',
      synth: new ShellStep('Synth', {
        input: CodePipelineSource.gitHub('OWNER/REPO', 'main'),
        commands: ['npm ci', 'npm run build', 'npx cdk synth']
      })
    });
  }
}
```
在 `bin/my-pipeline.ts`（如果您的專案資料夾未命名為 ，可能會有所不同`my-pipeline`)：  

```
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { MyPipelineStack } from '../lib/my-pipeline-stack';

const app = new cdk.App();
new MyPipelineStack(app, 'MyPipelineStack', {
  env: {
    account: '111111111111',
    region: 'eu-west-1',
  }
});

app.synth();
```
在 `lib/my-pipeline-stack.js`（如果您的專案資料夾未命名為 ，可能會有所不同`my-pipeline`)：  

```
const cdk = require('aws-cdk-lib');
const { CodePipeline, CodePipelineSource, ShellStep } = require('aws-cdk-lib/pipelines');

 class MyPipelineStack extends cdk.Stack {
  constructor(scope, id, props) {
    super(scope, id, props);

    const pipeline = new CodePipeline(this, 'Pipeline', {
      pipelineName: 'MyPipeline',
      synth: new ShellStep('Synth', {
        input: CodePipelineSource.gitHub('OWNER/REPO', 'main'),
        commands: ['npm ci', 'npm run build', 'npx cdk synth']
      })
    });
  }
}

module.exports = { MyPipelineStack }
```
在 `bin/my-pipeline.js`（如果您的專案資料夾未命名為 ，可能會有所不同`my-pipeline`)：  

```
#!/usr/bin/env node

const cdk = require('aws-cdk-lib');
const { MyPipelineStack } = require('../lib/my-pipeline-stack');

const app = new cdk.App();
new MyPipelineStack(app, 'MyPipelineStack', {
  env: {
    account: '111111111111',
    region: 'eu-west-1',
  }
});

app.synth();
```
在 `my-pipeline/my-pipeline-stack.py`（如果您的專案資料夾未命名為 ，可能會有所不同`my-pipeline`)：  

```
import aws_cdk as cdk
from constructs import Construct
from aws_cdk.pipelines import CodePipeline, CodePipelineSource, ShellStep

class MyPipelineStack(cdk.Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        pipeline =  CodePipeline(self, "Pipeline",
                        pipeline_name="MyPipeline",
                        synth=ShellStep("Synth",
                            input=CodePipelineSource.git_hub("OWNER/REPO", "main"),
                            commands=["npm install -g aws-cdk",
                                "python -m pip install -r requirements.txt",
                                "cdk synth"]
                        )
                    )
```
在 `app.py` 中：  

```
#!/usr/bin/env python3
import aws_cdk as cdk
from my_pipeline.my_pipeline_stack import MyPipelineStack

app = cdk.App()
MyPipelineStack(app, "MyPipelineStack",
    env=cdk.Environment(account="111111111111", region="eu-west-1")
)

app.synth()
```
在 `src/main/java/com/myorg/MyPipelineStack.java`（如果您的專案資料夾未命名為 ，可能會有所不同`my-pipeline`)：  

```
package com.myorg;

import java.util.Arrays;
import software.constructs.Construct;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;
import software.amazon.awscdk.pipelines.CodePipeline;
import software.amazon.awscdk.pipelines.CodePipelineSource;
import software.amazon.awscdk.pipelines.ShellStep;

public class MyPipelineStack extends Stack {
    public MyPipelineStack(final Construct scope, final String id) {
        this(scope, id, null);
    }

    public MyPipelineStack(final Construct scope, final String id, final StackProps props) {
        super(scope, id, props);

        CodePipeline pipeline = CodePipeline.Builder.create(this, "pipeline")
             .pipelineName("MyPipeline")
             .synth(ShellStep.Builder.create("Synth")
                .input(CodePipelineSource.gitHub("OWNER/REPO", "main"))
                .commands(Arrays.asList("npm install -g aws-cdk", "cdk synth"))
                .build())
             .build();
    }
}
```
在 `src/main/java/com/myorg/MyPipelineApp.java`（如果您的專案資料夾未命名為 ，可能會有所不同`my-pipeline`)：  

```
package com.myorg;

import software.amazon.awscdk.App;
import software.amazon.awscdk.Environment;
import software.amazon.awscdk.StackProps;

public class MyPipelineApp {
    public static void main(final String[] args) {
        App app = new App();

        new MyPipelineStack(app, "PipelineStack", StackProps.builder()
            .env(Environment.builder()
                .account("111111111111")
                .region("eu-west-1")
                .build())
            .build());

        app.synth();
    }
}
```
在 `src/MyPipeline/MyPipelineStack.cs`（如果您的專案資料夾未命名為 ，可能會有所不同`my-pipeline`)：  

```
using Amazon.CDK;
using Amazon.CDK.Pipelines;

namespace MyPipeline
{
    public class MyPipelineStack : Stack
    {
        internal MyPipelineStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            var pipeline = new CodePipeline(this, "pipeline", new CodePipelineProps
            {
                PipelineName = "MyPipeline",
                Synth = new ShellStep("Synth", new ShellStepProps
                {
                    Input = CodePipelineSource.GitHub("OWNER/REPO", "main"),
                    Commands = new string[] { "npm install -g aws-cdk", "cdk synth" }
                })
            });
        }
    }
}
```
在 `src/MyPipeline/Program.cs`（如果您的專案資料夾未命名為 ，可能會有所不同`my-pipeline`)：  

```
using Amazon.CDK;

namespace MyPipeline
{
    sealed class Program
    {
        public static void Main(string[] args)
        {
            var app = new App();
            new MyPipelineStack(app, "MyPipelineStack", new StackProps
            {
                Env = new Amazon.CDK.Environment {
                    Account = "111111111111", Region = "eu-west-1" }
            });

            app.Synth();
        }
    }
}
```

```
package main

import (
	"github.com/aws/aws-cdk-go/awscdk/v2"
	codebuild "github.com/aws/aws-cdk-go/awscdk/v2/awscodebuild"
	ssm "github.com/aws/aws-cdk-go/awscdk/v2/awsssm"
	pipeline "github.com/aws/aws-cdk-go/awscdk/v2/pipelines"
	"github.com/aws/constructs-go/constructs/v10"
	"github.com/aws/jsii-runtime-go"
	"os"
)

// my CDK Stack with resources
func NewCdkStack(scope constructs.Construct, id *string, props *awscdk.StackProps) awscdk.Stack {
	stack := awscdk.NewStack(scope, id, props)

	// create an example ssm parameter
	_ = ssm.NewStringParameter(stack, jsii.String("ssm-test-param"), &ssm.StringParameterProps{
		ParameterName: jsii.String("/testparam"),
		Description:   jsii.String("ssm parameter for demo"),
		StringValue:   jsii.String("my test param"),
	})

	return stack
}

// my CDK Application
func NewCdkApplication(scope constructs.Construct, id *string, props *awscdk.StageProps) awscdk.Stage {
	stage := awscdk.NewStage(scope, id, props)

	_ = NewCdkStack(stage, jsii.String("cdk-stack"), &awscdk.StackProps{Env: props.Env})

	return stage
}

// my CDK Pipeline
func NewCdkPipeline(scope constructs.Construct, id *string, props *awscdk.StackProps) awscdk.Stack {
	stack := awscdk.NewStack(scope, id, props)

	// GitHub repo with owner and repository name
	githubRepo := pipeline.CodePipelineSource_GitHub(jsii.String("owner/repo"), jsii.String("main"), &pipeline.GitHubSourceOptions{
		Authentication: awscdk.SecretValue_SecretsManager(jsii.String("my-github-token"), nil),
	})

	// self mutating pipeline
	myPipeline := pipeline.NewCodePipeline(stack, jsii.String("cdkPipeline"), &pipeline.CodePipelineProps{
		PipelineName: jsii.String("CdkPipeline"),
		// self mutation true - pipeline changes itself before application deployment
		SelfMutation: jsii.Bool(true),
		CodeBuildDefaults: &pipeline.CodeBuildOptions{
			BuildEnvironment: &codebuild.BuildEnvironment{
				// image version 6.0 recommended for newer go version
				BuildImage: codebuild.LinuxBuildImage_FromCodeBuildImageId(jsii.String("aws/codebuild/standard:6.0")),
			},
		},
		Synth: pipeline.NewCodeBuildStep(jsii.String("Synth"), &pipeline.CodeBuildStepProps{
			Input: githubRepo,
			Commands: &[]*string{
				jsii.String("npm install -g aws-cdk"),
				jsii.String("cdk synth"),
			},
		}),
	})

	// deployment of actual CDK application
	myPipeline.AddStage(NewCdkApplication(stack, jsii.String("MyApplication"), &awscdk.StageProps{
		Env: targetAccountEnv(),
	}), &pipeline.AddStageOpts{
		Post: &[]pipeline.Step{
			pipeline.NewCodeBuildStep(jsii.String("Manual Steps"), &pipeline.CodeBuildStepProps{
				Commands: &[]*string{
					jsii.String("echo \"My CDK App deployed, manual steps go here ... \""),
				},
			}),
		},
	})

	return stack
}

// main app
func main() {
	defer jsii.Close()

	app := awscdk.NewApp(nil)

	// call CDK Pipeline
	NewCdkPipeline(app, jsii.String("CdkPipelineStack"), &awscdk.StackProps{
		Env: pipelineEnv(),
	})

	app.Synth(nil)
}

// env determines the AWS environment (account+region) in which our stack is to
// be deployed. For more information see: https://docs.aws.amazon.com/cdk/latest/guide/environments.html
func pipelineEnv() *awscdk.Environment {
	return &awscdk.Environment{
		Account: jsii.String(os.Getenv("CDK_DEFAULT_ACCOUNT")),
		Region:  jsii.String(os.Getenv("CDK_DEFAULT_REGION")),
	}
}

func targetAccountEnv() *awscdk.Environment {
	return &awscdk.Environment{
		Account: jsii.String(os.Getenv("CDK_DEFAULT_ACCOUNT")),
		Region:  jsii.String(os.Getenv("CDK_DEFAULT_REGION")),
	}
}
```

您必須手動部署管道一次。之後，管道會從原始程式碼儲存庫將自己保持在最新狀態。因此，請確定儲存庫中的程式碼是您想要部署的程式碼。檢查您的變更並推送至 GitHub，然後部署：

```
git add --all
git commit -m "initial commit"
git push
cdk deploy
```

**提示**  
現在您已完成初始部署，您的本機 AWS 帳戶不再需要管理存取權。這是因為應用程式的所有變更都會透過管道部署。您只需要將 推送到 GitHub 即可。

## 應用程式階段
<a name="cdk-pipeline-stages"></a>

若要定義可一次新增到管道的多堆疊 AWS 應用程式，請定義 的子類別` [Stage](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Stage.html) `。（這與 CDK 管道模組`CdkStage`中的不同。)

階段包含組成您應用程式的堆疊。如果堆疊之間存在相依性，堆疊會自動以正確的順序新增至管道。不依賴彼此的堆疊會平行部署。您可以呼叫 ，在堆疊之間新增相依性關係`stack1.addDependency(stack2)`。

Stages 接受預設`env`引數，這會成為其中堆疊的預設環境。（堆疊仍然可以指定自己的環境。)。

` [addStage()](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines.CodePipeline.html#addwbrstagestage-optionss) ` 使用 [Stage](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Stage.html) 的執行個體呼叫 ，將應用程式新增至管道。階段可以執行個體化並多次新增至管道，以定義 DTAP 或多區域應用程式管道的不同階段。

我們將建立包含簡單 Lambda 函數的堆疊，並將該堆疊放置在階段中。然後，我們會將階段新增至管道，以便進行部署。

**Example**  
建立新的檔案`lib/my-pipeline-lambda-stack.ts`，以保留包含 Lambda 函數的應用程式堆疊。  

```
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Function, InlineCode, Runtime } from 'aws-cdk-lib/aws-lambda';

export class MyLambdaStack extends cdk.Stack {
    constructor(scope: Construct, id: string, props?: cdk.StackProps) {
      super(scope, id, props);

      new Function(this, 'LambdaFunction', {
        runtime: Runtime.NODEJS_18_X,
        handler: 'index.handler',
        code: new InlineCode('exports.handler = _ => "Hello, CDK";')
      });
    }
}
```
建立新的檔案`lib/my-pipeline-app-stage.ts`以保留我們的階段。  

```
import * as cdk from 'aws-cdk-lib';
import { Construct } from "constructs";
import { MyLambdaStack } from './my-pipeline-lambda-stack';

export class MyPipelineAppStage extends cdk.Stage {

    constructor(scope: Construct, id: string, props?: cdk.StageProps) {
      super(scope, id, props);

      const lambdaStack = new MyLambdaStack(this, 'LambdaStack');
    }
}
```
編輯 `lib/my-pipeline-stack.ts` 以將階段新增至我們的管道。  

```
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { CodePipeline, CodePipelineSource, ShellStep } from 'aws-cdk-lib/pipelines';
import { MyPipelineAppStage } from './my-pipeline-app-stage';

export class MyPipelineStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const pipeline = new CodePipeline(this, 'Pipeline', {
      pipelineName: 'MyPipeline',
      synth: new ShellStep('Synth', {
        input: CodePipelineSource.gitHub('OWNER/REPO', 'main'),
        commands: ['npm ci', 'npm run build', 'npx cdk synth']
      })
    });

    pipeline.addStage(new MyPipelineAppStage(this, "test", {
      env: { account: "111111111111", region: "eu-west-1" }
    }));
  }
}
```
建立新的檔案`lib/my-pipeline-lambda-stack.js`，以保留包含 Lambda 函數的應用程式堆疊。  

```
const cdk = require('aws-cdk-lib');
const { Function, InlineCode, Runtime } = require('aws-cdk-lib/aws-lambda');

class MyLambdaStack extends cdk.Stack {
    constructor(scope, id, props) {
      super(scope, id, props);

      new Function(this, 'LambdaFunction', {
        runtime: Runtime.NODEJS_18_X,
        handler: 'index.handler',
        code: new InlineCode('exports.handler = _ => "Hello, CDK";')
      });
    }
}

module.exports = { MyLambdaStack }
```
建立新的檔案`lib/my-pipeline-app-stage.js`以保留我們的階段。  

```
const cdk = require('aws-cdk-lib');
const { MyLambdaStack } = require('./my-pipeline-lambda-stack');

class MyPipelineAppStage extends cdk.Stage {

    constructor(scope, id, props) {
      super(scope, id, props);

      const lambdaStack = new MyLambdaStack(this, 'LambdaStack');
    }
}

module.exports = { MyPipelineAppStage };
```
編輯 `lib/my-pipeline-stack.ts` 以將階段新增至我們的管道。  

```
const cdk = require('aws-cdk-lib');
const { CodePipeline, CodePipelineSource, ShellStep } = require('aws-cdk-lib/pipelines');
const { MyPipelineAppStage } = require('./my-pipeline-app-stage');

 class MyPipelineStack extends cdk.Stack {
  constructor(scope, id, props) {
    super(scope, id, props);

    const pipeline = new CodePipeline(this, 'Pipeline', {
      pipelineName: 'MyPipeline',
      synth: new ShellStep('Synth', {
        input: CodePipelineSource.gitHub('OWNER/REPO', 'main'),
        commands: ['npm ci', 'npm run build', 'npx cdk synth']
      })
    });

    pipeline.addStage(new MyPipelineAppStage(this, "test", {
      env: { account: "111111111111", region: "eu-west-1" }
    }));

  }
}

module.exports = { MyPipelineStack }
```
建立新的檔案`my_pipeline/my_pipeline_lambda_stack.py`，以保留包含 Lambda 函數的應用程式堆疊。  

```
import aws_cdk as cdk
from constructs import Construct
from aws_cdk.aws_lambda import Function, InlineCode, Runtime

class MyLambdaStack(cdk.Stack):
    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        Function(self, "LambdaFunction",
            runtime=Runtime.NODEJS_18_X,
            handler="index.handler",
            code=InlineCode("exports.handler = _ => 'Hello, CDK';")
        )
```
建立新的檔案`my_pipeline/my_pipeline_app_stage.py`以保留我們的階段。  

```
import aws_cdk as cdk
from constructs import Construct
from my_pipeline.my_pipeline_lambda_stack import MyLambdaStack

class MyPipelineAppStage(cdk.Stage):
    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        lambdaStack = MyLambdaStack(self, "LambdaStack")
```
編輯 `my_pipeline/my-pipeline-stack.py` 以將階段新增至我們的管道。  

```
import aws_cdk as cdk
from constructs import Construct
from aws_cdk.pipelines import CodePipeline, CodePipelineSource, ShellStep
from my_pipeline.my_pipeline_app_stage import MyPipelineAppStage

class MyPipelineStack(cdk.Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        pipeline =  CodePipeline(self, "Pipeline",
                        pipeline_name="MyPipeline",
                        synth=ShellStep("Synth",
                            input=CodePipelineSource.git_hub("OWNER/REPO", "main"),
                            commands=["npm install -g aws-cdk",
                                "python -m pip install -r requirements.txt",
                                "cdk synth"]))

        pipeline.add_stage(MyPipelineAppStage(self, "test",
            env=cdk.Environment(account="111111111111", region="eu-west-1")))
```
建立新的檔案`src/main/java/com.myorg/MyPipelineLambdaStack.java`，以保留包含 Lambda 函數的應用程式堆疊。  

```
package com.myorg;

import software.constructs.Construct;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;

import software.amazon.awscdk.services.lambda.Function;
import software.amazon.awscdk.services.lambda.Runtime;
import software.amazon.awscdk.services.lambda.InlineCode;

public class MyPipelineLambdaStack extends Stack {
    public MyPipelineLambdaStack(final Construct scope, final String id) {
        this(scope, id, null);
    }

    public MyPipelineLambdaStack(final Construct scope, final String id, final StackProps props) {
        super(scope, id, props);

        Function.Builder.create(this, "LambdaFunction")
          .runtime(Runtime.NODEJS_18_X)
          .handler("index.handler")
          .code(new InlineCode("exports.handler = _ => 'Hello, CDK';"))
          .build();

    }

}
```
建立新的檔案`src/main/java/com.myorg/MyPipelineAppStage.java`以保留我們的階段。  

```
package com.myorg;

import software.constructs.Construct;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.Stage;
import software.amazon.awscdk.StageProps;

public class MyPipelineAppStage extends Stage {
    public MyPipelineAppStage(final Construct scope, final String id) {
        this(scope, id, null);
    }

    public MyPipelineAppStage(final Construct scope, final String id, final StageProps props) {
        super(scope, id, props);

        Stack lambdaStack = new MyPipelineLambdaStack(this, "LambdaStack");
    }

}
```
編輯 `src/main/java/com.myorg/MyPipelineStack.java` 以將階段新增至我們的管道。  

```
package com.myorg;

import java.util.Arrays;
import software.constructs.Construct;
import software.amazon.awscdk.Environment;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;
import software.amazon.awscdk.StageProps;
import software.amazon.awscdk.pipelines.CodePipeline;
import software.amazon.awscdk.pipelines.CodePipelineSource;
import software.amazon.awscdk.pipelines.ShellStep;

public class MyPipelineStack extends Stack {
    public MyPipelineStack(final Construct scope, final String id) {
        this(scope, id, null);
    }

    public MyPipelineStack(final Construct scope, final String id, final StackProps props) {
        super(scope, id, props);

        final CodePipeline pipeline = CodePipeline.Builder.create(this, "pipeline")
            .pipelineName("MyPipeline")
            .synth(ShellStep.Builder.create("Synth")
                .input(CodePipelineSource.gitHub("OWNER/REPO", "main"))
                .commands(Arrays.asList("npm install -g aws-cdk", "cdk synth"))
                .build())
            .build();

        pipeline.addStage(new MyPipelineAppStage(this, "test", StageProps.builder()
            .env(Environment.builder()
                .account("111111111111")
                .region("eu-west-1")
                .build())
            .build()));
    }
}
```
建立新的檔案`src/MyPipeline/MyPipelineLambdaStack.cs`，以保留包含 Lambda 函數的應用程式堆疊。  

```
using Amazon.CDK;
using Constructs;
using Amazon.CDK.AWS.Lambda;

namespace MyPipeline
{
    class MyPipelineLambdaStack : Stack
    {
        public MyPipelineLambdaStack(Construct scope, string id, StackProps props=null) : base(scope, id, props)
        {
            new Function(this, "LambdaFunction", new FunctionProps
            {
                Runtime = Runtime.NODEJS_18_X,
                Handler = "index.handler",
                Code = new InlineCode("exports.handler = _ => 'Hello, CDK';")
            });
        }
    }
}
```
建立新的檔案`src/MyPipeline/MyPipelineAppStage.cs`以保留我們的階段。  

```
using Amazon.CDK;
using Constructs;

namespace MyPipeline
{
    class MyPipelineAppStage : Stage
    {
        public MyPipelineAppStage(Construct scope, string id, StageProps props=null) : base(scope, id, props)
        {
            Stack lambdaStack = new MyPipelineLambdaStack(this, "LambdaStack");
        }
    }
}
```
編輯 `src/MyPipeline/MyPipelineStack.cs` 以將階段新增至我們的管道。  

```
using Amazon.CDK;
using Constructs;
using Amazon.CDK.Pipelines;

namespace MyPipeline
{
    public class MyPipelineStack : Stack
    {
        internal MyPipelineStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            var pipeline = new CodePipeline(this, "pipeline", new CodePipelineProps
            {
                PipelineName = "MyPipeline",
                Synth = new ShellStep("Synth", new ShellStepProps
                {
                    Input = CodePipelineSource.GitHub("OWNER/REPO", "main"),
                    Commands = new string[] { "npm install -g aws-cdk", "cdk synth" }
                })
            });

            pipeline.AddStage(new MyPipelineAppStage(this, "test", new StageProps
            {
                Env = new Environment
                {
                    Account = "111111111111", Region = "eu-west-1"
                }
            }));
        }
    }
}
```

由 新增的每個應用程式階段`addStage()`都會新增對應的管道階段，由`addStage()`呼叫傳回的` [StageDeployment](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines.StageDeployment.html) `執行個體表示。您可以透過呼叫 或 `addPre()``addPost()`方法，將部署前或部署後動作新增至階段。

**Example**  

```
// import { ManualApprovalStep } from 'aws-cdk-lib/pipelines';

const testingStage = pipeline.addStage(new MyPipelineAppStage(this, 'testing', {
  env: { account: '111111111111', region: 'eu-west-1' }
}));

    testingStage.addPost(new ManualApprovalStep('approval'));
```

```
// const { ManualApprovalStep } = require('aws-cdk-lib/pipelines');

const testingStage = pipeline.addStage(new MyPipelineAppStage(this, 'testing', {
  env: { account: '111111111111', region: 'eu-west-1' }
}));

testingStage.addPost(new ManualApprovalStep('approval'));
```

```
# from aws_cdk.pipelines import ManualApprovalStep

testing_stage = pipeline.add_stage(MyPipelineAppStage(self, "testing",
    env=cdk.Environment(account="111111111111", region="eu-west-1")))

testing_stage.add_post(ManualApprovalStep('approval'))
```

```
// import software.amazon.awscdk.pipelines.StageDeployment;
// import software.amazon.awscdk.pipelines.ManualApprovalStep;

StageDeployment testingStage =
        pipeline.addStage(new MyPipelineAppStage(this, "test", StageProps.builder()
                .env(Environment.builder()
                        .account("111111111111")
                        .region("eu-west-1")
                        .build())
                .build()));

testingStage.addPost(new ManualApprovalStep("approval"));
```

```
var testingStage = pipeline.AddStage(new MyPipelineAppStage(this, "test", new StageProps
{
    Env = new Environment
    {
        Account = "111111111111", Region = "eu-west-1"
    }
}));

testingStage.AddPost(new ManualApprovalStep("approval"));
```

您可以將階段新增至 ` [Wave](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines.Wave.html) `以平行部署，例如將階段部署到多個帳戶或區域時。

**Example**  

```
const wave = pipeline.addWave('wave');
wave.addStage(new MyApplicationStage(this, 'MyAppEU', {
  env: { account: '111111111111', region: 'eu-west-1' }
}));
wave.addStage(new MyApplicationStage(this, 'MyAppUS', {
  env: { account: '111111111111', region: 'us-west-1' }
}));
```

```
const wave = pipeline.addWave('wave');
wave.addStage(new MyApplicationStage(this, 'MyAppEU', {
  env: { account: '111111111111', region: 'eu-west-1' }
}));
wave.addStage(new MyApplicationStage(this, 'MyAppUS', {
  env: { account: '111111111111', region: 'us-west-1' }
}));
```

```
wave = pipeline.add_wave("wave")
wave.add_stage(MyApplicationStage(self, "MyAppEU",
    env=cdk.Environment(account="111111111111", region="eu-west-1")))
wave.add_stage(MyApplicationStage(self, "MyAppUS",
    env=cdk.Environment(account="111111111111", region="us-west-1")))
```

```
// import software.amazon.awscdk.pipelines.Wave;
final Wave wave = pipeline.addWave("wave");
wave.addStage(new MyPipelineAppStage(this, "MyAppEU", StageProps.builder()
        .env(Environment.builder()
                .account("111111111111")
                .region("eu-west-1")
                .build())
        .build()));
wave.addStage(new MyPipelineAppStage(this, "MyAppUS", StageProps.builder()
        .env(Environment.builder()
                .account("111111111111")
                .region("us-west-1")
                .build())
        .build()));
```

```
var wave = pipeline.AddWave("wave");
wave.AddStage(new MyPipelineAppStage(this, "MyAppEU", new StageProps
{
    Env = new Environment
    {
        Account = "111111111111", Region = "eu-west-1"
    }
}));
wave.AddStage(new MyPipelineAppStage(this, "MyAppUS", new StageProps
{
    Env = new Environment
    {
        Account = "111111111111", Region = "us-west-1"
    }
}));
```

## 測試部署
<a name="cdk-pipeline-validation"></a>

您可以將步驟新增至 CDK 管道，以驗證您正在執行的部署。例如，您可以使用 CDK Pipeline 程式庫的 ` [ShellStep](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines.ShellStep.html) `來執行如下任務：
+ 嘗試存取由 Lambda 函數支援的新部署 Amazon API Gateway 
+ 透過發出 CLI AWS 命令來檢查已部署資源的設定

在最簡單的形式中，新增驗證動作如下所示：

**Example**  

```
// stage was returned by pipeline.addStage

stage.addPost(new ShellStep("validate", {
  commands: ['../tests/validate.sh'],
}));
```

```
// stage was returned by pipeline.addStage

stage.addPost(new ShellStep("validate", {
  commands: ['../tests/validate.sh'],
}));
```

```
# stage was returned by pipeline.add_stage

stage.add_post(ShellStep("validate",
  commands=[''../tests/validate.sh'']
))
```

```
// stage was returned by pipeline.addStage

stage.addPost(ShellStep.Builder.create("validate")
        .commands(Arrays.asList("'../tests/validate.sh'"))
        .build());
```

```
// stage was returned by pipeline.addStage

stage.AddPost(new ShellStep("validate", new ShellStepProps
{
    Commands = new string[] { "'../tests/validate.sh'" }
}));
```

Many AWS CloudFormation 部署會導致產生具有不可預測名稱的資源。因此，CDK 管道提供在部署之後讀取 AWS CloudFormation 輸出的方法。這可讓您將負載平衡器產生的 URL 傳遞 （例如） 至測試動作。

若要使用輸出，請公開您感興趣的`CfnOutput`物件。然後，在步驟的 `envFromCfnOutputs` 屬性中傳遞它，讓它在該步驟中做為環境變數使用。

**Example**  

```
// given a stack lbStack that exposes a load balancer construct as loadBalancer
this.loadBalancerAddress = new cdk.CfnOutput(lbStack, 'LbAddress', {
  value: `https://${lbStack.loadBalancer.loadBalancerDnsName}/`
});

// pass the load balancer address to a shell step
stage.addPost(new ShellStep("lbaddr", {
  envFromCfnOutputs: {lb_addr: lbStack.loadBalancerAddress},
  commands: ['echo $lb_addr']
}));
```

```
// given a stack lbStack that exposes a load balancer construct as loadBalancer
this.loadBalancerAddress = new cdk.CfnOutput(lbStack, 'LbAddress', {
  value: `https://${lbStack.loadBalancer.loadBalancerDnsName}/`
});

// pass the load balancer address to a shell step
stage.addPost(new ShellStep("lbaddr", {
  envFromCfnOutputs: {lb_addr: lbStack.loadBalancerAddress},
  commands: ['echo $lb_addr']
}));
```

```
# given a stack lb_stack that exposes a load balancer construct as load_balancer
self.load_balancer_address = cdk.CfnOutput(lb_stack, "LbAddress",
    value=f"https://{lb_stack.load_balancer.load_balancer_dns_name}/")

# pass the load balancer address to a shell step
stage.add_post(ShellStep("lbaddr",
    env_from_cfn_outputs={"lb_addr": lb_stack.load_balancer_address}
    commands=["echo $lb_addr"]))
```

```
// given a stack lbStack that exposes a load balancer construct as loadBalancer
loadBalancerAddress = CfnOutput.Builder.create(lbStack, "LbAddress")
                            .value(String.format("https://%s/",
                                    lbStack.loadBalancer.loadBalancerDnsName))
                            .build();

stage.addPost(ShellStep.Builder.create("lbaddr")
    .envFromCfnOutputs(     // Map.of requires Java 9 or later
        java.util.Map.of("lbAddr", loadBalancerAddress))
    .commands(Arrays.asList("echo $lbAddr"))
    .build());
```

```
// given a stack lbStack that exposes a load balancer construct as loadBalancer
loadBalancerAddress = new CfnOutput(lbStack, "LbAddress", new CfnOutputProps
{
    Value = string.Format("https://{0}/", lbStack.loadBalancer.LoadBalancerDnsName)
});

stage.AddPost(new ShellStep("lbaddr", new ShellStepProps
{
    EnvFromCfnOutputs = new Dictionary<string, CfnOutput>
    {
        {  "lbAddr", loadBalancerAddress }
    },
    Commands = new string[] { "echo $lbAddr" }
}));
```

您可以在 中直接撰寫簡單的驗證測試`ShellStep`，但當測試超過幾行時，這種方法變得不明確。對於更複雜的測試，您可以透過 `inputs` 屬性`ShellStep`將其他檔案 （例如完整的 shell 指令碼或其他語言的程式） 帶入 。輸入可以是具有輸出的任何步驟，包括來源 （例如 GitHub 儲存庫） 或其他 `ShellStep`。

如果檔案可在測試中直接使用 （例如，如果它們本身是可執行檔），則從來源儲存庫引入檔案是適當的。在此範例中，我們將 GitHub 儲存庫宣告為 `source`（而不是將其內嵌化為 的一部分`CodePipeline`)。然後，我們會將此檔案集傳遞給管道和驗證測試。

**Example**  

```
const source = CodePipelineSource.gitHub('OWNER/REPO', 'main');

const pipeline = new CodePipeline(this, 'Pipeline', {
  pipelineName: 'MyPipeline',
  synth: new ShellStep('Synth', {
    input: source,
    commands: ['npm ci', 'npm run build', 'npx cdk synth']
  })
});

const stage = pipeline.addStage(new MyPipelineAppStage(this, 'test', {
  env: { account: '111111111111', region: 'eu-west-1' }
}));

stage.addPost(new ShellStep('validate', {
  input: source,
  commands: ['sh ../tests/validate.sh']
}));
```

```
const source = CodePipelineSource.gitHub('OWNER/REPO', 'main');

const pipeline = new CodePipeline(this, 'Pipeline', {
  pipelineName: 'MyPipeline',
  synth: new ShellStep('Synth', {
    input: source,
    commands: ['npm ci', 'npm run build', 'npx cdk synth']
  })
});

const stage = pipeline.addStage(new MyPipelineAppStage(this, 'test', {
  env: { account: '111111111111', region: 'eu-west-1' }
}));

stage.addPost(new ShellStep('validate', {
  input: source,
  commands: ['sh ../tests/validate.sh']
}));
```

```
source   = CodePipelineSource.git_hub("OWNER/REPO", "main")

pipeline =  CodePipeline(self, "Pipeline",
                pipeline_name="MyPipeline",
                synth=ShellStep("Synth",
                    input=source,
                    commands=["npm install -g aws-cdk",
                        "python -m pip install -r requirements.txt",
                        "cdk synth"]))

stage = pipeline.add_stage(MyApplicationStage(self, "test",
            env=cdk.Environment(account="111111111111", region="eu-west-1")))

stage.add_post(ShellStep("validate", input=source,
    commands=["sh ../tests/validate.sh"],
))
```

```
final CodePipelineSource source = CodePipelineSource.gitHub("OWNER/REPO", "main");

final CodePipeline pipeline = CodePipeline.Builder.create(this, "pipeline")
        .pipelineName("MyPipeline")
        .synth(ShellStep.Builder.create("Synth")
                .input(source)
                .commands(Arrays.asList("npm install -g aws-cdk", "cdk synth"))
                .build())
        .build();

final StageDeployment stage =
        pipeline.addStage(new MyPipelineAppStage(this, "test", StageProps.builder()
                .env(Environment.builder()
                        .account("111111111111")
                        .region("eu-west-1")
                        .build())
                .build()));

stage.addPost(ShellStep.Builder.create("validate")
        .input(source)
        .commands(Arrays.asList("sh ../tests/validate.sh"))
        .build());
```

```
var source = CodePipelineSource.GitHub("OWNER/REPO", "main");

var pipeline = new CodePipeline(this, "pipeline", new CodePipelineProps
{
    PipelineName = "MyPipeline",
    Synth = new ShellStep("Synth", new ShellStepProps
    {
        Input = source,
        Commands = new string[] { "npm install -g aws-cdk", "cdk synth" }
    })
});

var stage = pipeline.AddStage(new MyPipelineAppStage(this, "test", new StageProps
{
    Env = new Environment
    {
        Account = "111111111111", Region = "eu-west-1"
    }
}));

stage.AddPost(new ShellStep("validate", new ShellStepProps
{
    Input = source,
    Commands = new string[] { "sh ../tests/validate.sh" }
}));
```

如果需要編譯測試，則從語法步驟取得其他檔案是適當的，這是合成的一部分。

**Example**  

```
const synthStep = new ShellStep('Synth', {
  input: CodePipelineSource.gitHub('OWNER/REPO', 'main'),
  commands: ['npm ci', 'npm run build', 'npx cdk synth'],
});

const pipeline = new CodePipeline(this, 'Pipeline', {
  pipelineName: 'MyPipeline',
  synth: synthStep
});

const stage = pipeline.addStage(new MyPipelineAppStage(this, 'test', {
  env: { account: '111111111111', region: 'eu-west-1' }
}));

// run a script that was transpiled from TypeScript during synthesis
stage.addPost(new ShellStep('validate', {
  input: synthStep,
  commands: ['node tests/validate.js']
}));
```

```
const synthStep = new ShellStep('Synth', {
  input: CodePipelineSource.gitHub('OWNER/REPO', 'main'),
  commands: ['npm ci', 'npm run build', 'npx cdk synth'],
});

const pipeline = new CodePipeline(this, 'Pipeline', {
  pipelineName: 'MyPipeline',
  synth: synthStep
});

const stage = pipeline.addStage(new MyPipelineAppStage(this, "test", {
  env: { account: "111111111111", region: "eu-west-1" }
}));

// run a script that was transpiled from TypeScript during synthesis
stage.addPost(new ShellStep('validate', {
  input: synthStep,
  commands: ['node tests/validate.js']
}));
```

```
synth_step = ShellStep("Synth",
                input=CodePipelineSource.git_hub("OWNER/REPO", "main"),
                commands=["npm install -g aws-cdk",
                  "python -m pip install -r requirements.txt",
                  "cdk synth"])

pipeline   = CodePipeline(self, "Pipeline",
                pipeline_name="MyPipeline",
                synth=synth_step)

stage = pipeline.add_stage(MyApplicationStage(self, "test",
            env=cdk.Environment(account="111111111111", region="eu-west-1")))

# run a script that was compiled during synthesis
stage.add_post(ShellStep("validate",
    input=synth_step,
    commands=["node test/validate.js"],
))
```

```
final ShellStep synth = ShellStep.Builder.create("Synth")
                            .input(CodePipelineSource.gitHub("OWNER/REPO", "main"))
                            .commands(Arrays.asList("npm install -g aws-cdk", "cdk synth"))
                            .build();

final CodePipeline pipeline = CodePipeline.Builder.create(this, "pipeline")
        .pipelineName("MyPipeline")
        .synth(synth)
        .build();

final StageDeployment stage =
        pipeline.addStage(new MyPipelineAppStage(this, "test", StageProps.builder()
                .env(Environment.builder()
                        .account("111111111111")
                        .region("eu-west-1")
                        .build())
                .build()));

stage.addPost(ShellStep.Builder.create("validate")
        .input(synth)
        .commands(Arrays.asList("node ./tests/validate.js"))
        .build());
```

```
var synth = new ShellStep("Synth", new ShellStepProps
{
    Input = CodePipelineSource.GitHub("OWNER/REPO", "main"),
    Commands = new string[] { "npm install -g aws-cdk", "cdk synth" }
});

var pipeline = new CodePipeline(this, "pipeline", new CodePipelineProps
{
    PipelineName = "MyPipeline",
    Synth = synth
});

var stage = pipeline.AddStage(new MyPipelineAppStage(this, "test", new StageProps
{
    Env = new Environment
    {
        Account = "111111111111", Region = "eu-west-1"
    }
}));

stage.AddPost(new ShellStep("validate", new ShellStepProps
{
    Input = synth,
    Commands = new string[] { "node ./tests/validate.js" }
}));
```

## 安全注意事項
<a name="cdk-pipeline-security"></a>

任何形式的持續交付都有固有的安全風險。在[共同責任模型](https://aws.amazon.com/compliance/shared-responsibility-model/)下 AWS ，您必須負責 AWS 雲端中資訊的安全性。CDK Pipelines 程式庫透過整合安全預設值和建模最佳實務，為您提供先機。

不過，就其本質而言，需要高度存取權才能實現其預期目的的程式庫無法保證完整的安全性。 AWS 和您的組織之外有許多攻擊媒介。

尤其請注意下列事項：
+ 請注意您依賴的軟體。審查您在管道中執行的所有第三方軟體，因為它可以變更要部署的基礎設施。
+ 使用相依性鎖定來防止意外升級。CDK Pipelines 會遵守 `package-lock.json`和 ，`yarn.lock`以確保您的相依性是您所預期的相依性。
+ CDK Pipelines 會在您自己的帳戶中建立的資源上執行，而這些資源的組態是由透過管道提交程式碼的開發人員所控制。因此，CDK 管道本身無法防範嘗試略過合規檢查的惡意開發人員。如果您的威脅模型包含編寫 CDK 程式碼的開發人員，您應該具備外部合規機制，例如 [AWS CloudFormation Hooks](https://aws.amazon.com/blogs/mt/proactively-keep-resources-secure-and-compliant-with-aws-cloudformation-hooks/) （預防性） 或 [AWS Config](https://aws.amazon.com/config/) （被動），而 AWS CloudFormation 執行角色沒有停用的許可。
+ 生產環境的登入資料應該是短期的。在引導和初始佈建之後，開發人員完全不需要擁有帳戶登入資料。變更可以透過管道部署。一開始就不需要登入資料，以減少登入資料洩漏的可能性。

## 故障診斷
<a name="cdk-pipeline-troubleshooting"></a>

開始使用 CDK 管道時，通常會遇到下列問題。

 **管道：內部故障**   

```
CREATE_FAILED  | {aws}::CodePipeline::Pipeline | Pipeline/Pipeline
Internal Failure
```
檢查您的 GitHub 存取字符。它可能遺失，或可能沒有存取儲存庫的許可。

 **金鑰：政策包含具有一或多個無效主體的陳述式**   

```
CREATE_FAILED | {aws}::KMS::Key | Pipeline/Pipeline/ArtifactsBucketEncryptionKey
Policy contains a statement with one or more invalid principals.
```
其中一個目標環境尚未使用新的引導堆疊進行引導。請確定所有目標環境都已引導。

 **堆疊處於 ROLLBACK\_COMPLETE 狀態，無法更新**   

```
Stack <STACK_NAME> is in ROLLBACK_COMPLETE state and cannot be updated. (Service:
AmazonCloudFormation; Status Code: 400; Error Code: ValidationError; Request
ID: ...)
```
堆疊的先前部署失敗，且處於無法重試的狀態。從 AWS CloudFormation 主控台刪除堆疊，然後重試部署。