Hey everyone! Welcome back to our AWS ECS journey. Today we're diving into three crucial components that will make our containerized application secure, scalable, and automatically deployable.
If you've been following along, we've already set up our VPC, subnets, and ECS cluster. Now it's time to add the security layer, traffic distribution, and automated builds.
What We're Building Today
Today we'll create:
- Security Groups - Our network firewall rules
- Application Load Balancer - Traffic distribution across containers
- CodeBuild Project - Automated container image builds
Think of security groups as your network bouncer, the load balancer as your traffic director, and CodeBuild as your automated factory worker.
Security Groups - Your Network Firewall
Security groups act like a firewall for your AWS resources. They control what traffic can come in and go out of your services.
Why Security Groups Matter
Without proper security groups, your application could be:
- Exposed to unwanted traffic
- Vulnerable to attacks
- Unable to communicate with other services
Let's create our security group configuration:
# infra/securitygroup/sg.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Creating Security Group for Learning Purpose'
Parameters:
TeamNameValue:
Type: String
Description: TeamName Tag Value
Default: "awslearner"
EnvironmentValue:
Type: String
Description: Environment Tag Value
Default: "dev"
SGName:
Type: String
Description: Name of the Security Group
Default: "learner-public-sg"
VpcCidr:
Type: String
Default: 10.0.0.0/18
Description: VPC CIDR range for allowed inbound traffic
VPC:
Type: AWS::SSM::Parameter::Value<String>
Description: VPC ID
Default: "/learner/vpcid"
SSMName:
Type: String
Description: SG ID
Default: "/learner/public/sgid"
Resources:
# Firewall rules for your containers
ECSPublicSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Ref SGName
GroupDescription: "Security group for ECS service accessible only inside VPC"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: !Ref VpcCidr
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: !Ref VpcCidr
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: !Ref SGName
- Key: TeamName
Value: !Ref TeamNameValue
- Key: Environment
Value: !Ref EnvironmentValue
# Saves security group ID for ECS to use
SGSSM:
Type: AWS::SSM::Parameter
Properties:
Name: !Ref SSMName
Type: String
Value: !Ref ECSPublicSG
Description: "Security Group ID for ECS Fargate service"
Tags:
Name: !Ref SSMName
TeamName: !Ref TeamNameValue
Environment: !Ref EnvironmentValue
Outputs:
ECSServiceSecurityGroupId:
Description: Security Group ID for ECS Fargate Service
Value: !Ref ECSPublicSG
What This Security Group Does
This configuration creates a security group that:
- Allows HTTP traffic (port 80) from both VPC and internet
- Allows HTTPS traffic (port 443) from within VPC
- Stores the security group ID in SSM Parameter Store for other services to reference
- Allows all outbound traffic for container operations
Application Load Balancer - Your Traffic Director
The Application Load Balancer distributes incoming traffic across multiple containers and handles routing based on URL paths.
# infra/alb/alb.yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Creating Application Load Balancer for Learning Purpose"
Parameters:
Name:
Type: String
Description: The name of the load balancer.
Default: "learner-alb"
Type:
Type: String
Description: The type of load balancer. The default is application
Default: application
Scheme:
Type: String
Description: The nodes of an Internet-facing load balancer have public IP addresses
Default: internet-facing
TeamNameValue:
Type: String
Description: TeamName Tag Value
Default: "awslearner"
EnvironmentValue:
Type: String
Description: Environment Tag Value
Default: "dev"
PublicSubnetIds:
Type: AWS::SSM::Parameter::Value<String>
Description: Subnet ID
Default: "/learner/public/subnetids"
SecurityGroup:
Type: AWS::SSM::Parameter::Value<String>
Description: Security Group
Default: "/learner/public/sgid"
VpcId:
Type: AWS::SSM::Parameter::Value<String>
Description: VPC ID
Default: "/learner/vpcid"
SSMName:
Type: String
Default: "/learner/target/value"
Resources:
# Traffic distributor that spreads requests across your containers
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
LoadBalancerAttributes:
- Key: 'idle_timeout.timeout_seconds'
Value: '600'
Name: !Ref Name
Scheme: !Ref Scheme
SecurityGroups:
- !Ref SecurityGroup
Subnets: !Split
- ","
- !Ref PublicSubnetIds
Type: !Ref Type
Tags:
- Key: Name
Value: !Ref Name
- Key: TeamName
Value: !Ref TeamNameValue
- Key: Environment
Value: !Ref EnvironmentValue
# Group of containers that will receive traffic
TargetGroup1:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: "learner-tg"
Port: 80
Protocol: HTTP
VpcId: !Ref VpcId
TargetType: ip
# Saves target group info for ECS service to use
Target1SSM:
Type: AWS::SSM::Parameter
Properties:
Name: !Ref SSMName
Type: String
Value: !Ref TargetGroup1
Description: "Target SSM"
Tags:
Name: !Ref SSMName
TeamName: !Ref TeamNameValue
Environment: !Ref EnvironmentValue
# Listens for incoming web requests on port 80
MyListener:
Type: AWS::ElasticLoadBalancingV2::Listener
DependsOn: ApplicationLoadBalancer
Properties:
LoadBalancerArn: !Ref ApplicationLoadBalancer
Port: 80
Protocol: HTTP
DefaultActions:
- Type: fixed-response
FixedResponseConfig:
ContentType: "text/plain"
MessageBody: "Error: Page not found. Please check the URL and try again."
StatusCode: "404"
# Routes API requests to your containers
ApiListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn: !Ref MyListener
Priority: 1
Actions:
- Type: forward
ForwardConfig:
TargetGroups:
- TargetGroupArn: !Ref TargetGroup1
Conditions:
- Field: path-pattern
Values:
- "/api/*"
Load Balancer Features
This setup includes:
-
Smart Routing: Only
/api/*
requests go to containers, others get 404 - SSM Integration: Stores target group ARN for ECS service to reference
- Extended Timeout: 10-minute idle timeout for long-running requests
- Multi-AZ: Distributes across both availability zones using subnet IDs from SSM
CodeBuild - Your Automated Factory
Note: Update the
GitHubRepo
parameter default value with your own repository details before deployment.
CodeBuild automatically builds your container images from your GitHub repository. The GitHub connection should already be established through the AWS Console before deploying this template.
Setting Up GitHub Connection
Before deploying CodeBuild, make sure you've connected your GitHub account in the AWS Console:
- Go to CodeBuild → Settings → Source providers
- Connect to GitHub and authorize AWS access
- This allows CodeBuild to access your repository
# infra/codebuild/codebuild.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: "Creating CodeBuild Project for Learning Purpose"
Parameters:
TeamNameValue:
Type: String
Description: TeamName Tag Value
Default: "awslearner"
EnvironmentValue:
Type: String
Description: Environment Tag Value
Default: "dev"
ProjectName:
Type: String
Default: learner-project
GitHubRepo:
Type: String
Description: "GitHub repository in format: owner/repo"
Default: <Provide Repo Details>
GitHubBranch:
Type: String
Default: feature/setup
CodeBuildRoleName:
Type: String
Description: "Name of IAM role for CodeBuild"
Default: "learner-build-role"
Resources:
# Log storage for your build process
CodeBuildLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/aws/codebuild/${ProjectName}"
RetentionInDays: 14
Tags:
- Key: Name
Value: !Sub "/aws/codebuild/${ProjectName}"
- Key: TeamName
Value: !Ref TeamNameValue
- Key: Environment
Value: !Ref EnvironmentValue
# Automated builder that creates your container images
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Ref ProjectName
ServiceRole: !Sub arn:aws:iam::${AWS::AccountId}:role/${CodeBuildRoleName}
Artifacts:
Type: NO_ARTIFACTS
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/standard:6.0
Source:
Type: GITHUB
Location: !Sub "https://github.com/${GitHubRepo}"
GitCloneDepth: 1
BuildSpec: buildspec.yml
ReportBuildStatus: true
SourceVersion: !Ref GitHubBranch
TimeoutInMinutes: 30
LogsConfig:
CloudWatchLogs:
GroupName: !Ref CodeBuildLogGroup
Status: ENABLED
Tags:
- Key: Name
Value: !Ref ProjectName
- Key: TeamName
Value: !Ref TeamNameValue
- Key: Environment
Value: !Ref EnvironmentValue
Outputs:
CodeBuildProjectName:
Value: !Ref CodeBuildProject
Description: "Name of the CodeBuild Project"
The Build Process (buildspec.yml)
Your buildspec.yml file defines what happens during the build:
# buildspec.yml
version: 0.2
env:
variables:
REPO_NAME: "learner-ecr"
APP_NAME: "myapp"
phases:
pre_build:
commands:
- echo Getting AWS account ID...
- ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
- echo Account ID is $ACCOUNT_ID
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
- echo generating buildnumber...
- NEW_BUILD_NUMBER=$CODEBUILD_BUILD_NUMBER
- echo Build number value is $NEW_BUILD_NUMBER
build:
commands:
- echo Building Docker image...
- docker build -t $APP_NAME:$NEW_BUILD_NUMBER .
- docker tag $APP_NAME:$NEW_BUILD_NUMBER $ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$REPO_NAME:$NEW_BUILD_NUMBER
post_build:
commands:
- echo Pushing the Docker image to ECR...
- docker push $ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$REPO_NAME:$NEW_BUILD_NUMBER
- echo Docker image pushed to ECR successfully
- echo Updating SSM Parameter with new image arn
- IMAGE_URI="$ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$REPO_NAME:$NEW_BUILD_NUMBER"
- echo Updating SSM Parameter with image URI $IMAGE_URI
- aws ssm put-parameter --name "/learner/imagearn/value" --value "$IMAGE_URI" --type String --overwrite --region $AWS_DEFAULT_REGION
What This Build Process Does
- Gets Account Info: Retrieves AWS account ID for ECR login
- ECR Authentication: Logs into your private container registry
- Builds Image: Creates Docker image with unique build number
- Tags Image: Properly tags for ECR repository
- Pushes to ECR: Stores the built image in your registry
- Updates SSM: Stores the new image URI for ECS service to use
Deployment Steps
Now let's deploy everything in the right order:
Step 1: Deploy Security Groups
aws cloudformation deploy \
--template-file infra/securitygroup/sg.yaml \
--stack-name AWSLearner-SG-Stack \
--capabilities CAPABILITY_NAMED_IAM
Step 2: Deploy Application Load Balancer
aws cloudformation deploy \
--template-file infra/alb/alb.yaml \
--stack-name AWSLearner-ALB-Stack \
--capabilities CAPABILITY_NAMED_IAM
Step 3: Deploy CodeBuild Project
aws cloudformation deploy \
--template-file infra/codebuild/codebuild.yaml \
--stack-name AWSLearner-CodeBuild-Stack \
--capabilities CAPABILITY_NAMED_IAM
Common Issues and Solutions
Security Group Problems
Issue: Can't access load balancer
Solution: Check that port 80 is open in ALB security group
Load Balancer Health Checks Failing
Issue: Target group shows unhealthy targets
Solution: Ensure your app responds to /api/health
endpoint
CodeBuild Failures
Issue: Build fails with permission errors
Solution: Verify ECR permissions in CodeBuild role
What's Next?
In Day 5, we'll:
- Deploy our ECS service with auto-scaling
- Connect everything together
- Test the complete application flow
Key Takeaways
Today we built the security and automation foundation:
- Security Groups protect your resources with firewall rules
- Load Balancers distribute traffic and ensure high availability
- CodeBuild automates your container image creation
These components work together to create a secure, scalable, and maintainable container platform.
The security groups ensure only authorised traffic reaches your containers, the load balancer provides high availability and health monitoring, and CodeBuild keeps your deployments automated and consistent.
Tomorrow we'll bring it all together with the ECS service deployment. Stay tuned!
This is part of my AWS learning journey. Follow along as we build a complete containerized application on AWS using Infrastructure as Code.
Previous Posts:
- Day 1: VPC and Networking Setup
- Day 2: Internet Gateway, Public Route Table & ECR Repository
- Day 3: IAM Roles, SSM & ECS Cluster
Coming Next:
- Day 5: ECS Service with Auto-Scaling
💻 About Me
Hi! I'm Utkarsh, a Cloud Specialist & AWS Community Builder who loves turning complex AWS topics into fun chai-time stories ☕
Top comments (0)