mrparkers / terraform-provider-keycloak

Terraform provider for Keycloak
https://registry.terraform.io/providers/mrparkers/keycloak/latest/docs
MIT License
624 stars 307 forks source link

Doc/Question: master realm and realm updates? #427

Open max-pfeiffer opened 3 years ago

max-pfeiffer commented 3 years ago

I am wondering how the provider deals with master realm and updates to this realm (or other user created realms): when we initially install Keycloak the master realm becomes autogenerated with this new installation.

So if we use keycloak_realm resource with realm = "master" will this actually just alter the already created master realm or recreate new from scratch?

Not so clear to us when running configuration updates. What would be the best practice do deal with master realm configuration?

tomrutsaert commented 3 years ago

You have to import the state of the existing master realm first. You can do that by adding resource "keycloak_realm" "master" {} in your tf config. And then running the following command: terraform import keycloak_realm.master master

After this you can use terraform as if you created the resource via terraform

This is also stated for each resource at the end of the resource documentation page: For realms: https://registry.terraform.io/providers/mrparkers/keycloak/latest/docs/resources/realm

jcharlytown commented 3 years ago

Hi,

I am facing a similar issue, however somewhat more complicated I think.

I'd like to disable the "security-admin-console" Client on Realms created via terraform. Going through the management console of the master realms, this is basically a single checkbox to uncheck. However, I cannot think of an automated way to perform this via terraform.

The "security-admin-console" client is automatically created during creation of the realm (similar to the master realm during setup). So, after creating the realm via terraform "realm" resource, it exists in the realm, and cannot be "recreated" using an "openid_client" resource, however is also missing from the local terraform state.

I do understand that importing the client from the realm and then patching works. However, I cannot see how to automate this process properly. I basically want all my realms, including ones to be created in the future via terraform to have this client disabled. Is this possible with terraform manifests only, without manual interference?

This is not exactly the usecase as @max-pfeiffer described, however, @max-pfeiffer will face the same kind of issue when adding entire keycloak installations to be managed via terraform dynamically.

Thanks!

max-pfeiffer commented 3 years ago

@tomrutsaert Thanks for your reply. I saw that terraform import command in the documentation. But like @jcharlytown it's not clear to me how to use it. Because as he said we would like to manage our Keycloak installation dynamically.

Is it maybe possible to post some example how to do this actually?

We have a very similar use case as @jcharlytown : we would like to add additional security measures to "security-admin-console" client in master realm. Actually this is what you would like to do generally as you manage the whole Keycloak setup with Terraform. There is no point for any users to have access to the admin console. This is just exposing a security threat.

ghost commented 3 years ago

@max-pfeiffer @jcharlytown

cant provide full fledged working examples, but maybe i can give you the right direction to solve.

As @tomrutsaert described, you can import any realm and client with terraform import. Just define terraform keycloak realm resource for the master in your .tf:

resource "keycloak_realm" "master" {
    realm                   = "master"
    enabled                 = true
    display_name            = "master realm"
    display_name_html       = "<b>master realm</b>"
}

Then run terraform import keycloak_realm.master master in your terminal. The state will be imported in your statefile (not in the .tf file!). When you run terraform plan now, you see terraform will plan to override the master realm with the state you defined in the resources in your .tf file. So now you can just copy/paste all variables from it to your resource "keycloak_realm" "master" at your .tf file. When you run now terraform plan or apply, you see that there should be no differences anymore and you handle now the master realm via terraform without re-create it.

With existing clients, for example the account client of the master realm, its the same. You have to extend now the .tf file with a resource "keycloak_openid_client" "account" and run terraform import keycloak_openid_client.account master/<clientid> Run terraform plan to see the mismatch, copy/paste everything, done.

If you have automatic deployments, for example with docker, puppet etc. where its necessary to import everything automatic while provisioning, then its a little bit more tricky. Then you have to build yourself a own script, which is run inside your deployment workflow. Take a look at https://github.com/mrparkers/terraform-provider-keycloak/blob/master/scripts/create-terraform-client.sh This s a good starting point. It alredy gives you a example how to get the access/bearer token from the Keycloak REST-API and perform post/get/update requests via the Keycloak REST-API like creating the needed terraform-client. You just have to extend the script. Could be work as simple bash instructions as following

  1. Use the script to create the terraform client
  2. Extend the script to Import the master realm to your terraform statefile
  3. Use the get() function from the script and extend the script to identify the client-uid via the Keycloak REST-API. Jq is very helpful here: https://stedolan.github.io/jq/
  4. Use the identified client-uid to import your desired client in terraform
  5. Do the same with everything you want to manage from the alredy existing resources, like role-mappings etc.

Its possible to manage the whole master realm, with all clients and other resources during a fresh keycloak deployment. Of course, you can manage also the clients from a new realm, which will be automaticly created by keycloak during realm creation, this way.

Its not so comfortable to do it, but i think @mrparkers gives a good starting point with the script he used for the docker-compose file for creating the test environment.

whiskeysierra commented 3 years ago

Someone might find this useful. I worked around this using terraform's resource targeting and data source outputs:

terraform init

# We need to import the master realm, since it's created by default
terraform import keycloak_realm.realm master

# Apply the data source to lookup the account-console client-id to import it
terraform apply -target data.keycloak_openid_client.account-console
client_id=$(terraform output -json | jq -r '.["admin-console-client-id"].value')
terraform import keycloak_openid_client.account-console "my-realm/$client_id"

# Now we can apply everything else
terraform apply

What you need to have is a data source to lookup the account-console's client id and a resource for the very same OpenID client:

data "keycloak_openid_client" "account-console" {
  realm_id  = keycloak_realm.realm.id
  client_id = "account-console"
}

resource "keycloak_openid_client" "account-console" {
  realm_id              = keycloak_realm.realm.id
  client_id             = "account-console"
  name                  = "$${client_account-console}"
  access_type           = "PUBLIC"
  root_url              = "$${authBaseUrl}"
  base_url              = "/realms/my-realm/account/"
  full_scope_allowed    = false
  valid_redirect_uris   = ["/realms/my-realm/account/*"]
  web_origins           = ["+"]
  admin_url             = "/realms/my-realm/account"
  standard_flow_enabled = true
  //noinspection HCLUnknownBlockType
  authentication_flow_binding_overrides {
    browser_id = keycloak_authentication_flow.browser.id
  }
}

... as well as an output on your root module:

output "admin-console-client-id" {
  value = data.keycloak_openid_client.account-console.id
}
vyom-soft commented 4 months ago

How can I update the master realm attributes, like

 otp_policy {
           algorithm         = "HmacSHA1" 
           digits             = 6 -> 8
          ...
        }

get the "realm master"

data "keycloak_realm" "master" {
  realm = "master"
}

Doing the below throws duplicate exception

resource "keycloak_realm" "master_realm" {
  realm           = data.keycloak_realm.master.id
  otp_policy {
   digits            = 8
  }
}