DEV Community

Cover image for AWS VPC to ECS – Day 3: IAM Roles, SSM & ECS Cluster
Utkarsh Rastogi for AWS Community Builders

Posted on • Edited on

AWS VPC to ECS – Day 3: IAM Roles, SSM & ECS Cluster

What's up everyone! πŸ‘‹ Welcome back to my "AWS VPC to ECS with CloudFormation" series - Day 3 is here!

So in my last blog posts, I got the networking foundation sorted with VPC and subnets. Now I'm diving into something that honestly used to give me nightmares - IAM permissions and security stuff. But you know what? Once I stopped being scared of it and actually understood what's happening, it became way less intimidating!

What I'm Tackling Today

Alright, so before I can actually run any containers, I need these three things set up:

  • IAM Roles - Because AWS won't let you do anything without proper permissions (and rightfully so!)
  • SSM Parameters - My secret weapon for managing configurations
  • ECS Cluster - The actual home where my containers will live

Real talk - I used to just copy IAM policies from Stack Overflow and pray they worked. Bad idea. Trust me, learn from my mistakes! πŸ˜…

Today's Architecture - What We're Building

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                           Day 3: Security & Orchestration                      β”‚
β”‚                                                                                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚   IAM Roles     β”‚    β”‚ SSM Parameters  β”‚    β”‚      ECS Cluster            β”‚ β”‚
β”‚  β”‚                 β”‚    β”‚                 β”‚    β”‚                             β”‚ β”‚
β”‚  β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚    β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚    β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚  β”‚ β”‚ECS Executionβ”‚ β”‚    β”‚ β”‚Image ARN    β”‚ β”‚    β”‚ β”‚     Fargate Platform    β”‚ β”‚ β”‚
β”‚  β”‚ β”‚    Role     β”‚ β”‚    β”‚ β”‚Parameter    β”‚ β”‚    β”‚ β”‚                         β”‚ β”‚ β”‚
β”‚  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚    β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚    β”‚ β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚
β”‚  β”‚                 β”‚    β”‚                 β”‚    β”‚ β”‚  β”‚   Container Tasks   β”‚ β”‚ β”‚ β”‚
β”‚  β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚    β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚    β”‚ β”‚  β”‚   (Future Home)     β”‚ β”‚ β”‚ β”‚
β”‚  β”‚ β”‚ECS Task     β”‚ β”‚    β”‚ β”‚Config Valuesβ”‚ β”‚    β”‚ β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚
β”‚  β”‚ β”‚    Role     β”‚ β”‚    β”‚ β”‚(Future)     β”‚ β”‚    β”‚ β”‚                         β”‚ β”‚ β”‚
β”‚  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚    β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚    β”‚ β”‚  Auto Scaling Ready     β”‚ β”‚ β”‚
β”‚  β”‚                 β”‚    β”‚                 β”‚    β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚  β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚    β”‚                 β”‚    β”‚                             β”‚ β”‚
β”‚  β”‚ β”‚CodeBuild    β”‚ β”‚    β”‚                 β”‚    β”‚                             β”‚ β”‚
β”‚  β”‚ β”‚    Role     β”‚ β”‚    β”‚                 β”‚    β”‚                             β”‚ β”‚
β”‚  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚    β”‚                 β”‚    β”‚                             β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚           β”‚                       β”‚                           β”‚                 β”‚
β”‚           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                 β”‚
β”‚                                   β”‚                                             β”‚
β”‚                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                            β”‚
β”‚                    β”‚        Secure Foundation     β”‚                            β”‚
β”‚                    β”‚     Ready for Containers    β”‚                            β”‚
β”‚                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

1. IAM Roles - Finally Making Peace with Permissions

Okay, confession time. IAM used to be my biggest AWS fear. All those JSON policies, trust relationships, conditions... I'd just copy-paste stuff and hope it worked. Spoiler alert: it usually didn't, and I'd spend hours debugging "Access Denied" errors.

But here's what changed everything for me - I started thinking of IAM roles like job descriptions. Each role has specific responsibilities, and AWS just wants to make sure the right "employee" is doing the right job.

Here's my complete IAM setup:

