Infrastructure as Code on AWS — AWS CloudFormation and CI/CD with GitHub Actions
Tags: aws, iac, githubactions, cloudformation
Published: 2023-03-13 14:19:56

![Architecture](https://cdn-images-1.medium.com/max/2000/1*puGcFDRN8DuHpGs2L6BwHw.png)

Hello everyone. I want to share my experience using **AWS CloudFormation** and **GitHub Actions**. Infrastructure as Code is a DevOps process of provisioning and managing cloud resources with only writing template files as JSON **or** YAML. Infrastructure as Code also can decrease time when creating manual resources to automate resources.

In Infrastructure as Code, AWS has a service — [AWS CloudFormation](https://aws.amazon.com/cloudformation/). **AWS CloudFormation** can help automation manage AWS resources. Example, you have a task — create [App Runner](https://aws.amazon.com/apprunner) service. With AWS CloudFormation, need one JSON or YAML file to create App Runner service.

Before creating a YAML file, learn [CloudFormation template formats](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-formats.html). For this tutorial, create an AWS CloudFormation template with a scenario when want to create App Runner service.

Search and click AWS CloudFormation, click Create stack, choose Template is ready and choose upload a template file. Then upload your template file and click Next. Fill in the stack name and click Next. Step 3 and step 4 is skip. Click Create submit to create CloudFormation stack.

![](https://cdn-images-1.medium.com/max/2000/1*v5lF_cnUA07OfvY3wGi8YQ.jpeg)

![Template file.](https://cdn-images-1.medium.com/max/2000/1*oNHfFrTBuiif2ma_TC7jdQ.jpeg)

While waiting for the CloudFormation stack is progress, I want to explain the CloudFormation template about creating AWS App Runner on my GitHub:

[learnaws/cloudformation.yaml at main · budionosan/learnaws · GitHub](https://github.com/budionosan/learnaws/blob/main/AWS%20CloudFormation%20feat%20GitHub%20Actions/cloudformation.yaml)

```
    AWSTemplateFormatVersion: "2010-09-09"
    Description: Create AWS AppRunner with AWS CloudFormation
    Resources:
      AppRunner:
        Type: AWS::AppRunner::Service
        Properties:
```

**Description** — describe the CloudFormation template for creating AWS services.

**Resources** — define AWS services that want to the template.

**App Runner** — logical ID name.

**Type** — AWS services. For example, AWS App Runner means AWS::AppRunner::Service or Amazon EC2 means AWS::EC2::Instance and many more.

**Properties** — configuration from AWS services (for this tutorial, AWS App Runner)

![CloudFormation stack in progress.](https://cdn-images-1.medium.com/max/2502/1*eh4RuGz-f0_fmqX7_PD6sQ.jpeg)

![CloudFormation stack detail.](https://cdn-images-1.medium.com/max/2530/1*oP5OvQBW4eD2P0t2d6AlIA.jpeg)

Go to AWS App Runner and check App Runner service is already created or not. This service is in progress and has a default domain.

![App Runner.](https://cdn-images-1.medium.com/max/2420/1*mylMmKhLEZV6rEteM2mZcg.jpeg)

Back to CloudFormation, the stack that was already created is complete.

![CloudFormation stack is complete.](https://cdn-images-1.medium.com/max/2544/1*Chboz2Bsup-bvE_PccPnnA.jpeg)

```
          ServiceName: <YOUR_APPRUNNER_SERVICE>
          SourceConfiguration:
            AuthenticationConfiguration:
              AccessRoleArn: "arn:aws:iam::<YOUR_AWS_ACCOUNT>:role/service-role/<YOUR_IAM_ROLE>"
            AutoDeploymentsEnabled: true
            ImageRepository:
              ImageIdentifier: <YOUR_ECR_REPOSITORY>
              ImageRepositoryType: ECR
              ImageConfiguration:
                Port: 8080
          InstanceConfiguration:
            Cpu: 1 vCPU
            Memory: 2 GB
```

**Service Name** — App Runner service name.

**Source Configuration** — App Runner configuration from source code or container image.

**Authentication Configuration** (**Access Role Arn**)— IAM role for AWS App Runner that associate with Amazon ECR.

**Auto Deployments Enabled** — enable automation deployment (CI/CD needed).

**Image Repository** — ECR private name and image configuration port.

**Instance Configuration** — vCPU and GB that needed to deploy web application.

![CloudFormation stack events detail.](https://cdn-images-1.medium.com/max/2506/1*0OrCkwmwZ59NUF3xnXP_LQ.jpeg)

![CloudFormation stack resources.](https://cdn-images-1.medium.com/max/2000/1*cqEl_fOYbJAcxv1diYQdaw.jpeg)

Back to AWS App Runner, the App Runner service is already created and now running. Then click the service name.

![App Runner](https://cdn-images-1.medium.com/max/2470/1*SKoo-ytGyHsBTZB6t9LtrA.jpeg)

The App Runner service has a source from Amazon ECR private. Try opening the App Runner domain.

![App Runner detail](https://cdn-images-1.medium.com/max/2420/1*sTwyk63NigRo9zxsgj6Mow.jpeg)

Yes! My web application is successful and running. But I want to execute CI/CD with **GitHub Actions** to update this web application faster.

![](https://cdn-images-1.medium.com/max/2536/1*q0BUorIrKPPL6ay6fF0IRQ.jpeg)

**CI/CD** — **continuous integration and continuous delivery** are DevOps processes that help developers and operations to integrate and deliver your application source code or container image, also automatically build, test and deploy the application to the cloud (AWS and many more). CI/CD has a pipeline or workflow that can build, test and deploy to the cloud.

In CI/CD, GitHub has a feature for CI/CD — **GitHub Actions**. [GitHub Actions](https://github.com/features/actions) can automate pipelines or workflows from a development environment to a production environment. For example, you want to update your application and need automation deployment to deploy to the cloud. With GitHub Actions, need one YML file only to run the automation deployment to the cloud.

Before creating a YML file, learn [GitHub Action formats](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions#create-an-example-workflow). For this tutorial, create a GitHub Actions workflow with a scenario when updating the App Runner service and add ECR private image.

My GitHub Actions workflow for this tutorial in my GitHub:

[serverlesshackathon/pipeline.yml at learncicdonaws · budionosan/serverlesshackathon (github.com)](https://github.com/budionosan/serverlesshackathon/blob/learncicdonaws/.github/workflows/pipeline.yml)

```
    name: CI/CD to AWS AppRunner with GitHub Actions
    on:
      push:
        branches: [learncicdonaws] # Trigger workflow on git push to main branch
      workflow_dispatch: # Allow manual invocation of the workflow
    
    jobs:  
      deploy:
        runs-on: ubuntu-latest
        
        steps:      
          - name: Checkout
            uses: actions/checkout@v3
            with:
              persist-credentials: false
              
          - name: Configure AWS credentials
            id: aws-credentials
            uses: aws-actions/configure-aws-credentials@v1
            with:
              aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
              aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
              aws-region: ${{ secrets.AWS_REGION }}     
    
          - name: Login to Amazon ECR
            id: login-ecr
            uses: aws-actions/amazon-ecr-login@v1        
    
          - name: Build, tag, and push image to Amazon ECR
            id: build-image
            env:
              ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
              ECR_REPOSITORY: firsteks
              IMAGE_TAG: ${{ github.sha }}
            run: |
              docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
              docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
              echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
              
          - name: Deploy to App Runner Image
            id: deploy-apprunner
            uses: awslabs/amazon-app-runner-deploy@main
            with:
              service: apprunner-ecr-cloudformation-app
              image: ${{ steps.build-image.outputs.image }}
              access-role-arn: ${{ secrets.ROLE_ARN }}
              region: ${{ secrets.AWS_REGION }}
              cpu : 1
              memory : 2
              port: 8080
```

**Name** — GitHub Actions workflow name.

**Branches** — destination push to the repository.

**actions/checkout@v3** — Action for checking out a repo. You can read the [reference](https://github.com/actions/checkout).

**aws-actions/configure-aws-credentials@v1** — configure AWS credential environment variables. You can read the [reference](https://github.com/aws-actions/configure-aws-credentials).

**aws-actions/amazon-ecr-login@v1** — Login into Amazon ECR. You can read the [reference](https://github.com/aws-actions/amazon-ecr-login).

**awslabs/amazon-app-runner-deploy@main** — Deploys the application using source code or container image in AWS App Runner. You can read the [reference](https://github.com/awslabs/amazon-app-runner-deploy).

Create GitHub Actions workflow like this.

```
<yourrepositoryname>/.github/workflows/<yourpipeline>.yml
```

![](https://cdn-images-1.medium.com/max/2000/1*DxaWpdlYGE1CQq9ma2oXMQ.jpeg)

Then go to **Settings**, see Security, and see Secret and variables, click Actions.

![](https://cdn-images-1.medium.com/max/2000/1*xshPzTM8ESwp-p20EdC98g.jpeg)

Create a secret with click **New repository secret**. For this tutorial, create AWS Access Key ID and AWS Secret Access Key. To get the AWS access key, go to IAM and click Users. On the Users page, click Add users, fill in the username, set permissions — attach policy and click **Create user**.

After creating a user, click the user that has already been created and go to Security credentials. Scroll to Access Keys. Click the access key and see [this instruction](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html?icmpid=docs_iam_console#Using_CreateAccessKey) to get an access key. **NOTE**: Must require a download or copy of the access key to notepad. Then fill in the secret name and access key like below like this.

![](https://cdn-images-1.medium.com/max/2000/1*1NQ4b02rOXd1tqdSSTUdMA.jpeg)

![](https://cdn-images-1.medium.com/max/2000/1*zWdwNB7MCQZOAe8pajZ-Yw.jpeg)

Go to Settings in your GitHub account then search Developer settings. Click personal access tokens and choose Fine-grained tokens. Then click Generate new token and follow the instructions. Token from your GitHub account is needed when pushing the repository because requires input username and password.

![](https://cdn-images-1.medium.com/max/2000/1*26tPRL5R7ac_Zln4ITAXVg.jpeg)

![](https://cdn-images-1.medium.com/max/2000/1*xtIjWJaXrj8dR1B9FZDNtA.jpeg)

**NOTE**: Optional can use IDE tools anywhere. Then go to AWS Cloud9 (I use this service for my IDE). AWS Cloud9 has preinstalled tools and packages such as Git and Docker.

Clone the repository and update the code from the repository. After updating the code, add all code, commit and push to the repository. But when first commit, write like this.

```
git config --global user.name "yourname"
git config --global user.email youremail
```

After configuration, when push, show input username and password that already created with Fine-grained tokens. If successful, see a screenshot like this and check GitHub Actions in the repository.

![Git](https://cdn-images-1.medium.com/max/2000/1*0YojLINjfQGx2I5bjh8PPA.jpeg)

After push from git, automatically GitHub Actions workflow is now running like this and running one job from the workflow.

![GitHub Actions process to ECR.](https://cdn-images-1.medium.com/max/2636/1*qvitatk37hgevCFFXp66Kw.jpeg)

Check AWS App Runner service is already updated and is now in progress. See source from ECR also updated.

![](https://cdn-images-1.medium.com/max/2488/1*Wt04_yb9BO7mqlBsib3G9g.jpeg)

Check Amazon ECR and see the image tag — GitHub.SHA is same with the App Runner source.

![](https://cdn-images-1.medium.com/max/2000/1*vSZ60jYWKt3M9_VmzEtOpA.jpeg)

Check GitHub Actions and see if the job is successful. These jobs need time 39 seconds only. Very faster.

![](https://cdn-images-1.medium.com/max/2630/1*K0WBlQk_giXU5IWq0OCRSw.jpeg)

Click Deploy to App Runner image step and explain App Runner service is updated. This step needs time 1 second only.

![](https://cdn-images-1.medium.com/max/2000/1*kazcpgC0R9bsnOxPCGzpLw.jpeg)

Go to AWS App Runner. See notifications that this service is successfully updated and click default domain.

![](https://cdn-images-1.medium.com/max/2516/1*5KAlHgSkFD0yeB_hnNoPQg.jpeg)

Yes! I change the text on the front end side with CI/CD by GitHub Actions.

![Web application is updated.](https://cdn-images-1.medium.com/max/2698/1*LyyFXzL7nrbAyFGY1wn-JQ.jpeg)

See two screenshots like this. **BOTTOM** — web application from App Runner service with ECR latest image. **TOP** — web application from App Runner service with ECR updated image.

![](https://cdn-images-1.medium.com/max/2698/1*LyyFXzL7nrbAyFGY1wn-JQ.jpeg)

![](https://cdn-images-1.medium.com/max/2536/1*q0BUorIrKPPL6ay6fF0IRQ.jpeg)

Thank you very much :)