gruntwork-io / terragrunt

Terragrunt is a flexible orchestration tool that allows Infrastructure as Code written in OpenTofu/Terraform to scale.
https://terragrunt.gruntwork.io/
MIT License
8.08k stars 981 forks source link

Unable to get GCP service account impersonation working via config #1587

Closed brandonjbjelland closed 3 years ago

brandonjbjelland commented 3 years ago

Howdy maintainers 👋 This project is great and I probably do too much in it. A case study:

Context

While working on GCP projects, I find it's a good practice to avoid downloading service account keys and instead rely on service account impersonation to authenticate. Overall this is a security boon and makes for a handy facility in simulating what various customers experience having different permission sets (particularly important for platform-eng). This impersonation technique works fine for terraform, you just need to fetch an OAuth token before running any commands:

gcloud config set auth/impersonate_service_account customer-foo-infra-admin@identity-repository.iam.gserviceaccount.com && \
export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)

From here, terragrunt and terraform are happy, but the experience could be better for me and other engineers working in this way. Configuration should be able to resolve this.

The problem

When working across domain boundaries (e.g. simulating a role of a downstream customer) and switching to other well-scoped identities of infra owners of a domain, issuing the impersonation commands every time before tf/tg invocation is cumbersome and makes for a less portable IaC codebase. I would hope to solve this in terragrunt configuration but have so far failed. hcl files nested at the appropriate directory depths are great at capturing hierarchical configuration that can be inherited at deeper layers. I'd like to leverage that practice here.

I haven't dug into the codebase yet, but from what I gather, the shells of before and after hooks of all flavors are subshells or forks - they have no channel back to the parent execution context where the core terraform command is likely issued. There seems to be no mechanism for issuing a command directly before a terraform command such that the shell context persists. For example, none of the following successfully persists the impersonation command:

terraform {
  source = "excessive/interpolation/and/conditionals/leveraging/locals"
  extra_arguments "set_oauth_credential" {
    commands = ["*"]
    env_vars = {
      "GOOGLE_OAUTH_ACCESS_TOKEN" = "$(gcloud auth print-access-token)", # I believe this is working but uses my outermost shell's identitiy
    }
  }
  before_hook "gcloud_auth_script" {
    commands = get_terraform_commands_that_need_input()
    execute  =     execute  = [
      "${find_in_parent_folders("scripts")}/impersonate.sh", # a script containing the command sequence in the block above
      local.team_vars.locals.impersonated_service_account
    ]
  }
  before_hook "gcloud_auth" { # an alternative, direct invocation
    commands = get_terraform_commands_that_need_input()
    execute  = [
      "gcloud",
      "config",
      "set",
      "auth/impersonate_service_account",
      local.team_vars.locals.impersonated_service_account
    ]
  }
}
...

I've tried all manner of before_hook, after_hook and the special hook varieties to no effect. Reordering these blocks also gives no change. Output confirms the hooks run but impersonation doesn't actually take root in the session that counts:

INFO[0018] Executing hook: gcloud_auth
Updated property [auth/impersonate_service_account]. # success? Nope
... # a failing IAM role membership using whoami below
googleapi: Error 400: Account brandon@example.com is of type "user". Please set the type prefix to be "user:"., badRequest

Verification

This data source reveals the executor's identity and always shows a user unless I run the impersonation command sequence of the terragrunt command:

data "google_client_openid_userinfo" "whoami" {}

In the above outputs of data.google_client_openid_userinfo.whoami show the executor of terraform. Unless impersonating from my outermost shell (using both the impersonation AND var export), the inner shell always runs under my user account.

Feature request or guidance

Is there a way to run commands that persist environment context to the shell that runs terraform commands? If so, that should solve my issue. If not, could this be a candidate for a different flavor of special hook?

yorinasub17 commented 3 years ago

Take a look at https://github.com/gruntwork-io/terragrunt/pull/1262, which is a feature proposal to address this exact pain point. It is currently focused on AWS and aws-vault, but the feature could be extended to support GCP as well.

Unfortunately, more design thought is needed to make it generally extensible (and why the effort was stalled).

brandonjbjelland commented 3 years ago

Thanks for that redirect - I'll close this issue and follow along over there. 👍