Search code examples
githubterraforminfrastructureterraform-provider-github

Best practice for using variables to configure and create new Github repository instance in Terraform instead of updating-in-place


I am trying to set up a standard Github repository template for my organization that uses Terraform to spin up new repos with the configured settings.

Every time I try to update the configuration file to create a new instance of the repository with a new name, instead it will try to update-in-place any repo that was already created using that file.

My question is what is the best practice for making my configuration file reusable with input variables like repo name? Should I make a module or is there some way of reusing that file otherwise?

Thanks for the help.


Solution

  • Terraform is a desired-state-configuration system, which means that your configuration should represent the full set of objects that should exist rather than an instruction to create a single object.

    Therefore the typical way to add a new repository is to add a new resource block declaring that new repository, and leave the existing ones unchanged. Terraform will then see that there's a new resource not currently tracked in the state and will propose to create it.

    If your repositories are configured in some systematic way that you can describe using a mechanical rule rather than manual configuration then you can potentially use the for_each meta-argument to declare multiple resource instances from the same resource block, using Terraform language expressions to describe the systematic rule.

    For example, you could create a local value with a higher-level data structure that describes what should be different between your repositories and then use that data structure with for_each on a single resource block:

    locals {
      repositories = tomap({
        example_1 = {
          description = "First example repository"
        }
        example_2 = {
          description = "Second example repository"
        }
      })
    }
    
    resource "github_repository" "all" {
      for_each = local.repositories
    
      name        = each.key
      description = each.value.description
      private     = true
    }
    

    For simplicity in this example I've only made the name and description variable between the instances, but you can add whatever extra attributes you need for each of the elements of local.repositories and then access them via each.value inside the resource block.

    The private argument above illustrates how this approach can avoid the need to re-state argument values that will be the same for each declared repository, and have your local.repositories data structure focus only on the minimum attributes needed to describe the variations you need for your local policies around GitHub repositories.

    A resource block with for_each set appears as a map of objects when used in expressions elsewhere, using the same keys as in the map given in for_each. Therefore if you need to access the repository ids, or any other attribute of the systematically-declared objects, you can write Terraform expressions that work with maps. For example, if you want to output all of the repository ids as a map of strings:

    output "repository_ids" {
      value = tomap({
        for k, r in github_repository.all : k => r.repo_id
      })
    }