Complete Guide to Infrastructure as Code, Event-Driven Automation, and Cost Optimization
This diagram illustrates the four core pillars of AWS Infrastructure Automation:
This sequence diagram shows the typical IaC deployment workflow:
aws cloudformation create-stack \
--stack-name my-vpc-stack \
--template-body file://vpc-template.yaml \
--parameters ParameterKey=Environment,ParameterValue=prod \
--capabilities CAPABILITY_IAM \
--tags Key=Project,Value=WebApp Key=Owner,Value=DevTeam
--stack-name: Unique identifier for the stack within the region--template-body: Path to the CloudFormation template file--parameters: Key-value pairs for template parameters--capabilities: Required when creating IAM resources--tags: Metadata for resource organization and cost trackingAWSTemplateFormatVersion: '2010-09-09'
Description: 'Production VPC with public and private subnets'
Parameters:
Environment:
Type: String
Default: prod
AllowedValues: [dev, staging, prod]
Description: Environment name for resource naming
VpcCidr:
Type: String
Default: '10.0.0.0/16'
Description: CIDR block for VPC
This template header section defines:
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub '${Environment}-vpc'
- Key: Environment
Value: !Ref Environment
This creates the main VPC with the following properties:
Other VPC Options: InstanceTenancy (default|dedicated), Ipv6CidrBlock
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub '${Environment}-igw'
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
This configuration creates internet connectivity:
Execution Order: VPC → InternetGateway → VPCGatewayAttachment
This diagram shows the dependency chain for VPC infrastructure:
aws cloudformation update-stack \
--stack-name my-vpc-stack \
--template-body file://vpc-template-v2.yaml \
--parameters ParameterKey=Environment,ParameterValue=prod \
ParameterKey=VpcCidr,ParameterValue=10.1.0.0/16 \
--capabilities CAPABILITY_IAM
When updating stacks, CloudFormation determines what changes are needed:
Some resource changes require replacement (deletion and recreation). Always review change sets before applying updates to production environments.
# Check stack status
aws cloudformation describe-stacks \
--stack-name my-vpc-stack \
--query 'Stacks[0].StackStatus'
# Validate template before deployment
aws cloudformation validate-template \
--template-body file://vpc-template.yaml
# Check for configuration drift
aws cloudformation detect-stack-drift \
--stack-name my-vpc-stack
The CDK provides three levels of constructs:
# Initialize new CDK project
mkdir my-cdk-project
cd my-cdk-project
cdk init app --language=typescript
# Install additional CDK modules
npm install @aws-cdk/aws-ec2 @aws-cdk/aws-iam @aws-cdk/aws-lambda
This creates a new CDK project with TypeScript:
import * as cdk from '@aws-cdk/core';
import * as ec2 from '@aws-cdk/aws-ec2';
export class VpcStack extends cdk.Stack {
public readonly vpc: ec2.Vpc;
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Create VPC with public and private subnets
this.vpc = new ec2.Vpc(this, 'MyVpc', {
maxAzs: 2,
cidr: '10.0.0.0/16',
subnetConfiguration: [
{
cidrMask: 24,
name: 'public',
subnetType: ec2.SubnetType.PUBLIC,
},
{
cidrMask: 24,
name: 'private',
subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
}
]
});
}
}
This L2 construct automatically creates:
Other Subnet Types: PRIVATE_ISOLATED, PRIVATE_WITH_EGRESS
# Synthesize CloudFormation template
cdk synth
# Deploy stack
cdk deploy VpcStack --require-approval never
# Deploy with parameters
cdk deploy VpcStack \
--parameters VpcCidr=10.1.0.0/16 \
--parameters Environment=prod
# Cross-stack references
export class DatabaseStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, vpc: ec2.Vpc) {
super(scope, id);
const database = new rds.Database(this, 'Database', {
vpc: vpc, // Reference VPC from another stack
engine: rds.DatabaseInstanceEngine.mysql({
version: rds.MysqlEngineVersion.VER_8_0
})
});
}
}
CDK enables sharing resources between stacks:
This architecture shows how AWS events trigger automated responses:
aws events put-rule \
--name "SecurityGroupChangeRule" \
--event-pattern '{
"source": ["aws.ec2"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["ec2.amazonaws.com"],
"eventName": [
"AuthorizeSecurityGroupIngress",
"RevokeSecurityGroupIngress"
]
}
}' \
--state ENABLED
This rule monitors security group changes:
aws events put-targets \
--rule "SecurityGroupChangeRule" \
--targets "Id"="1","Arn"="arn:aws:lambda:us-east-1:123456789012:function:ProcessSecurityGroupChange"
# Grant EventBridge permission to invoke Lambda
aws lambda add-permission \
--function-name ProcessSecurityGroupChange \
--statement-id allow-eventbridge \
--action lambda:InvokeFunction \
--principal events.amazonaws.com \
--source-arn arn:aws:events:us-east-1:123456789012:rule/SecurityGroupChangeRule
Connecting EventBridge to Lambda requires:
Execution Order: Rule → Target → Permission
import json
import boto3
def lambda_handler(event, context):
ec2 = boto3.client('ec2')
# Extract security group ID from event
detail = event['detail']
sg_id = detail['requestParameters']['groupId']
# Check for dangerous rules (0.0.0.0/0 on port 22)
response = ec2.describe_security_groups(GroupIds=[sg_id])
sg = response['SecurityGroups'][0]
for rule in sg['IpPermissions']:
if rule.get('FromPort') == 22:
for ip_range in rule.get('IpRanges', []):
if ip_range.get('CidrIp') == '0.0.0.0/0':
# Remove dangerous rule
ec2.revoke_security_group_ingress(
GroupId=sg_id,
IpPermissions=[rule]
)
# Send notification
sns = boto3.client('sns')
sns.publish(
TopicArn='arn:aws:sns:us-east-1:123456789012:security-alerts',
Message=f'Removed dangerous SSH rule from {sg_id}',
Subject='Security Group Auto-Remediation'
)
return {'statusCode': 200}
This Lambda function automatically:
This sequence shows the automated response to security group changes:
# Create VPC Flow Logs
aws ec2 create-flow-logs \
--resource-type VPC \
--resource-ids vpc-12345678 \
--traffic-type ALL \
--log-destination-type cloud-watch-logs \
--log-group-name VPCFlowLogs \
--deliver-logs-permission-arn arn:aws:iam::123456789012:role/flowlogsRole
This approach ensures consistent deployments across environments:
# Store environment-specific parameters
aws ssm put-parameter \
--name "/myapp/dev/vpc-cidr" \
--value "10.0.0.0/16" \
--type "String" \
--description "VPC CIDR for development environment"
aws ssm put-parameter \
--name "/myapp/prod/vpc-cidr" \
--value "10.1.0.0/16" \
--type "String" \
--description "VPC CIDR for production environment"
# Store encrypted parameters
aws ssm put-parameter \
--name "/myapp/prod/database-password" \
--value "SuperSecretPassword123!" \
--type "SecureString" \
--key-id "alias/parameter-store-key"
Parameters:
Environment:
Type: String
Default: dev
AllowedValues: [dev, staging, prod]
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Sub '{{resolve:ssm:/myapp/${Environment}/vpc-cidr:1}}'
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub '${Environment}-vpc'
CloudFormation can resolve parameters at deployment time:
Other Resolvers: resolve:secretsmanager, resolve:ssm-secure
# Master template referencing nested stacks
Resources:
NetworkStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/my-templates/network-stack.yaml
Parameters:
Environment: !Ref Environment
VpcCidr: !Sub '{{resolve:ssm:/myapp/${Environment}/vpc-cidr:1}}'
Tags:
- Key: StackType
Value: Network
SecurityStack:
Type: AWS::CloudFormation::Stack
DependsOn: NetworkStack
Properties:
TemplateURL: https://s3.amazonaws.com/my-templates/security-stack.yaml
Parameters:
VpcId: !GetAtt NetworkStack.Outputs.VpcId
Environment: !Ref Environment
#!/bin/bash
ENVIRONMENTS=("dev" "staging" "prod")
TEMPLATE_URL="https://s3.amazonaws.com/my-templates/master-template.yaml"
for ENV in "${ENVIRONMENTS[@]}"; do
echo "Deploying to $ENV environment..."
aws cloudformation deploy \
--template-file master-template.yaml \
--stack-name "myapp-$ENV" \
--parameter-overrides Environment=$ENV \
--capabilities CAPABILITY_IAM \
--tags Environment=$ENV Project=MyApp \
--region us-east-1
# Wait for stack completion
aws cloudformation wait stack-deploy-complete \
--stack-name "myapp-$ENV" \
--region us-east-1
echo "$ENV deployment completed successfully!"
done
This script enables consistent deployments across environments:
Automated cost optimization involves multiple approaches:
# Create EventBridge rule for evening shutdown
aws events put-rule \
--name "StopDevInstancesEvening" \
--schedule-expression "cron(0 18 * * MON-FRI *)" \
--state ENABLED \
--description "Stop development instances at 6 PM weekdays"
# Create rule for morning startup
aws events put-rule \
--name "StartDevInstancesMorning" \
--schedule-expression "cron(0 8 * * MON-FRI *)" \
--state ENABLED \
--description "Start development instances at 8 AM weekdays"
import boto3
import json
def lambda_handler(event, context):
ec2 = boto3.client('ec2')
# Get the action from the event source
rule_name = event['source']
action = 'start' if 'Start' in rule_name else 'stop'
# Find instances with Environment=dev tag
response = ec2.describe_instances(
Filters=[
{'Name': 'tag:Environment', 'Values': ['dev']},
{'Name': 'instance-state-name',
'Values': ['stopped'] if action == 'start' else ['running']}
]
)
instance_ids = []
for reservation in response['Reservations']:
for instance in reservation['Instances']:
instance_ids.append(instance['InstanceId'])
if instance_ids:
if action == 'start':
ec2.start_instances(InstanceIds=instance_ids)
message = f"Started {len(instance_ids)} development instances"
else:
ec2.stop_instances(InstanceIds=instance_ids)
message = f"Stopped {len(instance_ids)} development instances"
# Send notification
sns = boto3.client('sns')
sns.publish(
TopicArn='arn:aws:sns:us-east-1:123456789012:cost-optimization',
Message=message,
Subject=f'EC2 Instance {action.title()} Automation'
)
return {
'statusCode': 200,
'body': json.dumps(f'Action: {action}, Instances: {len(instance_ids)}')
}
This Lambda function:
# Create Auto Scaling Group
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name MyApp-ASG \
--launch-template LaunchTemplateName=MyApp-LaunchTemplate,Version=1 \
--min-size 1 \
--max-size 10 \
--desired-capacity 2 \
--vpc-zone-identifier "subnet-12345678,subnet-87654321" \
--target-group-arns arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/MyApp-TG/1234567890123456 \
--health-check-type ELB \
--health-check-grace-period 300 \
--tags Key=Environment,Value=prod,PropagateAtLaunch=true
# Create CloudWatch billing alarm
aws cloudwatch put-metric-alarm \
--alarm-name "HighBillingAlarm" \
--alarm-description "Alert when monthly charges exceed $100" \
--metric-name EstimatedCharges \
--namespace AWS/Billing \
--statistic Maximum \
--period 86400 \
--threshold 100 \
--comparison-operator GreaterThanThreshold \
--dimensions Name=Currency,Value=USD \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:billing-alerts \
--unit None
Prerequisite: Enable billing alerts in AWS Billing console
This sequence shows the daily cost optimization cycle:
Comprehensive testing covers three phases:
# Validate template syntax
aws cloudformation validate-template \
--template-body file://infrastructure-template.yaml
# Estimate costs before deployment
aws cloudformation estimate-template-cost \
--template-body file://infrastructure-template.yaml \
--parameters ParameterKey=InstanceType,ParameterValue=t3.medium
# Create change set to preview changes
aws cloudformation create-change-set \
--stack-name my-infrastructure \
--template-body file://infrastructure-template.yaml \
--change-set-name preview-changes-v2 \
--capabilities CAPABILITY_IAM
# Install cfn-nag security scanner
gem install cfn-nag
# Scan CloudFormation template for security issues
cfn_nag_scan --input-path infrastructure-template.yaml
# Generate JSON output for CI/CD integration
cfn_nag_scan --input-path infrastructure-template.yaml --output-format json
cfn-nag identifies common security issues:
#!/bin/bash
STACK_NAME="my-infrastructure"
REGION="us-east-1"
echo "Starting post-deployment validation for $STACK_NAME"
# Get stack outputs
VPC_ID=$(aws cloudformation describe-stacks \
--stack-name $STACK_NAME \
--region $REGION \
--query 'Stacks[0].Outputs[?OutputKey==`VpcId`].OutputValue' \
--output text)
PUBLIC_SUBNET_ID=$(aws cloudformation describe-stacks \
--stack-name $STACK_NAME \
--region $REGION \
--query 'Stacks[0].Outputs[?OutputKey==`PublicSubnetId`].OutputValue' \
--output text)
# Test 1: Verify VPC exists and is available
echo "Testing VPC availability..."
VPC_STATE=$(aws ec2 describe-vpcs \
--vpc-ids $VPC_ID \
--region $REGION \
--query 'Vpcs[0].State' \
--output text)
if [ "$VPC_STATE" != "available" ]; then
echo "❌ VPC is not available: $VPC_STATE"
exit 1
else
echo "✅ VPC is available"
fi
# Test 2: Verify internet connectivity from public subnet
echo "Testing internet connectivity..."
INSTANCE_ID=$(aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type t3.micro \
--subnet-id $PUBLIC_SUBNET_ID \
--associate-public-ip-address \
--user-data '#!/bin/bash
yum update -y
yum install -y awscli
curl -s http://checkip.amazonaws.com > /tmp/public-ip.txt' \
--query 'Instances[0].InstanceId' \
--output text)
# Wait for instance to be running
aws ec2 wait instance-running --instance-ids $INSTANCE_ID --region $REGION
# Test connectivity (simplified - in practice, use Systems Manager)
sleep 60 # Allow time for user data script
# Cleanup test instance
aws ec2 terminate-instances --instance-ids $INSTANCE_ID --region $REGION
echo "✅ All tests passed successfully!"
# Detect drift in stack
aws cloudformation detect-stack-drift \
--stack-name my-infrastructure
# Get drift detection status
DRIFT_ID=$(aws cloudformation detect-stack-drift \
--stack-name my-infrastructure \
--query 'StackDriftDetectionId' \
--output text)
# Wait for drift detection to complete
aws cloudformation describe-stack-drift-detection-status \
--stack-drift-detection-id $DRIFT_ID
# Get detailed drift results
aws cloudformation describe-stack-resource-drifts \
--stack-name my-infrastructure
Drift detection identifies manual changes to resources:
# Create Config rule for security group compliance
aws configservice put-config-rule \
--config-rule '{
"ConfigRuleName": "security-group-ssh-check",
"Description": "Checks whether security groups allow unrestricted SSH access",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "INCOMING_SSH_DISABLED"
},
"Scope": {
"ComplianceResourceTypes": [
"AWS::EC2::SecurityGroup"
]
}
}'
# Create remediation configuration
aws configservice put-remediation-configurations \
--remediation-configurations '[{
"ConfigRuleName": "security-group-ssh-check",
"TargetType": "SSM_DOCUMENT",
"TargetId": "RemoveUnrestrictedSourceInSecurityGroup",
"TargetVersion": "1",
"Parameters": {
"AutomationAssumeRole": {
"StaticValue": {
"Values": ["arn:aws:iam::123456789012:role/ConfigRemediationRole"]
}
},
"GroupId": {
"ResourceValue": {
"Value": "RESOURCE_ID"
}
}
},
"Automatic": true,
"MaximumAutomaticAttempts": 3
}]'
This comprehensive workflow ensures infrastructure quality:
# Create Systems Manager automation document
aws ssm create-document \
--name "NetworkConnectivityTest" \
--document-type "Automation" \
--document-format "YAML" \
--content '{
"schemaVersion": "0.3",
"description": "Test network connectivity in VPC",
"assumeRole": "arn:aws:iam::123456789012:role/SSMAutomationRole",
"parameters": {
"SubnetId": {
"type": "String",
"description": "Subnet ID to test connectivity"
},
"TargetUrl": {
"type": "String",
"default": "https://aws.amazon.com",
"description": "URL to test connectivity"
}
},
"mainSteps": [
{
"name": "LaunchTestInstance",
"action": "aws:runInstances",
"inputs": {
"ImageId": "ami-0abcdef1234567890",
"InstanceType": "t3.micro",
"SubnetId": "{{ SubnetId }}",
"UserData": "IyEvYmluL2Jhc2gKY3VybCAtcyB7eyBUYXJnZXRVcmwgfX0gPiAvdG1wL2Nvbm5lY3Rpdml0eS10ZXN0LnR4dA=="
}
},
{
"name": "WaitForInstance",
"action": "aws:waitForAwsResourceProperty",
"inputs": {
"Service": "ec2",
"Api": "DescribeInstances",
"InstanceIds": ["{{ LaunchTestInstance.InstanceIds }}"],
"PropertySelector": "$.Reservations[0].Instances[0].State.Name",
"DesiredValues": ["running"]
}
},
{
"name": "TerminateTestInstance",
"action": "aws:changeInstanceState",
"inputs": {
"InstanceIds": ["{{ LaunchTestInstance.InstanceIds }}"],
"DesiredState": "terminated"
}
}
]
}'
# Create custom metric for application performance
aws cloudwatch put-metric-data \
--namespace "MyApp/Performance" \
--metric-data MetricName=ResponseTime,Value=245,Unit=Milliseconds,Dimensions=[{Name=Environment,Value=prod}]
# Create performance alarm
aws cloudwatch put-metric-alarm \
--alarm-name "HighResponseTime" \
--alarm-description "Alert when response time exceeds 500ms" \
--metric-name ResponseTime \
--namespace MyApp/Performance \
--statistic Average \
--period 300 \
--threshold 500 \
--comparison-operator GreaterThanThreshold \
--dimensions Name=Environment,Value=prod \
--evaluation-periods 2 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:performance-alerts
These patterns enable sophisticated deployment strategies:
# CodeBuild project for GitOps automation
aws codebuild create-project \
--name "infrastructure-gitops" \
--source '{
"type": "GITHUB",
"location": "https://github.com/myorg/infrastructure-repo",
"buildspec": "buildspec.yml"
}' \
--artifacts '{"type": "NO_ARTIFACTS"}' \
--environment '{
"type": "LINUX_CONTAINER",
"image": "aws/codebuild/amazonlinux2-x86_64-standard:3.0",
"computeType": "BUILD_GENERAL1_MEDIUM",
"privilegedMode": true
}' \
--service-role "arn:aws:iam::123456789012:role/CodeBuildServiceRole"
# Create blue environment stack
aws cloudformation create-stack \
--stack-name "myapp-blue" \
--template-body file://application-stack.yaml \
--parameters ParameterKey=Environment,ParameterValue=blue \
ParameterKey=Color,ParameterValue=blue \
--capabilities CAPABILITY_IAM
# Health check function
aws lambda create-function \
--function-name "HealthChecker" \
--runtime "python3.9" \
--role "arn:aws:iam::123456789012:role/LambdaExecutionRole" \
--handler "health_check.lambda_handler" \
--zip-file "fileb://health-check-function.zip" \
--environment Variables='{
"BLUE_ENDPOINT": "https://blue.myapp.com",
"GREEN_ENDPOINT": "https://green.myapp.com"
}'
Blue/Green deployment requires:
This sequence shows zero-downtime deployment:
# Deploy to multiple regions with CodePipeline
aws codepipeline create-pipeline \
--pipeline '{
"name": "MultiRegionDeployment",
"roleArn": "arn:aws:iam::123456789012:role/CodePipelineRole",
"artifactStore": {
"us-east-1": {
"type": "S3",
"location": "codepipeline-artifacts-us-east-1"
},
"us-west-2": {
"type": "S3",
"location": "codepipeline-artifacts-us-west-2"
}
},
"stages": [
{
"name": "Source",
"actions": [{
"name": "SourceAction",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "S3",
"version": "1"
},
"configuration": {
"S3Bucket": "my-source-bucket",
"S3ObjectKey": "infrastructure.zip"
},
"outputArtifacts": [{"name": "SourceOutput"}]
}]
},
{
"name": "DeployPrimary",
"actions": [{
"name": "DeployUSEast1",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "CloudFormation",
"version": "1"
},
"configuration": {
"ActionMode": "CREATE_UPDATE",
"StackName": "MyApp-Primary",
"TemplatePath": "SourceOutput::template.yaml",
"Capabilities": "CAPABILITY_IAM",
"RoleArn": "arn:aws:iam::123456789012:role/CloudFormationRole"
},
"inputArtifacts": [{"name": "SourceOutput"}],
"region": "us-east-1"
}]
},
{
"name": "DeploySecondary",
"actions": [{
"name": "DeployUSWest2",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "CloudFormation",
"version": "1"
},
"configuration": {
"ActionMode": "CREATE_UPDATE",
"StackName": "MyApp-Secondary",
"TemplatePath": "SourceOutput::template.yaml",
"Capabilities": "CAPABILITY_IAM",
"RoleArn": "arn:aws:iam::123456789012:role/CloudFormationRole"
},
"inputArtifacts": [{"name": "SourceOutput"}],
"region": "us-west-2"
}]
}
]
}'
This diagram shows the proper order for creating infrastructure components. Each level depends on the completion of the previous level.
Create foundational security components before any other resources.
# 1a. Create CloudFormation execution role
aws iam create-role \
--role-name CloudFormationExecutionRole \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "cloudformation.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
# 1b. Attach administrator policy (for demo - use least privilege in production)
aws iam attach-role-policy \
--role-name CloudFormationExecutionRole \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
# 1c. Create Lambda execution role for automation
aws iam create-role \
--role-name LambdaAutomationRole \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
IAM resources must be created first because:
Create the network foundation that all other resources will use.
# 2a. Deploy VPC stack (depends on IAM roles from step 1)
aws cloudformation create-stack \
--stack-name core-networking \
--template-body file://vpc-template.yaml \
--parameters ParameterKey=Environment,ParameterValue=prod \
ParameterKey=VpcCidr,ParameterValue=10.0.0.0/16 \
--capabilities CAPABILITY_IAM \
--role-arn arn:aws:iam::123456789012:role/CloudFormationExecutionRole
# 2b. Wait for VPC stack completion before proceeding
aws cloudformation wait stack-create-complete \
--stack-name core-networking
# 2c. Get VPC outputs for next steps
VPC_ID=$(aws cloudformation describe-stacks \
--stack-name core-networking \
--query 'Stacks[0].Outputs[?OutputKey==`VpcId`].OutputValue' \
--output text)
Create security rules that reference the VPC from Step 2.
# 3a. Create application security group (depends on VPC from step 2)
aws ec2 create-security-group \
--group-name app-security-group \
--description "Security group for application servers" \
--vpc-id $VPC_ID
# 3b. Get security group ID
SG_ID=$(aws ec2 describe-security-groups \
--filters "Name=group-name,Values=app-security-group" \
"Name=vpc-id,Values=$VPC_ID" \
--query 'SecurityGroups[0].GroupId' \
--output text)
# 3c. Add ingress rules
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--protocol tcp \
--port 443 \
--cidr 0.0.0.0/0
Deploy application infrastructure using networking and security from previous steps.
# 4a. Deploy application stack (depends on VPC and security groups)
aws cloudformation create-stack \
--stack-name application-resources \
--template-body file://application-template.yaml \
--parameters ParameterKey=VpcId,ParameterValue=$VPC_ID \
ParameterKey=SecurityGroupId,ParameterValue=$SG_ID \
ParameterKey=Environment,ParameterValue=prod \
--capabilities CAPABILITY_IAM \
--role-arn arn:aws:iam::123456789012:role/CloudFormationExecutionRole
# 4b. Wait for application deployment
aws cloudformation wait stack-create-complete \
--stack-name application-resources
Set up monitoring for the deployed application infrastructure.
# 5a. Create SNS topic for alerts
aws sns create-topic --name infrastructure-alerts
# 5b. Get topic ARN
TOPIC_ARN=$(aws sns list-topics \
--query 'Topics[?contains(TopicArn, `infrastructure-alerts`)].TopicArn' \
--output text)
# 5c. Create CloudWatch alarms for application
aws cloudwatch put-metric-alarm \
--alarm-name "ApplicationHealthCheck" \
--alarm-description "Application health monitoring" \
--metric-name HealthCheck \
--namespace AWS/ApplicationELB \
--statistic Average \
--period 300 \
--threshold 1 \
--comparison-operator LessThanThreshold \
--evaluation-periods 2 \
--alarm-actions $TOPIC_ARN
Set up automation that responds to infrastructure events.
# 6a. Create Lambda function for automation (depends on all previous resources)
aws lambda create-function \
--function-name InfrastructureAutomation \
--runtime python3.9 \
--role arn:aws:iam::123456789012:role/LambdaAutomationRole \
--handler lambda_function.lambda_handler \
--zip-file fileb://automation-function.zip \
--environment Variables='{
"VPC_ID": "'$VPC_ID'",
"SECURITY_GROUP_ID": "'$SG_ID'",
"SNS_TOPIC_ARN": "'$TOPIC_ARN'"
}'
# 6b. Create EventBridge rule for infrastructure changes
aws events put-rule \
--name "InfrastructureChangeRule" \
--event-pattern '{
"source": ["aws.ec2", "aws.cloudformation"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["ec2.amazonaws.com", "cloudformation.amazonaws.com"]
}
}'
# 6c. Add Lambda as target for the rule
aws events put-targets \
--rule "InfrastructureChangeRule" \
--targets "Id"="1","Arn"="arn:aws:lambda:us-east-1:123456789012:function:InfrastructureAutomation"
This mind map shows the interconnected nature of infrastructure automation components. Success requires integration across all these areas rather than focusing on individual tools.
aws cloudformation validate-templateThis guide provides a comprehensive foundation for AWS Infrastructure Automation. Continue learning by implementing these patterns in your own environment and adapting them to your specific requirements.