

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

# 教學課程：自動停止缺少必要標籤的 Amazon EC2 執行個體
<a name="monitor-example"></a>

隨著您 AWS 帳戶 和管理 AWS 的資源集區不斷成長，您可以使用標籤來更輕鬆地分類資源。標籤通常用於成本分配和安全性等關鍵使用案例。若要有效管理 AWS 資源，您的資源需要持續加上標籤。通常，當佈建資源時，它會取得所有適當的標籤。不過，稍後的程序可能會導致標籤變更，進而導致企業標籤政策偏離。透過監控標籤的變更，您可以發現標籤偏離並立即回應。這可讓您更有信心，取決於您的資源正確分類的程序將產生所需的結果。

下列範例示範如何監控 Amazon EC2 執行個體上的標籤變更，以確認指定的執行個體是否繼續擁有所需的標籤。如果執行個體的標籤變更，且執行個體不再具有所需的標籤，則會叫用 Lambda 函數來自動關閉執行個體。為什麼要這樣做？ 它可確保所有資源都根據您的公司標籤政策進行標記，以實現有效的成本分配，或能夠根據[屬性型存取控制 (ABAC)](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction_attribute-based-access-control.html) 信任安全性。

**重要**  
我們強烈建議您在不會不小心關閉重要執行個體的非生產帳戶中執行此教學課程。  
本教學課程中的範例程式碼刻意限制此案例的影響僅限於執行個體 IDs 清單上的執行個體。您必須使用您願意為測試關閉IDs 來更新清單。這有助於確保您不會意外關閉 區域中的每個執行個體 AWS 帳戶。  
測試之後，請確定您的所有執行個體都根據公司的標記策略進行標記。然後，您可以移除將函數限制為清單上執行個體 IDs程式碼。

此範例使用 JavaScript和 16.x 版本的 。 Node.js此範例使用範例 AWS 帳戶 ID 123456789012 和 AWS 區域 美國東部 （維吉尼亞北部） (`us-east-1`)。將它們取代為您自己的測試帳戶 ID 和區域。

**注意**  
如果您的主控台預設使用不同的區域，請務必在每次變更主控台時切換您在本教學課程中所使用的區域。此教學課程失敗的常見原因是在兩個不同的區域中具有執行個體和函數。

如果您使用不同於 的區域`us-east-1`，請確定將下列程式碼範例中的所有參考變更為您選擇的區域。

