Importing Existing Repositories
This runbook explains :
- The prerequisites for importing an existing repository
- How to grant team access
- How to import existing repositories into Terraform state safely
Before you start
- Ensure signed commits is enabled or follow these steps to do so if you have not
- Create a feature branch
- Ensure the repository has been added to the GitHub app as one of those the app has permissions to so that a 403 error never surfaces during the import operation. For more details, check the Troubleshooting runbook
Importing Existing Repos
Important
⚠️ Never import a repository without first checking its current settings.
Terraform will overwrite anything that does not match your configuration. Missing a setting could break a live site, remove team access, or destroy rulesets.
Step 1: Query the Repo’s Current Settings
##### Basic repository settings
gh api "repos/ministryofjustice/REPO_NAME" --jq '{
description,
visibility,
has_wiki,
has_projects,
has_issues,
has_discussions,
homepage_url,
is_template,
allow_auto_merge,
allow_squash_merge,
allow_merge_commit,
allow_rebase_merge,
delete_branch_on_merge,
web_commit_signoff_required
}'
##### Team access
gh api "repos/ministryofjustice/REPO_NAME/teams" \
--jq '.[] | "\(.slug) (id: \(.id), permission: \(.permission))"'
##### Branch protection rulesets
gh api "repos/ministryofjustice/REPO_NAME/rulesets" \
--jq '.[] | {name, enforcement, target}'
##### Pages config (if applicable)
gh api "repos/ministryofjustice/REPO_NAME/pages" 2>/dev/null \
--jq '{cname: .cname, build_type: .build_type}'
##### Check if dependabot security updates are enabled
gh api "repos/ministryofjustice/REPO_NAME/automated-security-fixes" 2>/dev/null
Step 2: Write Config That Matches Reality
Add the repository to the github-repositories.tf with settings that match what the API returned.
Repository behaviour and access
Repositories are defined in github-repositories.tf
in local.github_repositories.
Example:
container_platform_my_new_repo = {
name = "container-platform-my-new-repo"
description = "Repository for ..."
has_projects = true
access = {
admins = [data.github_team.cloud_platform_engineers.id]
pushers = [data.github_team.all_org_members.id]
}
}
Common options already used in this repository:
visibility(publicorprivate, defaults topublic)has_discussionspages_enabledpages_configuration = { cname = "..." }Full list of supported repository feature flags/defaults:
Resource implementation details (rulesets, security, access bindings):
Team data sources
Teams are defined as data "github_team" blocks in data.tf
Commonly used teams:
data.github_team.cloud_platform_engineers.iddata.github_team.all_org_members.id
If a new team is needed, add a data "github_team" block in data.tf first.
Pay special attention to:
description: use the exact current descriptionhomepage_url: don’t drop it if one existsis_template: if it’s a template, set this totruevisibility:public,private, orinternalpages_enabled/pages_configuration: if the repository has GitHub Pageshas_wiki,has_projects,has_discussions: match current values
Example:
container_platform_terraform_gatekeeper = {
name = "container-platform-terraform-gatekeeper"
description = "Gatekeeper Terraform module for the Container Platform"
has_projects = true
access = {
admins = [module.github_team.id, data.github_team.cloud_platform_engineers.id]
pushers = [data.github_team.all_org_members.id]
}
}
Things the module defaults handle (you only need to set if different from defaults):
visibilitydefaults to"public"has_projectsdefaults tofalsehas_discussionsdefaults tofalsepages_enableddefaults tofalseuse_templatedefaults tofalseis_templatedefaults tofalse
[!NOTE] Existing repositories will have
use_templatedefault tofalsein the module call. Do not setuse_template = truefor repositories that already exist. This will cause an error.
Step 3: Create Import Blocks
Create imports.tf with import blocks for resources that already exist on GitHub:
# Always import the repository itself
import {
to = module.github_repositories["REPO_KEY"].github_repository.this
id = "REPO_NAME"
}
# Import dependabot if the app has access (check for 403 first)
import {
to = module.github_repositories["REPO_KEY"].github_repository_dependabot_security_updates.this
id = "REPO_NAME"
}
# Import each team that ALREADY has access
# Only import teams that exist on the repository AND are in your config
import {
to = module.github_repositories["REPO_KEY"].github_team_repository.admin["TEAM_ID"]
id = "TEAM_ID:REPO_NAME"
}
Example can be found here
Step 4: Review the Plan
Before merging, check the Terraform plan output carefully:
~(update): verify every change is intentional+(create): new resources like rulesets and team access are expected-(destroy): red flag unless you’re deliberately removing something- Template block removal (
- template {}): cosmetic and safe
Watch for these dangerous changes:
- Description being overwritten
homepage_urlbeing set tonullpagesblock being removedis_templatechanging fromtruetofalse- Rulesets being rewritten or deleted
Open a PR
- Commit changes with a clear message
- Open a PR describing:
- repository added or changed
- access model (admin/push teams)
The CI workflow will run terraform plan automatically on the PR.
Step 5: Clean Up
After the apply succeeds, delete imports.tf in a follow-up PR.
Import blocks are only needed for the initial import.
Common Pitfalls
Importing repositories the app doesn’t have access to
- You’ll get 403 errors.
- Only import repositories in the GitHub App’s installation scope.
- Click Configure on the Container Platform Access github app, authenticate, search for the repository you intend importing then add it
Importing repositories owned by other teams
- Don’t manage repositories that belong to other teams.
- Example:
template-repositoryis an org-level repository, not a container-platform repository.
Importing to state before merging code
- If you run
terraform importlocally, the resource is in remote state immediately. - If anyone else runs a plan/apply before your code is merged, Terraform will try to destroy those resources.
- If you run
Not checking for existing rulesets
- The module creates a new ruleset called
main. - If the repository already has a different ruleset, it won’t be managed and will coexist.
- Check if the existing one conflicts.
- The module creates a new ruleset called
Troubleshooting
terraform planshows “will be created” for an existing repository- The repository has not been imported into state.
Error about template on existing repository
- Ensure
use_templateis not set totruefor repositories that already exist.
- Ensure
GitHub repository access not applied
- Check
accesskeys reference validdata.github_teamdata sources indata.tf.
- Check
Auth errors during plan/apply
- Locally: ensure
TF_VAR_github_tokenis exported. - CI: check that
CLIENT_IDandAPP_PRIVATE_KEYrepository secrets are set correctly.
- Locally: ensure