CloudFormation stacks can be deployed from GitHub Actions, but the process often involves a surprising amount of manual configuration for security and state management.
Let’s see a real-world example of deploying a simple S3 bucket using CloudFormation from a GitHub Actions workflow.
Workflow File (.github/workflows/deploy-cfn.yml):
name: Deploy CloudFormation Stack
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Create or Update CloudFormation Stack
uses: aws-actions/aws-cloudformation-stack@v1
with:
name: my-s3-bucket-stack
region: us-east-1
template: ./cloudformation/s3-bucket.yaml
capabilities: CAPABILITY_IAM
parameters: |
BucketName=my-unique-github-action-bucket-${{ github.run_id }}
CloudFormation Template (cloudformation/s3-bucket.yaml):
AWSTemplateFormatVersion: '2010-09-09'
Description: A simple S3 bucket.
Parameters:
BucketName:
Type: String
Description: Name for the S3 bucket.
Resources:
MyS3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
AccessControl: Private
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
This workflow does the following:
- Checks out your repository code. This makes your CloudFormation template available to the workflow.
- Configures AWS Credentials. It uses the
aws-actions/configure-aws-credentialsaction to securely inject your AWS access key ID and secret access key (stored as GitHub secrets) into the runner environment. This is crucial for granting your workflow permission to interact with AWS. - Creates or Updates the CloudFormation Stack. The
aws-actions/aws-cloudformation-stackaction is a wrapper around the AWS CLI’scloudformation deploycommand. It takes the stack name, region, template file, and any necessary capabilities (likeCAPABILITY_IAMif your template creates IAM resources) or parameters.
The parameters section dynamically sets the BucketName using the github.run_id to ensure uniqueness for each deployment.
The problem this solves is automating infrastructure provisioning. Instead of manually creating resources in the AWS console or running aws cloudformation deploy locally, you can define your infrastructure as code in a Git repository and have it automatically deployed whenever changes are pushed to a specific branch.
Internally, the aws-actions/aws-cloudformation-stack action orchestrates the aws cloudformation deploy command. This command checks if a stack with the given name already exists. If it does, it performs an update; otherwise, it creates a new stack. It reads your template, resolves parameters, and sends the deployment request to the CloudFormation service. The service then provisions or modifies the resources defined in your template.
The capabilities parameter is essential. If your CloudFormation template creates IAM roles or policies, you must explicitly acknowledge this by including CAPABILITY_IAM or CAPABILITY_NAMED_IAM in your deploy command or action. Without it, CloudFormation will refuse to create or update the stack to prevent accidental creation of powerful IAM permissions.
The parameters section allows you to pass values into your CloudFormation template. These can be hardcoded, or as shown, dynamically generated using GitHub Actions context variables like ${{ github.run_id }} or ${{ github.sha }}. This makes your deployments more flexible and environment-specific.
When you define resources like S3 buckets with PublicAccessBlockConfiguration, you are telling CloudFormation to enforce best practices for security by default. This is part of the "infrastructure as code" paradigm – you define the desired state, and CloudFormation ensures that state is achieved and maintained, including security configurations.
A common point of confusion is how CloudFormation tracks the state of your infrastructure. It doesn’t just execute commands; it maintains a record of the stack’s resources, their states, and any drift from the defined template. This state is managed by AWS’s CloudFormation service itself, not by the GitHub Actions runner. The action simply initiates the process and reports back the outcome.
The next concept you’ll likely encounter is managing drift detection and handling stack failures gracefully within your CI/CD pipeline.