AWSTemplateFormatVersion: "2010-09-09"
Description: "Creating IAM Roles for Learning Purpose"

Parameters:
  RoleName:
    Type: String
    Description: Name of IAM Role
    Default: "learner-ecs-role"
  TaskRoleName:
    Type: String
    Description: Name of IAM Role
    Default: "learner-ecs-task-exc-role"
  BuildRoleName:
    Type: String
    Description: Name of IAM Role
    Default: "learner-build-role"
  PolicyName:
    Type: String
    Description: Name of IAM Policy
    Default: "learner-ecs-permission-policy"
  PolicyName1:
    Type: String
    Description: Name of IAM Policy
    Default: "learner-ecs-policy"
  TeamNameValue:
    Type: String
    Description: TeamName Tag Value
    Default: "awslearner"
  EnvironmentValue:
    Type: String
    Description: Environment Tag Value
    Default: "dev"

Resources:
  # Permissions for ECS to manage your containers
  ECSRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref RoleName
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
      Policies:
        - PolicyName: !Ref PolicyName 
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - 'ecs:*'
                  - 'ec2:Describe*'
                  - 'elasticloadbalancing:Describe*'
                Resource: '*'
      Tags:
        - Key: Name
          Value: !Ref RoleName
        - Key: TeamName
          Value: !Ref TeamNameValue
        - Key: Environment
          Value: !Ref EnvironmentValue

  # Permissions for containers to start and access logs
  ECSTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref TaskRoleName
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: [ecs-tasks.amazonaws.com]
            Action: ['sts:AssumeRole']
            Condition:
              ArnLike:
                aws:SourceArn: !Sub arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:*
              StringEquals:
                aws:SourceAccount: !Ref AWS::AccountId
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
      Policies:
        - PolicyName: !Ref PolicyName1
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - 'ecs:*'
                Resource: '*'
      Tags:
        - Key: Name
          Value: !Ref TaskRoleName
        - Key: TeamName
          Value: !Ref TeamNameValue
        - Key: Environment
          Value: !Ref EnvironmentValue

  # Permissions for CodeBuild to build and push images
  CodeBuildRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref BuildRoleName
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: codebuild.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: CodeBuildECRAccess
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - ecr:GetAuthorizationToken
                  - ecr:BatchCheckLayerAvailability
                  - ecr:CompleteLayerUpload
                  - ecr:InitiateLayerUpload
                  - ecr:PutImage
                  - ecr:UploadLayerPart
                Resource: "*"
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: "*"
              - Effect: Allow
                Action:
                  - s3:GetObject
                Resource: "arn:aws:s3:::*"
              - Effect: Allow
                Action:
                  - codeconnections:*
                Resource: "*"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AWSCodeBuildDeveloperAccess
        - arn:aws:iam::aws:policy/AmazonSSMFullAccess
      Tags:
        - Key: Name
          Value: !Ref BuildRoleName
        - Key: TeamName
          Value: !Ref TeamNameValue
        - Key: Environment
          Value: !Ref EnvironmentValue
Enter fullscreen mode Exit fullscreen mode

Time to deploy this bad boy:

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

Quick heads up: That --capabilities CAPABILITY_NAMED_IAM flag is super important - CloudFormation needs your explicit permission to mess with IAM stuff. Learned that one the hard way when my first deployment just sat there doing nothing! πŸ€¦β€β™‚οΈ

2. SSM Parameters - My New Best Friend

Okay, can we talk about SSM Parameter Store for a sec? This thing has literally changed my life! No more hardcoding database URLs, API keys, or any other config values directly in my templates.

I remember my early days when I'd hardcode everything and then spend hours trying to figure out why my dev environment was hitting production databases. Yeah... not my proudest moments.

Here's my SSM setup:

AWSTemplateFormatVersion: '2010-09-09'
Description: 'Creating SSM Parameters for Learning Purpose'

Parameters:
  TeamNameValue:
    Type: String
    Description: TeamName Tag Value
    Default: "awslearner"
  EnvironmentValue:
    Type: String
    Description: Environment Tag Value
    Default: "dev"
  SSMName:
    Type: String
    Default: "/learner/imagearn/value"

