I am trying to create a very simple structure on GCP using Terraform: a compute instance + storage bucket. I did some research across GCP documentation, Terraform documentation, SO questions as well and still can't understand what's the trick here. There is one suggestion to use google_project_iam_binding, but reading thruogh some articles it seems to be dangerous (read: insecure solution). There's also a general answer with only GCP descriptions, nit using tf terms here, which is still a bit confusing. And also concluding the similar question here, I confirm that the domain name ownership was verified via Google Console.

So, I ended up with the following:

data "google_iam_policy" "admin" {
  binding {
    role = "roles/iam.serviceAccountUser"
    members = [
      "user:[email protected]",
      "serviceAccount:${google_service_account.serviceaccount.email}",
    ]
  }
}
resource "google_service_account" "serviceaccount" {
  account_id   = "sa-1"
}
resource "google_service_account_iam_policy" "admin-acc-iam" {
  service_account_id = google_service_account.serviceaccount.name
  policy_data        = data.google_iam_policy.admin.policy_data
}
resource "google_storage_bucket_iam_policy" "policy" {
  bucket      = google_storage_bucket.storage_bucket.name
  policy_data = data.google_iam_policy.admin.policy_data
}
resource "google_compute_network" "vpc_network" {
  name                    = "vpc-network"
  auto_create_subnetworks = "true"
}
resource "google_compute_instance" "instance_1" {
  name         = "instance-1"
  machine_type = "f1-micro"
  boot_disk {
    initialize_params {
      image = "cos-cloud/cos-stable"
    }
  }
  network_interface {
    network = google_compute_network.vpc_network.self_link
    access_config {
    }
  }
}
resource "google_storage_bucket" "storage_bucket" {
  name          = "bucket-1"
  location      = "US"
  force_destroy = true
  website {
    main_page_suffix = "index.html"
    not_found_page   = "404.html"
  }
  cors {
    origin          = ["http://the.domain.name"]
    method          = ["GET", "HEAD", "PUT", "POST", "DELETE"]
    response_header = ["*"]
    max_age_seconds = 3600
  }
}

but if I terraform apply, logs show me an error like that

Error: Error setting IAM policy for service account 'trololo': googleapi: Error 403: Permission iam.serviceAccounts.setIamPolicy is required to perform this operation on service account trololo., forbidden
2020/09/28 19:19:34 [TRACE] statemgr.Filesystem: removing lock metadata file .terraform.tfstate.lock.info
  on main.tf line 35, in resource "google_service_account_iam_policy" "admin-acc-iam":
  35: resource "google_service_account_iam_policy" "admin-acc-iam" {
2020/09/28 19:19:34 [TRACE] statemgr.Filesystem: unlocking terraform.tfstate using fcntl flock
Error: googleapi: Error 403: The bucket you tried to create is a domain name owned by another user., forbidden
  on main.tf line 82, in resource "google_storage_bucket" "storage_bucket":

and some useless debug info. What's wrong? What account is missing what permissions and how to assign them securely?

1

There are 1 answers

0
magnump0 On BEST ANSWER

I found the problem. As always, in 90% of cases, the issue is sitting in front of the computer.

Here are the steps that helped me to understand and to resolve the problem:

  1. I read few more articles and especially this and this answer were very helpful to understand relations between users, service accounts, permissions
  2. I understood that doing terraform destroy is also very important since there is no rollback of unsuccessful deploy of a new infrastructure changes (like with DB migrations for example) - thus you have to clean up either with destroy or manually
  3. completely removed the "user:${var.admin_email}" user account IAM policy since it useless; everything has to be managed by the newly created service account
  4. left the main service account with most permissions untouched (the one which was created manually and downloaded the access key) since Terraform used it's credentials
  5. and changed the IAM policy for the new service account as roles/iam.serviceAccountAdmin instead of a User - thanks @Wojtek_B for the hint

After this everything works smooth!