**Topics**
+ [步驟 1. 建立 Lambda 函式](#monitor-example-step-1)
+ [步驟 2. 設定必要的 IAM 許可](#monitor-example-step-2)
+ [步驟 3。對您的 Lambda 函數進行初步測試](#monitor-example-step-3)
+ [步驟 4. 建立啟動函數的 EventBridge 規則](#monitor-example-step-4)
+ [步驟 5. 測試完整的解決方案](#monitor-example-step-6)
+ [教學課程摘要](#summary)

## 步驟 1. 建立 Lambda 函式
<a name="monitor-example-step-1"></a>

**建立 Lambda 函數**

1. 開啟 [AWS Lambda 管理主控台](https://console.aws.amazon.com/lambda/home)。

1. 選擇**建立函數**，然後選擇**從頭開始撰寫**。

1. 針對 **Function name (函數名稱)**，輸入 **AutoEC2Termination**。

1. 針對 **執行時間**，請選擇 **Node.js 16.x**。

1. 將所有其他欄位保留在其預設值，然後選擇**建立函數**。

1. 在`AutoEC2Termination`詳細資訊頁面的**程式碼**索引標籤上，開啟 **index.js** 檔案以檢視其程式碼。
   + 如果開啟具有 **index.js** 的標籤，您可以選擇該標籤中的編輯方塊來編輯其程式碼。
   + 如果具有 **index.js** 的標籤未開啟，請在導覽窗格中 **AutoEC2Terminator** 資料夾下按兩下 **index.js** 檔案。然後選擇**開啟**。

1. 在 **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);
       }
     });
   };
   ```

1. 選擇**部署**以儲存變更，並啟用新版本的函數。

此 Lambda 函數會檢查 Amazon EC2 執行個體的標籤，如 EventBridge 中的標籤變更事件所報告。在此範例中，如果事件中的執行個體缺少所需的標籤索引鍵，`valid-key`或該標籤沒有值 `valid-value`，則函數會嘗試停止執行個體。您可以變更此邏輯檢查或您特定使用案例的標籤需求。

在瀏覽器中保持 Lambda 主控台視窗開啟。

## 步驟 2. 設定必要的 IAM 許可
<a name="monitor-example-step-2"></a>

在函式成功執行之前，您必須授予函式停止 EC2 執行個體的許可。 AWS 提供的角色[https://console.aws.amazon.com/iamv2/home#/roles/details/lambda_basic_execution](https://console.aws.amazon.com/iamv2/home#/roles/details/lambda_basic_execution)沒有該許可。在本教學課程中，您會修改連接至函數執行角色的預設 IAM 許可政策，該角色名為 `AutoEC2Termination-role-uniqueid`。本教學課程所需的最低額外許可為 `ec2:StopInstances`。

如需建立 Amazon EC2 特定 IAM 政策的詳細資訊，請參閱《*IAM 使用者指南*》中的 [Amazon EC2：允許啟動或停止 EC2 執行個體，並以程式設計方式在主控台中修改安全群組](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_ec2_instance-securitygroup.html)。

**建立 IAM 許可政策並將其連接至 Lambda 函數的執行角色**

1. 在不同的瀏覽器索引標籤或視窗中，開啟 IAM 主控台[的角色](https://console.aws.amazon.com/iamv2/home#/roles)頁面。

1. 開始輸入角色名稱 **AutoEC2Termination**，並在它出現在清單中時，選擇角色名稱。

1. 在角色的**摘要**頁面上，選擇**許可**索引標籤，然後選擇已連接的政策名稱。

1. 在政策的**摘要**頁面上，選擇**編輯政策**。

1. 在**視覺化編輯器**索引標籤上，選擇**新增其他許可**。

1. 針對**服務**，選擇 **EC2**。

1. 針對**動作**，選擇 **StopInstances**。您可以在搜尋列**Stop**中輸入 ，然後選擇它`StopInstances`何時出現。

1. 針對**資源**，選擇**所有資源**，選擇**檢閱政策**，然後選擇**儲存變更**。

   這會自動建立新的政策版本，並將該版本設定為預設值。

   您的最終政策看起來應該類似下列範例。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Sid": "VisualEditor0",
               "Effect": "Allow",
               "Action": "ec2:StopInstances",
               "Resource": "*"
           },
           {
               "Sid": "VisualEditor1",
               "Effect": "Allow",
               "Action": "logs:CreateLogGroup",
               "Resource": "arn:aws:logs:us-east-1:123456789012:*"
           },
           {
               "Sid": "VisualEditor2",
               "Effect": "Allow",
               "Action": [
                   "logs:CreateLogStream",
                   "logs:PutLogEvents"
               ],
               "Resource": "arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/AutoEC2Termination:*"
           }
       ]
   }
   ```

------

## 步驟 3。對您的 Lambda 函數進行初步測試
<a name="monitor-example-step-3"></a>

在此步驟中，您會將測試事件提交至函數。Lambda 測試功能的運作方式是提交手動提供的測試事件。函數會處理測試事件，就好像事件來自 EventBridge。您可以使用不同的值定義多個測試事件，以執行程式碼的所有不同部分。在此步驟中，您會提交測試事件，指出 Amazon EC2 執行個體的標籤已變更，且新標籤不包含必要的標籤索引鍵和值。

**測試您的 Lambda 函數**

1. 使用 Lambda 主控台返回視窗或索引標籤，並開啟 **AutoEC2Termination** 函數的測試****索引標籤。

1. 選擇**建立新事件**。

1. **事件名稱**輸入 **SampleBadTagChangeEvent**。

1. 在**事件 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
     }
   }
   ```

1. 選擇 **關閉** ，然後選擇 **測試** 。

   測試似乎失敗，但沒關係。

   您應該會在**回應**下的**執行結果**索引標籤中看到下列錯誤。

   ```
   {
     "errorType": "InvalidInstanceID.NotFound",
     "errorMessage": "The instance ID 'i-0000000aaaaaaaaaa' does not exist",
     ...
   }
   ```

   發生錯誤是因為測試事件中指定的執行個體不存在。

   **執行結果**索引標籤上的資訊，在**函數日誌**區段 中，示範您的 Lambda 函數已成功嘗試停止 EC2 執行個體。不過，它失敗，因為程式碼一開始會嘗試停止執行個體[https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_StartInstances.html#API_StartInstances_RequestParameters](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_StartInstances.html#API_StartInstances_RequestParameters)的操作，這表示執行個體 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
   ```

1. 若要證明程式碼不會在使用正確的標籤時嘗試停止執行個體，您可以建立並提交另一個測試事件。

   選擇**程式碼來源**上方的**測試**索引標籤。主控台會顯示您現有的 **SampleBadTagChangeEvent** 測試事件。

1. 選擇**建立新事件**。

1. 針對 **Event name (事件名稱)**，輸入 **SampleGoodTagChangeEvent**。

1. 在第 17 行，刪除 **NOT-** 以將值變更為 **valid-value**。

