Skip to main content

Updating Plan and Apply Roles

info

This guide is specifically for infrastructure-live repositories that use the terragrunt-scale-catalog. For repositories using the Account Factory, see Configuring IAM Roles.

When Pipelines is installed, it creates two roles in your cloud account: a Plan role and an Apply role. The Plan role is used during pull/merge requests and provides read-only access to your cloud resources. The Apply role provides write access and is only used on your Deploy Branch. Pipelines enforces this separation through the OIDC handshake between your repository and the cloud, so Terragrunt can only authenticate with the Apply role when running on the Deploy Branch.

This guide walks through how to modify these roles when you need to grant Pipelines access to different types of cloud resources.

Identifying existing roles

Your plan and apply roles are managed by the bootstrap stack in your repository. Replace <ACCOUNT_NAME> with the name of the account you are working with. This stack is initially created at:

<ACCOUNT_NAME>/_global/bootstrap/terragrunt.stack.hcl

This stack sources the pipelines-bootstrap stack from the Terragrunt Scale Catalog (GitHub | GitLab).

The role names are determined by the oidc_resource_prefix stack value, which defaults to pipelines. This creates two IAM roles in your AWS account:

  • <oidc_resource_prefix>-plan (e.g., pipelines-plan)
  • <oidc_resource_prefix>-apply (e.g., pipelines-apply)
note

The oidc_resource_prefix is frequently customized to include a random prefix to prevent naming collisions with existing resources in your account.

The IAM policies attached to these roles are controlled by the bootstrap_iam_policy stack value defined in the pipelines-bootstrap stack. This value defaults to default, which tells the stack to load default_plan_iam_policy.json (GitHub | GitLab) and default_apply_iam_policy.json (GitHub | GitLab) as the role policies. A restrictive option is also available, which provides a more locked-down set of permissions.

You can also override the policies entirely by passing custom JSON directly via the plan_iam_policy and apply_iam_policy stack values.

Customizing roles

The following example walks through adding permissions for a new cloud service to your plan and apply roles.

In this example, we'll add SageMaker permissions, but the same approach works for any AWS service.

1. Copy the default policy files

Start by downloading the default policy files from the Terragrunt Scale Catalog into your bootstrap directory. We'll rename them to custom_ to indicate they're your own versions:

curl -o <ACCOUNT_NAME>/_global/bootstrap/custom_plan_iam_policy.json \
https://raw.githubusercontent.com/gruntwork-io/terragrunt-scale-catalog/main/stacks/aws/github/pipelines-bootstrap/default_plan_iam_policy.json

curl -o <ACCOUNT_NAME>/_global/bootstrap/custom_apply_iam_policy.json \
https://raw.githubusercontent.com/gruntwork-io/terragrunt-scale-catalog/main/stacks/aws/github/pipelines-bootstrap/default_apply_iam_policy.json

2. Add SageMaker permissions

Add read-only SageMaker access to your plan role policy. Open custom_plan_iam_policy.json and add a new entry to the Statement array alongside the existing statements:

<ACCOUNT_NAME>/_global/bootstrap/custom_plan_iam_policy.json
{
"Version": "2012-10-17",
"Statement": [
// ... existing statements ...
{
"Sid": "SageMakerReadOnlyAccess",
"Effect": "Allow",
"Action": [
"sagemaker:Describe*",
"sagemaker:List*",
"sagemaker:Get*"
],
"Resource": "*"
}
]
}

Then add full SageMaker access to your apply role policy. Open custom_apply_iam_policy.json and add a new entry to the Statement array:

<ACCOUNT_NAME>/_global/bootstrap/custom_apply_iam_policy.json
{
"Version": "2012-10-17",
"Statement": [
// ... existing statements ...
{
"Sid": "SageMakerAccess",
"Effect": "Allow",
"Action": [
"sagemaker:*"
],
"Resource": "*"
}
]
}

3. Load your custom policies in the bootstrap stack

Update your terragrunt.stack.hcl to pass the custom policy files to the bootstrap stack. Set plan_iam_policy and apply_iam_policy in the values block, using templatefile to load the JSON (the ${state_bucket_name} template variable is used in the policies for state bucket access):

<ACCOUNT_NAME>/_global/bootstrap/terragrunt.stack.hcl
stack {
# ... your existing stack configuration ...
# Your locals block should already contain:
# account_hcl = read_terragrunt_config(find_in_parent_folders("account.hcl"))

values = {
# ... your existing values ...

plan_iam_policy = templatefile("${get_terragrunt_dir()}/custom_plan_iam_policy.json", {
state_bucket_name = local.account_hcl.locals.state_bucket_name
})
apply_iam_policy = templatefile("${get_terragrunt_dir()}/custom_apply_iam_policy.json", {
state_bucket_name = local.account_hcl.locals.state_bucket_name
})
}
}

4. Deploy the changes via Pipelines

  1. Create a new branch with your changes.
  2. Open a pull/merge request targeting your Deploy Branch. Pipelines will run a plan showing the IAM policy updates.
  3. Review the plan output to confirm the policy changes match what you expect.
  4. Merge the pull/merge request. Pipelines will apply the updated IAM policies to your plan and apply roles.

Inspecting roles in the console

Replace <OIDC_RESOURCE_PREFIX> with your oidc_resource_prefix value from the bootstrap stack (e.g., pipelines).

To verify your roles in the AWS console:

  1. Navigate to IAM > Roles in the AWS Management Console.
  2. Search for your prefix. You should see two roles:

IAM Roles in the AWS ConsoleIAM Roles in the AWS Console

  • <OIDC_RESOURCE_PREFIX>-plan
  • <OIDC_RESOURCE_PREFIX>-apply
  1. Click on either role, select the Permissions tab, and expand the policy under Permissions policies. After deploying your changes, you should see the new permissions (e.g., SageMaker actions) reflected here.
  2. Select the Trust relationships tab to verify the OIDC trust policy. The plan role uses a StringLike condition allowing any branch, while the apply role uses StringEquals restricting access to your Deploy Branch.