本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
教學課程:自動停止缺少必要標籤的 Amazon EC2 執行個體
隨著您 AWS 帳戶 和管理 AWS 的資源集區不斷成長,您可以使用標籤來更輕鬆地分類資源。標籤通常用於成本分配和安全性等關鍵使用案例。若要有效管理 AWS 資源,您的資源需要持續加上標籤。通常,當佈建資源時,它會取得所有適當的標籤。不過,稍後的程序可能會導致標籤變更,導致企業標籤政策偏離。透過監控標籤的變更,您可以發現標籤偏離並立即回應。這可讓您更有信心,依賴正確分類之資源的程序將產生所需的結果。
下列範例示範如何監控 Amazon EC2 執行個體上的標籤變更,以確認指定的執行個體是否繼續具有所需的標籤。如果執行個體的標籤變更,且執行個體不再具有所需的標籤,則會叫用 Lambda 函數來自動關閉執行個體。為什麼要這樣做? 它可確保所有資源都根據您的公司標籤政策進行標記,以實現有效的成本分配,或能夠根據屬性型存取控制 (ABAC) 信任安全性。
重要
我們強烈建議您在不會不小心關閉重要執行個體的非生產帳戶中執行此教學課程。
本教學課程中的範例程式碼刻意限制此案例的影響,僅限於執行個體 IDs 清單上的執行個體。您必須使用您願意為測試關閉IDs 來更新清單。這有助於確保您不會意外關閉 區域中的每個執行個體 AWS 帳戶。
測試之後,請確定您的所有執行個體都根據公司的標記策略進行標記。然後,您可以移除將函數限制為清單上執行個體 IDs程式碼。
此範例使用 JavaScript和 16.x 版本的 。 Node.js此範例使用範例 AWS 帳戶 ID 123456789012 和 AWS 區域 美國東部 (維吉尼亞北部) (us-east-1)。將它們取代為您自己的測試帳戶 ID 和區域。
注意
如果您的主控台預設使用不同的區域,請務必在每次變更主控台時,在此教學課程中切換您使用的區域。此教學課程失敗的常見原因是在兩個不同的區域中具有執行個體和函數。
如果您使用不同於 的區域us-east-1,請確定將下列程式碼範例中的所有參考變更為您選擇的區域。
主題
步驟 1. 建立 Lambda 函式
建立 Lambda 函數
-
開啟 AWS Lambda 管理主控台
。 -
選擇建立函數,然後選擇從頭開始撰寫。
-
針對 Function name (函數名稱),輸入
AutoEC2Termination。 -
針對 執行時間,請選擇 Node.js 16.x。
-
將所有其他欄位保留在其預設值,然後選擇建立函數。
-
在
AutoEC2Termination詳細資訊頁面的程式碼索引標籤上,開啟 index.js 檔案以檢視其程式碼。-
如果開啟具有 index.js 的標籤,您可以選擇該標籤中的編輯方塊來編輯其程式碼。
-
如果具有 index.js 的標籤未開啟,請在導覽窗格中的 AutoEC2Terminator 資料夾下按兩下 index.js 檔案。然後選擇開啟。
-
-
在 index.js 索引標籤中,將下列程式碼貼到編輯器方塊中,取代任何已存在的程式碼。
將 值取代
RegionToMonitor為您要在其中執行此函數的區域。// Set the following line to specify which Region's instances you want to monitor // Only instances in this Region are succesfully stopped on a match const RegionToMonitor = "us-east-1" // Specify the instance ARNs to check. // This limits the function for safety to avoid the tutorial shutting down all instances in account // The first ARN is a "dummy" that matches the test event you create in Step 3. // Replace the second ARN with one that matches a real instance that you want to monitor and that you can // safely stop const InstanceList = [ "i-0000000aaaaaaaaaa", "i-05db4466d02744f07" ]; // The tag key name and value that marks a "valid" instance. Instances in the previous list that // do NOT have the following tag key and value are stopped by this function const ValidKeyName = "valid-key"; const ValidKeyValue = "valid-value"; // Load and configure the AWS SDK const AWS = require('aws-sdk'); // Set the AWS Region AWS.config.update({region: RegionToMonitor}); // Create EC2 service object. const ec2 = new AWS.EC2({apiVersion: '2016-11-15'}); exports.handler = (event, context, callback) => { // Retrieve the details of the reported event. var detail = event.detail; var tags = detail["tags"]; var service = detail["service"]; var resourceType = detail["resource-type"]; var resource = event.resources[0]; var resourceSplit = resource.split("/"); var instanceId = resourceSplit[resourceSplit.length - 1]; // If this event is not for an EC2 resource, then do nothing. if (!(service === "ec2")) { console.log("Event not for correct service -- no action (", service, ")" ); return; } // If this event is not about an instance, then do nothing. if (!(resourceType === "instance")) { console.log("Event not for correct resource type -- no action (", resourceType, ")" ); return; } // CAUTION - Removing the following 'if' statement causes the function to run against // every EC2 instance in the specified Region in the calling AWS 帳戶. // If you do this and an instance is not tagged with the approved tag key // and value, this function stops that instance. // If this event is not for the ARN of an instance in our include list, then do nothing. if (InstanceList.indexOf(instanceId)<0) { console.log("Event not for one of the monitored instances -- no action (", resource, ")"); return; } console.log("Tags changed on monitored EC2 instance (",instanceId,")"); // Check attached tags for expected tag key and value pair if ( tags.hasOwnProperty(ValidKeyName) && tags[ValidKeyName] == "valid-value"){ // Required tags ARE present console.log("The instance has the required tag key and value -- no action"); callback(null, "no action"); return; } // Required tags NOT present console.log("This instance is missing the required tag key or value -- attempting to stop the instance"); var params = { InstanceIds: [instanceId], DryRun: true }; // call EC2 to stop the selected instances ec2.stopInstances(params, function(err, data) { if (err && err.code === 'DryRunOperation') { // dryrun succeeded, so proceed with "real" stop operation params.DryRun = false; ec2.stopInstances(params, function(err, data) { if (err) { console.log("Failed to stop instance"); callback(err, "fail"); } else if (data) { console.log("Successfully stopped instance", data.StoppingInstances); callback(null, "Success"); } }); } else { console.log("Dryrun attempt failed"); callback(err); } }); }; -
選擇部署以儲存變更,並啟用新版本的函數。
此 Lambda 函數會檢查 Amazon EC2 執行個體的標籤,如 EventBridge 中的標籤變更事件所報告。在此範例中,如果事件中的執行個體缺少所需的標籤索引鍵,valid-key或該標籤沒有值 valid-value,則函數會嘗試停止執行個體。您可以變更此邏輯檢查或您特定使用案例的標籤需求。
在瀏覽器中保持 Lambda 主控台視窗開啟。
步驟 2. 設定所需的 IAM 許可
在函式成功執行之前,您必須授予函式停止 EC2 執行個體的許可。 AWS 提供的角色lambda_basic_executionAutoEC2Termination-role-。本教學課程所需的最低額外許可為 uniqueidec2:StopInstances。
如需建立 Amazon EC2 特定 IAM 政策的詳細資訊,請參閱《IAM 使用者指南》中的 Amazon EC2:允許啟動或停止 EC2 執行個體,以及以程式設計方式在主控台中修改安全群組。
建立 IAM 許可政策並將其連接至 Lambda 函數的執行角色
-
在不同的瀏覽器索引標籤或視窗中,開啟 IAM 主控台的角色
頁面。 -
開始輸入角色名稱
AutoEC2Termination,並在它出現在清單中時,選擇角色名稱。 -
在角色的摘要頁面上,選擇許可索引標籤,然後選擇已連接的政策名稱。
-
在政策的摘要頁面上,選擇編輯政策。
-
在視覺化編輯器索引標籤上,選擇新增其他許可。
-
針對服務,選擇 EC2。
-
針對動作,選擇 StopInstances。您可以在搜尋列
Stop中輸入 ,然後選擇它StopInstances何時出現。 -
針對資源,選擇所有資源,選擇檢閱政策,然後選擇儲存變更。
這會自動建立新的政策版本,並將該版本設定為預設值。
您的最終政策看起來應該類似下列範例。
步驟 3。對您的 Lambda 函數進行初步測試
在此步驟中,您會將測試事件提交至函數。Lambda 測試功能的運作方式是提交手動提供的測試事件。函數會處理測試事件,就好像事件來自 EventBridge。您可以使用不同的值定義多個測試事件,以執行程式碼的所有不同部分。在此步驟中,您會提交測試事件,指出 Amazon EC2 執行個體的標籤已變更,且新標籤不包含必要的標籤索引鍵和值。
測試您的 Lambda 函數
-
使用 Lambda 主控台返回視窗或索引標籤,並開啟 AutoEC2Termination 函數的測試索引標籤。
-
選擇建立新事件。
-
事件名稱輸入
SampleBadTagChangeEvent。 -
在事件 JSON 中,將文字取代為範例事件,如下列範例文字所示。您不需要修改帳戶、區域或執行個體 ID,此測試事件才能正常運作。
{ "version": "0", "id": "bddcf1d6-0251-35a1-aab0-adc1fb47c11c", "detail-type": "Tag Change on Resource", "source": "aws.tag", "account": "123456789012", "time": "2018-09-18T20:41:38Z", "region": "us-east-1", "resources": [ "arn:aws:ec2:us-east-1:123456789012:instance/i-0000000aaaaaaaaaa" ], "detail": { "changed-tag-keys": [ "valid-key" ], "tags": { "valid-key": "NOT-valid-value" }, "service": "ec2", "resource-type": "instance", "version": 3 } } -
選擇 關閉 ,然後選擇 測試 。
測試似乎失敗,但沒關係。
您應該會在回應下的執行結果索引標籤中看到下列錯誤。
{ "errorType": "InvalidInstanceID.NotFound", "errorMessage": "The instance ID 'i-0000000aaaaaaaaaa' does not exist", ... }發生錯誤是因為測試事件中指定的執行個體不存在。
執行結果索引標籤上的資訊,在函數日誌區段 中,示範您的 Lambda 函數已成功嘗試停止 EC2 執行個體。不過,它失敗,因為程式碼一開始會嘗試停止執行個體
DryRun的操作,這表示執行個體 ID 無效。START RequestId: 390c1f8d-0d9b-4b44-b087-8de64479ab44 Version: $LATEST 2022-11-30T20:17:30.427Z 390c1f8d-0d9b-4b44-b087-8de64479ab44 INFO Tags changed on monitored EC2 instance ( i-0000000aaaaaaaaaa ) 2022-11-30T20:17:30.427Z 390c1f8d-0d9b-4b44-b087-8de64479ab44 INFO This instance is missing the required tag key or value -- attempting to stop the instance 2022-11-30T20:17:31.206Z 390c1f8d-0d9b-4b44-b087-8de64479ab44 INFO Dryrun attempt failed 2022-11-30T20:17:31.207Z 390c1f8d-0d9b-4b44-b087-8de64479ab44 ERROR Invoke Error {"errorType":"InvalidInstanceID.NotFound","errorMessage":"The instance ID 'i-0000000aaaaaaaaaa' does not exist","code":"InvalidInstanceID.NotFound","message":"The instance ID 'i-0000000aaaaaaaaaa' does not exist","time":"2022-11-30T20:17:31.205Z","requestId":"a5192c3b-142d-4cec-bdbc-685a9b7c7abf","statusCode":400,"retryable":false,"retryDelay":36.87870631147607,"stack":["InvalidInstanceID.NotFound: The instance ID 'i-0000000aaaaaaaaaa' does not exist"," at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/services/ec2.js:50:35)"," at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20)"," at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:78:10)"," at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:686:14)"," at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10)"," at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12)"," at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10"," at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9)"," at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:688:12)"," at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:116:18)"]} END RequestId: 390c1f8d-0d9b-4b44-b087-8de64479ab44 -
若要證明程式碼不會在使用正確的標籤時嘗試停止執行個體,您可以建立並提交另一個測試事件。
選擇程式碼來源上方的測試索引標籤。主控台會顯示您現有的 SampleBadTagChangeEvent 測試事件。
-
選擇建立新事件。
-
針對 Event name (事件名稱),輸入
SampleGoodTagChangeEvent。 -
在第 17 行,刪除
NOT-以將值變更為valid-value。 -
在測試事件視窗頂端,選擇儲存,然後選擇測試。
輸出會顯示以下內容,示範 函數辨識有效標籤,且不嘗試關閉執行個體。
START RequestId: 53631a49-2b54-42fe-bf61-85b9e91e86c4 Version: $LATEST 2022-12-01T23:24:12.244Z 53631a49-2b54-42fe-bf61-85b9e91e86c4 INFO Tags changed on monitored EC2 instance ( i-0000000aaaaaaaaaa ) 2022-12-01T23:24:12.244Z 53631a49-2b54-42fe-bf61-85b9e91e86c4 INFO The instance has the required tag key and value -- no action END RequestId: 53631a49-2b54-42fe-bf61-85b9e91e86c4在瀏覽器中保持 Lambda 主控台開啟。
步驟 4. 建立啟動函數的 EventBridge 規則
現在,您可以建立符合事件並指向 Lambda 函數的 EventBridge 規則。
若要建立 EventBridge 規則
-
在不同的瀏覽器索引標籤或視窗中,開啟 EventBridge 主控台
至建立規則頁面。 -
針對名稱,輸入
ec2-instance-rule,然後選擇下一步。 -
向下捲動至建立方法,然後選擇自訂模式 (JSON 編輯器)。
-
在編輯方塊中,貼上下列模式文字,然後選擇下一步。
{ "source": [ "aws.tag" ], "detail-type": [ "Tag Change on Resource" ], "detail": { "service": [ "ec2" ], "resource-type": [ "instance" ] } }此規則符合 Amazon EC2 執行個體
Tag Change on Resource的事件,並叫用您在下一個步驟中指定為目標的任何內容。 -
接著,將 Lambda 函數新增為目標。在目標 1 方塊中的選取目標下,選擇 Lambda 函數。
-
在函數下,選擇您先前建立的 AutoEC2Termination 函數,然後選擇下一步。
-
在設定標籤頁面上,選擇下一步。然後在檢閱和建立頁面上,選擇建立規則。這也會自動授予 EventBridge 叫用指定 Lambda 函數的許可。
步驟 5. 測試完整的解決方案
您可以建立 EC2 執行個體並查看變更標籤時會發生的情況,以測試最終結果。
使用實際執行個體測試監控解決方案
-
開啟執行個體頁面的 Amazon EC2 主控台
。 -
建立 Amazon EC2 執行個體。啟動它之前,請使用 鍵
valid-key和 值 連接標籤valid-value。如需有關如何建立和啟動執行個體的資訊,請參閱《Amazon EC2 使用者指南》中的步驟 1:啟動執行個體。在程序中,若要啟動執行個體,請在步驟 3 中輸入名稱標籤,同時選擇新增其他標籤、選擇新增標籤,然後輸入 的金鑰valid-key和 的值valid-value。如果此執行個體僅用於本教學課程的目的,且您打算在完成之後刪除此執行個體,則可以在沒有金鑰對的情況下繼續。當您到達步驟 1 結尾時,請返回本教學課程;您不需要執行步驟 2:連線至執行個體。 -
從主控台複製 InstanceId。
-
從 Amazon EC2 主控台切換至 Lambda 主控台。選擇 AutoEC2Termination 函數,選擇程式碼索引標籤,然後選擇 index.js 索引標籤來編輯程式碼。
-
貼上您從 Amazon EC2 主控台複製的值,
InstanceList以變更 中的第二個項目。請確定RegionToMonitor值符合包含您貼上之執行個體的 區域。 -
選擇部署以啟用您的變更。函數現在已準備好透過指定區域中該執行個體的標籤變更來啟用。
-
從 Lambda 主控台切換至 Amazon EC2 主控台。
-
刪除 valid-key 標籤或變更該金鑰的值,以變更連接至執行個體的標籤。
注意
如需有關如何在執行中的 Amazon EC2 執行個體上變更標籤的資訊,請參閱《Amazon EC2 使用者指南》中的在個別資源上新增和刪除標籤。
-
等待幾秒鐘,然後重新整理主控台。執行個體應該將其執行個體狀態變更為停止,然後變更為已停止。
-
使用您的 函數從 Amazon EC2 主控台切換到 Lambda 主控台,然後選擇監控索引標籤。
-
選擇日誌索引標籤,然後在最近調用表格中,選擇 LogStream 欄中的最新項目。
Amazon CloudWatch 主控台會開啟日誌事件頁面,以進行 Lambda 函數的最後一次調用。最後一個項目看起來應該類似下列範例。
2022-11-30T12:03:57.544-08:00 START RequestId: b5befd18-2c41-43c8-a320-3a4b2317cdac Version: $LATEST 2022-11-30T12:03:57.548-08:00 2022-11-30T20:03:57.548Z b5befd18-2c41-43c8-a320-3a4b2317cdac INFO Tags changed on monitored EC2 instance ( arn:aws:ec2:us-west-2:123456789012:instance/i-1234567890abcdef0 ) 2022-11-30T12:03:57.548-08:00 2022-11-30T20:03:57.548Z b5befd18-2c41-43c8-a320-3a4b2317cdac INFO This instance is missing the required tag key or value -- attempting to stop the instance 2022-11-30T12:03:58.488-08:00 2022-11-30T20:03:58.488Z b5befd18-2c41-43c8-a320-3a4b2317cdac INFO Successfully stopped instance [ { CurrentState: { Code: 64, Name: 'stopping' }, InstanceId: 'i-1234567890abcdef0', PreviousState: { Code: 16, Name: 'running' } } ] 2022-11-30T12:03:58.546-08:00 END RequestId: b5befd18-2c41-43c8-a320-3a4b2317cdac
教學課程摘要
本教學課程示範如何建立 EventBridge 規則,以比對 Amazon EC2 執行個體資源事件上的標籤變更。規則指向 Lambda 函數,如果執行個體沒有所需的標籤,該函數會自動關閉執行個體。
Amazon EventBridge 支援 AWS 資源上的標籤變更,開啟了在許多 間建置事件驅動型自動化的可能性 AWS 服務。將此功能與 結合 AWS Lambda ,為您提供工具來建置無伺服器解決方案,以安全地存取 AWS 資源、隨需擴展並符合成本效益。
tag-change-on-resource EventBridge 事件的其他可能使用案例包括:
-
如果有人從不尋常的 IP 地址存取您的資源,請啟動警告 - 使用標籤來存放存取您的資源的每個訪客的來源 IP 地址。標籤的變更會產生 CloudWatch 事件。您可以使用該事件將來源 IP 地址與有效 IP 地址清單進行比較,並在來源 IP 地址無效時啟用警告電子郵件。
-
監控資源的標籤型存取控制是否有變更 – 如果您已使用屬性 (標籤) 型存取控制 (ABAC) 設定資源的存取權,您可以使用標籤的任何變更所產生的 EventBridge 事件來提示安全團隊進行稽核。