教程:自动停止缺少所需标签的 Amazon EC2 实例 - 标记 Amazon 资源和标签编辑器
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

教程:自动停止缺少所需标签的 Amazon EC2 实例

作为你的水池 Amazon 资源和 Amazon Web Services 账户 你可以管理增长,你可以使用标签来更轻松地对资源进行分类。标签通常用于关键使用案例,例如成本分配和安全性。为了有效管理 Amazon 资源,则需要对您的资源进行一致的标记。通常,当资源在配置时会获得所有相应的标签。但是,稍后的流程可能会导致标签变更,从而偏离企业标签策略。通过监控标签变更,您可以发现标签偏差并立即做出响应。这样,对于那些依赖于对资源进行正确分类的流程会产生预期的结果,您就更有信心了。

以下示例演示如何监控 Amazon EC2 实例上的标签更改,以验证指定实例是否继续具有所需的标签。如果实例的标签发生变化并且该实例不再具有所需的标签,则调用 Lambda 函数来自动关闭该实例。您为什么要进行此操作? 它可以确保根据您的公司标签策略对所有资源进行标记,以实现有效的成本分配,或者能够基于属性的访问控制来信任安全性()ABAC

重要

我们强烈建议您在非生产账户中执行本教程,以免无意中关闭重要实例。

本教程中的示例代码故意将这种情况的影响限制在实例列表中的实例上IDs。您必须使用愿意关闭以进行测试的实例IDs来更新列表。这有助于确保您不会意外关闭您所在区域中的所有实例 Amazon Web Services 账户.

测试后,请确保根据贵公司的标记策略对所有实例进行标记。然后,您可以删除将函数限制为仅限列表中的实例IDs的代码。

此示例使用 JavaScript 还有 16.x 版本的 Node.js。 该示例使用示例 Amazon Web Services 账户 ID 123456789012 还有 Amazon Web Services 区域 美国东部(弗吉尼亚北部)(us-east-1)。将这些替换为您自己的测试账户 ID 和区域。

注意

如果您的控制台默认使用其他区域,请确保在更改控制台时切换了本教程中使用的区域。本教程失败的一个常见原因:实例和函数位于两个不同的区域。

如果您使用的区域与 us-east-1 不同,请确保将以下代码示例中的所有引用更改为所选区域。

第 1 步。创建 Lambda 函数

创建 Lambda 函数
  1. 打开 Amazon Lambda 管理控制台

  2. 选择 创建函数,然后选择 从头开始创作

  3. 对于函数名称,请键入 AutoEC2Termination

  4. 对于运行时系统,选择 Node.js 16.x

  5. 将所有其他字段保留为默认值,然后选择创建函数

  6. AutoEC2Termination 详情页面的代码选项卡上,打开 index.js 文件以查看其代码。

    • 如果已打开带有 index.js 的选项卡,则可以在该选项卡中选择编辑框来编辑代码。

    • 如果包含 index.js 的选项卡未打开,请在导航窗格中 “自动” EC2Terminator 文件夹下单击 index.js 文件。然后选择 Open

  7. 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 Amazon Web Services 账户. // 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); } }); };
  8. 选择部署以保存您的更改并激活新版本函数。

此 Lambda 函数会检查中标签更改事件所报告的亚马逊EC2实例的标签。 EventBridge在此示例中,如果事件中的实例缺少所需的标签密钥 valid-key 或该标签没有值 valid-value,则该函数会尝试停止该实例。您可以根据自己的特定用例更改此逻辑检查或标签要求。

使 Lambda 控制台浏览器窗口保持打开状态。

第 2 步。设置所需的IAM权限

在函数成功运行之前,您必须向该函数授予停止EC2实例的权限。这些区域有: Amazon 提供的角色 lambda_basic_execution没有该权限。在本教程中,您将修改附加到名为的函数执行角色的默认IAM权限策略AutoEC2Termination-role-uniqueid。本教程所需的最低额外权限为 ec2:StopInstances

有关创建亚马逊EC2特定IAM策略的更多信息,请参阅《IAM用户指南中的 “AmazonEC2:允许以编程方式启动或停止EC2实例以及修改安全组”。

