AWS CodePipeline and AWS CodeBuild are a great, serverless way to build your Docker containers and deploy them to ECS (or wherever). You can even use an IAM role with CodeBuild to let your build pipeline access other services like S3, etc.

AWS CodeBuild already provisions a container for you to use, and that’s where it runs all the commands in your buildspec file. But if you’re using CodeBuild to build a Docker container, then you’ll be running a container (the one you’re building) inside another container (the CodeBuild one, where your buildspec stuff runs).

How can you use an IAM role inside your build container?

Let me clarify the problem real fast.

Suppose CodeBuild has an IAM role, and the IAM role allows access to an S3 bucket. CodeBuild itself could access S3 in a straightforward manner just using plain old bash commands in your buildspec.

---
phases:
  build:
    commands:
      - aws s3 cp ... # This would work fine
      - docker build -t myapp:latest . # This should work fine too as long as it does not interact with S3.

But what if you want to access S3 from inside the container that you’re building? Eg. What if your Dockerfile looked like this?

FROM ubuntu:16.04
RUN aws s3 cp ...

When your buildspec runs docker build, it’ll invoke a new container that it’s building. That new container won’t automatically inherit the IAM role from CodeBuild, and so your S3 stuff won’t work.

Since we’re doing a container in a container, I just wanted to get that out of the way to show which specific problem we’re attacking here.

Okay, how do you get the role to work inside the build container?

Turns out, CodeBuild is probably built on top of ECS. And ECS uses an environment variable called $AWS_CONTAINER_CREDENTIALS_RELATIVE_URI to locate the credentials for a role. This is all documented pretty well for ECS, but I didn’t find any documentation at all that states that CodeBuild also uses this same convention (AWS support told me).

CodeBuild exposes this environment variable by default inside the CodeBuild container. So you need to get it into the container you are building as a build arg.

buildspec.yml:

---
phases:
  build:
    commands:
      - >
        docker build
        --build-arg AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
        --build-arg AWS_REGION=$AWS_REGION
        -t myapp:latest
        .

Dockerfile:

ARG AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
RUN aws s3 cp ...

The build container should now be able to know how to hit the metadata service and retrieve credentials from the role.

Or if you want to use a lower level tool to verify that you have credentials, you can curl it in your Dockerfile using:

RUN curl "http://169.254.170.2${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}"