terraform tips: gcp project IAM
Tags: terraform
Published: 2023-06-26 11:12:40

---
title: terraform tips: gcp project IAM
published: true
description: tips for terraform for gcp iam resource creation
tags: terraform
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-06-23 12:17 +0000
---
## Introduction
In this post, we'll go over some tips for managing GCP Project IAM resources using Terraform. These tips are drawn from my experience working with 10 GCP projects and approximately 500 IAM resources. I hope that regardless of whether you're a beginner or a seasoned Terraform user, you might find something of interest or value in this post.

## For groups, users
### for_each with map objects for arguments
In the following code, we utilize for_each with map objects for arguments. This approach significantly reduces resource declaration and we can focus on "for whom" and "to what".
This helped me when I collaborated with an engineer who is not familiar with terraform.

#### google_project_iam_member
```terraform
locals {
  group_list = [
    { group: "developer@example.com", role: "roles/viewer", project:  "your-project-id" },
    { group: "admin@example.com", role: "roles/owner", project: "your-project-id" },
  ]
}

resource "google_project_iam_member" "this" {
  # By making inex like that, state is uniqu
  for_each = { for i in local.group_list : "${i.group}_${i.role}_${i.project}" => i }
  project  = each.value.project_id
  role     = each.value.role
  member   = "group:${each.value.group}"
}

```

- pros: it is very simple. this tip can be applyed to other iam_mambers resourses not only google_project_iam_member.
- cons: it tends to increase map object size because iam member resoueces do not allaw multiple members.


#### google_project_iam_bindings
```terraform
locals {
  group_list = [
    # projects can be parameterize if there are a lot.
    { role : "roles/viewer", projects : ["your-project-id-1","your-project-id-2"],  members : [
      "group:admin@example.com",
      "group:developer@example.com",
    ] },
    { role : "roles/owner", projects :  ["your-project-id-1","your-project-id-2"], members : [
      "group:admin@example.com",
    ] },
  ]
  
  # need some tricks for nested map object
  group_list_flattened = flatten([
    for i in local.group_list : [
      for project in i.projects : {
        role        = i.role
        project     = project
        members     = i.members
      }
    ]
  ])
}

resource "google_project_iam_binding" "this" {
  for_each = { for i in local.group_list_flattened : "${i.role}_${i.project}" => i }

  project = each.value.project_id
  role    = each.value.role
  members = each.value.members
}
```
- pros: reduce more code compared with google_project_iam_member if you need to create common iam for a lot of projects.
- cons: the nature of iam_binding, there is a risk to override iam settings outside of this resource.

## For service accounts
###  Types of service accounts
there are generally two types of service accounts.
- service accounts for tools(cicd pipeline,ETL pipeline, monitoring tool, something like that).
- service accounts for application code.

### Service accounts for tools
For this type of service accounts, calling resources directly or using a simple custom module is good because these service accounts need limited permission to one project. Sometimes we need to add an extra role for a specific purpose like accessing a BigQuery dataset that is located in other GCP projects (e.g. data analysts need specific logs for the application or something similar). In those cases, using a single IAM member/IAM binding resource is sufficient because it emphasizes that those roles are exceptional.
Here's an example:

```terraform
locals {
  projects = [
   "your-projec-id-1",   
   "your-projec-id-2",
  ]
}
module "service_account" {

  source     = "terraform-google-modules/service-accounts/google"
  project_id = "service-account-project-id"
  prefix     = ""
  names      = ["service-account-name"]
  project_roles = concat(
    # for custom role
    formatlist("%s=>projects/%s/roles/custom_role_name", local.projects, local.projects),
    # for default role
    formatlist("%s=>roles/viewer", local.projects),   
}
```

### Service accounts for application code
- For this type of service accounts, calling resources directly or using a simple custom module is good because this service accounts need limited permission to one projects. sometimes we need to add an extra role for a specific purpose　like accessing bigquery dataset that is located to other gcp projects.(data analysts need specific log for application or something like that). In those cases, using single iam member/iam binding resource is sufficient because that emphasize those roles are unusual. 

Here's an example:
```terraform
# module definition. 
resource "google_service_account" "this" {
  project = var.project

  account_id   = var.name
  display_name = var.name
}

resource "time_sleep" "this" {
  # add some deletion because google_project_iam_member sometimes fail if immediately add iam after service account creation.
  depends_on = [
    google_service_account.this
  ]
  triggers = {
    email = google_service_account.this.email
  }
  create_duration = "10s"
}

resource "google_project_iam_member" "this" {
  depends_on = [time_sleep.this]

  for_each = toset(var.roles)

  project = var.project
  role    = each.key
  member  = "serviceAccount:${google_service_account.this.email}"
}


variable "project" {
  type    = string
}

variable "name" {
  type    = string
}

variable "roles" {
  type    = list(string)
  default = []
}

# call module
module "serviceaccount" {
  source = "your-module-path"

  name     = "service-account-name"
  project = "your-project-id"
  roles = [
    "roles/viewer",
  ]
}

```

## Wrapping Up
We've walked through a few different ways to juggle Project IAM resources in GCP using Terraform, This is just a small tip But Hopefully, you've found a trick or two you can use in your own Terraform codes.
Actually, this is my first tech post, so I welcome your feedback. 
Thank you for reading!