

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

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

# 部署 AWS CDK 應用程式
<a name="deploy"></a>

 AWS 雲端開發套件 (AWS CDK) 部署是佈建基礎設施的程序 AWS。

## AWS CDK 部署的運作方式
<a name="deploy-how"></a>

 AWS CDK 利用 AWS CloudFormation 服務來執行部署。部署之前，您會合成 CDK 堆疊。這會為應用程式中的每個 CDK 堆疊建立 CloudFormation 範本和部署成品。部署是從本機開發機器或從*持續整合和持續交付 (CI/CD)* 環境啟動。在部署期間，資產會上傳至引導的資源，而 CloudFormation 範本會提交至 CloudFormation 來佈建您的 AWS 資源。

若要讓部署成功，需要下列項目：
+  AWS CDK 命令列界面 (AWS CDK CLI) 必須具備有效的許可。
+ 必須引導 AWS 環境。
+  AWS CDK 必須知道要上傳資產的引導資源。

## CDK 部署的先決條件
<a name="deploy-prerequisites"></a>

您必須先完成下列操作，才能部署 AWS CDK 應用程式：
+ 設定 CDK CLI 的安全登入資料。
+ 引導您的 AWS 環境。
+ 為每個 CDK 堆疊設定 AWS 環境。
+ 開發您的 CDK 應用程式。<a name="deploy-prerequisites-creds"></a>

 **設定安全登入資料**   
若要使用 CDK CLI 與 互動 AWS，您必須在本機電腦上設定安全登入資料。如需說明，請參閱[設定 AWS CDK CLI 的安全登入資料](configure-access.md)。<a name="deploy-prerequisites-bootstrap"></a>

 **引導您的 AWS 環境**   
部署一律與一或多個 AWS [環境](environments.md)相關聯。您必須先[引導](bootstrapping.md)環境，才能部署環境。引導會在您的環境中佈建資源，讓 CDK 用來執行和管理部署。這些資源包括 Amazon Simple Storage Service (Amazon S3) 儲存貯體和 Amazon Elastic Container Registry (Amazon ECR) 儲存庫，用於存放和管理[資產](assets.md)。這些資源也包含 AWS Identity and Access Management (IAM) 角色，用於在開發和部署期間提供許可。  
我們建議您使用 AWS CDK Command Line Interface (AWS CDK CLI) `cdk bootstrap`命令來引導您的環境。您可以視需要自訂引導或在環境中手動建立這些資源。如需說明，請參閱[引導您的環境以搭配 AWS CDK 使用](bootstrapping-env.md)。<a name="deploy-prerequisites-env"></a>

 **設定 AWS 環境**   
每個 CDK 堆疊都必須與環境建立關聯，以判斷堆疊的部署位置。如需說明，請參閱[設定環境以搭配 AWS CDK 使用](configure-env.md)。<a name="deploy-prerequisites-develop"></a>

 **開發您的 CDK 應用程式**   
在 CDK [專案](projects.md)中，您可以建立和開發 CDK 應用程式。在您的應用程式中，您可以建立一或多個 CDK [堆疊](stacks.md)。在堆疊中，您可以從 AWS 建構程式庫匯入和使用[建構](constructs.md)來定義基礎設施。您的 CDK 應用程式必須至少包含一個堆疊，才能部署。

## CDK 應用程式合成
<a name="deploy-how-synth"></a>

若要執行合成，建議您使用 CDK CLI `cdk synth`命令。`cdk deploy` 命令也會在啟動部署之前執行合成。不過，透過使用 `cdk synth`，您可以在啟動部署之前驗證 CDK 應用程式並擷取錯誤。

