如何通过CloudFormation基于东京区域今日AMIs创建对应数量EC2实例?
Alright, let's tackle this problem step by step. The core challenge here is that CloudFormation doesn't natively support dynamic resource creation based on runtime data like today's freshly backed-up AMIs. But we can solve this with a Lambda-backed Custom Resource to fetch the AMI list, then use CloudFormation's dynamic iteration tools to spin up matching EC2 instances. Here's a practical, production-ready solution:
Core Approach
We'll break this into three key parts:
- A Lambda function that queries your Tokyo region (
ap-northeast-1) for self-owned AMIs created today. - A CloudFormation Custom Resource that invokes this Lambda to get the AMI list at stack deployment time.
- Dynamic EC2 instance creation using CloudFormation's
Fn::ForEachto build one instance per AMI.
Step 1: Define the Lambda Function & IAM Role
First, we need a Lambda function with permissions to describe your AMIs, plus an IAM role to grant those permissions.
AWSTemplateFormatVersion: '2010-09-09' Resources: # IAM Role for Lambda to access EC2 and write logs LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: EC2DescribeImagesAccess PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: ec2:DescribeImages Resource: '*' # Lambda Function to fetch today's self-owned AMIs FetchTodayAmisFunction: Type: AWS::Lambda::Function Properties: Handler: index.lambda_handler Runtime: python3.11 Role: !GetAtt LambdaExecutionRole.Arn Code: ZipFile: | import boto3 import datetime import json from botocore.exceptions import ClientError ec2 = boto3.client('ec2', region_name='ap-northeast-1') def lambda_handler(event, context): # Calculate today's start time in UTC (adjust to JST if needed) today = datetime.datetime.now(datetime.timezone.utc).replace( hour=0, minute=0, second=0, microsecond=0 ) today_iso = today.isoformat() try: # Filter AMIs: self-owned, created today response = ec2.describe_images( Owners=['self'], Filters=[ { 'Name': 'creation-date', 'Values': [f'>={today_iso}'] } ] ) # Extract AMI IDs ami_ids = [ami['ImageId'] for ami in response['Images']] # Return success to CloudFormation return { 'Status': 'SUCCESS', 'PhysicalResourceId': context.log_stream_name, 'Data': {'AmiIds': ','.join(ami_ids)} } except ClientError as e: # Return error details if something breaks return { 'Status': 'FAILED', 'PhysicalResourceId': context.log_stream_name, 'Reason': str(e) } Timeout: 30
Step 2: Add the Custom Resource to Fetch AMIs
Next, we'll define a Custom Resource that calls the Lambda function to get our AMI list when the stack deploys:
# Custom Resource to retrieve today's AMIs TodayAmis: Type: Custom::FetchTodayAmis Properties: ServiceToken: !GetAtt FetchTodayAmisFunction.Arn
Step 3: Dynamically Create EC2 Instances
Finally, we use CloudFormation's Fn::ForEach (requires the latest template features) to iterate over the AMI list and create one EC2 instance per AMI:
# Dynamically create EC2 instances for each AMI EC2InstanceStack: Type: AWS::CloudFormation::Stack Properties: TemplateBody: AWSTemplateFormatVersion: '2010-09-09' Resources: Fn::ForEach::AmiIteration: - AmiId - !Split [",", !GetAtt TodayAmis.AmiIds] - !Sub "EC2Instance-${AmiId}": Type: AWS::EC2::Instance Properties: ImageId: !Ref AmiId InstanceType: t2.micro # Replace with your desired instance type # Add your required config here: SecurityGroups, KeyName, SubnetId, etc. SecurityGroups: [!Ref InstanceSecurityGroup] # Example reference # Example Security Group (customize to your needs) InstanceSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Basic security group for EC2 instances SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 0.0.0.0/0 # Restrict this to your IP range in production
Key Notes & Customizations
- Instance Configuration: Replace
t2.microwith your preferred instance type, and add critical properties likeKeyName,SubnetId, or additionalSecurityGroupsbased on your VPC setup. - Timezone Adjustment: The Lambda uses UTC for the "today" cutoff. If you need to use Tokyo local time (JST, UTC+9), adjust the datetime calculation to offset accordingly.
- AMI Filtering: If your daily backups use specific tags (e.g.,
BackupType: Daily), add an extra filter to the Lambda'sdescribe_imagescall to avoid picking up unrelated AMIs. - Legacy CloudFormation Support: If you can't use
Fn::ForEach, you can modify the Lambda to directly create EC2 instances—but this means CloudFormation won't manage their lifecycle (not recommended for long-term use).
内容的提问来源于stack exchange,提问作者user2155404

