Search code examples
terraformterraform-provider-github

How to skip for_each loop if key does not exist in terraform


My problem statement is simple but I am not able to find a solution anywhere on the internet.

I have users list as locals:

// users

locals {
  allUsers = {

    dev_user_1 = {
      Name   = "user1"
      Email  = "user1@abc.com"
      GitHub = "user1" # github username
      Team   = "Dev"
    }

    devops_user_2 = {
      Name   = "user2"
      Email  = "user2@abc.com"
      GitHub = "user2" # github username
      Team   = "DevOps"
    }

    product_user_3 = {
      Name   = "user3"
      Email  = "user3@abc.com"
      Team   = "Product"
    }
  }
}

These are the local tags that are being used for purposes of creating access to internal tools such as Github, Monitoring tools, etc.

Now, for the 2 users who belong to the Dev and DevOps team, they need access to Github ORG, while, the product user only needs access to some dashboards but not to Github, hence, the tag is missing.

How can I loop over the terraform resource github_membership to skip this product user (or simply anyone who does not have tag key GitHub?)

I am trying the following code, but no luck

// Send GitHub invite
resource "github_membership" "xyzTeam" {
  for_each = local.allUsers
  username = each.value.GitHub
  role     = "member"
}

Errors:

╷
│ Error: Unsupported attribute
│
│   on users.tf line 12, in resource "github_membership" "xyzTeam":
│   12:   username = each.value.GitHub
│     ├────────────────
│     │ each.value is object with 3 attributes
│
│ This object does not have an attribute named "GitHub".

What I did to solve this issue?

  1. Set GitHub key for everyone but it's value as null. Error:
╷
│ Error: "username": required field is not set
│
│   with github_membership.xyzTeam["user3"],
│   on users.tf line 10, in resource "github_membership" "xyzTeam":
│   10: resource "github_membership" "devops" {
│
╵
  1. If I left the value empty, errors:
Error: PATCH https://api.github.com/user/memberships/orgs/XYZ: 422 You can only update an organization membership's state to 'active'. []
  1. for k, v in local.allUsers : k => v if v != "" Same error because it tries to create the user with empty value still, and fails ultimately.

I cannot think of anything else. If someone can help to create separate locals from these existing locals, which creates the list of locals that grep the GitHub values, that hack would be super helpful.


Solution

  • You had the right idea with your third attempt, but the conditional logic in the for expression is slightly off. You need to use the can function instead:

    { for user, attributes in local.allUsers : user => attributes if can(attributes.GitHub) }
    

    If the nested map contains a Github key, then can(attributes.Github) returns true, and the map constructor will contain the key-value pair. With this algorithm, you can construct a new map from the old map with the entries removed that do not contain a Github key in the nested map value.