1. 在**測試事件**視窗頂端，選擇**儲存**，然後選擇**測試**。

   輸出會顯示下列項目，示範函數辨識有效標籤，且不嘗試關閉執行個體。

   ```
   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 規則
<a name="monitor-example-step-4"></a>

現在，您可以建立符合事件並指向 Lambda 函數的 EventBridge 規則。

**若要建立 EventBridge 規則**

1. 在不同的瀏覽器索引標籤或視窗中，開啟 [EventBridge 主控台](https://console.aws.amazon.com/events/home#/rules/create)至**建立規則**頁面。

1. 針對**名稱**，輸入 **ec2-instance-rule**，然後選擇**下一步**。

1. 向下捲動至**建立方法**，然後選擇**自訂模式 (JSON 編輯器）**。

1. 在編輯方塊中，貼上下列模式文字，然後選擇**下一步**。

   ```
   {
     "source": [
       "aws.tag"
     ],
     "detail-type": [
       "Tag Change on Resource"
     ],
     "detail": {
       "service": [
         "ec2"
       ],
       "resource-type": [
         "instance"
       ]
     }
   }
   ```

   此規則符合 Amazon EC2 執行個體`Tag Change on Resource`的事件，並叫用您在下一個步驟中指定為**目標**的任何內容。

1. 接著，將 Lambda 函數新增為目標。在**目標 1** 方塊中的**選取目標**下，選擇 **Lambda 函數**。

1. 在**函數**下，選擇您先前建立的 **AutoEC2Termination** 函數，然後選擇**下一步**。

1. 在**設定標籤**頁面上，選擇**下一步**。然後在**檢閱和建立**頁面上，選擇**建立規則**。這也會自動授予 EventBridge 叫用指定 Lambda 函數的許可。

## 步驟 5. 測試完整的解決方案
<a name="monitor-example-step-6"></a>

您可以建立 EC2 執行個體並查看變更標籤時會發生的情況，以測試最終結果。

**使用實際執行個體測試監控解決方案**

1. 開啟 [Amazon EC2 主控台](https://console.aws.amazon.com/ec2/v2/home#Instances:)至**執行個體**頁面。

1. 建立 Amazon EC2 執行個體。啟動它之前，請使用 鍵`valid-key`和 值 附加標籤`valid-value`。如需有關如何建立和啟動執行個體的資訊，請參閱《*Amazon EC2 使用者指南*》中的[步驟 1：啟動執行個體](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EC2_GetStarted.html#ec2-launch-instance)。在程序中，*若要啟動執行個體*，請在步驟 3 中輸入**名稱**標籤，同時選擇**新增其他標籤**、選擇**新增標籤**，然後輸入 的**金鑰** **valid-key**和 **的值****valid-value**。如果此執行個體僅用於本教學課程的目的，且您打算在完成之後刪除此執行個體，則可以**在沒有金鑰對的情況下繼續**。當您到達**步驟 1 **結尾時，請返回本教學課程；您不需要執行**步驟 2：連線至執行個體**。

1. 從主控台複製 **InstanceId**。

1. 從 Amazon EC2 主控台切換至 Lambda 主控台。選擇 **AutoEC2Termination** 函數，選擇**程式碼**索引標籤，然後選擇 **index.js** 索引標籤來編輯程式碼。

1. 貼上您從 Amazon EC2 主控台複製的值，`InstanceList`以變更 中的第二個項目。請確定該`RegionToMonitor`值符合包含您貼上之執行個體的 區域。

1. 選擇**部署**以啟用您的變更。函數現在已準備好透過指定區域中該執行個體的標籤變更來啟用。

1. 從 Lambda 主控台切換至 Amazon EC2 主控台。

1. 刪除 **valid-key** **標籤**或變更該金鑰的值，以變更連接至執行個體的標籤。
**注意**  
如需有關如何在執行中的 Amazon EC2 執行個體上變更標籤的資訊，請參閱《*Amazon EC2 使用者指南*》中的[在個別資源上新增和刪除標籤](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#adding-or-deleting-tags)。

1. 等待幾秒鐘，然後重新整理主控台。執行個體應該將其**執行個體狀態**變更為**停止**，然後變更為**已停止**。

1. 使用您的 函數從 Amazon EC2 主控台切換到 Lambda 主控台，然後選擇**監控**索引標籤。

1. 選擇**日誌**索引標籤，然後在**最近調用**表格中，選擇 **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
   ```

## 教學課程摘要
<a name="summary"></a>

本教學課程示範如何建立 EventBridge 規則，以比對 Amazon EC2 執行個體資源事件上的標籤變更。規則指向 Lambda 函數，如果執行個體沒有所需的標籤，該函數會自動關閉執行個體。

Amazon EventBridge 支援 AWS 資源上的標籤變更，開啟了在許多 間建置事件驅動型自動化的可能性 AWS 服務。將此功能與 結合 AWS Lambda ，為您提供工具來建置無伺服器解決方案，以安全地存取 AWS 資源、隨需擴展並符合成本效益。

tag-change-on-resource EventBridge 事件的其他可能使用案例包括：
+ **如果有人從不尋常的 IP 地址存取您的資源，請啟動警告** - 使用標籤來存放存取您資源的每個訪客的來源 IP 地址。標籤的變更會產生 CloudWatch 事件。您可以使用該事件將來源 IP 地址與有效 IP 地址清單進行比較，並在來源 IP 地址無效時啟用警告電子郵件。
+ **監控資源的標籤型存取控制是否有變更** – 如果您已使用[屬性 （標籤） 型存取控制 (ABAC)](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction_attribute-based-access-control.html) 設定資源的存取權，您可以使用標籤的任何變更所產生的 EventBridge 事件來提示安全團隊進行稽核。