petitviolet blog

    Terraform to manage GCP Service Accounts



    The Google provider of Terraform has some mechanisms to manage Service Accounts in GCP as followings.

    • google_service_account_iam
      • google_service_account_iam_policy
      • google_service_account_iam_binding
      • google_service_account_iam_member
    • google_project_iam
      • google_project_iam_policy
      • google_project_iam_binding
      • google_project_iam_member


    Strongly recommend using google_service_account_iam_member and google_project_iam_member to manage GCP service accounts. Not use google_service_account_iam_policy and google_project_iam_policy.


    This resource is to configure GCP service accounts that perform operations within a resource. Meaning that if a service account doesn't need to interact with other GCP resources, google_service_account_iam is the best choice over google_project_iam. As the document describes, google_service_account_iam_policy and google_service_account_iam_binding are Authoritative, which is possible to delete existing resources that are not managed by terraform. Thus, I recommend using google_service_account_iam_member resource over another two since only google_service_account_iam_member performs additive operation, it's a little bit boring to configure all pairs of roles and members though.

    Indeed, my service account for applying terraform plans was locked out because of wrong usage of google_service_account_iam, then subsequent apply failed due to lack of permission because the service account had been deleted unexpectedly.


    As I described above, google_project_iam is to configure GCP service accounts that need to interact with other GCP resources. So, basically we can use google_service_account_iam, but sometimes we have to use it. For example, it requires google_project_iam configurations for giving a permission (roles/cloudsql.client) on a Cloud Run resource to act as a client for a Cloud SQL instance.

    This code snippet shows how google_project_iam_member can be used in configuring the above scenario.

    resource "google_project_iam_member" "cloud_sql_client" {
      project = "${var.gcp_project}"
      role    = "roles/cloudsql.client"
      member  = "serviceAccount:${local.cloud_sql_service_accounts_email}"
      condition {
        expression  = " == '${}' && resource.type == ''"
        title       = "cloudsql.client policy"
        description = "Cloud SQL instance client"

    This document describes google_project_iam resources and also it mentions that wrong usage of google_project_iam_policy may lock yourself out of your project. It's the reason why I recommend using google_project_iam_member rather than google_project_iam_policy. In addition, there is google_project_iam_binding, but it's also marked as "authoritive", whereas google_project_iam_member is "Non-authoritive". "Authoritive" means that it's possible to delete existing resources by following given configurations. So, even though it takes a time to configure all of role and member mappings, using google_project_iam_member is the safest way, I believe.

    How to configure Service Accounts by Terraform

    Basic usage of google_service_account_iam_member looks like below.
    resource "google_service_account" "sa" {
      account_id   = "my-sa"
      display_name = "my ServiceAccount"
    resource "google_service_account_iam_member" "sa_run" {
      service_account_id =
      role               = "roles/run.serviceAgent"
      member             = "serviceAccount:${}"

    First of all, creating a service account is done by google_service_account resource, then giving a role to the created service account.

    As another example, creating a service account for operating GitHub Actions that needs to deploy Cloud Run.

    resource "google_service_account" "github_actions" {
      account_id   = "github-actions"
      display_name = "github-actions"
    locals {
      github_actions_roles = [
    resource "google_project_iam_member" "github_actions" {
      count = length(local.github_actions_roles)
      project = var.gcp_project
      role    = local.github_actions_roles[count.index]
      member  = "serviceAccount:${}"

    As the same with the previous example, create a service account and give permissions needed.