Skip to content

Instantly share code, notes, and snippets.

@Kelvinrr
Created January 30, 2025 16:43
Show Gist options
  • Save Kelvinrr/be2c215096758b2aacec1fee3020e0eb to your computer and use it in GitHub Desktop.
Save Kelvinrr/be2c215096758b2aacec1fee3020e0eb to your computer and use it in GitHub Desktop.

The Basics

First, make sure you installed aws with mamba install awscli, make sure to not install this in the same environment as isis.

Log into AWS via the cli https://docs.aws.amazon.com/signin/latest/userguide/command-line-sign-in.html

CloudFormation works simply by defining services in YAML templates that imitate the parameters you would input into the command line.

For example, in order to create a new ECR record, that is, a repository to push docker images to, you would do the following using the AWS CLI:

aws ecr create-repository \
    --repository-name postgres \
    --image-scanning-configuration scanOnPush=true

But on CloudFormation yaml:

# ECRTemplate.yml
AWSTemplateFormatVersion: 2010-09-09
Description: postgres ECR Deploy
Resources: 
  Astrocloud: 
    Type: AWS::ECR::Repository
    Properties: 
        RepositoryName: "postgres"
        ImageScanningConfiguration:
            ScanOnPush: true

and then deploy the stack with:

aws cloudformation deploy --stack-name PostgresTemplate --template-file ECRTemplate.yml --role-arn your-role-arn

NOTE: Might not need role-arn if you're using a free account since your account might not be as locked down as ours

You can see the parallels clearly by looking at the CloudFormation docs for creating the ECR vs the CLI docs for creating an ECR

Why use CF over the CLI?

Infrastructure as Code (IaC), it allows us to create complete stack deployments more consistently, define them more explicitly in CloudFormation templates, and to manage them more purposefully through source control. Almost as important, our AWS services are configured such that we as developers and users have limited permissions to create or modify a service outside a CloudFormation context. This is because of the issues inherit with modifying services manually via AWS CLI or console.

Additionally, real deployments are often more complex and require policies and roles to be attached.

Here is a real example of an ECR deployment for the Astrocloud service with accounts censored:

AWSTemplateFormatVersion: 2010-09-09
Description: Astrocloud ECR Deploy
Resources: 
  Astrocloud: 
    Type: AWS::ECR::Repository
    Properties: 
        RepositoryName: "astrocloud"
        RepositoryPolicyText: 
          Version: "2012-10-17"
          Statement: 
            - 
              Sid: AllowPushPull
              Effect: Allow
              Principal: 
                AWS: 
                  - "arn:aws:iam::xxxxxxxxxx:role/xxx-xxxxxxxxxx"
              Action: 
                - "ecr:GetDownloadUrlForLayer"
                - "ecr:BatchGetImage"
                - "ecr:BatchCheckLayerAvailability"
                - "ecr:PutImage"
                - "ecr:InitiateLayerUpload"
                - "ecr:UploadLayerPart"
                - "ecr:CompleteLayerUpload"

And complete deployments require multiple services. Managing them via CLI or Console alone would be time-consuming and error-prone. For example, the complete Astrocloud container deployment running on a Fargate deployment with details removed. Basically, this is a web service running from a docker container.

#prod ssl 443
AWSTemplateFormatVersion: 2010-09-09
Description: Astrocloud SSL Deploy
Resources: 
  LogGroup:
    Type: 'AWS::Logs::LogGroup'
    Properties:
      Things...
      
  astrocloudProdTaskDefinition: 
    Type: AWS::ECS::TaskDefinition
    Properties:
      Stuff...
      ContainerDefinitions:
	     - Stuff...
      RequiresCompatibilities:
        More Stuff... 
        
  ECSastrocloudProdCluster:
    Type: 'AWS::ECS::Cluster'
    Properties:
      - Things...
  
  astrocloudProdFargateAlb:
    Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
    Properties:
      So many properties...
  
  astrocloudProdFargateTargetGroup:
    Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
    Properties:
       More things....
       
  astrocloudListener:
    Type: 'AWS::ElasticLoadBalancingV2::Listener'
    Properties:
      Things...
      
  astrocloudProdFargateAlbSG:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      Things...
  
  astrocloudProdSlot1ContainerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      ...Things
  
  astrocloudProdSlot1DNS:
    Type: "AWS::Route53::RecordSet"
    Properties:
      DNS Routing Stuff...
      
  astrocloudProdSlot1ECSService:
    Type: AWS::ECS::Service
    DependsOn:
      - astrocloudListener
    Properties:
      omg even more stuff 

How do I know what I need to put in my CF template?

Every CF template is basically this:

# Some boilerplate at the top saying this is a AWS CF template 
AWSTemplateFormatVersion: 2010-09-09
Description: Astrocloud SSL Deploy
Resources:
  
  LogicalID:
    Type: Resource type
    Properties:
      Set of properties

Resource Type is the AWS service you are trying to create. Some ecamples:

  • DNS record: AWS::Route53::RecordSet
  • Docker Repo: AWS::ECR::Repository
  • Security Group: AWS::EC2::SecurityGroup
  • Creating a box on the cloud (i.e., an EC2 instance): AWS::EC2::Instance

LogicalID is a name you want to give the resource, this is a variable you set and used to reference the resource in within the stack e.g., !REF LogicalID or !GetAtt LogicalID.Property in situations where one service needs to reference another one. Like routing a DNS resource to a Load Balancing service:

  astrocloudProdSlot1DNS:
    Type: "AWS::Route53::RecordSet"
    Properties:
      HostedZoneName: nau.edu.
      Name: myservice.nau.edu
      Type: A
      AliasTarget:
        HostedZoneId: !GetAtt 'astrocloudProdFargateAlb.CanonicalHostedZoneID'
        DNSName: !GetAtt 'astrocloudProdFargateAlb.DNSName'
        
  astrocloudProdFargateAlb:
    Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
    Properties:
      Name: astrocloudProdFargateAlb
      Scheme: internal
      Subnets:
        - subnet-xxxxxxxxx
        - subnet-xxxxxxxxx
        - subnet-xxxxxxxxx
      Type: application
      SecurityGroups:
        - !GetAtt astrocloudProdFargateAlbSG.GroupId

You can usually google "<resource> CloudFormation" and it'll give you the docs for that specific resource type. The docs include the complete list of properties for the resource type.

Look through the properties and intuit what you probably need and what is optional. Properties that are strictly optional are labeled as such in the AWS docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment