diff --git a/README.md b/README.md index 4d793c8..a366d18 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ Common issues and solutions: |------|--------|---------| | [images](#module\_images) | git::https://github.e.it.census.gov/terraform-modules/aws-ecr-copy-images.git/ | tf-upgrade | | [ingress\_resources](#module\_ingress\_resources) | git::https://github.e.it.census.gov/SCT-Engineering/tfmod-istio-service-ingress.git | n/a | +| [post\_install](#module\_post\_install) | git::https://github.e.it.census.gov/SCT-Engineering/tfmod-config-job.git//config-job | n/a | | [service\_account](#module\_service\_account) | git::https://github.e.it.census.gov/SCT-Engineering/tfmod-config-job.git//service-account | n/a | ## Resources @@ -97,7 +98,6 @@ Common issues and solutions: |------|------| | [helm_release.keycloak](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource | | [helm_release.keycloak-db](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource | -| [kubernetes_job.keycloak_config](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/job) | resource | | [kubernetes_namespace.keycloak](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace) | resource | | [kubernetes_secret.keycloak_secrets](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | | [null_resource.git_version](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | diff --git a/config_job.tf b/config_job.tf index c8eff5e..2b5d677 100644 --- a/config_job.tf +++ b/config_job.tf @@ -1,372 +1,23 @@ locals { - config_job_script = <<-CONFIG -#!/bin/bash -set -e - -# Error handling -function handle_error { - echo "Error occurred in configuration at line $1" - exit 1 -} -trap 'handle_error $LINENO' ERR - -# Setup variables -ROOT_URL="" -REDIRECT_URL="https://*" -KC_URL="http://keycloak-keycloakx-http:${local.public_port_number}" -DB_SECRET="${kubernetes_secret.keycloak_secrets["keycloak-db-creds"].metadata[0].name}" -USER_SECRET="${kubernetes_secret.keycloak_secrets["keycloak-user-creds"].metadata[0].name}" -NAMESPACE="${kubernetes_namespace.keycloak.metadata[0].name}" -REALM="${var.default_realm}" -KC_TOKEN="" - -# Helper to get PostgreSQL password -function get_postgres_password { - kubectl get secret -n $NAMESPACE $DB_SECRET -o jsonpath="{.data.postgres-password}" | base64 --decode -} - -# Helper to get Keycloak credentials -function kc_user { - kubectl get secret -n $NAMESPACE $USER_SECRET -o jsonpath="{.data.username}" | base64 --decode -} - -function kc_passwd { - kubectl get secret -n $NAMESPACE $USER_SECRET -o jsonpath="{.data.password}" | base64 --decode -} - -# Execute SQL with proper password handling -function execute_sql_as_postgres { - local pg_password=$(get_postgres_password) - echo "Executing SQL command..." - kubectl exec -n $NAMESPACE -t keycloak-db-postgresql-0 -c postgresql -- \ - bash -c "PGPASSWORD=$POSTGRES_POSTGRES_PASSWORD psql -U postgres -c \"$1\"" -} - -# Fix metrics permissions -function fix_metrics_permissions { - echo "Setting up permissions for metrics exporter..." - - execute_sql_as_postgres " - DO \$\$ - BEGIN - GRANT pg_monitor TO keycloak; - GRANT SELECT ON pg_stat_database TO keycloak; - ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO keycloak; - GRANT EXECUTE ON FUNCTION pg_ls_waldir(text) TO keycloak; - END; - \$\$; - " - - echo "Metrics permissions granted" -} - -# Check if PostgreSQL is ready -function wait_for_postgresql { - echo "Waiting for PostgreSQL to be ready..." - - for i in {1..30}; do - echo "PostgreSQL check attempt $i of 30" - if kubectl exec -n $NAMESPACE -t keycloak-db-postgresql-0 -c postgresql -- pg_isready -h localhost -U postgres > /dev/null 2>&1; then - echo "PostgreSQL is ready" - return 0 - fi - sleep 10 - done - - echo "PostgreSQL failed to become ready" - return 1 -} - -# Check if Keycloak is ready -function wait_for_keycloak { - echo "Waiting for Keycloak to be ready..." - - for i in {1..30}; do - echo "Keycloak check attempt $i of 30" - # if curl -s -o /dev/null -w %%{http_code} $KC_URL/auth/health/ready | grep -q "200"; then - echo "Keycloak is ready" - return 0 - # fi - sleep 10 - done - - echo "Keycloak failed to become ready" - return 1 -} - -# Login to Keycloak admin console -function login_keycloak { - echo "Logging in to Keycloak..." - - local response=$(curl -s -X POST "$KC_URL/auth/realms/master/protocol/openid-connect/token" \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -d "username=$(kc_user)" \ - -d "password=$(kc_passwd)" \ - -d "grant_type=password" \ - -d "client_id=admin-cli") - - KC_TOKEN=$(echo $response | jq -r '.access_token') - - if [[ "$KC_TOKEN" == "null" || -z "$KC_TOKEN" ]]; then - echo "Failed to login to Keycloak: $response" - return 1 - fi - - echo "Login successful" - return 0 -} - -# Create a realm with error handling -function create_realm { - echo "Creating realm: $REALM" - - # Check if realm exists - local status=$(curl -s -o /dev/null -w %%{http_code} \ - -H "Authorization: Bearer $KC_TOKEN" \ - "$KC_URL/auth/admin/realms/$REALM") - - if [ "$status" == "200" ]; then - echo "Realm $REALM already exists" - return 0 - fi - - # Create realm - local response=$(curl -s -X POST \ - -H "Authorization: Bearer $KC_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{\"realm\":\"$REALM\",\"enabled\":true}" \ - "$KC_URL/auth/admin/realms") - - if [[ $? -ne 0 ]]; then - echo "Failed to create realm: $response" - return 1 - fi - - echo "Realm $REALM created successfully" - return 0 -} - -# Create admin user with proper error handling -function create_admin_user { - local username=$(kc_user) - local password=$(kc_passwd) - local email="${var.admin_email}" - - echo "Creating admin user: $username in realm $REALM" - - # Check if user exists - local user_id=$(curl -s \ - -H "Authorization: Bearer $KC_TOKEN" \ - "$KC_URL/auth/admin/realms/$REALM/users?username=$username" | jq -r '.[0].id') - - if [[ "$user_id" != "null" && -n "$user_id" ]]; then - echo "User $username already exists with ID $user_id" - else - # Create user - curl -s -X POST \ - -H "Authorization: Bearer $KC_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{\"username\":\"$username\",\"enabled\":true,\"emailVerified\":true,\"email\":\"$email\"}" \ - "$KC_URL/auth/admin/realms/$REALM/users" - - # Get user ID again - user_id=$(curl -s \ - -H "Authorization: Bearer $KC_TOKEN" \ - "$KC_URL/auth/admin/realms/$REALM/users?username=$username" | jq -r '.[0].id') - fi - - # Set password - curl -s -X PUT \ - -H "Authorization: Bearer $KC_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{\"type\":\"password\",\"value\":\"$password\",\"temporary\":false}" \ - "$KC_URL/auth/admin/realms/$REALM/users/$user_id/reset-password" - - echo "User $username configured successfully" -} - -# Create roles with error handling -function create_roles { - # Create admin role - echo "Creating 'admin' role" - local status=$(curl -s -o /dev/null -w %%{http_code} \ - -H "Authorization: Bearer $KC_TOKEN" \ - "$KC_URL/auth/admin/realms/$REALM/roles/admin") - - if [ "$status" != "200" ]; then - curl -s -X POST \ - -H "Authorization: Bearer $KC_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{\"name\":\"admin\",\"description\":\"Admin role for $REALM\"}" \ - "$KC_URL/auth/admin/realms/$REALM/roles" - fi - - # Create user role - echo "Creating 'user' role" - status=$(curl -s -o /dev/null -w %%{http_code} \ - -H "Authorization: Bearer $KC_TOKEN" \ - "$KC_URL/auth/admin/realms/$REALM/roles/user") - - if [ "$status" != "200" ]; then - curl -s -X POST \ - -H "Authorization: Bearer $KC_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{\"name\":\"user\",\"description\":\"User role for $REALM\"}" \ - "$KC_URL/auth/admin/realms/$REALM/roles" - fi - - echo "Roles created successfully" -} - -# Assign role to user -function assign_role_to_user { - local role_name="user" - local username="user" - - echo "Assigning '$role_name' role to $username" - - # Get user ID - local user_id=$(curl -s \ - -H "Authorization: Bearer $KC_TOKEN" \ - "$KC_URL/auth/admin/realms/$REALM/users?username=$username" | jq -r '.[0].id') - - if [[ -z "$user_id" || "$user_id" == "null" ]]; then - echo "User $username not found" - return 1 - fi - - # Get role representation - local role=$(curl -s \ - -H "Authorization: Bearer $KC_TOKEN" \ - "$KC_URL/auth/admin/realms/$REALM/roles/$role_name") - - # Add role to user - curl -s -X POST \ - -H "Authorization: Bearer $KC_TOKEN" \ - -H "Content-Type: application/json" \ - -d "[$role]" \ - "$KC_URL/auth/admin/realms/$REALM/users/$user_id/role-mappings/realm" - - echo "Role $role_name assigned to $username" -} - -# Create client -function create_client { - local client_id="${local.user_client_id}" - local client_secret="${random_uuid.user_secret.result}" - - echo "Creating client: $client_id in realm $REALM" - - # Check if client exists - local status=$(curl -s -o /dev/null -w %%{http_code} \ - -H "Authorization: Bearer $KC_TOKEN" \ - "$KC_URL/auth/admin/realms/$REALM/clients?clientId=$client_id") - - if [[ "$status" == "200" ]]; then - echo "Client $client_id may already exist, checking..." - local exists=$(curl -s \ - -H "Authorization: Bearer $KC_TOKEN" \ - "$KC_URL/auth/admin/realms/$REALM/clients?clientId=$client_id" | jq length) - - if [[ "$exists" -gt 0 ]]; then - echo "Client $client_id already exists" - return 0 - fi - fi - - # Create client - local response=$(curl -s -X POST \ - -H "Authorization: Bearer $KC_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{ - \"clientId\":\"$client_id\", - \"secret\":\"$client_secret\", - \"redirectUris\":[\"$REDIRECT_URL\"], - \"directAccessGrantsEnabled\":true, - \"protocol\":\"openid-connect\", - \"publicClient\":false - }" \ - "$KC_URL/auth/admin/realms/$REALM/clients") - - echo "Client $client_id created" -} - -# Add mapper to client -function add_client_mapper { - local client_id="${local.user_client_id}" - - echo "Adding realm roles mapper to client $client_id" - - # Get internal client ID - local id=$(curl -s \ - -H "Authorization: Bearer $KC_TOKEN" \ - "$KC_URL/auth/admin/realms/$REALM/clients?clientId=$client_id" | jq -r '.[0].id') - - if [[ -z "$id" || "$id" == "null" ]]; then - echo "Client $client_id not found" - return 1 - fi - - # Add mapper - curl -s -X POST \ - -H "Authorization: Bearer $KC_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{ - \"name\":\"realm roles\", - \"protocol\":\"openid-connect\", - \"protocolMapper\":\"oidc-usermodel-realm-role-mapper\", - \"config\":{ - \"multivalued\":\"true\", - \"userinfo.token.claim\":\"true\", - \"id.token.claim\":\"true\", - \"access.token.claim\":\"true\", - \"claim.name\":\"realm_access.roles\", - \"jsonType.label\":\"String\" - } - }" \ - "$KC_URL/auth/admin/realms/$REALM/clients/$id/protocol-mappers/models" - - echo "Mapper added to client $client_id" -} - -# Scale Keycloak deployment -function scale_keycloak { - echo "Scaling Keycloak deployment to 3 replicas..." - # kubectl scale --replicas=3 deployment/keycloak-keycloakx -n $NAMESPACE - - # Wait for scaling to complete - # kubectl rollout status deployment/keycloak-keycloakx -n $NAMESPACE - echo "Keycloak scaled successfully" -} - -# Main execution starts here -echo "Starting Keycloak configuration process" - -# Step 1: Wait for PostgreSQL and fix permissions -wait_for_postgresql || { echo "PostgreSQL readiness check failed"; exit 1; } -fix_metrics_permissions - -# Step 2: Wait for Keycloak to be ready -wait_for_keycloak || { echo "Keycloak readiness check failed"; exit 1; } - -# Step 3: Login to Keycloak -login_keycloak || { echo "Keycloak login failed"; exit 1; } - -# Step 4: Create and configure realm -create_realm -create_admin_user -create_roles -assign_role_to_user - -# Step 5: Setup client -create_client -add_client_mapper - -# Step 6: Scale Keycloak for production use -scale_keycloak - -echo "Keycloak configuration completed successfully" -exit 0 - + config_job_script = <