From a Repository to the Cloud: Maximizing Efficiency with GitHub Actions and AWS

Dario Fernando Rodriguez Herrera
Globant
Published in
9 min readJun 21, 2023
Photo by Luca Bravo on Unsplash

Nowadays, automation has become an essential aspect of the modern technological work environment; it plays a crucial role in improving efficiency, productivity, and quality. By eliminating manual interventions, automating activities not only saves time and effort but also minimizes errors and enables individuals and organizations to focus on more strategic and value-added tasks.

In a developer environment, many techniques and practices are recommended to improve the results, making the software development cycle faster and more reliable. One of them is CI/CD practices (continuous integration/continuous deployment) that act as a bridge, connecting development and operations teams and of course, their activities by emphasizing the importance of automation in building, testing, and deploying software solutions.

GitHub Actions is a powerful CI/CD platform provided by GitHub that allows developer teams to automate different tasks in their software development cycle, streamlining their processes and improving collaboration within their teams. It uses GitHub Runners to execute workflows that let us perform actions in different cloud platforms such as AWS, GCP, Azure Cloud, etc.

In this article, we will explore how to configure and integrate one of two types of GitHub runners of GitHub Actions with AWS using a custom OpenID Connect (OIDC) authentication protocol provider.

Overview

Firstly, there are some concepts that we need to understand before starting:

  • Workflows: They are sequences of actions defined in source code files, serving as pipelines that specify a set of automated steps to be executed.
  • GitHub Runners: These virtual machines serve as the infrastructure required to execute workflows, enabling development teams to automate tasks and processes.
  • OIDC: This is an interoperable authentication protocol based on the OAuth 2.0 framework, which simplifies the way to verify the identity of users.

There are two types of GitHub Runners: self-hosted and GitHub-hosted runners:

  • Self-hosted runners are machines that you configure on your own, but you have to manage them too. The advantage of using this kind of “runners” is that it provides us with a full level of customization on hardware and software. By utilizing self-hosted runners, you can run workflows in your preferred environment: on-premises, in a private cloud, or any other infrastructure, and you have complete control over the security settings.
  • GitHub-hosted runners are virtual machines offered by GitHub, providing us with preconfigured environments to execute our workflows. Every GitHub subscription has storage and GitHub-hosted runners running minutes included. By using this kind of “runners”, we delegate the responsibility of setting up and maintaining the hardware and software, ensuring compatibility and letting us focus on what the workflow has to do.

In summary, both types of runners offer us flexibility and scalability to meet our needs regarding workflows and integration with AWS services for CI/CD pipelines, but it is important to check some conditions before selecting one of them:

Table 1. Self-hosted vs GitHub-hosted runners

There aren’t good or bad options; it depends on what we need. For example, if our team is small, we have a free GitHub subscription, execute simple workflows to deploy some infrastructure in AWS, GCP, or Azure, and don’t need some special configuration regarding security, probably using GitHub-hosted runners is the best option. But if we are working in a big team, we use GitHub Actions to perform complex tasks that need external libraries or a specific operating system, we need some specific configuration regarding security, and we have an operational capacity to maintain our runners, most likely our best option is to use self-hosted runners. This decision depends on our conditions and capacities (operational, financial, etc.).

As we mentioned before, we will explain how to configure GitHub-hosted runners in this article.

Prerequisites

Before we dive into the details of integrating GitHub Actions with AWS, there are a few prerequisites that need to be in place:

  • An AWS account with the necessary permissions to create and manage IAM roles and policies.
  • Administrator access in a GitHub enterprise instance.
  • A GitHub repository where you will configure your workflows.
  • Basic knowledge of GitHub Actions and AWS Identity and Access Management (IAM).

Considerations

Hands-on

The following diagram shows the architecture of our solution, where we can see the artifacts needed in each platform and how they interact:

Diagram 1. Solution’s architecture

The configuration has to be done on both platforms: GitHub and AWS, following these steps:

  1. Configure a custom OIDC issuer in GitHub.
  2. Configure an IAM OIDC identity provider in AWS.
  3. Create an IAM role and attach it to an Identity Provider.
  4. Update the GitHub Actions workflow to connect with AWS.

Configure custom OIDC issuer in GitHub

When you integrate GitHub Actions with a cloud provider, it grants access to all repositories in GitHub, and this could be insecure and dangerous. For this reason, it’s necessary to configure a custom OIDC issuer in GitHub Actions, limiting access only to repositories whose JWT token has been issued by a specific provider.

To configure the custom GitHub Actions OpenID Connect (OIDC) issuer, you have to follow these steps:

  1. Authenticate in GitHub Enterprise as an administrator.
  2. Set the custom issuer:

Authenticate in GitHub Enterprise as an administrator

Depending on how your Enterprise security was configured and what platform you want to use (browser, desktop, command line, etc.), you have to authenticate in GitHub and ensure that you have Administrator access granted. Here you can find a guide about how to do it, depending on your preferences.

Set the custom issuer

You can set the custom issued in two ways:

If you have GitHub CLI installed, execute the following command, replacing first where it says ENTERPRISE with the name of your enterprise:

