当CloudWatch Events收到有关创建新EC2实例的通知时,检测该实例是否属于CloudFormation堆栈的最简单方法是什么?
我的第一个方法是在实例上调用DescribeInstances
来查找aws:cloudformation:stack-id
标记,但显然在添加该标记之前会有一段延迟,所以这会变得混乱。
我应该直接查询CloudFormation API吗?当我看到关于新EC2实例的CloudWatch事件时,是否有一个订购保证,如果我调用CloudFormation API,此EC2实例将显示为成员?
答案 0 :(得分:1)
要确定CloudWatch Event中返回的EC2实例ID是否与正在运行的CloudFormation堆栈中的实例相对应,您可以使用当前堆栈ID调用DescribeStackResources
API作为StackName
request parameter。如果返回任何StackResource
并且PhysicalResourceId
与事件返回的EC2实例ID匹配,那么您可以确定该事件对应于当前堆栈中的实例。
这是一个完整的,有效的例子:
Description: Run a Lambda function when the EC2 instance is created using a CloudWatch Event.
Mappings:
# amzn-ami-hvm-2016.09.1.20161221-x86_64-gp2
RegionMap:
us-east-1:
"64": "ami-9be6f38c"
Parameters:
InstanceType:
Description: EC2 instance type
Type: String
Default: m3.medium
AllowedValues: [m3.medium, m3.large, m3.xlarge, m3.2xlarge, c3.large,
c3.xlarge, c3.2xlarge, c3.4xlarge, c3.8xlarge, r3.large, r3.xlarge, r3.2xlarge, r3.4xlarge,
r3.8xlarge, i2.xlarge, i2.2xlarge, i2.4xlarge, i2.8xlarge]
ConstraintDescription: Please choose a valid instance type.
Resources:
WebServer:
Type: AWS::EC2::Instance
DependsOn: EventLambdaPermission
Properties:
ImageId: !FindInMap [ RegionMap, !Ref "AWS::Region", 64]
InstanceType: !Ref InstanceType
EventRule:
Type: AWS::Events::Rule
Properties:
Description: EventRule
EventPattern:
source: ["aws.ec2"]
detail-type: ["EC2 Instance State-change Notification"]
detail:
state: [pending]
State: ENABLED
Targets:
- Arn: !GetAtt EC2StateChange.Arn
Id: TargetFunction
EC2StateChange:
Type: AWS::Lambda::Function
Properties:
Description: Sends a Wait Condition signal to Handle when an EC2 Instance State-change from this stack is received.
Handler: index.handler
Role: !GetAtt LambdaExecutionRole.Arn
Code:
ZipFile: !Sub |
var AWS = require('aws-sdk');
exports.handler = function(event, context) {
console.log("Request received:\n", JSON.stringify(event));
var instanceId = event.detail['instance-id'];
var cloudformation = new AWS.CloudFormation();
cloudformation.describeStackResources({StackName: '${AWS::StackId}'}).promise().then((stackData)=>{
if (stackData.StackResources.find((stack)=> stack.PhysicalResourceId == instanceId)) {
finish(context, {
"Status" : "SUCCESS",
"UniqueId" : "InstanceId",
"Data" : instanceId,
"Reason" : ""
});
} else {
console.log("Instance ID not found in this stack");
context.done();
}
}).catch((e)=>{
console.log("Error:\n",JSON.stringify(e));
finish(context, {
"Status" : "FAILED",
"UniqueId" : "InstanceId",
"Data" : instanceId,
"Reason" : e.message
});
});
};
function finish(context, response) {
responseBody = JSON.stringify(response);
var https = require("https");
var url = require("url");
var parsedUrl = url.parse('${Handle}');
var options = {
hostname: parsedUrl.hostname,
port: 443,
path: parsedUrl.path,
method: "PUT",
headers: {
"content-type": "",
"content-length": responseBody.length
}
};
console.log("Options:\n", JSON.stringify(options));
var request = https.request(options, function(response) {
console.log("Status code: " + response.statusCode);
console.log("Status message: " + response.statusMessage);
console.log("Done!");
context.done();
});
request.on("error", function(error) {
console.log("send(..) failed executing https.request(..): " + error);
context.done();
});
request.write(responseBody);
request.end();
}
Timeout: 30
Runtime: nodejs4.3
EventLambdaPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref EC2StateChange
Action: "lambda:InvokeFunction"
Principal: "events.amazonaws.com"
SourceArn: !GetAtt EventRule.Arn
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal: {Service: [lambda.amazonaws.com]}
Action: ['sts:AssumeRole']
Path: /
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
Policies:
- PolicyName: EC2Policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'cloudformation:DescribeStackResources'
Resource: ['*']
Handle:
Type: AWS::CloudFormation::WaitConditionHandle
Wait:
Type: AWS::CloudFormation::WaitCondition
Properties:
Handle: !Ref Handle
Timeout: 300
Outputs:
Result:
Value: !GetAtt Wait.Data
答案 1 :(得分:0)
wjordan comment中的答案对我有用,似乎可靠,没有任何竞争条件。
解决方案:只要在Cloudwatch事件流中检测到实例,就针对cloudformation API调用DescribeStackResources(..)
并传递{PhysicalResourceId: instanceId}
。它将返回实例所属的堆栈ID,如果它不是任何cloudformation堆栈的一部分,则返回空结果。
我在Go中实现了它并将其放在tleyden/awsutil repo中的github上。