创建IAM权限策略并将其附加到 Lambda 函数的执行角色
  1. 在不同的浏览器选项卡或窗口中,打开IAM控制台的 “角色” 页面。

  2. 开始键入角色名称 AutoEC2Termination ,当角色名称出现在列表中时,选择角色名称。

  3. 在角色的摘要页面上,选择权限选项卡,然后选择已附加的一个策略的名称。

  4. 在策略的摘要页面上,选择 编辑策略

  5. 可视化编辑器选项卡上,选择添加额外权限

  6. 对于 Service,选择 EC2

  7. 在 “操作” 中,选择StopInstances。您可以在搜索栏中键入 Stop ,在它出现时选中 StopInstances

  8. 对于资源,选择所有资源,选择检查策略,然后选择保存更改

    这将自动创建新版本的策略并将该版本设置为默认版本。

    您的最终策略应类似于以下示例。

    { "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 函数进行初步测试

在本步骤中,您将向函数提交测试事件。Lambda 测试功能通过提交手动提交的测试事件来运行。该函数处理测试事件,就像事件来自一样 EventBridge。您可以定义多个具有不同值的测试事件,以测试代码的各个不同部分。在此步骤中,您将提交一个测试事件,该事件表明 Amazon EC2 实例的标签已更改,并且新标签不包含所需的标签键和值。

测试 Lambda 函数
  1. 使用 Lambda 控制台返回窗口或选项卡,然后打开自动EC2Termination函数的 “测试” 选项卡。

  2. 选择 创建新事件

  3. 对于事件名称,输入 SampleBadTagChangeEvent

  4. 事件中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 } }
  5. 选择 Save(保存),然后选择 Test(测试)。

    测试似乎失败了,但没关系。

    您应该会在响应下的执行结果选项卡中看到以下错误。

    { "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
  6. 要证明代码在使用正确标签时不会尝试停止实例,您可以创建并提交另一个测试事件。

    选择代码源上方的测试选项卡。控制台显示您现有的SampleBadTagChangeEvent测试事件。

  7. 选择 创建新事件

  8. 对于事件名称,键入 SampleGoodTagChangeEvent

  9. 在第 17 行中,删除 NOT-,将值更改为 valid-value

  10. 测试事件窗口的顶部,选择保存,然后选择测试

    输出显示以下内容,这表明该函数可以识别有效标签并且不会尝试关闭实例。

    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 规则
  1. 在其他浏览器选项卡或窗口中,打开EventBridge 控制台,进入 “创建规则” 页面。

  2. 名称中,输入 ec2-instance-rule,然后选择下一步

  3. 向下滚动到 “创建方法”,然后选择 “自定义模式(JSON编辑器)”。

  4. 在编辑框中,粘贴以下图案文本,然后选择下一步

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

    此规则Tag Change on Resource会匹配 Amazon EC2 实例的事件,并在下一步中调用您指定为 Targ et 的任何内容。

  5. 接下来,将 Lambda 函数添加为目标。在目标 1框中,在选择目标下,选择 Lambda 函数

  6. 在 “函数” 下,选择您之前创建的 “自动” EC2Termination 函数,然后选择 “下一步”。

  7. 请在 配置标签页面上,选择 下一步。然后,请在 审核和创建页面上,选择 创建规则。这还会自动授予调用 EventBridge 指定 Lambda 函数的权限。

第 5 步。测试完整的解决方案

您可以通过创建EC2实例并观察更改其标签时会发生什么来测试最终结果。

使用真实实例测试监控解决方案
  1. 打开 A mazon EC2 控制台,进入实例页面。

  2. 创建一个 Amazon EC2 实例。在启动之前,请附加带有密钥 valid-key 和值 valid-value 的标签。有关如何创建和启动实例的信息,请参阅 Amazon EC2 用户指南中的步骤 1:启动实例。在启动实例的过程中,在步骤 3 中,输入名称标签,还要选择添加额外标签,选择添加标签,然后输入密钥 valid-key valid-value。如果此实例仅用于本教程,并且您计划在完成后删除此实例,您可以在没有密钥对的情况下继续。完成步骤 1 后,返回本教程即可,您无需执行步骤 2:连接到实例

  3. InstanceId从控制台复制。

  4. 从亚马逊EC2控制台切换到 Lambda 控制台。选择您的自动EC2Termination功能,选择代码选项卡,然后选择 index.js 选项卡来编辑您的代码。

  5. InstanceList通过粘贴您从 Amazon EC2 控制台复制的值来更改中的第二个条目。确保该 RegionToMonitor 值与包含您粘贴的实例的区域相匹配。

  6. 选择部署以激活您的更改。现在,该函数已准备就绪,可以通过在指定区域对该实例进行标签更改来激活。

  7. 从 Lambda 控制台切换到亚马逊EC2控制台。

  8. 通过删除有效密钥标签或更改该密钥的值来更改附加到实例的标签

    注意

    有关如何在正在运行的亚马逊EC2实例上更改标签的信息,请参阅亚马逊EC2用户指南中的在单个资源上添加和删除标签

  9. 等待几秒钟,然后刷新控制台。该实例应将其实例状态更改为正在停止,然后更改为已停止

  10. 使用您的函数从 Amazon EC2 控制台切换到 Lambda 控制台,然后选择监控选项卡。

  11. 选择 “日” 选项卡,然后在 “最近的调用” 表中选择该列中的最新条目。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

教程摘要

本教程演示了如何为Amazon EC2 实例创建与资源事件的标签更改相匹配的 EventBridge 规则。该规则指向一个 Lambda 函数,如果实例没有所需的标签,该函数会自动关闭该实例。

Amazon EventBridge 支持更改标签 Amazon 资源为在许多领域构建事件驱动的自动化开辟了可能性 Amazon Web Services 服务。 将此功能与 Amazon Lambda 为您提供构建可访问的无服务器解决方案的工具 Amazon 资源安全、按需扩展,且具有成本效益。

该 tag-change-on-resource EventBridge 活动的其他可能用例包括:

  • 如果有人从异常 IP 地址访问您的资源,会发出警告 – 使用标签,储存访问您的资源的每位访客的源 IP 地址。对标签的更改会生成一个 CloudWatch 事件。您可以使用该事件将源 IP 地址与有效 IP 地址列表进行比较,并在源 IP 地址无效时激活警告电子邮件。

  • 监控资源的基于标签的访问控制是否发生了变化 — 如果您使用基于属性(标签)的访问控制 (ABAC) 设置了对资源的访问权限,则可以使用对标签进行任何更改所生成 EventBridge的事件来提示您的安全团队进行审计。