Search code examples
terraformterraform-provider-github

How does one automate Terraform considering one needs to add to variables manually?


A usecase that I am working on uses Jira to collect information on users namely: username and github team to which the user must be added to

and then kicks off Terraform scripts to add that user to GitHub teams as specified. While this works as expected, since I am using a single resource declaration that goes through an array of users and their roles like so :

###
# Resource to define each user's membership in the org
###
resource "github_membership" "github_member" {
  for_each = var.github_memberships
  username = each.value.username
  role     = each.value.role
}


###
# Resource to define each team in the org
###
resource "github_team" "team" {
  for_each = toset(var.github_teams)
  name     = each.value

  lifecycle {
    ignore_changes = [description, privacy]
  }
}


###
# Resource to define a user's membership in a team
###
resource "github_team_membership" "team_membership" {
  for_each = [some unique value i create using locals]
  team_id  = github_team.team[each.value["team_name"]].id
  username = github_membership.github_member[each.value["username"]].username
  role     = each.value["role"]
}

i need to maintain a list of variables like so :

##
# Membership here refers to simply belonging to GitHub as part of org, not any team
###
variable "github_memberships" {
  type = map(object({username: string, role: string}))
  default = {
    "username_1" = {
      username = "username_1",
      role     = "member"
    },
    "username_2" = {
      username = "username_2",
      role     = "member"
    },
    "username_3" = {
      username = "username_3",
      role     = "member"
    },
    "username_4" = {
      username = "username_4",
      role     = "member"
    }
  }
}

###
# List of teams in the org
###
variable "github_teams" {
  type    = list(string)
  default = ["test_team"]
}

variable "github_team_memberships" {
  type = map(object({members: list(object({username: string, role: string}))}))
  default = {
    "test_team" = {
      members = [{username = "username_1", role = "maintainer"}, {username = "username_2", role = "member"}]
    }
  }
}

now if were to add a new user to test_team :

module "GitHubAccessRequest" {
  source = "./child_modules/GitHub_Team_Assigner_Child"
  requested_username = "username_3"
  requested_teams    = ["test_team"]
  requested_role     = "member"
}

TF will add username_3, but the next time around when we use username_4, since username_3 was not added to the variable github_team_memberships manually like username_1 and username_2 are TF will try to delete username_3 and add username_4.

Does this mean that there is no way to automate this process? Every time a ticket is submitted and TF is run the developer needs to update this variable github_team_memberships manually to keep track? This might be possible to do with small teams but for large organization can become quiet cumbersome.


Solution

  • Terraform is a declarative language that persists static objects into a state.

    Hence it does not really suits use cases where one would want to kick a one-time apply to create new resources. The code always has to reflect what Terraform is managing.

    Currently, here are some solutions to workaround this:

    Reduce Terraform scope

    Only use Terraform for static resources (GitHub Teams only for example). Everything that is dynamic (team membership) is managed with a small tool using the GitHub API directly. When a ticket is created, the tool is launched programatically to add or delete membership to a team.

    It has the advantage of automating the process while removing Terraform as an intermediate tool between the ticket creation and the team membership assignation.

    Generate Terraform code automatically

    Program a tool that update Terraform code accordingly to what the ticket is asking and then run the Terraform code.

    The pros on this is that for a simple use case, it can be implemented in a quick-win strategy with a small script.

    Use Terraform CDK

    Although it's quite young and not marketed as a production ready tool (like Terraform was for a long time before reaching 1.0), Terraform CDK might be what answers this need the best on the long term.

    Long story short (to paraphrase their README), it allows one to programatically define Terraform constructs without writing Terraform files themselves. Think of it as an SDK for Terraform files.