DEV Community

Cover image for AWS VPC to ECS - Day 4: Security Groups, Load Balancer & CodeBuild Setup
Utkarsh Rastogi for AWS Community Builders

Posted on • Edited on

AWS VPC to ECS - Day 4: Security Groups, Load Balancer & CodeBuild Setup

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
Enter fullscreen mode Exit fullscreen mode

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/*"
Enter fullscreen mode Exit fullscreen mode

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:

  1. Go to CodeBuild → Settings → Source providers
  2. Connect to GitHub and authorize AWS access
  3. 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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

What This Build Process Does

  1. Gets Account Info: Retrieves AWS account ID for ECR login
  2. ECR Authentication: Logs into your private container registry
  3. Builds Image: Creates Docker image with unique build number
  4. Tags Image: Properly tags for ECR repository
  5. Pushes to ECR: Stores the built image in your registry
  6. 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
Enter fullscreen mode Exit fullscreen mode

Step 2: Deploy Application Load Balancer

aws cloudformation deploy \
  --template-file infra/alb/alb.yaml \
  --stack-name AWSLearner-ALB-Stack \
  --capabilities CAPABILITY_NAMED_IAM
Enter fullscreen mode Exit fullscreen mode

Step 3: Deploy CodeBuild Project

aws cloudformation deploy \
  --template-file infra/codebuild/codebuild.yaml \
  --stack-name AWSLearner-CodeBuild-Stack \
  --capabilities CAPABILITY_NAMED_IAM
Enter fullscreen mode Exit fullscreen mode

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)