本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
AWS CloudFormation Lambda Hooks 可讓您針對自己的自訂程式碼評估 CloudFormation 和 AWS Cloud Control API 操作。您的勾點可能會封鎖操作以繼續,或向發起人發出警告,並允許操作繼續。當您建立 Lambda Hook 時,您可以將其設定為攔截和評估下列 CloudFormation 操作:
-
資源操作
-
堆疊操作
-
變更集操作
開發 Lambda Hook
當 Hooks 調用 Lambda 時,Lambda 最多會等待 30 秒來評估輸入。Lambda 將傳回 JSON 回應,指出勾點是否成功或失敗。
請求輸入
傳遞至 Lambda 函數的輸入取決於勾點目標操作 (範例:堆疊、資源或變更集)。
回應輸入
為了在請求成功或失敗時與 Hooks 通訊,您的 Lambda 函數需要傳回 JSON 回應。
以下是回應 Hooks 預期的範例形狀:
{
"hookStatus": "SUCCESS" or "FAILED" or "IN_PROGRESS",
"errorCode": None or "NonCompliant" or "InternalFailure"
"message": String,
"clientRequestToken": String
"callbackContext": None,
"callbackDelaySeconds": Integer,
}
- hookStatus
-
勾點的狀態。此為必要欄位。
有效值: (
SUCCESS
|FAILED
|IN_PROGRESS
)注意
勾點可以傳回
IN_PROGRESS
3 次。如果未傳回任何結果,勾點將會失敗。對於 Lambda Hook,這表示您的 Lambda 函數最多可叫用 3 次。 - errorCode
-
顯示操作是否已評估並判斷為無效,或者如果勾點中發生錯誤,則阻止評估。如果勾點失敗,則此欄位為必要欄位。
有效值:(
NonCompliant
|InternalFailure
) - message
-
給發起人的訊息,說明勾點成功或失敗的原因。
注意
評估 CloudFormation 操作時,此欄位會截斷為 4096 個字元。
評估 Cloud Control API 操作時,此欄位會截斷為 1024 個字元。
- clientRequestToken
-
作為 Hook 請求輸入而提供的請求字符。此為必要欄位。
- callbackContext
-
如果您表示
hookStatus
是IN_PROGRESS
,則當您重新叫用 Lambda 函數時,會傳遞作為輸入提供的額外內容。 - callbackDelaySeconds
-
Hooks 應該等待多久才能再次叫用此 Hook。
範例
以下是成功回應的範例:
{
"hookStatus": "SUCCESS",
"message": "compliant",
"clientRequestToken": "123avjdjk31"
}
以下是失敗回應的範例:
{
"hookStatus": "FAILED",
"errorCode": "NON_COMPLIANT",
"message": "S3 Bucket Versioning must be enabled.",
"clientRequestToken": "123avjdjk31"
}
使用 Lambda Hooks 評估資源操作
每當您建立、更新或刪除資源時,即視為資源操作。例如,如果您執行更新建立新資源的 CloudFormation 堆疊,表示您已完成資源操作。當您使用 Cloud Control API 建立、更新或刪除資源時,這也被視為資源操作。您可以在 Hook TargetOperations
組態中將 CloudFormation Lambda Hook 設定為目標RESOURCE
和CLOUD_CONTROL
操作。
注意
只有在使用來自 cloud-control delete-resource
或 的操作觸發程序刪除資源時,才會叫用 delete
Hook 處理常式cloudformation delete-stack
。
Lambda Hook 資源輸入語法
當您的 Lambda 被叫用進行資源操作時,您將會收到 JSON 輸入,其中包含資源屬性、提議屬性和 Hook 叫用的相關內容。
以下是 JSON 輸入的範例形狀:
{
"awsAccountId": String,
"stackId": String,
"changeSetId": String,
"hookTypeName": String,
"hookTypeVersion": String,
"hookModel": {
"LambdaFunction": String
},
"actionInvocationPoint": "CREATE_PRE_PROVISION" or "UPDATE_PRE_PROVISION" or "DELETE_PRE_PROVISION"
"requestData": {
"targetName": String,
"targetType": String,
"targetLogicalId": String,
"targetModel": {
"resourceProperties": {...},
"previousResourceProperties": {...}
}
},
"requestContext": {
"調用": 1,
"callbackContext": null
}
}
awsAccountId
-
AWS 帳戶 包含要評估之資源的 ID。
stackId
-
此操作所屬的 CloudFormation 堆疊的堆疊 ID。如果發起人是 Cloud Control API,則此欄位為空白。
changeSetId
-
啟動勾點調用的變更集 ID。如果資源變更是由 Cloud Control API 或
create-stack
、update-stack
或delete-stack
操作啟動,則此值為空。 hookTypeName
-
正在執行的勾點名稱。
hookTypeVersion
-
正在執行的勾點版本。
hookModel
-
LambdaFunction
-
Hook 調用的目前 Lambda ARN。
actionInvocationPoint
-
佈建邏輯中 Hook 執行的確切點。
有效值: (
CREATE_PRE_PROVISION
|UPDATE_PRE_PROVISION
|DELETE_PRE_PROVISION
) requestData
-
targetName
-
要建立的目標資源名稱。
targetType
-
要建立的目標類型,例如
AWS::S3::Bucket
。 targetLogicalId
-
要評估之資源的邏輯 ID。如果 Hook 調用的原始伺服器是 CloudFormation,這將是 CloudFormation 範本中定義的邏輯資源 ID。如果此 Hook 調用的原始伺服器是 Cloud Control API,這會是建構值。
targetModel
-
resourceProperties
-
正在修改之資源的提議屬性。如果正在刪除資源,則此值將為空。
previousResourceProperties
-
目前與正在修改的資源相關聯的屬性。如果正在建立資源,則此值將為空。
requestContext
-
- 調用
-
目前執行勾點的嘗試。
- callbackContext
-
如果 Hookwas 設定為
IN_PROGRESS
,並callbackContext
傳回,則撤銷後將會在這裡。
Lambda Hook 資源變更輸入範例
在下列範例輸入中,Guard Hook 將收到變更AWS::DynamoDB::Table
的資源定義。ProvisionedThroughput
和 ReadCapacityUnits
參數會從 3 更新為 10。
如需 資源可用屬性的詳細資訊,請參閱 AWS::DynamoDB::Table 。
{
"awsAccountId": "123456789",
"stackId": "arn:aws:cloudformation:eu-central-1:123456789:stack/test-stack/123456abcd",
"hookTypeName": "my::lambda::resourcehookfunction",
"hookTypeVersion": "00000008",
"hookModel": {
"LambdaFunction": "arn:aws:lambda:eu-central-1:123456789:function:resourcehookfunction"
},
"actionInvocationPoint": "UPDATE_PRE_PROVISION",
"requestData": {
"targetName": "AWS::DynamoDB::Table",
"targetType": "AWS::DynamoDB::Table",
"targetLogicalId": "DDBTable",
"targetModel": {
"resourceProperties": {
"AttributeDefinitions": [
{
"AttributeType": "S",
"AttributeName": "Album"
},
{
"AttributeType": "S",
"AttributeName": "Artist"
}
],
"ProvisionedThroughput": {
"WriteCapacityUnits": 5,
"ReadCapacityUnits": 10
},
"KeySchema": [
{
"KeyType": "HASH",
"AttributeName": "Album"
},
{
"KeyType": "RANGE",
"AttributeName": "Artist"
}
]
},
"previousResourceProperties": {
"AttributeDefinitions": [
{
"AttributeType": "S",
"AttributeName": "Album"
},
{
"AttributeType": "S",
"AttributeName": "Artist"
}
],
"ProvisionedThroughput": {
"WriteCapacityUnits": 5,
"ReadCapacityUnits": 5
},
"KeySchema": [
{
"KeyType": "HASH",
"AttributeName": "Album"
},
{
"KeyType": "RANGE",
"AttributeName": "Artist"
}
]
}
}
},
"requestContext": {
"invocation": 1,
"callbackContext": null
}
}
資源操作的 Lambda 函數範例
下列範例 Lambda Hook 目標為 Node.js
。這是一個簡單的函數,會讓 DynamoDB 的任何資源更新失敗,嘗試將 ProvisionedThroughput ReadCapacity
設定為大於 10 的 。如果勾點成功,訊息「ReadCapacity 已正確設定」將顯示給發起人。如果請求驗證失敗,勾點將會失敗,狀態為「ReadCapacity 不能超過 10。」
export const handler = async (event, context) => {
var targetModel = event?.requestData?.targetModel;
var targetName = event?.requestData?.targetName;
var response = {
"hookStatus": "SUCCESS",
"message": "ReadCapacity is correctly configured.",
"clientRequestToken": event.clientRequestToken
};
if (targetName == "AWS::DynamoDB::Table") {
var readCapacity = targetModel?.resourceProperties?.ProvisionedThroughput?.ReadCapacityUnits;
if (readCapacity > 10) {
response.hookStatus = "FAILED";
response.errorCode = "NonCompliant";
response.message = "ReadCapacity must be cannot be more than 10.";
}
}
return response;
};
使用 Lambda Hooks 評估堆疊操作
每當您使用新範本建立、更新或刪除堆疊時,都可以透過評估新範本並可能封鎖堆疊操作以繼續,來設定 CloudFormation Lambda Hook 開始。您可以在 Hook TargetOperations
組態中將 CloudFormation Lambda Hook 設定為目標STACK
操作。
Lambda Hook 堆疊輸入語法
當您的 Lambda 被叫用進行堆疊操作時,您將會收到 JSON 請求,其中包含 Hook 叫用內容actionInvocationPoint
、 和 請求內容。由於 CloudFormation 範本的大小,以及 Lambda 函數接受的有限輸入大小,實際範本會存放在 Amazon S3 物件中。的輸入requestData
包含另一個物件的 Amazon S3 已簽章 URL,其中包含目前和先前的範本版本。
以下是 JSON 輸入的範例形狀:
{
"clientRequesttoken": String,
"awsAccountId": String,
"stackID": String,
"changeSetId": String,
"hookTypeName": String,
"hookTypeVersion": String,
"hookModel": {
"LambdaFunction":String
},
"actionInvocationPoint": "CREATE_PRE_PROVISION" or "UPDATE_PRE_PROVISION" or "DELETE_PRE_PROVISION"
"requestData": {
"targetName": "STACK",
"targetType": "STACK",
"targetLogicalId": String,
"payload": String (S3 Presigned URL)
},
"requestContext": {
"invocation": Integer,
"callbackContext": String
}
}
clientRequesttoken
-
作為 Hook 請求輸入而提供的請求字符。此為必要欄位。
awsAccountId
-
AWS 帳戶 包含要評估之堆疊的 ID。
stackID
-
CloudFormation 堆疊的堆疊 ID。
changeSetId
-
啟動勾點調用的變更集 ID。如果堆疊變更是由 Cloud Control API 或 、
update-stack
或delete-stack
操作啟動create-stack
,則此值為空。 hookTypeName
-
正在執行的勾點名稱。
hookTypeVersion
-
正在執行的勾點版本。
hookModel
-
LambdaFunction
-
Hook 調用的目前 Lambda ARN。
actionInvocationPoint
-
佈建邏輯中 Hook 執行的確切點。
有效值: (
CREATE_PRE_PROVISION
|UPDATE_PRE_PROVISION
|DELETE_PRE_PROVISION
) requestData
-
targetName
-
此值將為
STACK
。 targetType
-
此值將為
STACK
。 targetLogicalId
-
堆疊名稱。
payload
-
Amazon S3 預先簽章的 URL,其中包含具有目前和先前範本定義的 JSON 物件。
requestContext
-
如果正在重新叫用勾點,則會設定此物件。
invocation
-
目前執行勾點的嘗試。
callbackContext
-
如果勾點設定為
callbackContext
IN_PROGRESS
並傳回,則撤銷時將會在這裡。
請求資料中的 payload
屬性是程式碼需要擷取的 URL。收到 URL 後,您會取得具有下列結構描述的物件:
{
"template": String,
"previousTemplate": String
}
template
-
提供給
create-stack
或 的完整 CloudFormation 範本update-stack
。它可以是 JSON 或 YAML 字串,取決於提供給 CloudFormation 的內容。在
delete-stack
操作中,此值將為空。 previousTemplate
-
先前的 CloudFormation 範本。它可以是 JSON 或 YAML 字串,取決於提供給 CloudFormation 的內容。
在
delete-stack
操作中,此值將為空。
Lambda Hook 堆疊變更輸入範例
以下是堆疊變更輸入的範例。Hook 正在評估將 更新ObjectLockEnabled
為 true 的變更,並新增 Amazon SQS 佇列:
{
"clientRequestToken": "f8da6d11-b23f-48f4-814c-0fb6a667f50e",
"awsAccountId": "123456789",
"stackId": "arn:aws:cloudformation:eu-central-1:123456789:stack/david-ddb-test-stack/400b40f0-8e72-11ef-80ab-02f2902f0df1",
"changeSetId": null,
"hookTypeName": "my::lambda::stackhook",
"hookTypeVersion": "00000008",
"hookModel": {
"LambdaFunction": "arn:aws:lambda:eu-central-1:123456789:function:stackhookfunction"
},
"actionInvocationPoint": "UPDATE_PRE_PROVISION",
"requestData": {
"targetName": "STACK",
"targetType": "STACK",
"targetLogicalId": "my-cloudformation-stack",
"payload": "https://s3......"
},
"requestContext": {
"invocation": 1,
"callbackContext": null
}
}
這是 payload
的範例requestData
:
{
"template": "{\"Resources\":{\"S3Bucket\":{\"Type\":\"AWS::S3::Bucket\",\"Properties\":{\"ObjectLockEnabled\":true}},\"SQSQueue\":{\"Type\":\"AWS::SQS::Queue\",\"Properties\":{\"QueueName\":\"NewQueue\"}}}}",
"previousTemplate": "{\"Resources\":{\"S3Bucket\":{\"Type\":\"AWS::S3::Bucket\",\"Properties\":{\"ObjectLockEnabled\":false}}}}"
}
堆疊操作的 Lambda 函數範例
下列範例 Lambda Hook 以 為目標Node.js
。這是下載堆疊操作承載、剖析範本 JSON 並傳回 的簡單函數SUCCESS
。
export const handler = async (event, context) => {
var targetType = event?.requestData?.targetType;
var payloadUrl = event?.requestData?.payload;
var response = {
"hookStatus": "SUCCESS",
"message": "Stack update is compliant",
"clientRequestToken": event.clientRequestToken
};
try {
const templateHookPayloadRequest = await fetch(payloadUrl);
const templateHookPayload = await templateHookPayloadRequest.json()
if (templateHookPayload.template) {
// Do something with the template templateHookPayload.template
// JSON or YAML
}
if (templateHookPayload.previousTemplate) {
// Do something with the template templateHookPayload.previousTemplate
// JSON or YAML
}
} catch (error) {
console.log(error);
response.hookStatus = "FAILED";
response.message = "Failed to evaluate stack operation.";
response.errorCode = "InternalFailure";
}
return response;
};
使用 Lambda Hooks 評估變更集操作
每當您建立變更集時,都可以設定 CloudFormation Lambda Hook 來先評估新的變更集,並可能封鎖其執行。您可以在 Hook TargetOperations
組態中將 CloudFormation Lambda Hook 設定為目標CHANGE_SET
操作。
Lambda Hook 變更集輸入語法
變更集操作的輸入類似於堆疊操作,但 承載requestData
也包含變更集引入的資源變更清單。
以下是 JSON 輸入的範例形狀:
{
"clientRequesttoken": String,
"awsAccountId": String,
"stackID": String,
"changeSetId": String,
"hookTypeName": String,
"hookTypeVersion": String,
"hookModel": {
"LambdaFunction":String
},
"requestData": {
"targetName": "CHANGE_SET",
"targetType": "CHANGE_SET",
"targetLogicalId": String,
"payload": String (S3 Presigned URL)
},
"requestContext": {
"invocation": Integer,
"callbackContext": String
}
}
clientRequesttoken
-
作為 Hook 請求輸入而提供的請求字符。此為必要欄位。
awsAccountId
-
AWS 帳戶 包含要評估之堆疊的 ID。
stackID
-
CloudFormation 堆疊的堆疊 ID。
changeSetId
-
啟動勾點調用的變更集 ID。
hookTypeName
-
正在執行的勾點名稱。
hookTypeVersion
-
正在執行的勾點版本。
hookModel
-
LambdaFunction
-
Hook 調用的目前 Lambda ARN。
requestData
-
targetName
-
此值將為
CHANGE_SET
。 targetType
-
此值將為
CHANGE_SET
。 targetLogicalId
-
變更集 ARN。
payload
-
Amazon S3 預先簽章的 URL,其中包含具有目前範本的 JSON 物件,以及此變更集引入的變更清單。
requestContext
-
如果正在重新叫用勾點,則會設定此物件。
invocation
-
目前執行勾點的嘗試。
callbackContext
-
如果勾點設定為
callbackContext
IN_PROGRESS
並傳回,則撤銷時將會在這裡。
請求資料中的 payload
屬性是程式碼需要擷取的 URL。收到 URL 後,您會取得具有下列結構描述的物件:
{
"template": String,
"changedResources": [
{
"action": String,
"beforeContext": JSON String,
"afterContext": JSON String,
"lineNumber": Integer,
"logicalResourceId": String,
"resourceType": String
}
]
}
template
-
提供給
create-stack
或 的完整 CloudFormation 範本update-stack
。它可以是 JSON 或 YAML 字串,取決於提供給 CloudFormation 的內容。 changedResources
-
已變更資源的清單。
action
-
套用至資源的變更類型。
有效值: (
CREATE
|UPDATE
|DELETE
) beforeContext
-
變更前資源屬性的 JSON 字串。建立資源時,此值為 null。此 JSON 字串中的所有布林值和數值都是 STRINGS。
afterContext
-
如果執行此變更集,資源屬性的 JSON 字串。正在刪除資源時,此值為 null。此 JSON 字串中的所有布林值和數值都是 STRINGS。
lineNumber
-
範本中造成此變更的行號。如果動作是 null,則
DELETE
此值將為 Null。 logicalResourceId
-
要變更之資源的邏輯資源 ID。
resourceType
-
正在變更的資源類型。
Lambda Hook 變更集變更輸入範例
以下是變更集變更輸入的範例。在下列範例中,您可以看到變更集所引進的變更。第一個變更是刪除名為 的佇列CoolQueue
。第二個變更是新增名為 的新佇列NewCoolQueue
。上次變更是 的更新DynamoDBTable
。
{
"clientRequestToken": "f8da6d11-b23f-48f4-814c-0fb6a667f50e",
"awsAccountId": "123456789",
"stackId": "arn:aws:cloudformation:eu-central-1:123456789:stack/david-ddb-test-stack/400b40f0-8e72-11ef-80ab-02f2902f0df1",
"changeSetId": "arn:aws:cloudformation:eu-central-1:123456789:changeSet/davids-change-set/59ebd63c-7c89-4771-a576-74c3047c15c6",
"hookTypeName": "my::lambda::changesethook",
"hookTypeVersion": "00000008",
"hookModel": {
"LambdaFunction": "arn:aws:lambda:eu-central-1:123456789:function:changesethookfunction"
},
"actionInvocationPoint": "CREATE_PRE_PROVISION",
"requestData": {
"targetName": "CHANGE_SET",
"targetType": "CHANGE_SET",
"targetLogicalId": "arn:aws:cloudformation:eu-central-1:123456789:changeSet/davids-change-set/59ebd63c-7c89-4771-a576-74c3047c15c6",
"payload": "https://s3......"
},
"requestContext": {
"invocation": 1,
"callbackContext": null
}
}
這是 payload
的範例requestData.payload
:
{
template: 'Resources:\n' +
' DynamoDBTable:\n' +
' Type: AWS::DynamoDB::Table\n' +
' Properties:\n' +
' AttributeDefinitions:\n' +
' - AttributeName: "PK"\n' +
' AttributeType: "S"\n' +
' BillingMode: "PAY_PER_REQUEST"\n' +
' KeySchema:\n' +
' - AttributeName: "PK"\n' +
' KeyType: "HASH"\n' +
' PointInTimeRecoverySpecification:\n' +
' PointInTimeRecoveryEnabled: false\n' +
' NewSQSQueue:\n' +
' Type: AWS::SQS::Queue\n' +
' Properties:\n' +
' QueueName: "NewCoolQueue"',
changedResources: [
{
logicalResourceId: 'SQSQueue',
resourceType: 'AWS::SQS::Queue',
action: 'DELETE',
lineNumber: null,
beforeContext: '{"Properties":{"QueueName":"CoolQueue"}}',
afterContext: null
},
{
logicalResourceId: 'NewSQSQueue',
resourceType: 'AWS::SQS::Queue',
action: 'CREATE',
lineNumber: 14,
beforeContext: null,
afterContext: '{"Properties":{"QueueName":"NewCoolQueue"}}'
},
{
logicalResourceId: 'DynamoDBTable',
resourceType: 'AWS::DynamoDB::Table',
action: 'UPDATE',
lineNumber: 2,
beforeContext: '{"Properties":{"BillingMode":"PAY_PER_REQUEST","AttributeDefinitions":[{"AttributeType":"S","AttributeName":"PK"}],"KeySchema":[{"KeyType":"HASH","AttributeName":"PK"}]}}',
afterContext: '{"Properties":{"BillingMode":"PAY_PER_REQUEST","PointInTimeRecoverySpecification":{"PointInTimeRecoveryEnabled":"false"},"AttributeDefinitions":[{"AttributeType":"S","AttributeName":"PK"}],"KeySchema":[{"KeyType":"HASH","AttributeName":"PK"}]}}'
}
]
}
變更集操作的範例 Lambda 函數
下列範例 Lambda Hook 以 為目標Node.js
。這是一個簡單的函數,可下載變更集操作承載,逐一完成每個變更,然後在屬性傳回 之前列印前後的 SUCCESS
。
export const handler = async (event, context) => {
var payloadUrl = event?.requestData?.payload;
var response = {
"hookStatus": "SUCCESS",
"message": "Change set changes are compliant",
"clientRequestToken": event.clientRequestToken
};
try {
const changeSetHookPayloadRequest = await fetch(payloadUrl);
const changeSetHookPayload = await changeSetHookPayloadRequest.json();
const changes = changeSetHookPayload.changedResources || [];
for(const change of changes) {
var beforeContext = {};
var afterContext = {};
if(change.beforeContext) {
beforeContext = JSON.parse(change.beforeContext);
}
if(change.afterContext) {
afterContext = JSON.parse(change.afterContext);
}
console.log(beforeContext)
console.log(afterContext)
// Evaluate Change here
}
} catch (error) {
console.log(error);
response.hookStatus = "FAILED";
response.message = "Failed to evaluate change set operation.";
response.errorCode = "InternalFailure";
}
return response;
};