Skip to content

tedious ramblings

The blog of Robert Hafner

Menu
  • Projects
  • Resume
  • Sponsor
  • Archives
  • About
Menu

Using Github Actions OpenID Connect to push to AWS ECR without Credentials

Posted on October 29, 2021November 24, 2021 by Robert Hafner

One of the biggest pain points of using any CI/CD platform alongside a cloud service is managing credentials, and up until this week Github Actions was no exception. That all changes with the announcement of Github Actions OpenID Connect. With this feature Github Repositories can be given permissions directly from AWS IAM.

This is a really powerful tool that can save a lot of aggrevation and busy work when it comes to setting up CI/CD. I recently used it to simplify pushing images from Github to AWS ECR and thought others could benefit from the lessons I learned.

Setting up AWS

I’m going to use Terraform to manage the ECR Repositories. At the end we’re going to have a workspace with a reusable ECR module, the OIDC setup, and a few repositories.

OIDC Resource

The first thing we need to do is connect Github’s Open ID Connector to our AWS account using the Terraform aws_iam_openid_connect_provider resource. This is pretty straight forward but requires you dig around to find the right URL, Client List, and Thumbprint- these values are the same for every account, so you can copy and paste this over without problem.

And that’s it! With that one simple resource you’ve linked your account to Github.

ECR Repository Module

By creating a separate module for building a ECR Repository we can avoid a lot of boilerplate when creating multiple repositories.

Variables

Before we start creating resources we need a few variables- specifically the Github Organization, the name of the ECR Repository (which should match the Github Repository), and the OIDC Provider we created above. If you are only using a single Organization then you can put it as the default to save some boilerplate.

Repository

Next we create the repository. As mentioned above we’re going to give it the same name as the Git Repository.

Again pretty simple- just a single resource.

IAM Role

This is where the magic happens. Here we create the Role for the Github Action, doing a few very important things-

  • Add the Github OpenID Connect Provider as the Principal for the role.
  • Give Github the ability to assume this role by giving it the sts:AssumeRoleWIthWebItentity action. This is what actually allows Github to give this role to the Github Action.
  • Limit that access further with a condition against the repository name. After all, it wouldn’t be a good idea to let any Github Action take on this role.

Another thing to note is the Role Name- we’re going to need that later. We’re using a standard naming scheme so we can easily refer to the role from inside of our Github Actions.

IAM Policy

Now we’re at the point where we want to define the permissions that the Github Action has. In our example we’re pushing to an ECR Repository, but this method can be used for any AWS resources- pushing to S3, updating an ECS Task Definition, and even running Terraform itself are all possible with this method.

The policy we use is locked down to the specific ECR Repository- the Github Action that uses this can only act on the single repository. The ecr:GetAuthorizationToken permission is only needed to log in to the registry. Once the policy is created we attach it to the role from above.

Tying it Together

Now we’ve built out module, so lets head back to where we’ve defined our OIDC resource and create some repositories! To make this easy to maintain we put a list of repository names right at the top, and then use the Terraform for_each functionality to call the module for every name in that list.

If we were feeling really creative we could use the Github Terraform Provider to look up our repositories based on Organization and tags, but we’ll leave that exercise for another day.

Github Actions

Assuming the AWS Role

Now that we’re ready to run the actions on Github we need to assume the role we created for the repository. AWS has a published action for logging in that handles most of the work. Unfortunately this OIDC functionality is so new that they haven’t released a working version yet, so we have to work directly from their master branch until they do. This functionality was released in v1.6.0, so make sure to tie to v1 or (if using a more specific tag) a version at or later than v1.6.0.

The most important value here is the IAM Role Name. Since we’re using a standardized naming scheme across Github and ECR Repositories we can infer the role name instead of having to hard code it. That means the only things we need to provide are the AWS Account ID and the AWS Region that our container registry is in.

Pushing the Container

WIth all that out of the way here’s full action to build and deploy an image to ECR. In this we’re chaining together a variety of published actions from other vendors-

  • actions/checkout to actually pull the repository.
  • docker/setup-qemu-action to install an emulation layer to build multiplatform images.
  • docker/setup-buildx-action to use the docker buildx system, again for multiplatform images.
  • aws-actions/configure-aws-credentials to log into AWS.
  • aws-actions/amazon-ecr-login to log into AWS ECR.
  • docker/metadata-action to create a bunch of tags for our docker container.
  • docker/build-push-action to push the container.

Less Boilerplate, More Action

