[Virtual Event] The AI Readiness gap: Where infrastructure teams are getting AI wrong

Register ➡️

Terraform

How to Manage GitHub with Terraform

how to manage GitHub with Terraform

Managing a handful of GitHub repositories by hand is fine. Managing 200 of them, each with consistent access control, branch rules, secrets, and team permissions, is where clicking through settings pages falls apart. This is where Terraform comes in. With the Terraform GitHub provider, you manage repositories, branches, teams, issues, and Actions as infrastructure as code (IaC), the same way you manage your cloud.

In this article, we will explore some of the efficient ways to manage GitHub repositories with Terraform.

We will cover:

  1. Managing GitHub repositories — challenges
  2. How to set up Terraform GitHub provider
  3. Managing GitHub repositories with Terraform
  4. Managing GitHub branches with Terraform
  5. Managing GitHub issues with Terraform
  6. Managing GitHub Actions with Terraform

TL;DR

  • You can manage GitHub repositories, branches, teams, issues, and Actions as code with Terraform’s integrations/github provider.
  • Authenticate with a GitHub App token via the app_auth block for production, or a personal access token from GITHUB_TOKEN for smaller setups.
  • Enforce consistent review rules and access with github_branch_protection (or github_repository_ruleset) and github_team, applied the same way across every repository.

Managing GitHub repositories — challenges

GitHub provides a web-based centralized platform to store, manage, and share code. With its user-friendly interface and collaborative tools, it has become a popular choice for version control and project management.

Challenges in managing GitHub repositories and projects include: 

  • Access control, particularly when the number of contributors and repositories grows.
  • Security concerns such as exposure of secrets, unauthorized access, data leaks, and breaches.
  • Ensuring consistency across repositories and projects for compliance and collaboration.
  • Managing multiple repositories and projects by hand is slow and error-prone.
  • The complexity of scaling and automating GitHub management without the right tools and processes.

Terraform fixes this. Codify your GitHub setup once, then version, automate, reuse, and audit it like any other IaC. By managing GitHub with Terraform, we control organizations, repositories, branches, teams, projects, and Actions from one place.

For example, a team leader can use Terraform to grant or revoke access to specific repositories, ensuring compliance and reducing human error. With Terraform, we can use a single configuration file to manage access control for all of our repositories, making it much easier to ensure consistency and security across your organization.

To run this Terraform in a governed, automated workflow rather than from your laptop, you can use Spacelift. It manages your GitHub credentials per run and adds policy as code, programmatic configuration, context sharing, and drift detection out of the box, with a native GitHub integration. You can try it free by creating a trial account.

How to set up Terraform GitHub provider

Before we can start managing GitHub with Terraform, we need to set up the Terraform GitHub provider. To do this, we’ll need to provide our GitHub personal access token and set up the provider configuration.

The following code snippet shows how to set up the GitHub provider in Terraform.

terraform {
  required_providers {
    github = {
      source  = "integrations/github"
      version = "~> 6.0"
    }
  }
}

provider "github" {
  owner = "your_org_name"
  # The token is read from the GITHUB_TOKEN environment variable.
  # For production, prefer a GitHub App via the app_auth block.
}

resource "github_repository" "example" {
  name        = "repo0088"
  description = "This is my GitHub repository"
  visibility  = "private"
  auto_init   = true
}

The owner attribute is the name of your organization or user account. In my case, the organization is “letsdote-ch”. Declaring the provider version in required_providers matters here, because v6 introduced breaking changes from earlier versions.

Rather than hardcoding a token, we source it from the GITHUB_TOKEN environment variable. The provider accepts a classic personal access token, a fine-grained personal access token, or a GitHub App installation token via the app_auth block, which is the recommended option for production. 

For the examples in this post, the token needs the repo, admin:org, and delete_repo permissions (or the equivalent fine-grained scopes for repository administration and organization access).

You can also check out how to implement GitLab CI/CD pipeline with Terraform.

Managing GitHub repositories with Terraform

To manage GitHub repositories with Terraform, we can use the github_repository resource. This resource allows us to create, modify, or delete GitHub repositories programmatically.

Creating a new repository

The following code snippet shows how to create a new GitHub repository with Terraform.

resource "github_repository" "example" {
 name        = "repo0088"
 description = "This is my Github repository"
 visibility  = "private"
 auto_init   = true
}

Next, run terraform plan and terraform apply to confirm if this created a new repository on GitHub or not. Below is the terminal output for the apply command.

terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # github_repository.example will be created
  + resource "github_repository" "example" {
      + allow_auto_merge            = false
      + allow_merge_commit          = true
      + allow_rebase_merge          = true
      + allow_squash_merge          = true
      + archived                    = false
      + auto_init                   = true
      + default_branch              = (known after apply)
      + delete_branch_on_merge      = false
      + description                 = "This is my Github repository"
      + etag                        = (known after apply)
      + full_name                   = (known after apply)
      + git_clone_url               = (known after apply)
      + html_url                    = (known after apply)
      + http_clone_url              = (known after apply)
      + id                          = (known after apply)
      + merge_commit_message        = "PR_TITLE"
      + merge_commit_title          = "MERGE_MESSAGE"
      + name                        = "repo0088"
      + node_id                     = (known after apply)
      + private                     = (known after apply)
      + repo_id                     = (known after apply)
      + squash_merge_commit_message = "COMMIT_MESSAGES"
      + squash_merge_commit_title   = "COMMIT_OR_PR_TITLE"
      + ssh_clone_url               = (known after apply)
      + svn_url                     = (known after apply)
      + visibility                  = "private"
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

github_repository.example: Creating...
github_repository.example: Creation complete after 6s [id=repo0088]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Verify the operation by visiting the organization on GitHub and searching for the “repo0088” repository, as shown in the screenshot below:

search for the repository

Importing existing repositories

Instead of creating a new repository, we can import and manage existing GitHub repositories with Terraform using the terraform import command.

To do this, either select an existing repository from the organization’s GitHub account, or create one for testing purposes. We have created a repository named “repo0099-existing” for the sake of this example.

To import this repository under Terraform management, create the corresponding IaC configuration as shown below.

terraform import github_repository.existing_repo repo0099-existing 
github_repository.existing_repo: Importing from ID "repo0099-existing"...
github_repository.existing_repo: Import prepared!
  Prepared github_repository for import
github_repository.existing_repo: Refreshing state... [id=repo0099-existing]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

As indicated in the console output, we have successfully imported the existing repository from GitHub to Terraform management.

Note: For more details on how to use the import command, refer to Importing Existing Infrastructure into Terraform blog.

Managing GitHub branches with Terraform

To manage GitHub branches with Terraform, use the github_branch and github_branch_protection resources. These resources allow us to create or manage branches and enforce branch protection rules, such as requiring pull request reviews, requiring status checks, and restricting who can push to a branch. 

Creating a new branch

To create a branch for a repository, add the following resource block in the Terraform configuration file. It refers to the repository object name and creates the development branch.

resource "github_branch" "development" {
 repository = github_repository.example.name
 branch     = "development"
}

Run the terraform apply command and check if the branch is successfully created on the target repository.

This is confirmed by the screenshot below:

create new branch

Importing existing branches

We can use the import command to import existing GitHub branches that we want to manage with Terraform.

To do this, first, create a branch in one of the repositories using the GitHub web UI. You can also use this if you are working with existing repositories and branches.

import existing branch

For example, we would like to import the staging branch for repository repo0099-existing.

The overall import process is the same — create the corresponding configuration in Terraform Infrastructure as Code (as shown below) and then run the import command. 

resource "github_branch" "existing_staging" {
 repository = github_repository.existing_repo.name
 branch     = "staging"
}

The following terminal output shows how the import command imports the staging branch of a GitHub repository into Terraform:

terraform import github_branch.existing_staging repo0099-existing:staging
github_branch.existing_staging: Importing from ID "repo0099-existing:staging"...
github_branch.existing_staging: Import prepared!
  Prepared github_branch for import
github_branch.existing_staging: Refreshing state... [id=repo0099-existing:staging]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

Managing GitHub branch protection with Terraform

Creating a branch is only half the job. To enforce review and status-check rules on it, use the github_branch_protection resource:

resource "github_branch_protection" "default" {
  repository_id = github_repository.example.node_id
  pattern       = "main"
  enforce_admins = true

  required_pull_request_reviews {
    required_approving_review_count = 1
  }

  required_status_checks {
    strict = true
  }
}

On newer repositories, you can also use github_repository_ruleset, which maps to GitHub’s rulesets and supports organization-level rules that branch protection cannot.

Managing GitHub teams with Terraform

For organizations, access control usually runs through teams rather than individual collaborators. Use github_team to create a team and github_team_repository to grant it access to a repository:

resource "github_team" "platform" {
  name        = "platform"
  description = "Platform engineering team"
  privacy     = "closed"
}

resource "github_team_repository" "platform_example" {
  team_id    = github_team.platform.id
  repository = github_repository.example.name
  permission = "push"
}

Managing access this way means a single Terraform change updates permissions across every repository the team touches, instead of clicking through each one.

Managing GitHub issues with Terraform

In GitHub, an issue is a feature that is used to track enhancements, bugs, or any other tasks within a project. Issues allow users to collaborate with the project contributors. Anyone who has access to the GitHub repos can access and open the GitHub issues.

We can also link one issue with other issues or pull requests. This makes it easy to track down all the different bugs, tasks, and enhancements within a project.

Creating a new GitHub issue

To manage GitHub issues with Terraform, use the github_issue resource. This resource allows us to create, modify, or delete GitHub issues with code. 

The following code snippet shows how to create a new GitHub issue with Terraform:

resource "github_issue" "first_issue" {
 repository = github_repository.example.name
 title = "First Issue"
 body = "This is the first issue."
}

The plan command output on the terminal should highlight that one issue will be added to the selected repository as shown below.

terraform plan
github_repository.existing_repo: Refreshing state... [id=repo0099-existing]
github_repository.example: Refreshing state... [id=repo0088]
github_branch.development: Refreshing state... [id=repo0088:development]
github_branch.existing_staging: Refreshing state... [id=repo0099-existing:staging]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # github_issue.first_issue will be created
  + resource "github_issue" "first_issue" {
      + body       = "This is the first issue."
      + etag       = (known after apply)
      + id         = (known after apply)
      + issue_id   = (known after apply)
      + number     = (known after apply)
      + repository = "repo0088"
      + title      = "First Issue"
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Before running the apply command, make sure the ‘issues’ feature is enabled on the target repository. This feature is enabled by navigating to “Settings > General > Features.” The “Issues” checkbox is shown in the screenshot below:

features issues box

We must also update the corresponding resource configuration of the repository in the IaC code.

The resource block for our example repository now has an additional attribute named “has_issues” set as true, as shown below:

resource "github_repository" "example" {
 name        = "repo0088"
 has_issues  = true
 description = "This is my Github repository"
 visibility  = "private"
 auto_init   = true
}

If this is enabled, run the apply command and observe the successful terminal output.

You can verify in the GitHub UI that the issue was successfully created. 

verify issue

Managing GitHub Actions with Terraform

GitHub Actions enable the creation of several automated workflows, which are triggered through several events in a GitHub repository. They contain one or more jobs that perform stepwise individual tasks. So we can use Actions to perform a wide variety of tasks, such as building applications, running tests, and automated code reviews. GitHub provides a library containing all pre-built Actions that we can use as-is or customize to suit our personal needs.

Novibet logo in white

Novibet is in the cloud, and everything is provisioned through Terraform, which the team previously managed using GitHub Actions. However, as the organization scaled, managing Novibet’s IaC through a generic CI/CD platform began to stretch the capabilities of both the tool and the DevOps team. The Spacelift platform has enabled the team to deploy faster and with greater control as they move toward a platform engineering mindset and enable autonomy with guardrails.

Spacelift customer case study

Read the full story

To manage GitHub Actions with Terraform, use the github_actions_secret resource. This resource allows us to manage the secrets used in GitHub Actions. The following code shows how to create a new Github Actions secret with Terraform:

resource "github_actions_secret" "my_secret" {
 repository      = github_repository.example.name
 secret_name     = "MY_SECRET"
 plaintext_value = "my-secret-value"
}

Run the terraform plan and apply commands to store this secret in the target GitHub repo.

Look at the GitHub UI to see if the secret was successfully created.

github actions secret

Similarly, we can manage other GitHub Action resources using Terraform provider integrations/github.

Beyond secrets, the provider manages most of the Actions surface: github_actions_variable, github_actions_organization_secret, and github_actions_environment_secret, among others.

There is no dedicated resource for a workflow itself, because a workflow is just a YAML file in the .github/workflows/ directory. Rather than shelling out with null_resource and local-exec, use the github_repository_file resource to commit the workflow directly:

resource "github_repository_file" "ci_workflow" {
  repository          = github_repository.example.name
  branch              = "main"
  file                = ".github/workflows/ci.yml"
  content             = file("${path.module}/workflows/ci.yml")
  commit_message      = "Add CI workflow (managed by Terraform)"
  commit_author       = "Terraform"
  commit_email        = "terraform@example.com"
  overwrite_on_create = true
}

This keeps the workflow definition in your repository and under Terraform management, with no provisioner required.

Read more about managing Terraform with GitHub Actions.

Key points

Terraform simplifies GitHub management by treating repositories, branches, teams, issues, and Actions as code. You create, import, and modify them with the same declarative workflow you already use for your cloud, so your GitHub setup stays versioned, reviewable, and consistent across every repository.

And don’t forget to explore how Spacelift makes it easy to work with Terraform.

Note: Terraform moved to the BUSL license starting with version 1.6, so version 1.5.x and earlier remains open source under MPL 2.0. OpenTofu is an open-source fork of Terraform, created from version 1.5.6, that expands on Terraform’s existing concepts. It is a viable alternative to HashiCorp’s Terraform.

Orchestrate Terraform deployments with Spacelift

Orchestrate your Terraform workflows and build governed pipelines using policy as code, programmatic configuration, context sharing, drift detection, resource visualization, and many more.

Learn more

Frequently asked questions

  • Can Terraform create GitHub Actions workflows?

    Not with a dedicated resource, because a workflow is a YAML file, not an API object. Use the github_repository_file resource to commit the file to .github/workflows/, and Terraform manages it like any other resource.

  • Which authentication method should I use for the Terraform GitHub provider?

    A GitHub App installation token via the app_auth block is the recommended option for production, because it avoids tying automation to a single user’s account. A fine-grained personal access token is fine for smaller setups or testing. Source either from an environment variable rather than hardcoding it.

  • What happens if someone changes a Terraform-managed repository in the GitHub UI?

    Terraform detects the difference on the next plan and reports drift. Running apply reverts the manual change back to what your configuration declares, which is how you keep settings consistent across an organization.

Terraform Project Structure
Cheat Sheet

Get the Terraform file & project structure

PDF cheat sheet.

terraform files cheat sheet bottom overlay
Share your data and download the cheat sheet