Resources:
  # Placeholder for storing your container image location
  ImageSSM:
    Type: AWS::SSM::Parameter
    Properties:
      Name: !Ref SSMName
      Type: String
      Value: '1'
      Description: "Image ARN"
      Tags:
        Name: !Ref SSMName
        TeamName: !Ref TeamNameValue
        Environment: !Ref EnvironmentValue
Enter fullscreen mode Exit fullscreen mode

Why SSM Parameters Rock My World

Look, once you start using these, there's no going back:

  1. No more secrets in code - Everything stays encrypted and secure
  2. Change stuff without redeploying - Update a parameter, restart your service, boom!
  3. Super organized - That hierarchical naming (/learner/imagearn/value) keeps everything neat
  4. Plays nice with everything - Other AWS services can grab these values directly

Let's get this deployed:

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

3. ECS Cluster - The Container Playground

Now we're getting to the fun stuff! The ECS Cluster is basically the stage where all my containers are going to perform their magic. I'm going with Fargate because honestly? I don't want to deal with managing EC2 instances if I can avoid it.

Here's what I'm setting up:

AWSTemplateFormatVersion: '2010-09-09'
Description: 'Creating ECS Cluster for Learning Purpose'

Parameters:
  TeamNameValue:
    Type: String
    Description: TeamName Tag Value
    Default: "awslearner"
  EnvironmentValue:
    Type: String
    Description: Environment Tag Value
    Default: "dev"
  ClusterName:
    Type: String
    Description: Name of the ECS Cluster
    Default: "learner-cluster"

Resources:
  # Container hosting platform where your apps will run
  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Ref ClusterName
      CapacityProviders:
        - FARGATE
      DefaultCapacityProviderStrategy:
        - CapacityProvider: FARGATE
          Weight: 1
      Tags:
        - Key: Name
          Value: !Ref ClusterName
        - Key: TeamName
          Value: !Ref TeamNameValue
        - Key: Environment
          Value: !Ref EnvironmentValue
Enter fullscreen mode Exit fullscreen mode

Why Fargate is Perfect for This

Let me tell you why I'm obsessed with Fargate:

  • Serverless containers - I just tell AWS what I want, and they handle all the infrastructure nonsense
  • Scales like crazy - Traffic spike? No problem, Fargate's got my back
  • Secure by default - Each task gets its own little isolated bubble
  • Pay for what you use - Only charged for the actual compute time

Time to deploy:

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

How These Three Play Together

Here's the cool part - these aren't just random components. They work together like a well-oiled machine:

  1. IAM Roles are like the security guards - they make sure only the right services can access the right resources
  2. SSM Parameters are the configuration managers - they store all the dynamic stuff our services need
  3. ECS Cluster is the execution platform - where the actual magic happens

What's Coming Next?

In my next post (Day 4), I'm going to add the final pieces to this puzzle:

  • Security Groups - Network-level firewall rules
  • Application Load Balancer - Traffic distribution and health checks

We're getting close to actually running some containers! πŸŽ‰

My Key Takeaways

  1. Don't be scared of IAM - Take time to understand it instead of just copying policies
  2. SSM Parameters are life-changing - Use them for anything that might need to change
  3. ECS Cluster setup is actually pretty simple - The complexity comes with the services and tasks

When Things Go Wrong (They Will!)

Here's what I've learned from my many failures:

  • IAM issues: Check the trust policy - make sure the right service can actually assume the role
  • SSM parameter not found: Double-check the parameter name matches exactly what you're referencing
  • ECS cluster stuck: Give it a few minutes - these things take time to spin up

That's Day 3 in the books! We now have a solid, secure foundation that's ready for containers. Next time, we'll add the final infrastructure pieces and get ready to deploy our actual application.


How's your AWS journey going? Ever had any epic IAM fails? Share your war stories in the comments! πŸ‘‡

Up next: Day 4 - Security Groups & Load Balancer Setup


πŸ’» 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 (1)

Collapse
 
anik_sikder_313 profile image
Anik Sikder

This hits home. IAM used to feel like black magic until I started treating roles like job descriptions too. And yes to SSM Parameter Store total game changer for keeping configs clean and safe. Loving this series!