One thing you’ve probably noticed is that in all of that code there are only two unique values that can’t be picked up with Github Context variables– the AWS Account and Region. This is a sign that this one off action could be turned into it’s own standalone and reusable action. Using that action you end up with this much simpler action code.

Wrapping it Up

If you stuck with me this far you should have a good idea on how to link Github Actions to AWS, and maybe even some ideas on how to simplify your image building in general. If you have any questions please feel free to reach out- you can find me on twitter at @tedivm.

Share this:

  • Click to share on Mastodon (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Twitter (Opens in new window)
  • Click to share on Pocket (Opens in new window)
  • Click to print (Opens in new window)
  • More
  • Click to share on Pinterest (Opens in new window)
  • Click to share on Tumblr (Opens in new window)
  • Click to share on Skype (Opens in new window)
  • Click to email a link to a friend (Opens in new window)

9 thoughts on “Using Github Actions OpenID Connect to push to AWS ECR without Credentials”

  1. Anonymous says:
    November 1, 2021 at 7:53 am

    Thanks for the great write up of this functionality. One thing I have discovered from my implementation is that when using an ECR policy (resource based policy) which permits the IAM Federated user (in this case github actions), other IAM restrictions do not apply, as outlined in the policy evaluation logic described here:

    https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html

    Reply
  2. ordisius says:
    November 10, 2021 at 9:59 pm

    awesome post!!! I think there might be one typo in the terraform code var.openid_connect_provider.arn, probably started with that value as a variable, but then switched to the resource attribute

    Reply
    1. Robert Hafner says:
      November 11, 2021 at 10:23 am

      That’s actually because the Role is being created inside of the module that creates the ECR repo, so the resource is being passed in as a variable. This allows us to reuse the OIDC resource while still having a module for the ECR repo, which in turn lets us use the for_each resource when creating our repositories.

      Reply
  3. dennis says:
    December 7, 2021 at 10:55 pm

    I don’t understand how you get this ” AWS_ACCOUNT_ID: “999999999999” ” . can you give a insight on this.

    Reply
    1. Robert Hafner says:
      December 15, 2021 at 11:51 am

      That’s the AWS Account ID. Each account has a unique one, and you should put yours in there.

      Reply
  4. Paul C. says:
    December 22, 2021 at 6:45 pm

    Thanks Robert, this article was really helpful!

    Reply
  5. haussenfeffer says:
    January 31, 2022 at 12:56 pm

    Is there a folder structure that should be used when using these files, or a specific terraform version? When I put all the tf files in the same folder and I try to run terraform init (with the latest version, 1.1.4), I get

    Initializing modules…
    ╷
    │ Error: Invalid module source address
    │
    │ Module “repositories” (declared at main.tf line 14) has invalid source address “”: invalid source string: .
    ╵
    Thanks for the great article!

    Reply
  6. haussenfeffer says:
    January 31, 2022 at 2:09 pm

    Update: I think I put everything where it should go and got it at least terraform planning locally. Basically main.tf goes in the root ( which in my case is a “terraform” folder) and everything else goes under a modules/repositories folder (modules also being under the “terraform” folder) and the source referenced from the module is ‘./modules/repositories’ – I also did have to fix a typo or two (locals.repositories should be local.repositories in main.tf, and in github_iam_role.tf I had to change the identifiers line to identifiers = [var.oidc_arn] to match the passed-in module variable)

    Reply
  7. Klaas-Jan Wierenga says:
    June 20, 2022 at 7:29 am

    Hi Robert. Thanks for this excellent and detailed blog post. I managed to adapt it to our setup perfectly. Saved me tons of time. Thanks!

    Reply

Leave a Reply Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

About

Robert Hafner is a Principal Engineer based in Chicago focusing on distributed applications, infrastructure, and security. This blog is a running journal of projects, tutorials, and random ideas that pop into his head.

  • GitHub
  • Mastodon
  • LinkedIn

Popular Posts

  • JShrink reaches over 21,000,000 installs and releases v1.6!
  • Using Github Actions OpenID Connect to push to AWS ECR without Credentials
  • Rob’s Awesome Python Template
  • Getting AWS ECS to work on Ubuntu with Full GPU Support
  • A Walkthrough of PSR-6: Caching
  • Simple Multiprocessing with QuasiQueue
  • Building an Email Testing Environment with Vagrant, Dovecot and Travis-CI
  • Introducing DapperData for Formatting YAML and JSON
  • Multi-Py: Multiplatform Container Images for Python Packages
  • Telling OpenSSH to Pull Keys from Github with AuthorizedKeysCommand

Subscribe to Blog via Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

©2023 tedious ramblings | Built using WordPress and Responsive Blogily theme by Superb