Lately, I’ve been creating many demos for work, user-groups, or just for fun. These demos often need one or more GitHub repositories with some code in them. Not that surprising, I suppose.
In this blog post I want to share the simple approach I use to set these demo repositories up using Terraform. This allows me to quickly set up everything I need for a demo without having to have demo repositories lying around just in case I would need them. With that said, of course you will need a repository to store the code to set up the demo repositories - unless you store the code somewhere else.
Use the GitHub provider for Terraform#
In case you didn’t know (but you probably strongly suspected it after reading the introduction to this post) GitHub has a Terraform provider. This provider is developed by GitHub themselves (yes, I have gotten it confirmed by them). You can find the provider at the public Terraform registry.
integrations
namespace. Do not ask me where they got this name from, because I do not know! It’s the only provider under this namespace as you can see here.As with many providers there are a number of ways you can configure it. When I set up a quick demo I almost always do it from my own local environment. In this environment I am authenticated to GitHub using the GitHub CLI - I highly recommend you install it and authenticate it to GitHub on your own machine.
Assuming you also use the GitHub CLI, all you need to do to use the GitHub provider is the following:
terraform {
required_providers {
github = {
source = "integrations/github"
version = "~> 6.6.0"
}
}
}
provider "github" {
owner = var.github_organization
}
Here I configure what GitHub organization (or personal account) I am targeting using the owner
argument with the value from my github_organization
variable.
If you usually use your own private GitHub handle, then you could add the following to your Terraform configuration:
variable "github_organization" {
type = string
description = "GitHub organization name or personal handle"
# replace with your own default value
default = "mattias-fjellstrom"
}
Provision a demo repository#
I usually place the contents of the demo repository (or more commonly repositories) in a directory named repos
. Inside repos
I have one subdirectory for each demo repository I want to create for this particular demo.
To illustrate this, the directory structure might look something like this:
$ tree .
.
├── main.tf
└── repos
├── repo-1
│ ├── main.tf
│ ├── providers.tf
│ └── variables.tf
└── repo-2
├── main.tf
├── providers.tf
└── variables.tf
To create the corresponding repository with the correct content I do this:
locals {
# the path to where the repository content is
path = "${path.module}/repos/repo-1"
# get all filenames in the content directory
# instead of "**" representing all files and subdirectories you can use a pattern to get certain files
filenames = fileset(local.path, "**")
# create a map (filename => content)
content = { for filename in local.filenames : filename => file("${local.path}/${filename}") }
}
resource "github_repository" "demo" {
name = "my-demo-repository"
}
resource "github_repository_file" "all" {
for_each = local.content
repository = github_repository.demo.name
file = each.key
content = each.value
}
And that’s it!
Note how I used the fileset
function along with the **
pattern. If you only want to include certain files (like only your Terraform .tf
files) you can control this in the pattern. However, I recommend that all files you place in the repository directories are suppose to be part of the repo (and in that case **
is a good pattern).
To work with multiple demo repositories you can turn the sample code shown here into a module that takes the content directory and any other required arguments as input.
depends_on
or other tactics to get around this behavior.You can use the GitHub provider for Terraform for a lot more than what I did in this blog post. However, for creating demo repositories quickly you do not need more than this.