terraform-provider-rancher2 icon indicating copy to clipboard operation
terraform-provider-rancher2 copied to clipboard

Add token support to rancher2_user resource

Open sboschman opened this issue 3 years ago • 12 comments
trafficstars

See #315 for different use-cases.

The rancher2_user resource has the username/password which are required to do a 'login' and get an initial token. The ttl of this token is set to the 'auth-user-session-ttl-minutes'' by Rancher, regardless of any ttl in thr login request. So the 'login' token is used temporarily to make an api call to the token endpoint to create the token returned by the resource with the defined ttl. This also allows to create cluster scoped tokens.

The rancher2_token resource can only generate tokens for the api account used to authenticate the tf provider. The tf provider does not accept username/password as authentication, but if it did generating tokens for multiple users would create a sprawl of providers.

fixes #315

TODO:

  • test cases

sboschman avatar May 16 '22 09:05 sboschman

Thank you for picking this up I was going to do this as I had a need to create service account users and tokens

Shocktrooper avatar May 17 '22 17:05 Shocktrooper

Few Questions as you are developing this. Would it be better to either modify the current rancher2_token resource to allow a username/password combination to be passed in to generate a token on behalf of a user. The username and password are already stored in the rancher2_user resource so this shouldn't be a problem. We could also leave the current one alone and make a new resource like rancher2_service_token and leave the other token resource alone. This new resource would separate both of token resources capabilities to the end user. If you do continue with adding token management to the user object I would ask if you could make it take in a list of maps so that one can create multiple tokens per user vs just one per user. The downside I can see with multiple tokens on the user object is passing that data to outside of the resource, a lookup would be needed E.G. my_secret_token = lookup(rancher2_user.tokens, "token_name", "undefined").token

Shocktrooper avatar May 17 '22 17:05 Shocktrooper

Current impl is based on rawmind0's idea to modify rancher2_user resource ( https://github.com/rancher/terraform-provider-rancher2/issues/315#issuecomment-613142644)

If you need multiple tokens, you can feed the token from rancher2_user resource to a new provider instance. Which basically gives you the option to call any provider resource as the created user. This should also allow you to create a user (with desired permissions), put the token in a secret store shared with a team, and have the team call the api (maybe with tf) to manage their projects or something.

sboschman avatar May 17 '22 17:05 sboschman

Isn't that going to be a really wonky setup. I don't believe you can pass outputs/attributes from a resource into a provider configuration without having some exports into a file or environment variable for another terraform run in a separate directory.

Shocktrooper avatar May 17 '22 18:05 Shocktrooper

Isn't it the same way the bootstrap works, feeding resource output from one provider to the config of another provider? https://registry.terraform.io/providers/rancher/rancher2/latest/docs#example-usage

sboschman avatar May 17 '22 18:05 sboschman

Interesting that you can do that I didn't think you could pass resources into a provider configuration let alone in the same execution. I previously believed your providers had to be fully configured upon execution time. The official terraform documentation also seems to support that. I am wondering if the provider docs are wrong in the example. Reference

  • https://www.terraform.io/language/providers/configuration#:~:text=You%20can%20use,in%20the%20configuration).

Shocktrooper avatar May 17 '22 19:05 Shocktrooper

I'm using the bootstrap setup as outlined in the rancher2 provider example, seems to work as described.

The official tf docu lines you linked are correct I guess, but they never mention this scenario explicitly. I mean, the lines you linked are describing that you cannot use resource attributes in the config of the provider used for that resource. But the scenario with a second provider, using an alias, is never mentioned. They do mention 'only reference values that are known before the configuration is applied'. Which begs the question... when is the configuration for the second/aliased provider applied. Which seems to be after the resources of the first provider are synced, allowing you to use resource attributes from resources using the first provider to define config for the second provider.

This isn't working, which the official documentation tries to describe:

provider "rancher2" {
  api_url   = "..."
  token_key = rancher2_token.foo.token
}

resource "rancher2_token" "foo" {
  description = "foo token"
}

Using an alias to define a 2nd provider works:

provider "rancher2" {
  api_url   = "..."
  token_key = "..."
}

resource "rancher2_token" "foo" {
  description = "foo token"
}

provider "rancher2" {
  alias = "bar"
  api_url = "..."
  token_key =  rancher2_token.foo.token
}

sboschman avatar May 17 '22 20:05 sboschman

That is very interesting that that specific scenario works. Going forth though do we want to declare a separate provider for every service account that is created? Also unless you use something like terragrunt for pre-processing I don't think you can pass in a variable for the alias for the secondary providers

Shocktrooper avatar May 17 '22 21:05 Shocktrooper

Sounds like you are looking for a way to generate a lot of user accounts each with multiple tokens.

Modifying the rancher2_token resource feels a bit strange to me for the following reason. The api credentials are supplied to the provider, all resource calls are made using those credentials. Adding username/password to the rancher2_token resource changes this concept, as you are basically adding the credentials to the api directly to the resource. I am fine going this route, but I am not a maintainer of this provider, so before going that route some maintainer input would be appreciated.

Adding a new token resource is indeed another option. I have the same concerns with this option as with modifying the current rancher2_token resource. Not being a maintainer I am a bit reluctant to just start adding new resources. If current maintainers agree that a new resource is the best way to go, then I am fine going that direction.

Multiple tokens as part of the rancher2_user resource seems quite impossible to me atm. Looking at the HCL spec, I am thinking nested blocks with a logical key:

resource "rancher2_user" "foo" {
  token "token-1" {
  }
  token "token-2" {
  }
}

resource "..." "..." {
  bar = rancher2_user.foo.token["token-1"].token.
}

Unfortunately this is not possible with the current tf plugin sdk as far as I can tell. Currently the rancher2 provider is using v1 of terraform-plugin-sdk, this sdk also has a v2. And to add to my confusion, there also seems a completely new sdk - terraform-plugin-framework. Upgrading the rancher2 provider to use these newer sdk's is most likely a huge undertaking.

Apart from the issue of referencing individual tokens from a rancher2_user resource, like you mentioned before, I also ran into an issue with 'custom diffs' unable to reference nested attributes. After digging through the terraform sdk source I found out that this is explicitly disallowed (something with list items need to replaced as a whole). This is the reason I opted for token_config, bundling together only the input args, and adding all the exported attributes to the root.

sboschman avatar May 18 '22 08:05 sboschman

Sounds like you are looking for a way to generate a lot of user accounts each with multiple tokens.

I am 😄

Based on your discovery I would opt for probably a separate token resource named something like rancher2_service_account_token that does take a username/password input and we can leave the current token/user resource alone as it still has its uses

Shocktrooper avatar May 19 '22 00:05 Shocktrooper

Would this work in your case @Shocktrooper ?

# Create a rancher2 Token
resource "rancher2_user" "foo" {
  name = "foo"
  username = "foo"
  password = "changeme"
  enabled = true
}

resource "rancher2_global_role_binding" "foo-login" {
  name = "foo-login-binding"
  global_role_id = "user-base"
  user_id = rancher2_user.foo.id
}

resource "rancher2_service_account_token" "foo" {
  username = rancher2_user.foo.username
  password = rancher2_user.foo.password
  description = "foo token"
  ttl = 0

  depends_on = [
    rancher2_global_role_binding.foo-login
  ]
}

sboschman avatar May 19 '22 11:05 sboschman

@annablender Can we get a review for this so we can potentially add it to the v2.6.6 milestone?

Shocktrooper avatar Jul 01 '22 01:07 Shocktrooper

Closing this PR as it's a duplicate

a-blender avatar Apr 06 '23 14:04 a-blender