# GitHub CLI API
# https://cli.github.com/manual/gh_api
gh api \
- method PUT \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022–11–28" \
/enterprises/ENTERPRISE/actions/oidc/customization/issuer \
-F include_enterprise_slug=true# GitHub CLI API

If you don’t have GitHub CLI installed, you can use the curl command:

Execute the following command, adding a BEARER authorization token (<YOUR-TOKEN>), and replacing where it says ENTERPRISE with the name of your enterprise:

curl -L \
-X PUT \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer <YOUR-TOKEN>"\
-H "X-GitHub-Api-Version: 2022–11–28" \
https://api.github.com/enterprises/ENTERPRISE/actions/oidc/customization/issuer \
-d '{"include_enterprise_slug":true}'

That’s it! If everything went ok, you should have received a code 204 as a response.

Configuring AWS for OIDC Integration

To start integrating GitHub Actions with AWS, we need to set up an identity provider in AWS using OIDC:

Create an IAM OIDC identity provider in AWS.

Create an IAM OIDC identity provider: steps 1–3
  1. In the AWS console, go to IAM.
  2. In the left panel, go to the “Identity Provider” sub-menu.
  3. Click on “Add provider” located in the top-right corner.
Create an IAM OIDC identity provider: steps 4–8

4. In “Provider type”, select OpenID Connect.

5. Specify the secure OpenID Connect URL for authentication requests in. the “Provider URL”. It should be something like: token.actions.githubusercontent.com/<ENTERPRISE>

6. For the “Audience”: Use sts.amazonaws.com.

7. Optional: Add some tags if needed.

8. Click on “Add provider”

Create an IAM role in AWS and define the necessary permissions for the workflows.

Create an IAM role: steps 1–3
  1. In the AWS console, go to IAM.
  2. On the left panel, go to the “Roles” sub-menu.
  3. Click on “Create role” located in the top-right corner.
Create an IAM role: steps 4–7

4. Select “web identity” as Trusted entity type.

5. In “Identity provider”, you should see the identity provider created in the previous step; select it.

6. For the “Audience” select sts.amazonaws.com.

7. Press “Next”

Create an IAM role: steps 8–9

8. Create a policy or select all permissions this role needs. It depends on the types of tasks you will automate with GitHub Actions.

9. Press Next

Create an IAM role: steps 10–12

10. Assign a name for the role.

11. Add some description if needed.

12. The “Select trusted entities” box should be filled with a basic trusted policy automatically based on the information provided by you. This policy allows using this role to all sts: AssumeRoleWithWebIdentity API calls with a JWT token emitted by a unique URL provided by GitHub (configured in step: Configure custom OIDC issuer in GitHub).

13. Finally, press “Create role”.

This is all the configuration required to let GitHub-hosted runners assume an IAM role to execute tasks in AWS.

Updating GitHub Actions workflow to connect with AWS

Now you will need to make two changes to your workflow YAML file:

Add permissions settings for the token: In order for a job or workflow to be able to request the OIDC JWT token, some permissions must be assigned as shown in the following piece of code:

permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout

Use the official actions to configure AWS credentials: As mentioned earlier in this article, we’ll use the official actions to configure AWS credentials. For this, you need to add a step like the following, where you have to replace <account-id> and <role-name> with the values that you have obtained in previous steps in this document:

- name: configure aws credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: arn:aws:iam::<account-id>:role/<role-name>
role-session-name: samplerolesession
aws-region: ${{ env.AWS_REGION }}

Example:

The following is a simple example of how to properly configure the workflow YAML file, adding permissions and using the official action to configure AWS credentials. This code should be saved as a YAML file (<workflow_name>.yml) in the path “.github/workflows” on our repository:

Gist link

# Sample workflow to access AWS resources when the workflow is tied to the branch
# The workflow Creates a static website using aws s3
name: AWS example workflow
on:
push
env:
BUCKET_NAME : "<example-bucket-name>"
AWS_REGION : "<example-aws-region>"
# permission can be added at the job level or workflow level
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
jobs:
S3PackageUpload:
runs-on: ubuntu-latest
steps:
- name: Git clone the repository
uses: actions/checkout@v3
- name: configure aws credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: arn:aws:iam::1234567890:role/example-role
role-session-name: samplerolesession
aws-region: ${{ env.AWS_REGION }}
# Upload a file to AWS s3
- name: Copy index.html to s3
run: |
aws s3 cp ./index.html s3://${{ env.BUCKET_NAME }}/

Conclusions

This is a simple example of how to connect AWS and GitHub and take advantage of them daily, enhancing our teams by automating developer lifecycle tasks, trying to eliminate human errors, applying best practices in our processes as CI/CD pipelines, and above all, using them together securely.

It’s important to analyze all features of each of those two options that GitHub offers us regarding runners before deciding what to use in each case. Remember, there aren’t good or bad options; there are only features that need to be checked to enhance our process and avoid waste of money and resources.

Finally, as with almost everything, GitHub Actions and AWS are high-powered platforms designed for big things; with them, we can create a basic “hello world” application or support an entire enterprise infrastructure around the world; everything depends on us, on what we are eager to learn every day.

References

--

--