合成行為取決於您為 CDK [堆疊設定的堆疊合成器](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib-readme.html#stack-synthesizers)。如果您未設定合成器，` [DefaultStackSynthesizer](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.DefaultStackSynthesizer.html) `則會使用 。您也可以設定和自訂合成以符合您的需求。如需說明，請參閱[設定和執行 CDK 堆疊合成](configure-synth.md)。

為了讓您的合成 CloudFormation 範本成功部署到您的環境中，它必須與環境的引導方式相容。例如，您的 CloudFormation 範本必須指定要部署資產的正確 Amazon S3 儲存貯體。如果您使用預設方法引導您的環境，預設堆疊合成器將會運作。如果您自訂 CDK 行為，例如自訂引導或合成，CDK 部署行為可能會有所不同。<a name="deploy-how-synth-app"></a>

 **應用程式生命週期**   
當您執行合成時，CDK 應用程式會執行下列階段，稱為*應用程式生命週期*：    
 **建構 （或初始化）**   
您的程式碼會執行個體化所有定義的建構，然後將它們連結在一起。在此階段中，所有建構模組 （應用程式、堆疊及其子建構模組） 都會執行個體化，並執行建構模組鏈。您大部分的應用程式程式碼都會在此階段執行。  
 **準備**   
所有已實作 `prepare`方法的建構都參與最後一輪修改，以設定其最終狀態。準備階段會自動發生。身為使用者，您看不到此階段的任何意見回饋。很少需要使用「準備」勾點，通常不建議。在此階段中變更建構樹時請特別小心，因為操作順序可能會影響行為。  
在此階段，建置建構樹之後，您設定的任何[層面](aspects.md)也會套用。  
 **驗證**   
所有已實作 `validate`方法的建構可以自行驗證，以確保其處於將正確部署的狀態。您會在此階段收到任何驗證失敗的通知。一般而言，我們建議您盡快執行驗證 （通常是在您取得一些輸入時立即執行），並儘早擲出例外狀況。儘早執行驗證可改善可靠性，因為堆疊追蹤會更準確，並確保您的程式碼可以繼續安全地執行。  
 **合成**   
這是執行 CDK 應用程式的最後階段。它由呼叫 觸發`app.synth()`，它會周遊建構樹，並調用所有建構上的 `synthesize`方法。實作 的建構`synthesize`可以參與合成，並為產生的雲端組件產生部署成品。這些成品包括 CloudFormation 範本、 AWS Lambda 應用程式套件、檔案和 Docker 影像資產，以及其他部署成品。在大多數情況下，您不需要實作 `synthesize`方法。<a name="deploy-how-synth-run"></a>

 **執行您的應用程式**   
CDK CLI 需要知道如何執行您的 CDK 應用程式。如果您使用 `cdk init`命令從範本建立專案，則應用程式的 `cdk.json` 檔案會包含 `app`金鑰。此金鑰指定應用程式寫入語言的必要命令。如果您的語言需要編譯，命令列會在自動執行應用程式之前執行此步驟。  

**Example**  

```
{
  "app": "npx ts-node --prefer-ts-exts bin/my-app.ts"
}
```

```
{
  "app": "node bin/my-app.js"
}
```

```
{
    "app": "python app.py"
}
```

```
{
  "app": "mvn -e -q compile exec:java"
}
```

```
{
  "app": "dotnet run -p src/MyApp/MyApp.csproj"
}
```

```
{
  "app": "go mod download && go run my-app.go"
}
```
如果您未使用 CDK CLI 建立專案，或想要覆寫 中提供的命令列`cdk.json`，您可以在執行`cdk`命令時提供 ` --app `選項。

```
$ cdk --app '<executable>' <cdk-command> ...
```

命令的 `<executable>`部分指出執行 CDK 應用程式時應執行的命令。使用如下所示的引號，因為這類命令包含空格。`<cdk-command>` 是類似 `synth`或 的子命令`deploy`，可告訴 CDK CLI 您想要如何使用您的應用程式。請依照此執行該子命令所需的任何其他選項。

CDK CLI 也可以直接與已合成的雲端組件互動。若要這樣做，請傳遞雲端組件存放在 中的目錄`--app`。下列範例列出存放在 下的雲端組件中定義的堆疊`./my-cloud-assembly`。

```
$ cdk --app <./my-cloud-assembly> ls
```<a name="deploy-how-synth-assemblies"></a>

 **雲端組件**   
對 的呼叫`app.synth()`會指示 AWS CDK 從應用程式合成雲端組件。一般而言，您不會直接與雲端組件互動。這些檔案包含將應用程式部署至雲端環境所需的一切。例如，它包含應用程式中每個堆疊的 AWS CloudFormation 範本。它還包含您在應用程式中參考的任何檔案資產或 Docker 映像的副本。  
如需如何格式化[雲端組件的詳細資訊，請參閱雲端組件規格](https://github.com/aws/aws-cdk/blob/master/design/cloud-assembly.md)。  
若要與 AWS CDK 應用程式建立的雲端組件互動，您通常會使用 AWS CDK CLI。不過，任何可以讀取雲端組件格式的工具都可以用來部署您的應用程式。

## 部署您的應用程式
<a name="deploy-how-deploy"></a>

若要部署應用程式，建議您使用 CDK CLI `cdk deploy`命令啟動部署或設定自動化部署。

當您執行 時`cdk deploy`，CDK CLI 會啟動 `cdk synth`以準備部署。下圖說明部署內容中的應用程式生命週期：

![\[<shared id="AWS"/> CDK 應用程式生命週期的流程圖。\]](http://docs.aws.amazon.com/zh_tw/cdk/v2/guide/images/app-lifecycle_cdk-flowchart.png)


在部署期間，CDK CLI 會採用合成產生的雲端組件，並將其部署到 AWS 環境。資產會上傳至 Amazon S3 和 Amazon ECR，而 CloudFormation 範本會提交至 AWS CloudFormation 進行部署。

在 AWS CloudFormation 部署階段開始時，您的 CDK 應用程式已完成執行並結束。這具有以下含義：
+ CDK 應用程式無法回應部署期間發生的事件，例如正在建立的資源或整個部署完成。若要在部署階段執行程式碼，您必須將其插入 AWS CloudFormation 範本做為[自訂資源](cfn-layer.md#develop-customize-custom)。如需將自訂資源新增至應用程式的詳細資訊，請參閱 [AWS CloudFormation 模組](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudformation-readme.html)或[自訂資源](https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/custom-resource/)範例。您也可以設定 [Triggers](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.triggers-readme.html) 模組在部署期間執行程式碼。
+ CDK 應用程式可能需要使用執行時無法得知的值。例如，如果 AWS CDK 應用程式以自動產生的名稱定義 Amazon S3 儲存貯體，而您擷取 `bucket.bucketName`(Python：`bucket_name`) 屬性，則該值不是已部署儲存貯體的名稱。反之，您會取得`Token`值。若要判斷特定值是否可用，請呼叫 `cdk.isUnresolved(value)`(Python：`is_unresolved`)。如需詳細資訊[，請參閱權杖和 AWS CDK](tokens.md)。<a name="deploy-how-deploy-permissions"></a>

 **部署許可**   
必須先建立許可，才能執行部署。下圖說明使用預設引導程序和堆疊合成器時，預設部署期間使用的許可：  

![\[預設 <shared id="AWS"/> CDK 部署程序的流程圖。\]](http://docs.aws.amazon.com/zh_tw/cdk/v2/guide/images/default-deploy-process_cdk_flowchart.png)
  
 **演員啟動部署**   
部署是由*演員*使用 CDK CLI 啟動。演員可以是個人，也可以是 AWS CodePipeline 等服務。  
如有必要，CDK CLI 會在您執行 `cdk synth`時執行`cdk deploy`。在合成期間， AWS 身分會假設在 AWS 環境中`LookupRole`執行內容查詢。  
 **已建立許可**   
首先，演員的安全登入資料是用來對 進行身分驗證， AWS 並取得程序中的第一個 IAM 身分。對於人類執行者，如何設定和取得安全登入資料取決於您或您的組織管理使用者的方式。如需詳細資訊，請參閱[設定 AWS CDK CLI 的安全登入資料](configure-access.md)。對於 CodePipeline 等服務執行者，會擔任並使用 IAM 執行角色。  
接下來，在引導期間在您的 AWS 環境中建立的 IAM 角色會用來建立許可，以執行部署所需的動作。如需這些角色及其授予許可的詳細資訊，請參閱[引導期間建立的 IAM 角色](bootstrapping-env.md#bootstrapping-env-roles)。此程序包括下列項目：  
+  AWS 身分會擔任`DeploymentActionRole`角色並將`CloudFormationExecutionRole`角色傳遞給 CloudFormation，確保 CloudFormation 在您的 AWS 環境中執行任何動作時擔任該角色。 `DeploymentActionRole`會授予許可，以在您的環境中執行部署，並`CloudFormationExecutionRole`決定 CloudFormation 可執行的動作。
+  AWS 身分假設 `FilePublishingRole`，這會決定可以在引導期間建立的 Amazon S3 儲存貯體上執行的動作。
+  AWS 身分假設 `ImagePublishingRole`，這會決定可以在引導期間建立的 Amazon ECR 儲存庫上執行的動作。
+ 如有必要， AWS 身分會假設在 AWS 環境中`LookupRole`執行內容查詢。此動作也可能在範本合成期間執行。  
 **部署已執行**   
在部署期間，CDK CLI 會讀取引導版本參數以確認引導版本編號。 AWS CloudFormation 也會在部署時間讀取此參數以確認。如果部署工作流程中的許可有效，則會執行部署。資產會上傳至引導的資源，並使用 CloudFormation CloudFormation 服務做為 CloudFormation 堆疊部署合成時產生的 CloudFormation 範本，以佈建您的資源。

# AWS 合成時的 CDK 政策驗證
<a name="policy-validation-synthesis"></a>

## 合成時的政策驗證
<a name="policy-validation"></a>

如果您或您的組織使用任何政策驗證工具，例如 [AWS CloudFormation Guard](https://docs.aws.amazon.com/cfn-guard/latest/ug/what-is-guard.html) 或 [OPA](https://www.openpolicyagent.org/)，來定義 AWS CloudFormation 範本的限制，您可以在合成時將其與 AWS CDK 整合。透過使用適當的政策驗證外掛程式，您可以讓 AWS CDK 應用程式在合成後立即針對您的政策檢查 generated AWS CloudFormation 範本。如果有任何違規，合成將會失敗，而報告將會列印到主控台。

 AWS CDK 在合成時間執行的驗證會在部署生命週期的某個時間點驗證控制項，但不會影響在合成之外發生的動作。範例包括直接在主控台或透過服務 APIs採取的動作。它們在合成後不會抵抗 AWS CloudFormation 範本的更改。應以更授權的方式驗證相同規則集的一些其他機制應獨立設定，例如 [AWS CloudFormation hooks](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/hooks.html) 或 [AWS Config](https://docs.aws.amazon.com/config/latest/developerguide/WhatIsConfig.html)。不過， AWS CDK 在開發期間評估規則集的能力仍然很有用，因為它可以提高偵測速度和開發人員生產力。

 AWS CDK 政策驗證的目標是將開發期間所需的設定數量降至最低，並盡可能簡化。

**注意**  
此功能被視為實驗性功能，外掛程式 API 和驗證報告的格式都可能在未來變更。

## 適用於應用程式開發人員
<a name="for-application-developers"></a>

若要在您的應用程式中使用一或多個驗證外掛程式，請使用 的 `policyValidationBeta1` 屬性`Stage`：

```
import { CfnGuardValidator } from '@cdklabs/cdk-validator-cfnguard';
const app = new App({
  policyValidationBeta1: [
    new CfnGuardValidator()
  ],
});
// only apply to a particular stage
const prodStage = new Stage(app, 'ProdStage', {
  policyValidationBeta1: [...],
});
```

合成後，會以這種方式註冊的所有外掛程式都會立即被叫用，以驗證在您定義的範圍內產生的所有範本。特別是，如果您在 `App` 物件中註冊範本，則所有範本都必須經過驗證。

**警告**  
除了修改雲端組件之外，外掛程式還可以執行 AWS CDK 應用程式可以執行的任何操作。他們可以從檔案系統讀取資料、存取網路等。身為外掛程式的消費者，您有責任確認使用是否安全。

### AWS CloudFormation Guard 外掛程式
<a name="cfnguard-plugin"></a>

使用 [https://github.com/cdklabs/cdk-validator-cfnguard](https://github.com/cdklabs/cdk-validator-cfnguard) 外掛程式可讓您使用 [AWS CloudFormation Guard](https://github.com/aws-cloudformation/cloudformation-guard) 來執行政策驗證。`CfnGuardValidator` 外掛程式隨附一組內建的 [AWS Control Tower 主動控制](https://docs.aws.amazon.com/controltower/latest/userguide/proactive-controls.html)。您可以在[專案文件中](https://github.com/cdklabs/cdk-validator-cfnguard/blob/main/README.md)找到目前的規則集。如[合成時政策驗證](#policy-validation)中所述，我們建議組織使用 [AWS CloudFormation 勾點](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/hooks.html)設定更權威的驗證方法。

對於 [AWS Control Tower](https://docs.aws.amazon.com/controltower/latest/userguide/what-is-control-tower.html) 客戶，這些相同的主動控制可以部署到整個組織。當您在 AWS Control Tower 環境中啟用 AWS Control Tower 主動控制時，這些控制可以停止部署透過 AWS CloudFormation 部署的不合規資源。如需受管主動控制及其運作方式的詳細資訊，請參閱 [AWS Control Tower 文件](https://docs.aws.amazon.com/controltower/latest/userguide/proactive-controls.html)。

這些 AWS CDK 綁定控制項和受管 AWS Control Tower 主動控制項最好一起使用。在此案例中，您可以使用與 AWS Control Tower 雲端環境中作用中的相同主動控制來設定此驗證外掛程式。然後，您可以透過`cdk synth`在本機執行 ，快速獲得對 AWS CDK 應用程式將通過 AWS Control Tower 控制項的信心。

### 驗證報告
<a name="validation-report"></a>

當您合成 AWS CDK 應用程式時，系統會呼叫驗證器外掛程式，並列印結果。範例報告顯示如下。

```
Validation Report (CfnGuardValidator)
-------------------------------------
(Summary)
╔═══════════╤════════════════════════╗
║ Status    │ failure                ║
╟───────────┼────────────────────────╢
║ Plugin    │ CfnGuardValidator      ║
╚═══════════╧════════════════════════╝
(Violations)
Ensure S3 Buckets are encrypted with a KMS CMK (1 occurrences)
Severity: medium
  Occurrences:

    - Construct Path: MyStack/MyCustomL3Construct/Bucket
    - Stack Template Path: ./cdk.out/MyStack.template.json
    - Creation Stack:
        └──  MyStack (MyStack)
             │ Library: aws-cdk-lib.Stack
             │ Library Version: 2.50.0
             │ Location: Object.<anonymous> (/home/johndoe/tmp/cdk-tmp-app/src/main.ts:25:20)
             └──  MyCustomL3Construct (MyStack/MyCustomL3Construct)
                  │ Library: N/A - (Local Construct)
                  │ Library Version: N/A
                  │ Location: new MyStack (/home/johndoe/tmp/cdk-tmp-app/src/main.ts:15:20)
                  └──  Bucket (MyStack/MyCustomL3Construct/Bucket)
                       │ Library: aws-cdk-lib/aws-s3.Bucket
                       │ Library Version: 2.50.0
                       │ Location: new MyCustomL3Construct (/home/johndoe/tmp/cdk-tmp-app/src/main.ts:9:20)
    - Resource Name: amzn-s3-demo-bucket
    - Locations:
      > BucketEncryption/ServerSideEncryptionConfiguration/0/ServerSideEncryptionByDefault/SSEAlgorithm
  Recommendation: Missing value for key `SSEAlgorithm` - must specify `aws:kms`
  How to fix:
    > Add to construct properties for `cdk-app/MyStack/Bucket`
      `encryption: BucketEncryption.KMS`

Validation failed. See above reports for details
```

根據預設，報告會以人類可讀格式列印。如果您想要 JSON 格式的報告，`@aws-cdk/core:validationReportJson`請透過 CLI 使用 啟用報告，或直接將其傳遞給應用程式：

```
const app = new App({
  context: { '@aws-cdk/core:validationReportJson': true },
});
```

或者，您可以使用專案目錄中的 `cdk.json`或 `cdk.context.json` 檔案來設定此內容索引鍵/值對 （請參閱[內容值和 AWS CDK](context.md))。

如果您選擇 JSON 格式， AWS CDK 會將政策驗證報告列印到雲端組件目錄中名為 `policy-validation-report.json` 的檔案。對於預設的人類可讀格式，報告將列印到標準輸出。

## 對於外掛程式作者
<a name="plugin-authors"></a>

### 外掛程式
<a name="plugins"></a>

 AWS CDK 核心架構負責註冊和叫用外掛程式，然後顯示格式化的驗證報告。外掛程式的責任是做為 AWS CDK 架構和政策驗證工具之間的轉譯層。外掛程式可以使用 AWS CDK 支援的任何語言建立。如果您要建立可能由多種語言使用的外掛程式，建議您在 中建立外掛程式，`TypeScript`以便您可以使用 JSII 在每個 AWS CDK 語言中發佈外掛程式。

### 建立外掛程式
<a name="creating-plugins"></a>

 AWS CDK 核心模組與政策工具之間的通訊協定是由 `IPolicyValidationPluginBeta1` 介面定義。若要建立新的外掛程式，您必須撰寫實作此界面的類別。您需要實作兩件事：外掛程式名稱 （透過覆寫 `name` 屬性） 和 `validate()`方法。

框架將呼叫 `validate()`，傳遞 `IValidationContextBeta1` 物件。要驗證的範本位置由 提供`templatePaths`。外掛程式應該會傳回 的執行個體`ValidationPluginReportBeta1`。此物件代表使用者在合成結束時收到的報告。

```
validate(context: IPolicyValidationContextBeta1): PolicyValidationReportBeta1 {
  // First read the templates using context.templatePaths...
  // ...then perform the validation, and then compose and return the report.
  // Using hard-coded values here for better clarity:
  return {
    success: false,
    violations: [{
      ruleName: 'CKV_AWS_117',
      description: 'Ensure that AWS Lambda function is configured inside a VPC',
      fix: 'https://docs.bridgecrew.io/docs/ensure-that-aws-lambda-function-is-configured-inside-a-vpc-1',
      violatingResources: [{
        resourceName: 'MyFunction3BAA72D1',
        templatePath: '/home/johndoe/myapp/cdk.out/MyService.template.json',
        locations: 'Properties/VpcConfig',
      }],
    }],
  };
}
```

請注意，外掛程式不允許修改雲端組件中的任何內容。任何嘗試這樣做都會導致合成失敗。

如果您的外掛程式依賴外部工具，請記住，有些開發人員可能尚未在工作站中安裝該工具。為了將摩擦降至最低，強烈建議您提供一些安裝指令碼與外掛程式套件，以自動化整個程序。更好的是，請在安裝套件時執行該指令碼。例如`npm`，使用 ，您可以將它新增至 `package.json` 檔案中的`postinstall`[指令碼](https://docs.npmjs.com/cli/v9/using-npm/scripts)。

### 處理豁免
<a name="handling-exemptions"></a>

如果您的組織具有處理豁免的機制，則可以將其實作為驗證器外掛程式的一部分。

說明可能豁免機制的範例案例：
+ 組織具有不允許公有 Amazon S3 儲存貯體的規則，*但*在某些情況下除外。
+ 開發人員正在建立屬於其中一個案例的 Amazon S3 儲存貯體，並請求豁免 （例如建立票證）。
+ 安全工具知道如何從註冊豁免的內部系統讀取

在此案例中，開發人員會在內部系統中請求例外狀況，然後需要某種方法來「註冊」該例外狀況。將 新增至 防護外掛程式範例，您可以透過篩選掉內部票證系統中具有相符豁免的違規，來建立處理豁免的外掛程式。

如需範例實作，請參閱現有的外掛程式。
+  [@cdklabs/cdk-validator-cfnguard](https://github.com/cdklabs/cdk-validator-cfnguard) 

# 使用 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\$1COMPLETE 狀態，無法更新**   

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

# 在 CDK 應用程式中建置和部署容器映像資產
<a name="build-containers"></a>

當您使用 AWS 雲端開發套件 (AWS CDK) 建置容器映像資產時，預設會使用 Docker 來執行這些動作。如果您想要使用不同的容器管理工具，您可以透過 `CDK_DOCKER`環境變數取代 Docker。

## 範例：使用 AWS CDK 建置和發佈容器映像資產
<a name="build-containers-intro-example"></a>

以下是使用 Docker 預設建置容器資產並將其發佈至 Amazon Elastic Container Registry (Amazon ECR) 的 AWS CDK 應用程式簡單範例：

 **專案結構**   

```
my-cdk-app/
├── lib/
│   ├── my-stack.ts
│   └── docker/
│       ├── Dockerfile
│       └── app/
│           └── index.js
├── bin/
│   └── my-cdk-app.ts
├── package.json
├── tsconfig.json
└── cdk.json
```

 **Dockerfile**   

```
FROM public.ecr.aws/lambda/nodejs:16

# Copy application code
COPY app/ /var/task/

# (Optional) Install dependencies
# RUN npm install

# The Lambda Node.js base image looks for index.handler by default
```

 **應用程式碼**   
在 `lib/docker/app/index.js` 中：  

```
console.log("Hello from inside the container!");
```

 **CDK 堆疊**   

```
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ecr_assets from 'aws-cdk-lib/aws-ecr-assets';

export class MyStack extends cdk.Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    // Define a Docker image asset
    const dockerImageAsset = new ecr_assets.DockerImageAsset(this, 'MyDockerImage', {
      directory: 'lib/docker', // Path to the directory containing the Dockerfile
    });

    // Output the ECR URI
    new cdk.CfnOutput(this, 'ECRImageUri', {
      value: dockerImageAsset.imageUri,
    });
  }
}
```

 **CDK 應用程式**   

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

const app = new cdk.App();
new MyStack(app, 'MyStack');
```

執行 時`cdk deploy`， AWS 雲端開發套件 (AWS CDK) 命令列界面 (CLI) 會執行下列動作：

1.  **建置 Docker 映像** – 根據指定目錄中`Dockerfile`的 在`docker build`本機執行 (`lib/docker`)。

1.  **標記映像** – 根據映像內容，執行 `docker tag` 以使用唯一的雜湊標記建置的映像。

1.  **發佈至 Amazon ECR** – 執行 `docker push` 將容器映像發佈至 Amazon ECR 儲存庫。此儲存庫必須已存在。它會在預設引導程序期間建立。

1.  **輸出映像 URI** – 成功部署後，發佈的容器映像的 Amazon ECR URI 會在命令提示中輸出。這是 Amazon ECR 中 Docker 映像的 URI。

## 如何使用另一個容器管理工具取代 Docker
<a name="build-container-replace"></a>

使用 `CDK_DOCKER`環境變數來指定替換容器管理工具之二進位檔的路徑。以下是使用 取代 Docker 的範例Finch：

```
$ which finch
/usr/local/bin/finch # Locate the path to the binary

$ export CDK_DOCKER='/usr/local/bin/finch' # Set the environment variable

$ cdk deploy # Deploy using the replacement
```

不支援別名或連結。若要取代 Docker，您必須使用 `CDK_DOCKER`環境變數。

## 支援的 Docker 插入式替換引擎
<a name="build-container-supported"></a>

 Finch 受支援，雖然可能有一些 Docker 功能無法使用，或隨著工具的演進，運作方式可能不同。如需 Finch 的詳細資訊，請參閱* AWS 開放原始碼部落格*中的[準備飛行：宣布 Finch 1.0 GA！](https://aws.amazon.com/blogs/opensource/ready-for-flight-announcing-finch-1-0-ga/)。

其他容器管理工具可能會運作。CDK 不會檢查您用來判斷是否支援哪個 Docker 替換。如果工具具有同等的 Docker 命令且行為類似，則應該可以運作。

# 故障診斷 AWS CDK 部署
<a name="deploy-troubleshoot"></a>

部署 AWS 雲端開發套件 (AWS CDK) 應用程式時的常見問題疑難排解。

## 部署時建立不正確的服務主體
<a name="deploy-troubleshoot-sp"></a>

使用服務主體部署包含 AWS Identity and Access Management (IAM) 角色的 CDK 應用程式時，您會發現正在建立服務主體的不正確網域。

以下是建立 IAM 角色的基本範例，Amazon CloudWatch Logs 可以使用其服務主體擔任該角色：

```
import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';

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

    // Create an IAM role for CloudWatch Logs to assume
    const cloudWatchLogsRole = new iam.Role(this, 'CloudWatchLogsRole', {
      assumedBy: new iam.ServicePrincipal('logs.amazonaws.com'), // This is for CloudWatch Logs
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSCloudWatchLogsFullAccess')
      ]
    });

    // You can then use this role in other constructs or configurations where CloudWatch Logs needs to assume a role
  }
}
```

部署此堆疊時，`logs.amazonaws.com`應該建立名為 的服務主體。在大多數情況下， AWS 服務會針對服務主體使用下列命名：`<service>.amazonaws.com`。

### 常見原因
<a name="deploy-troubleshoot-sp-causes"></a>

如果您使用的 AWS CDK 版本早於 v2.150.0，您可能會遇到此錯誤。在舊版 AWS CDK 中，服務主體的命名未標準化，這可能會導致建立具有不正確網域的服務主體。

在 AWS CDK v2.51.0 中，已盡可能將所有自動建立的服務主體標準化，以實作修正。 `<service>.amazonaws.com`允許 `@aws-cdk/aws-iam:standardizedServicePrincipals`功能旗標，即可使用此修正。

從 AWS CDK v2.150.0 開始，這成為預設行為。

### Resolution
<a name="deploy-troubleshoot-sp-resolution"></a>

升級至 AWS CDK v2.150.0 或更新版本。

如果您無法升級至 AWS CDK v2.150.0 或更新版本，則必須至少升級至 v2.51.0 或更新版本。然後，在您的 `cdk.json` 檔案中允許下列功能旗標：`@aws-cdk/aws-iam:standardizedServicePrincipals`。