Introduction
In this blog post, we will be discussing a Terraform code block for creating an custom identity provider (myIdp).
The Keycloak Terraform provider does not have the resources to configure a custom identity provider or any social provider except Google, which was the main reason to create this blog post.
Terraform
is an infrastructure-as-code tool that enables the provisioning of cloud resources using code instead of manual intervention.
Using the null resource
. We trigger the creation and deletion of myIdp using a local-exec
provisioner.
First, let's take a look at the Terraform code block:
resource "null_resource" "myidp_identity_provider" {
triggers = {
myidp_idp_signature = filesha256("${path.module}/myidp_idp.json")
environment = var.environment
realm_id = var.realm_id
kc_admin_client_id = var.kc_admin_client_id
kc_admin_client_secret = var.kc_admin_client_secret
path_module = path.module
}
lifecycle {
ignore_changes = [triggers["environment"], triggers["realm_id"], triggers["kc_admin_client_id"], triggers["kc_admin_client_secret"], triggers["path_module"]]
}
provisioner "local-exec" {
command = "${path.module}/scripts/create_myidp_idp.sh ${var.environment} ${var.realm_id} ${var.kc_admin_client_id} ${var.kc_admin_client_secret} ${var.myidp_idp_client_id} ${var.myidp_idp_client_secret} ${path.module}"
}
provisioner "local-exec" {
when = destroy
command = "${path.module}/scripts/delete_myidp_idp.sh ${self.triggers.environment} ${self.triggers.realm_id} ${self.triggers.kc_admin_client_id} ${self.triggers.kc_admin_client_secret} ${self.triggers.path_module}"
}
}
Resource Explanation
null resource
is a resource that doesn't create any actual infrastructure but can be used to trigger provisioners
. The resource assigned the name myidp_identity_provider
.
Trigger Block
Next, we have a triggers
block, which specifies that the resource should be triggered when certain conditions are met. In this case, the triggers are the SHA-256 hash of the myidp_idp.json
file, which contains the configuration for the myidp identity provider, along with other variables such as environment, realm_id, kc_admin_client_id, and kc_admin_client_secret. The "path.module" variable is also included to ensure that the correct path is used as it will be used later to call the creation/deletion scripts.
Lifecycle Block
After the trigger block, we have a "lifecycle" block that specifies which triggers to ignore when the resource is destroyed. This is important because Terraform can't reference attributes of a resource that's being destroyed. Other than the myidp_idp_signature
trigger, ignore. The additional triggers were added to be used later in the Destroy-time provisioners as their connection configurations may only reference attributes of therelated resource, via 'self', 'count.index', or 'each.key'.
Provisioner Blocks
Finally, we have two "provisioner" blocks that use the local-exec
provisioner to execute shell scripts.
The first provisioner block creates the myidp identity provider using the variables specified in the trigger block.
The second provisioner block is only executed when the resource is being destroyed and deletes the myidp identity provider using the triggers stored in the resource state.
myidp_idp_signature
In the Terraform configuration file, myidp_idp_signature
is a trigger that is set to the SHA-256 hash of the file located at ${path.module}/myidp_idp.json
. The purpose of this trigger is to signal to Terraform that the null_resource should be recreated if the myidp_idp.json
file changes.
When Terraform detects a change in the SHA-256 hash of the myidp_idp.json
file, it will destroy and recreate the null_resource. This is important because the null_resource is used to execute the create_myidp_idp.sh
script, which generates an Identity Provider (IDP) in Keycloak based on the contents of the myidp_idp.json file.
By using the myidp_idp_signature
trigger, we ensure that the null_resource
is always recreated when the myidp_idp.json
file changes, which guarantees that the IDP in Keycloak is always up-to-date with the latest version of the myidp_idp.json
file.
Scripts
the scripts used in the provisioner blocks handle the authentication and creation/deletion of the myidp identity provider by using the Keycloak admin API. By separating the functionality into separate scripts, it makes the code more modular and easier to maintain.
There are three scripts used in the provisioner blocks: get_kc_admin_token.sh
, create_myidp_idp.sh
and delete_myidp_idp.sh
. let's take a look at each:
Get Keycloak Admin API Token
#!/usr/bin/env bash
environment=$1
kc_admin_client_id=$2
kc_admin_client_secret=$3
kc_url='https://keycloak-host.com'
kc_token_url="${kc_url}"'/realms/master/protocol/openid-connect/token'
# Get Access Token
response=$(curl -s -w "\n%{http_code}" "${kc_token_url}" \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id='"${kc_admin_client_id}"'' \
--data-urlencode 'client_secret='"${kc_admin_client_secret}"'' \
--data-urlencode 'grant_type=client_credentials')
response_code=$(echo "$response" | tail -n1)
response_body=$(echo "$response" | sed '$d')
# Check the response code
if [[ $response_code -eq 200 ]]; then
# Extract access token
access_token=$(echo "$response" | jq -r '.access_token')
# Return the access_token
echo "$access_token"
exit 0
else
echo "Error: The server returned a response code of $response_code and response body: $response_body"
exit 1
fi
The get_kc_admin_token.sh
script is used to authenticate with the Keycloak admin API to obtain an access token. This script is called by both "create_myidp_idp.sh" and delete_myidp_idp.sh
to ensure that the user has the necessary permissions to create or delete the myIdp identity provider. The script takes in the Keycloak admin URL, realm, client ID, and client secret as arguments and returns the access token as output.
Create myIdp Identity Provider
#!/usr/bin/env bash
environment=$1
realm_id=$2
kc_admin_client_id=$3
kc_admin_client_secret=$4
myidp_idp_client_id=$5
myidp_idp_client_secret=$6
path_module=$7
kc_url='https://keycloak-host.com'
kc_create_idp_url="${kc_url}"'/admin/realms/'"${realm_id}"'/identity-provider/instances'
# Get Access Token
access_token=$(source "${path_module}"/scripts/get_kc_admin_token.sh "${environment}" "${kc_admin_client_id}" "${kc_admin_client_secret}")
# Evaluate the myidp_idp.json file
myidp_idp_data=$(cat "${path_module}"/myidp_idp.json | sed "s/\${myidp_idp_client_id}/${myidp_idp_client_id}/g" | sed "s/\${myidp_idp_client_secret}/${myidp_idp_client_secret}/g")
# Make the create request and store the response code and body in variables
response=$(curl -s -w "\n%{http_code}" "$kc_create_idp_url" \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer '"$access_token"'' \
--data "$myidp_idp_data")
response_code=$(echo "$response" | tail -n1)
response_body=$(echo "$response" | sed '$d')
# Check the response code
if [[ $response_code -eq 201 ]]; then
echo "Success: myidp Idp created"
exit 0
else
echo "Error: The server returned a response code of $response_code and response body: $response_body"
exit 1
fi
The create_myidp_idp.sh
script is used to create the myIdp identity provider. It takes in the environment, realm ID, Keycloak admin client ID and secret, myIdp identity provider client ID and secret, and the path to the module as arguments. The script first calls get_kc_admin_token.sh
to authenticate with the Keycloak admin API and obtain an access token. It then uses the access token to make an HTTP POST request to the Keycloak admin API to create the myidp identity provider.
Delete myIdp Identity Provider
#!/usr/bin/env bash
environment=$1
realm_id=$2
kc_admin_client_id=$3
kc_admin_client_secret=$4
path_module=$5
kc_url='https://keycloak-host.com'
kc_delete_idp_url="${kc_url}"'/admin/realms/'"${realm_id}"'/identity-provider/instances/myidp'
# Get Access Token
access_token=$(source "${path_module}"/scripts/get_kc_admin_token.sh "${environment}" "${kc_admin_client_id}" "${kc_admin_client_secret}")
# Make the create request and store the response code and body in variables
response=$(curl -s --request DELETE -w "\n%{http_code}" "${kc_delete_idp_url}" \
--header 'Authorization: Bearer '"$access_token"'')
response_code=$(echo "$response" | tail -n1)
response_body=$(echo "$response" | sed '$d')
# Check the response code
if [[ $response_code -eq 204 ]]; then
echo "Success: myidp Idp deleted"
exit 0
elif [[ $response_code -eq 404 ]]; then
echo "Success: myidp Idp is not exist"
exit 0
else
echo "Error: The server returned a response code of $response_code and response body: $response_body"
exit 1
fi
The delete_myidp_idp.sh
script is used to delete the myidp identity provider. It takes in the environment, realm ID, Keycloak admin client ID and secret, and the path to the module as arguments. The script again calls get_kc_admin_token.sh
to authenticate with the Keycloak admin API and obtain an access token. It then uses the access token to make an HTTP DELETE request to the Keycloak admin API to delete the myIdp identity provider.
Conclusion
In conclusion, this Terraform code block demonstrates how to use a null resource with triggers and local-exec provisioners to create and delete a custom identity provider. By using Terraform, we can ensure that our infrastructure is consistent, repeatable, and auditable, making it easier to manage and maintain over time.
Top comments (0)