Bootstrap Pipelines in a New GitHub Repository
To configure Pipelines in a new GitHub repository, complete the following steps (which are explained in detail below):
- Create an
infrastructure-liverepository. - Configure the Gruntwork.io GitHub App to authorize your
infrastructure-liverepository, or ensure that the appropriate machine user tokens are set up as repository or organization secrets. - Create
.gruntworkHCL configurations to tell Pipelines how to authenticate in your environments. - Create
.github/workflows/pipelines.ymlto tell your GitHub Actions workflow how to run your pipelines. - Commit and push your changes to your repository.
Creating the infrastructure-live repository
Creating an infrastructure-live repository is fairly straightforward. First, create a new repository using the official GitHub documentation for creating repositories. Name the repository something like infrastructure-live and make it private (or internal).
Configuring SCM Access
Pipelines needs the ability to interact with Source Control Management (SCM) platforms to fetch resources (e.g. IaC code, reusable CI/CD code and the Pipelines binary itself).
There are two ways to configure SCM access for Pipelines:
- Using the Gruntwork.io GitHub App (recommended for most GitHub users).
- Using a machine user (recommended for GitHub users who cannot use the GitHub App).
Creating Cloud Resources for Pipelines
To start using Pipelines, you'll need to ensure that requisite cloud resources are provisioned in your cloud provider(s) to start managing your infrastructure with Pipelines.
If you are using the Gruntwork Account Factory, this will be done automatically during onboarding and in the process of vending every new AWS account, so you don't need to worry about this.
Clone your infrastructure-live repository to your local machine using Git.
If you don't have Git installed, you can install it by following the official guide for Git installation.
For example:
git clone git@github.com:acme/infrastructure-live.git
cd infrastructure-live
To bootstrap your infrastructure-live repository, we'll use Boilerplate to scaffold it with the necessary IaC code to provision the infrastructure necessary for Pipelines to function.
The easiest way to install Boilerplate is to use mise to install it.
If you don't have mise installed, you can install it by following the official guide for mise installation.
mise use -g boilerplate@latest
If you'd rather install a specific version of Boilerplate, you can use the ls-remote command to list the available versions.
mise ls-remote boilerplate
Cloud-specific bootstrap instructions
- AWS
- Azure
- GCP
The resources that you need provisioned in AWS to start managing resources with Pipelines are:
- An OpenID Connect (OIDC) provider
- An IAM role for Pipelines to assume when running Terragrunt plan commands
- An IAM role for Pipelines to assume when running Terragrunt apply commands
For every account you want Pipelines to manage infrastructure in.
This may seem like a lot to set up, but the content you need to add to your infrastructure-live repository is minimal. The majority of the work will be pulled from a reusable catalog that you'll reference in your infrastructure-live repository.
If you want to peruse the catalog that's used in the bootstrap process, you can take a look at the terragrunt-scale-catalog repository.
The process that we'll follow to get these resources ready for Pipelines is:
- Set up the Terragrunt configurations in your
infrastructure-liverepository for bootstrapping Pipelines in a single AWS account - Use Terragrunt to provision these resources in your AWS account
- (Optionally) Bootstrap additional AWS accounts until all your AWS accounts are ready for Pipelines
Bootstrap your infrastructure-live repository
To bootstrap your infrastructure-live repository, we'll use Boilerplate to scaffold it with the necessary content for Pipelines to function.
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/aws/github/infrastructure-live?ref=v1.14.0' \
--output-folder .
You can just reply y to all the prompts to include dependencies, and accept defaults unless you want to customize something.
Alternatively, you could run Boilerplate non-interactively by passing the --non-interactive flag. You'll need to supply the relevant values for required variables in that case.
e.g.
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/aws/github/infrastructure-live?ref=v1.14.0' \
--output-folder . \
--var 'AccountName=dev' \
--var 'GitHubOrgName=acme' \
--var 'GitHubRepoName=infrastructure-live' \
--var 'GitHubOrgID=1234567' \
--var 'GitHubRepoID=7654321' \
--var 'AWSAccountID=123456789012' \
--var 'AWSRegion=us-east-1' \
--var 'Partition=aws' \
--var 'StateBucketName=my-state-bucket' \
--non-interactive
You can also choose to store these values in a YAML file and pass it to Boilerplate using the --var-file flag.
AccountName: dev
GitHubOrgName: acme
GitHubRepoName: infrastructure-live
GitHubOrgID: 1234567
GitHubRepoID: 7654321
AWSAccountID: 123456789012
AWSRegion: us-east-1
Partition: aws
StateBucketName: my-state-bucket
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/aws/github/infrastructure-live?ref=v1.14.0' \
--output-folder . \
--var-file vars.yml \
--non-interactive
Partition should be aws for the standard AWS partition. If you're bootstrapping an AWS GovCloud or China account, set it to aws-us-gov or aws-cn respectively.
GitHubOrgID and GitHubRepoID may be left blank (or omitted entirely) if your repository was created before July 15, 2026 and hasn't opted in to GitHub's immutable subject-claim format for Actions OIDC tokens — Pipelines will continue to use the legacy name-based sub claim in that case.
You can look up the numeric owner/repo IDs for GitHubOrgID and GitHubRepoID with the GitHub CLI:
gh api repos/{owner}/{repo} --jq '{owner_id: .owner.id, repo_id: .id}'
Next, install Terragrunt and OpenTofu locally (the .mise.toml file in the root of the repository after scaffolding should already be set to the versions you want for Terragrunt and OpenTofu):
mise install
Provisioning the resources
Once you've set up the Terragrunt configurations, you can use Terragrunt to provision the resources in your AWS account.
Make sure that you're authenticated with AWS locally before proceeding.
You can follow the documentation here to authenticate with the AWS provider. You are advised to choose an authentication method that doesn't require any hard-coded credentials, like assuming an IAM role.
First, make sure that everything is set up correctly by running a plan in the bootstrap directory in name-of-account/_global where name-of-account is the name of the first AWS account you want to bootstrap.
terragrunt run --all --non-interactive --provider-cache --backend-bootstrap plan
We're using the --provider-cache flag here to ensure that we don't re-download the AWS provider on every run by leveraging the Terragrunt Provider Cache Server.
We're using the --backend-bootstrap flag here to tell Terragrunt to bootstrap the OpenTofu backend automatically for the account.
Next, apply the changes to your account.
terragrunt run --all --non-interactive --provider-cache apply
Optional: Bootstrapping additional AWS accounts
If you have multiple AWS accounts, and you want to bootstrap them as well, you can do so by following a similar, but slightly condensed process.
For each additional account you want to bootstrap, you'll use Boilerplate in the root of your infrastructure-live repository to scaffold out the necessary content for just that account.
If you are going to bootstrap more AWS accounts, you'll probably want to commit your existing changes before proceeding.
git add .
git commit -m "Add core Pipelines scaffolding [skip ci]"
The [skip ci] in the commit message is just in-case you push your changes up to your repository at this state, as you don't want to trigger Pipelines yet.
Just like before, you'll use Boilerplate to scaffold out the necessary content for just that account.
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/aws/github/infrastructure-live?ref=v1.14.0' \
--output-folder .
Again, you can just reply y to all the prompts to include dependencies, and accept defaults unless you want to customize something.
Alternatively, you could run Boilerplate non-interactively by passing the --non-interactive flag. You'll need to supply the relevant values for required variables in that case.
e.g.
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/aws/github/account?ref=v1.14.0' \
--output-folder . \
--var 'AccountName=prod' \
--var 'AWSAccountID=987654321012' \
--var 'AWSRegion=us-east-1' \
--var 'Partition=aws' \
--var 'StateBucketName=my-prod-state-bucket' \
--var 'GitHubOrgName=acme' \
--var 'GitHubRepoName=infrastructure-live' \
--var 'GitHubOrgID=1234567' \
--var 'GitHubRepoID=7654321' \
--non-interactive
If you prefer to store the values in a YAML file and pass it to Boilerplate using the --var-file flag, you can do so like this:
AccountName: prod
AWSAccountID: 987654321012
AWSRegion: us-east-1
Partition: aws
StateBucketName: my-prod-state-bucket
GitHubOrgName: acme
GitHubRepoName: infrastructure-live
GitHubOrgID: 1234567
GitHubRepoID: 7654321
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/aws/github/account?ref=v1.14.0' \
--output-folder . \
--var-file vars.yml \
--non-interactive
GitHubOrgID and GitHubRepoID may be left blank (or omitted entirely) if your repository was created before July 15, 2026 and hasn't opted in to GitHub's immutable subject-claim format for Actions OIDC tokens — Pipelines will continue to use the legacy name-based sub claim in that case.
You can look up the numeric owner/repo IDs for GitHubOrgID and GitHubRepoID with the GitHub CLI:
gh api repos/{owner}/{repo} --jq '{owner_id: .owner.id, repo_id: .id}'
Once you've scaffolded out the additional accounts you want to bootstrap, you can use Terragrunt to provision the resources in each of these accounts.
Make sure that you authenticate to each AWS account you are bootstrapping using AWS credentials for that account before you attempt to provision resources in it.
For each account you want to bootstrap, you'll need to run the following commands:
cd <account-name>/_global/bootstrap
terragrunt run --all --non-interactive --provider-cache --backend-bootstrap plan
terragrunt run --all --non-interactive --provider-cache apply
The resources that you need provisioned in Azure to start managing resources with Pipelines are:
- An Entra ID Application to use for plans
- A Flexible Federated Identity Credential for the application to authenticate with your repository on any branch
- A Service Principal for the application to be used in role assignments
- A role assignment for the service principal to access the Azure subscription
- A role assignment for the service principal to access the Azure Storage Account that holds OpenTofu state
- An Entra ID Application to use for applies
- A Federated Identity Credential for the application to authenticate with your repository on the deploy branch
- A Service Principal for the application to be used in role assignments
- A role assignment for the service principal to access the Azure subscription
For every Azure subscription you want Pipelines to manage infrastructure in.
This may seem like a lot to set up, but the content you need to add to your infrastructure-live repository is minimal. The majority of the work will be pulled from a reusable catalog that you'll reference in your infrastructure-live repository.
If you want to peruse the catalog that's used in the bootstrap process, you can take a look at the terragrunt-scale-catalog repository.
The process that we'll follow to get these resources ready for Pipelines is:
- Set up the Terragrunt configurations in your
infrastructure-liverepository for bootstrapping Pipelines in a single Azure subscription - Use Terragrunt to provision these resources in your Azure subscription
- Fill in the client IDs of the Entra ID applications in your Pipelines configuration
- (Optionally) Bootstrap additional Azure subscriptions until all your Azure subscriptions are ready for Pipelines
Bootstrap your infrastructure-live repository
To bootstrap your infrastructure-live repository, we'll use Boilerplate to scaffold it with the necessary content for Pipelines to function.
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/azure/github/infrastructure-live?ref=v1.14.0' \
--output-folder .
You can just reply y to all the prompts to include dependencies, and accept defaults unless you want to customize something.
Alternatively, you could run Boilerplate non-interactively by passing the --non-interactive flag. You'll need to supply the relevant values for required variables in that case.
e.g.
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/azure/github/infrastructure-live?ref=v1.14.0' \
--output-folder . \
--var 'AccountName=dev' \
--var 'GitHubOrgName=acme' \
--var 'GitHubRepoName=infrastructure-live' \
--var 'GitHubOrgID=1234567' \
--var 'GitHubRepoID=7654321' \
--var 'SubscriptionName=dev' \
--var 'AzureTenantID=00000000-0000-0000-0000-000000000000' \
--var 'AzureSubscriptionID=11111111-1111-1111-1111-111111111111' \
--var 'AzureLocation=East US' \
--var 'StateResourceGroupName=pipelines-rg' \
--var 'StateStorageAccountName=mysa' \
--var 'StateStorageContainerName=tfstate' \
--non-interactive
You can also choose to store these values in a YAML file and pass it to Boilerplate using the --var-file flag.
AccountName: dev
GitHubOrgName: acme
GitHubRepoName: infrastructure-live
GitHubOrgID: 1234567
GitHubRepoID: 7654321
SubscriptionName: dev
AzureTenantID: 00000000-0000-0000-0000-000000000000
AzureSubscriptionID: 11111111-1111-1111-1111-111111111111
AzureLocation: East US
StateResourceGroupName: pipelines-rg
StateStorageAccountName: mysa
StateStorageContainerName: tfstate
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/azure/github/infrastructure-live?ref=v1.14.0' \
--output-folder . \
--var-file vars.yml \
--non-interactive
StateStorageAccountName must be globally unique across Azure, 3 to 24 characters long, and use only lowercase letters and numbers.
GitHubOrgID and GitHubRepoID may be left blank (or omitted entirely) if your repository was created before July 15, 2026 and hasn't opted in to GitHub's immutable subject-claim format for Actions OIDC tokens — Pipelines will continue to use the legacy name-based sub claim in that case.
You can look up the numeric owner/repo IDs for GitHubOrgID and GitHubRepoID with the GitHub CLI:
gh api repos/{owner}/{repo} --jq '{owner_id: .owner.id, repo_id: .id}'
Next, install Terragrunt and OpenTofu locally (the .mise.toml file in the root of the repository after scaffolding should already be set to the versions you want for Terragrunt and OpenTofu):
mise install
Provisioning the resources
Once you've set up the Terragrunt configurations, you can use Terragrunt to provision the resources in your Azure subscription.
If you haven't already, you'll want to authenticate to Azure using the az CLI.
az login
The Azure provider reads the subscription to work in from the ARM_SUBSCRIPTION_ID environment variable, so export it before running any Terragrunt commands.
export ARM_SUBSCRIPTION_ID="11111111-1111-1111-1111-111111111111"
First, make sure that everything is set up correctly by running a plan in the bootstrap directory in name-of-subscription where name-of-subscription is the name of the first Azure subscription you want to bootstrap.
terragrunt run --all --non-interactive --provider-cache --backend-bootstrap plan
We're using the --provider-cache flag here to ensure that we don't re-download the Azure provider on every run by leveraging the Terragrunt Provider Cache Server.
We're using the --backend-bootstrap flag here to tell Terragrunt to bootstrap the Azure Storage backend automatically for the subscription.
Next, apply the changes to your subscription.
terragrunt run --all --non-interactive --provider-cache apply
Filling in the client IDs
Edit the .gruntwork/environment-<name-of-subscription>.hcl file in the root of your infrastructure-live repository to reference the client IDs of the applications we just provisioned.
environment "dev" {
filter {
paths = ["dev/*"]
}
authentication {
azure_oidc {
tenant_id = "00000000-0000-0000-0000-000000000000"
subscription_id = "11111111-1111-1111-1111-111111111111"
plan_client_id = "" # FIXME: Fill in the client ID for the plan application after bootstrapping
apply_client_id = "" # FIXME: Fill in the client ID for the apply application after bootstrapping
}
}
}
You can find the values for the plan_client_id and apply_client_id by running terragrunt stack output in the name-of-subscription/bootstrap directory.
terragrunt stack output
The relevant bits that you want to extract from the stack output are the following:
bootstrap = {
apply_app = {
client_id = "33333333-3333-3333-3333-333333333333"
}
plan_app = {
client_id = "44444444-4444-4444-4444-444444444444"
}
}
You can use those values to set the values for plan_client_id and apply_client_id in the .gruntwork/environment-<name-of-subscription>.hcl file.
Optional: Bootstrapping additional Azure subscriptions
If you have multiple Azure subscriptions, and you want to bootstrap them as well, you can do so by following a similar, but slightly condensed process.
For each additional subscription you want to bootstrap, you'll use Boilerplate in the root of your infrastructure-live repository to scaffold out the necessary content for just that subscription.
If you are going to bootstrap more Azure subscriptions, you'll probably want to commit your existing changes before proceeding.
git add .
git commit -m "Add core Pipelines scaffolding [skip ci]"
The [skip ci] in the commit message is just in-case you push your changes up to your repository at this state, as you don't want to trigger Pipelines yet.
Just like before, you'll use Boilerplate to scaffold out the necessary content for just that subscription.
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/azure/github/subscription?ref=v1.14.0' \
--output-folder .
Again, you can just reply y to all the prompts to include dependencies, and accept defaults unless you want to customize something.
Alternatively, you could run Boilerplate non-interactively by passing the --non-interactive flag. You'll need to supply the relevant values for required variables in that case.
e.g.
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/azure/github/subscription?ref=v1.14.0' \
--output-folder . \
--var 'AccountName=prod' \
--var 'GitHubOrgName=acme' \
--var 'GitHubRepoName=infrastructure-live' \
--var 'GitHubOrgID=1234567' \
--var 'GitHubRepoID=7654321' \
--var 'SubscriptionName=prod' \
--var 'AzureTenantID=00000000-0000-0000-0000-000000000000' \
--var 'AzureSubscriptionID=99999999-9999-9999-9999-999999999999' \
--var 'AzureLocation=East US' \
--var 'StateResourceGroupName=pipelines-rg' \
--var 'StateStorageAccountName=myprodsa' \
--var 'StateStorageContainerName=tfstate' \
--non-interactive
If you prefer to store the values in a YAML file and pass it to Boilerplate using the --var-file flag, you can do so like this:
AccountName: prod
GitHubOrgName: acme
GitHubRepoName: infrastructure-live
GitHubOrgID: 1234567
GitHubRepoID: 7654321
SubscriptionName: prod
AzureTenantID: 00000000-0000-0000-0000-000000000000
AzureSubscriptionID: 99999999-9999-9999-9999-999999999999
AzureLocation: East US
StateResourceGroupName: pipelines-rg
StateStorageAccountName: myprodsa
StateStorageContainerName: tfstate
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/azure/github/subscription?ref=v1.14.0' \
--output-folder . \
--var-file vars.yml \
--non-interactive
GitHubOrgID and GitHubRepoID may be left blank (or omitted entirely) if your repository was created before July 15, 2026 and hasn't opted in to GitHub's immutable subject-claim format for Actions OIDC tokens — Pipelines will continue to use the legacy name-based sub claim in that case.
You can look up the numeric owner/repo IDs for GitHubOrgID and GitHubRepoID with the GitHub CLI:
gh api repos/{owner}/{repo} --jq '{owner_id: .owner.id, repo_id: .id}'
Once you've scaffolded out the additional subscriptions you want to bootstrap, you can use Terragrunt to provision the resources in each of these subscriptions.
Before you provision resources in each subscription, export ARM_SUBSCRIPTION_ID with the ID of that subscription.
For each subscription you want to bootstrap, you'll need to run the following commands:
cd <subscription-name>/bootstrap
terragrunt run --all --non-interactive --provider-cache --backend-bootstrap plan
terragrunt run --all --non-interactive --provider-cache apply
Finally, edit each of the .gruntwork/environment-<name-of-subscription>.hcl files in the root of your infrastructure-live repository to reference the client IDs of the applications we just provisioned.
environment "prod" {
filter {
paths = ["prod/*"]
}
authentication {
azure_oidc {
tenant_id = "00000000-0000-0000-0000-000000000000"
subscription_id = "99999999-9999-9999-9999-999999999999"
plan_client_id = "" # FIXME: Fill in the client ID for the plan application after bootstrapping
apply_client_id = "" # FIXME: Fill in the client ID for the apply application after bootstrapping
}
}
}
Just like before, run terragrunt stack output in the name-of-subscription/bootstrap directory to find the values for plan_client_id and apply_client_id.
terragrunt stack output
bootstrap = {
apply_app = {
client_id = "55555555-5555-5555-5555-555555555555"
}
plan_app = {
client_id = "66666666-6666-6666-6666-666666666666"
}
}
The resources that you need provisioned in GCP to start managing resources with Pipelines are:
- A Workload Identity Pool with an OIDC provider
- A service account for Pipelines to impersonate when running Terragrunt plan commands
- A service account for Pipelines to impersonate when running Terragrunt apply commands
For every GCP project you want Pipelines to manage infrastructure in.
This may seem like a lot to set up, but the content you need to add to your infrastructure-live repository is minimal. The majority of the work will be pulled from a reusable catalog that you'll reference in your infrastructure-live repository.
If you want to peruse the catalog that's used in the bootstrap process, you can take a look at the terragrunt-scale-catalog repository.
The process that we'll follow to get these resources ready for Pipelines is:
- Set up the Terragrunt configurations in your
infrastructure-liverepository for bootstrapping Pipelines in a single GCP project - Use Terragrunt to provision these resources in your GCP project
- (Optionally) Bootstrap additional GCP projects until all your GCP projects are ready for Pipelines
Bootstrap your infrastructure-live repository
To bootstrap your infrastructure-live repository, we'll use Boilerplate to scaffold it with the necessary content for Pipelines to function.
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/gcp/github/infrastructure-live?ref=v1.14.0' \
--output-folder .
You can just reply y to all the prompts to include dependencies, and accept defaults unless you want to customize something.
Alternatively, you could run Boilerplate non-interactively by passing the --non-interactive flag. You'll need to supply the relevant values for required variables in that case.
e.g.
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/gcp/github/infrastructure-live?ref=v1.14.0' \
--output-folder . \
--var 'ProjectName=dev' \
--var 'GCPProjectID=my-project-123' \
--var 'GCPProjectNumber=123456789012' \
--var 'GCPRegion=us-central1' \
--var 'GitHubOrgName=acme' \
--var 'GitHubRepoName=infrastructure-live' \
--var 'GitHubOrgID=1234567' \
--var 'GitHubRepoID=7654321' \
--var 'StateBucketName=my-state-bucket' \
--non-interactive
You can also choose to store these values in a YAML file and pass it to Boilerplate using the --var-file flag.
ProjectName: dev
GCPProjectID: my-project-123
GCPProjectNumber: "123456789012"
GCPRegion: us-central1
GitHubOrgName: acme
GitHubRepoName: infrastructure-live
GitHubOrgID: 1234567
GitHubRepoID: 7654321
StateBucketName: my-state-bucket
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/gcp/github/infrastructure-live?ref=v1.14.0' \
--output-folder . \
--var-file vars.yml \
--non-interactive
GitHubOrgID and GitHubRepoID may be left blank (or omitted entirely) if your repository was created before July 15, 2026 and hasn't opted in to GitHub's immutable subject-claim format for Actions OIDC tokens — Pipelines will continue to use the legacy name-based sub claim in that case.
You can look up the numeric owner/repo IDs for GitHubOrgID and GitHubRepoID with the GitHub CLI:
gh api repos/{owner}/{repo} --jq '{owner_id: .owner.id, repo_id: .id}'
Next, install Terragrunt and OpenTofu locally (the .mise.toml file in the root of the repository after scaffolding should already be set to the versions you want for Terragrunt and OpenTofu):
mise install
Provisioning the resources
Once you've set up the Terragrunt configurations, you can use Terragrunt to provision the resources in your GCP project.
Make sure that you're authenticated with GCP locally before proceeding.
gcloud auth application-default login
First, make sure that everything is set up correctly by running a plan in the bootstrap directory in project-name where project-name is the name of the first GCP project you want to bootstrap.
terragrunt run --all --non-interactive --provider-cache --backend-bootstrap plan
We're using the --provider-cache flag here to ensure that we don't re-download the Google provider on every run by leveraging the Terragrunt Provider Cache Server.
We're using the --backend-bootstrap flag here to tell Terragrunt to bootstrap the GCS backend automatically for the project.
Next, apply the changes to your project.
terragrunt run --all --non-interactive --provider-cache apply
Optional: Bootstrapping additional GCP projects
If you have multiple GCP projects, and you want to bootstrap them as well, you can do so by following a similar, but slightly condensed process.
For each additional project you want to bootstrap, you'll use Boilerplate in the root of your infrastructure-live repository to scaffold out the necessary content for just that project.
If you are going to bootstrap more GCP projects, you'll probably want to commit your existing changes before proceeding.
git add .
git commit -m "Add core Pipelines scaffolding [skip ci]"
The [skip ci] in the commit message is just in-case you push your changes up to your repository at this state, as you don't want to trigger Pipelines yet.
Just like before, you'll use Boilerplate to scaffold out the necessary content for just that project.
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/gcp/github/project?ref=v1.14.0' \
--output-folder .
Again, you can just reply y to all the prompts to include dependencies, and accept defaults unless you want to customize something.
Alternatively, you could run Boilerplate non-interactively by passing the --non-interactive flag. You'll need to supply the relevant values for required variables in that case.
e.g.
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/gcp/github/project?ref=v1.14.0' \
--output-folder . \
--var 'ProjectName=prod' \
--var 'GCPProjectID=my-prod-project-456' \
--var 'GCPProjectNumber=456789012345' \
--var 'GCPRegion=us-central1' \
--var 'GitHubOrgName=acme' \
--var 'GitHubRepoName=infrastructure-live' \
--var 'GitHubOrgID=1234567' \
--var 'GitHubRepoID=7654321' \
--var 'StateBucketName=my-prod-state-bucket' \
--non-interactive
If you prefer to store the values in a YAML file and pass it to Boilerplate using the --var-file flag, you can do so like this:
ProjectName: prod
GCPProjectID: my-prod-project-456
GCPProjectNumber: "456789012345"
GCPRegion: us-central1
GitHubOrgName: acme
GitHubRepoName: infrastructure-live
GitHubOrgID: 1234567
GitHubRepoID: 7654321
StateBucketName: my-prod-state-bucket
boilerplate \
--template-url 'github.com/gruntwork-io/terragrunt-scale-catalog//templates/boilerplate/gcp/github/project?ref=v1.14.0' \
--output-folder . \
--var-file vars.yml \
--non-interactive
GitHubOrgID and GitHubRepoID may be left blank (or omitted entirely) if your repository was created before July 15, 2026 and hasn't opted in to GitHub's immutable subject-claim format for Actions OIDC tokens — Pipelines will continue to use the legacy name-based sub claim in that case.
You can look up the numeric owner/repo IDs for GitHubOrgID and GitHubRepoID with the GitHub CLI:
gh api repos/{owner}/{repo} --jq '{owner_id: .owner.id, repo_id: .id}'
Once you've scaffolded out the additional projects you want to bootstrap, you can use Terragrunt to provision the resources in each of these projects.
Make sure that you authenticate to GCP with an account that has permissions in each GCP project you are bootstrapping before you attempt to provision resources in it.
For each project you want to bootstrap, you'll need to run the following commands:
cd <project-name>/bootstrap
terragrunt run --all --non-interactive --provider-cache --backend-bootstrap plan
terragrunt run --all --non-interactive --provider-cache apply
Commit and push your changes
Commit and push your changes to your repository.
You should include [skip ci] in your commit message here to prevent triggering the Pipelines workflow.
git add .
git commit -m "Add Pipelines GitHub Actions workflow [skip ci]"
git push
🚀 You've successfully added Pipelines to your new repository!
Next steps
You have successfully completed the installation of Pipelines in a new repository. Proceed to Deploying your first infrastructure change to begin deploying changes.