|
| 1 | +import CustomizableValue from '/src/components/CustomizableValue' |
| 2 | + |
| 3 | +# Setup a Delegated Repository |
| 4 | + |
| 5 | +:::note |
| 6 | +[Automatic vending of delegated repositories by Account Factory](../../accountfactory/guides/delegated-repositories.md) is an Enterprise-only feature. |
| 7 | + |
| 8 | +If you are an Enterprise customer, Account Factory will automatically provision delegated repositories for you, and you may not need to follow the steps in this guide. The steps in this guide are for customers who are looking to manually set up delegated repositories, or for customers who are looking to understand how the process works from the perspective of Pipelines. |
| 9 | +::: |
| 10 | + |
| 11 | +## Introduction |
| 12 | + |
| 13 | +Infrastructure management delegation is a first-class concept in DevOps Foundations. To learn more about delegated repositories, click [here](../../accountfactory/architecture/#delegated-repositories). |
| 14 | + |
| 15 | +Reasons you might want to delegate management of infrastructure includes: |
| 16 | + |
| 17 | +- A different team is autonomously working on parts of infrastructure relevant to a specific account. |
| 18 | +- A GitHub Actions workflow in a repository needs to be able to make limited changes to infrastructure in a specific account. |
| 19 | + |
| 20 | + e.g. A repository has application code relevant to a container image that needs to be built and pushed to AWS ECR before it can be used in a Kubernetes cluster via a new deployment. |
| 21 | + |
| 22 | +The following guide assumes that you have already gone through [Pipelines Setup & Installation](../installation/prerequisites/awslandingzone.md). |
| 23 | + |
| 24 | +## Step 1 - Ensure the delegated account is set up |
| 25 | + |
| 26 | +Ensure that the account you want to delegate management for is set up. This includes the following: |
| 27 | + |
| 28 | +1. The account is created in AWS. |
| 29 | +2. An OIDC provider is set up in the account. |
| 30 | +3. The account has the following roles provisioned: |
| 31 | + - `infrastructure-live-access-control-plan` |
| 32 | + - `infrastructure-live-access-control-apply` |
| 33 | + |
| 34 | +If the account was provisioned normally using Account Factory, these roles should already be set up. |
| 35 | + |
| 36 | +If you want more information about exactly how this works, read [GitHub OIDC docs](https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services). |
| 37 | + |
| 38 | +## Step 2 - Ensure that the `infrastructure-live-access-control` repository is provisioned. |
| 39 | + |
| 40 | +The [infrastructure-live-access-control](../architecture/security-controls.md#infrastructure-access-control) repository is an optionally provisioned part of DevOps Foundations, and it's the recommended way of delegating access to infrastructure. |
| 41 | + |
| 42 | +If you don't have this repository set up, you can follow the steps in the [infrastructure-live-root-template](https://github.com/gruntwork-io/infrastructure-live-root-template) to provision it. |
| 43 | + |
| 44 | +This repository will be where you manage the IAM access that your delegated repository will have. |
| 45 | + |
| 46 | +## Step 3 - Provision the delegated role |
| 47 | + |
| 48 | +To provision a role that can be assumed by the delegated repository, you will want to add it to the `infrastructure-live-access-control` repository. |
| 49 | + |
| 50 | +:::tip |
| 51 | +Typically, CI roles created for Pipelines are created in pairs, one for the `plan` stage and one for the `apply` stage. This is because the `plan` stage should have more limited permissions than the `apply` stage, as plans typically only need read-only access. |
| 52 | + |
| 53 | +If you are creating a role to do something like push a container image to ECR on push to the repository, you may only need a single role. |
| 54 | +::: |
| 55 | + |
| 56 | +Use Terragrunt Scaffold to create the new role in your `infrastructure-live-access-control` repository. |
| 57 | + |
| 58 | +```bash |
| 59 | +# Assuming your `infrastructure-live-access-control` repository is named exactly that, |
| 60 | +# and the account you want to provision your new role in is called `acme`. |
| 61 | +mkdir acme/_global/ecr-push-role |
| 62 | +cd acme/_global/ecr-push-role |
| 63 | +terragrunt scaffold '[email protected]:gruntwork-io/terraform-aws-security.git//modules/github-actions-iam-role?ref=v0.73.2' |
| 64 | +``` |
| 65 | + |
| 66 | +This will give you a placeholder `terragrunt.hcl` file for a new role in your repository that you can customize to your needs. |
| 67 | + |
| 68 | +Alternatively, you can copy and paste the following: |
| 69 | + |
| 70 | +:::note |
| 71 | +Note the value of `allowed_sources`, which should be the organization, name, and ref of the repository you are delegating to. |
| 72 | + |
| 73 | +If you would like to make it so that all refs in a repository can assume this role, you can use `["*"]` as the value on the right hand side. |
| 74 | +::: |
| 75 | + |
| 76 | +```hcl |
| 77 | +terraform { |
| 78 | + source = "[email protected]:gruntwork-io/terraform-aws-security.git//modules/github-actions-iam-role?ref=v0.73.2" |
| 79 | +} |
| 80 | +
|
| 81 | +# Include the root `terragrunt.hcl` configuration, which has settings common across all environments & components. |
| 82 | +include "root" { |
| 83 | + path = find_in_parent_folders() |
| 84 | +} |
| 85 | +
|
| 86 | +# Include the component configuration, which has settings that are common for the component across all environments |
| 87 | +include "envcommon" { |
| 88 | + path = "${dirname(find_in_parent_folders("common.hcl"))}/_envcommon/landingzone/delegated-pipelines-plan-role.hcl" |
| 89 | + merge_strategy = "deep" |
| 90 | +} |
| 91 | +
|
| 92 | +inputs = { |
| 93 | + github_actions_openid_connect_provider_arn = "arn:aws:iam::${get_aws_account_id()}:oidc-provider/token.actions.githubusercontent.com" |
| 94 | + github_actions_openid_connect_provider_url = "https://token.actions.githubusercontent.com" |
| 95 | +
|
| 96 | + # ---------------------------------------------------------------------------------------------------------------- |
| 97 | + # This is the map of repositories to refs that are allowed to assume this role. |
| 98 | + # |
| 99 | + # Note that for a plan role, typically the only additional permissions that are required are read permissions that |
| 100 | + # grant Terragrunt permission to read the existing state in provisioned infrastructure, such that a plan of proposed |
| 101 | + # updates can be generated. |
| 102 | + # |
| 103 | + # Also note that all refs are allowed to assume this role, as the plan role is typically assumed in refs used |
| 104 | + # as sources for pull requests. Assign permissions keeping this in mind. |
| 105 | + # |
| 106 | + # Read more on least privilege below. |
| 107 | + # ---------------------------------------------------------------------------------------------------------------- |
| 108 | +
|
| 109 | + allowed_sources = { |
| 110 | + "$$ORGANIZATION$$/$$REPO$$" : ["$$REF$$"] |
| 111 | + } |
| 112 | +
|
| 113 | + # ---------------------------------------------------------------------------------------------------------------- |
| 114 | + # Least privilege is an important best practice, but can be a very difficult practice to engage in. |
| 115 | + # |
| 116 | + # The `envcommon` include above provides the minimal permissions required to interact with TF state, however |
| 117 | + # any further permissions are up to the user to define as needed for a given workflow. |
| 118 | + # |
| 119 | + # These permissions are meant to be continuously refined in a process of iteratively granting additional permissions |
| 120 | + # as needed to have workflows updated in CI correctly, and then removing excess permissions through continuous review. |
| 121 | + # |
| 122 | + # A common pattern used to refine permissions is to run a pipeline with a best guess at the permissions required, or |
| 123 | + # no permissions at all, and then review acccess denied errors and add the necessary permissions to have the pipeline |
| 124 | + # run successfully. |
| 125 | + # |
| 126 | + # As workload patterns become more commonplace, this repo will serve as a reference for the permissions required to |
| 127 | + # run similar workloads going forward. |
| 128 | + # ---------------------------------------------------------------------------------------------------------------- |
| 129 | +
|
| 130 | + iam_policy = { |
| 131 | + # Role workload permissions go here |
| 132 | + } |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +Note the `envcommon` include, which includes the common minimal configurations recommended for delegated roles in DevOps Foundations. |
| 137 | + |
| 138 | +You will likely need to expand the `iam_policy` block to include the permissions required for your specific workflow. |
| 139 | + |
| 140 | +For example, if you would like permissions to push to ECR, you might add the following: |
| 141 | + |
| 142 | +```hcl |
| 143 | +iam_policy = { |
| 144 | + "ECRPushPermissions" = { |
| 145 | + effect = "Allow" |
| 146 | + actions = [ |
| 147 | + "ecr:CompleteLayerUpload", |
| 148 | + "ecr:UploadLayerPart", |
| 149 | + "ecr:InitiateLayerUpload", |
| 150 | + "ecr:BatchCheckLayerAvailability", |
| 151 | + "ecr:PutImage", |
| 152 | + "ecr:BatchGetImage" |
| 153 | + ] |
| 154 | + resources = "arn:aws:ecr:region:${$$ACCOUNT_ID$$}:repository/$$REPOSITORY_NAME$$" |
| 155 | + }, |
| 156 | + "ECRAuthorizationToken" = { |
| 157 | + effect = "Allow", |
| 158 | + actions = ["ecr:GetAuthorizationToken"] |
| 159 | + resources = ["*"] |
| 160 | + } |
| 161 | +} |
| 162 | +``` |
| 163 | + |
| 164 | +## Step 4 - Apply the role |
| 165 | + |
| 166 | +Once you have customized the role to your needs, you can apply it by creating a pull request in the `infrastructure-live-access-control` repository. |
| 167 | + |
| 168 | +```bash |
| 169 | +git add . |
| 170 | +git commit -m "feat: Add ECR push role for acme account" |
| 171 | +git push |
| 172 | +gh pr create --base main --title "feat: Add ECR push role for acme account" --body "This PR adds the ECR push role for the acme account." |
| 173 | +``` |
| 174 | + |
| 175 | +Inspect the pull request, verify the plan, then merge the pull request to get it applied. |
| 176 | + |
| 177 | +## Step 5 - Set up the delegated repository |
| 178 | + |
| 179 | +Depending on what the repository needs to do in CI, your GitHub Actions workflow may be as simple as a file like the following placed in `.github/workflows/ci.yml`: |
| 180 | + |
| 181 | +```yaml |
| 182 | +name: CI |
| 183 | +on: [push] |
| 184 | + |
| 185 | +permissions: |
| 186 | + id-token: write |
| 187 | + contents: read |
| 188 | + |
| 189 | +jobs: |
| 190 | + build: |
| 191 | + runs-on: ubuntu-latest |
| 192 | + steps: |
| 193 | + - uses: actions/checkout@v4 |
| 194 | + |
| 195 | + - uses: aws-actions/configure-aws-credentials@v4 |
| 196 | + with: |
| 197 | + role-to-assume: arn:aws:ecr:$$REGION$$:$$ACCOUNT_ID$$:repository/$$REPOSITORY_NAME$$ |
| 198 | + role-session-name: acme-ecr-push |
| 199 | + aws-region: $$AWS_REGION$$ |
| 200 | + |
| 201 | + - name: Login to Amazon ECR |
| 202 | + id: login-ecr |
| 203 | + uses: aws-actions/amazon-ecr-login@v2 |
| 204 | + |
| 205 | + - name: Build, tag, and push docker image to Amazon ECR |
| 206 | + env: |
| 207 | + REGISTRY: ${{ steps.login-ecr.outputs.registry }} |
| 208 | + REPOSITORY: $$IMAGE_NAME$$ |
| 209 | + IMAGE_TAG: ${{ github.sha }} |
| 210 | + run: | |
| 211 | + docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG . |
| 212 | + docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG |
| 213 | +``` |
| 214 | +
|
0 commit comments