如何通过CloudFormation更新栈实现EC2实例文件的自动化同步更新?
Alright, let's walk through setting up this automated workflow so you can sync your local file changes to EC2 via CloudFormation with just a stack update. Here's how to do it step by step:
First, we need to make sure every time you update the stack, your modified local file gets pushed to S3 automatically. The easiest way is to wrap the S3 upload and CloudFormation update into a single script.
Create a bash script (name it something like update-stack-with-sync.sh) with these contents:
#!/bin/bash # Replace these values with your own LOCAL_FILE="./your-local-file.txt" S3_BUCKET_PATH="s3://your-bucket-name/path/to/remote-file.txt" STACK_NAME="your-ec2-stack-name" CFN_TEMPLATE="./your-cloudformation-template.yaml" # Upload the updated file to S3 (enable versioning on your bucket for rollbacks!) aws s3 cp "$LOCAL_FILE" "$S3_BUCKET_PATH" # Trigger the CloudFormation stack update aws cloudformation update-stack \ --stack-name "$STACK_NAME" \ --template-body file://"$CFN_TEMPLATE" \ --capabilities CAPABILITY_NAMED_IAM # Add this only if your template uses IAM resources
Then make it executable:
chmod +x update-stack-with-sync.sh
From now on, after editing your local file, just run ./update-stack-with-sync.sh and it handles both upload and stack update.
The original template probably copies the file once on instance creation. We need to adjust it so stack updates trigger a file sync to EC2. Here are two solid options:
Option 1: Use a Custom Resource + Lambda (Most Flexible)
This approach uses a Lambda function triggered by CloudFormation stack updates to send a command to your EC2 instance via SSM Run Command, pulling the latest file from S3.
Step 2.1 Add Lambda Resources to Your Template
Add these resources to your CloudFormation YAML:
Resources: # Your existing EC2 instance (keep this) MyEC2Instance: Type: AWS::EC2::Instance Properties: # Your existing config (AMI, instance type, etc.) # Optional: Initial file copy on first launch UserData: Fn::Base64: !Sub | #!/bin/bash aws s3 cp s3://your-bucket-name/path/to/remote-file.txt /home/ec2-user/ # IAM Role for Lambda to access SSM and CloudFormation SyncFileLambdaRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: SyncFilePermissions PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - ssm:SendCommand - ec2:DescribeInstances Resource: '*' - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: 'arn:aws:logs:*:*:*' # Lambda Function to trigger SSM command on EC2 SyncFileLambda: Type: AWS::Lambda::Function Properties: Handler: index.lambda_handler Runtime: python3.11 Role: !GetAtt SyncFileLambdaRole.Arn Code: ZipFile: | import boto3 ssm = boto3.client('ssm') def lambda_handler(event, context): instance_id = event['ResourceProperties']['InstanceId'] s3_path = event['ResourceProperties']['S3FilePath'] local_path = event['ResourceProperties']['LocalPath'] try: # Send command to EC2 to sync the file response = ssm.send_command( InstanceIds=[instance_id], DocumentName='AWS-RunShellScript', Parameters={'commands': [f'aws s3 cp {s3_path} {local_path}']} ) return {'Status': 'SUCCESS', 'PhysicalResourceId': response['Command']['CommandId']} except Exception as e: return {'Status': 'FAILED', 'Reason': str(e)} # Custom Resource that triggers Lambda on stack update SyncFileTrigger: Type: AWS::CloudFormation::CustomResource Properties: ServiceToken: !GetAtt SyncFileLambda.Arn InstanceId: !Ref MyEC2Instance S3FilePath: s3://your-bucket-name/path/to/remote-file.txt LocalPath: /home/ec2-user/ # Replace with your EC2 user's home directory DependsOn: MyEC2Instance
Notes for Option 1:
- Your EC2 instance needs the SSM Agent installed (Amazon Linux 2/2023 have it by default) and must be able to reach SSM (either via public internet or VPC endpoints for SSM).
- Make sure your EC2 instance's IAM role has permission to read from the S3 bucket.
Option 2: Use cfn-init (Simpler, No Lambda)
If you want to avoid Lambda, use CloudFormation's cfn-init tool to re-sync the file whenever the stack is updated. We'll add a parameter to force the sync trigger.
Step 2.1 Update Template with cfn-init
Parameters: FileSyncTrigger: Type: String Default: "v1" Description: "Change this value (e.g., v2, timestamp) to trigger a file sync on stack update" Resources: MyEC2Instance: Type: AWS::EC2::Instance Properties: # Your existing instance config UserData: Fn::Base64: !Sub | #!/bin/bash # Install cfn-bootstrap if not present yum install -y aws-cfn-bootstrap # Run cfn-init to sync the file /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource MyEC2Instance --region ${AWS::Region} Metadata: AWS::CloudFormation::Init: configSets: default: [sync-file] sync-file: files: "/home/ec2-user/your-file.txt": # Target path on EC2 source: !Sub "s3://your-bucket-name/path/to/remote-file.txt" mode: "000644" owner: "ec2-user" group: "ec2-user"
How Option 2 Works:
Every time you run the stack update script, just modify the FileSyncTrigger parameter (e.g., set it to v2, $(date +%s), etc.). CloudFormation detects the parameter change and re-runs cfn-init, which pulls the latest file from S3 to EC2.
- Edit your local file with changes
- Run your update script (for Option 1) or run the update command with a new
FileSyncTriggervalue (for Option 2) - SSH into your EC2 instance and check the file in the home directory — it should match your local modified version!
- Enable S3 Versioning on your bucket: This lets you roll back to previous file versions if something goes wrong.
- IAM Permissions: Ensure your EC2 instance's role has
s3:GetObjectpermission for the target file, and your local environment has permissions to runaws s3 cpandaws cloudformation update-stack. - SSM Access (Option 1): If your EC2 is in a private subnet, set up VPC endpoints for SSM and EC2 Messages so it can communicate with the SSM service without public internet access.
内容的提问来源于stack exchange,提问作者hmty

