Introduction

Learn how to deploy Keycloak as an Identity and Access Management (IAM) solution using Terraform on Atlas Cloud. This guide provides a complete infrastructure-as-code (IaC) approach for automated, repeatable deployments.

Prerequisites

Before you begin, ensure you have:

  • Terraform installed on your local machine
  • Atlas Cloud account with API access
  • CloudStack API credentials (API key and secret key)
  • Basic understanding of Terraform concepts

Terraform Overview

The provided Terraform configuration creates:

  • A Virtual Machine with Ubuntu 24.04 LTS
  • PostgreSQL database
  • Keycloak server with automated setup
  • Complete network configuration with public IP, port forwarding, and firewall rules

Step 1: Set Up Your Working Directory

Create a new directory for your Keycloak deployment:

mkdir keycloak-deployment
cd keycloak-deployment

Create the configuration files as shown below. The complete code is provided - no external file downloads needed.

Step 2: Get Your Atlas API Credentials

Before configuring Terraform, you need your Atlas Cloud API credentials:

  1. Log in to sky.runatlas.is
  2. Click your profile in the top-right corner
  3. Wait 10 seconds for your API keys to appear in the user card
  4. If no keys appear, click “Generate new API/Secret keys”
  5. Copy the API Key and Secret Key from the UI

Step 3: Create Terraform Configuration Files

Create the following files in your project directory:

main.tf

terraform {
  required_providers {
    cloudstack = {
      source  = "cloudstack/cloudstack"
      version = "0.6.0-rc3"
    }
  }
}
 
provider "cloudstack" {
  api_url    = var.cloudstack_api_url
  api_key    = var.cloudstack_api_key
  secret_key = var.cloudstack_secret_key
}
 
# Create a network for Keycloak
resource "cloudstack_network" "keycloak_network" {
  name             = "keycloak-network"
  display_text     = "Network for Keycloak IAM Server"
  cidr             = "10.1.0.0/24"
  network_offering = var.network_offering
  zone             = var.zone
}
 
# Create a virtual machine for Keycloak
resource "cloudstack_instance" "keycloak" {
  name             = "keycloak-server"
  display_name     = "Keycloak IAM Server"
  service_offering = var.instance_service_offering
  template         = var.instance_template
  zone             = var.zone
  network          = cloudstack_network.keycloak_network.name
 
  # User data script for initial setup
  user_data = templatefile("${path.module}/cloud-init.yaml", {
    admin_password = var.keycloak_admin_password
    db_password    = var.keycloak_db_password
  })
 
  tags = {
    Name        = "keycloak-server"
    Environment = var.environment
    Purpose     = "IAM"
  }
}
 
# Associate public IP address
resource "cloudstack_ipaddress" "keycloak_ip" {
  network = cloudstack_network.keycloak_network.name
}
 
# Create port forwarding rules for Keycloak
resource "cloudstack_port_forward" "keycloak_http" {
  ip_address_id = cloudstack_ipaddress.keycloak_ip.id
  forward {
    protocol     = "tcp"
    public_port   = 80
    private_port  = 8080
    virtual_machine_id = cloudstack_instance.keycloak.id
  }
}
 
resource "cloudstack_port_forward" "keycloak_https" {
  ip_address_id = cloudstack_ipaddress.keycloak_ip.id
  forward {
    protocol     = "tcp"
    public_port   = 443
    private_port  = 8443
    virtual_machine_id = cloudstack_instance.keycloak.id
  }
}
 
# Create firewall rules
resource "cloudstack_firewall" "keycloak_firewall" {
  ip_address_id = cloudstack_ipaddress.keycloak_ip.id
  rule {
    protocol   = "tcp"
    start_port = 80
    end_port   = 80
    cidr_list  = ["0.0.0.0/0"]
  }
  rule {
    protocol   = "tcp"
    start_port = 443
    end_port   = 443
    cidr_list  = ["0.0.0.0/0"]
  }
  rule {
    protocol   = "tcp"
    start_port = 22
    end_port   = 22
    cidr_list  = ["0.0.0.0/0"]
  }
}
 
# Output the public IP and access information
output "keycloak_public_ip" {
  description = "Public IP address of the Keycloak server"
  value       = cloudstack_ipaddress.keycloak_ip.ip_address
}
 
output "keycloak_admin_url" {
  description = "URL for Keycloak admin console"
  value       = "http://${cloudstack_ipaddress.keycloak_ip.ip_address}/admin"
}
 
output "keycloak_login_url" {
  description = "URL for Keycloak login page"
  value       = "http://${cloudstack_ipaddress.keycloak_ip.ip_address}/"
}

variables.tf

variable "cloudstack_api_url" {
  description = "CloudStack API URL"
  type        = string
  sensitive   = true
}
 
variable "cloudstack_api_key" {
  description = "CloudStack API key"
  type        = string
  sensitive   = true
}
 
variable "cloudstack_secret_key" {
  description = "CloudStack secret key"
  type        = string
  sensitive   = true
}
 
variable "zone" {
  description = "CloudStack zone name"
  type        = string
  default     = "is1"
}
 
variable "instance_service_offering" {
  description = "Instance service offering (CPU, memory configuration)"
  type        = string
  default     = "Atlas.a5"
}
 
variable "instance_template" {
  description = "Instance template (OS image)"
  type        = string
  default     = "Ubuntu 24.04 LTS"
}
 
variable "network_offering" {
  description = "Network offering for the Keycloak network"
  type        = string
  default     = "DefaultIsolatedNetworkOfferingWithSourceNatService"
}
 
variable "environment" {
  description = "Environment tag for resources"
  type        = string
  default     = "production"
}
 
variable "keycloak_admin_password" {
  description = "Password for Keycloak admin user"
  type        = string
  sensitive   = true
}
 
variable "keycloak_db_password" {
  description = "Password for Keycloak PostgreSQL database"
  type        = string
  sensitive   = true
}

cloud-init.yaml

#cloud-config
package_update: true
package_upgrade: true
 
packages:
  - openjdk-21-jdk
  - postgresql
  - postgresql-contrib
  - wget
  - unzip
  - nginx
 
runcmd:
  # Create keycloak user
  - useradd -r -s /bin/false keycloak
 
  # Download and install Keycloak
  - cd /tmp
  - wget -q https://github.com/keycloak/keycloak/releases/download/23.0.0/keycloak-23.0.0.tar.gz
  - tar -xzf keycloak-23.0.0.tar.gz
  - mv keycloak-23.0.0 /opt/keycloak
  - chown -R keycloak:keycloak /opt/keycloak
 
  # Configure PostgreSQL
  - sudo -u postgres psql -c "CREATE USER keycloak WITH PASSWORD '${db_password}';"
  - sudo -u postgres psql -c "CREATE DATABASE keycloak OWNER keycloak;"
  - sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak;"
 
  # Configure PostgreSQL for Keycloak
  - echo "listen_addresses = 'localhost'" >> /etc/postgresql/16/main/postgresql.conf
  - systemctl restart postgresql
 
  # Create Keycloak configuration
  - cat > /opt/keycloak/conf/keycloak.conf << EOF
db=postgres
db-url=jdbc:postgresql://localhost:5432/keycloak
db-username=keycloak
db-password=${db_password}
http-port=8080
https-port=8443
hostname=localhost
production-mode=true
EOF
 
  # Create systemd service for Keycloak
  - cat > /etc/systemd/system/keycloak.service << 'EOF'
[Unit]
Description=Keycloak Server
After=network.target postgresql.service
 
[Service]
Type=idle
User=keycloak
Group=keycloak
ExecStart=/opt/keycloak/bin/kc.sh start
TimeoutStartSec=600
TimeoutStopSec=600
Restart=always
RestartSec=10
 
[Install]
WantedBy=multi-user.target
EOF
 
  # Enable and start services
  - systemctl daemon-reload
  - systemctl enable keycloak
  - systemctl start keycloak
  - systemctl enable postgresql
 
  # Wait for Keycloak to start and create admin user
  - sleep 30
  - /opt/keycloak/bin/add-user-keycloak.sh -u admin -p '${admin_password}' --realm master
 
  # Restart Keycloak to apply admin user
  - systemctl restart keycloak
 
final_message: "Keycloak IAM server has been successfully configured!"

terraform.tfvars

# CloudStack Configuration
cloudstack_api_url    = "https://sky.runatlas.is/client/api"
cloudstack_api_key    = "your-copied-api-key"
cloudstack_secret_key = "your-copied-secret-key"
 
# Infrastructure Configuration
zone                    = "is1"
instance_service_offering = "Atlas.a5"
instance_template        = "Ubuntu 24.04 LTS"
network_offering        = "DefaultIsolatedNetworkOfferingWithSourceNatService"
environment             = "production"
 
# Keycloak Configuration
keycloak_admin_password = "YourSecureAdminPassword123!"
keycloak_db_password    = "YourSecureDBPassword456!"

Important: Replace the placeholder values with your actual Atlas Cloud configuration.

For additional examples and advanced configurations, see the RunAtlas Terraform Examples repository (coming soon).

Step 4: Initialize Terraform

Initialize the Terraform working directory:

terraform init

This will download the CloudStack provider plugin and prepare the environment.

Step 5: Review the Deployment Plan

Generate and review the execution plan:

terraform plan

Terraform will show you what resources will be created:

  • cloudstack_network.keycloak_network - Network for the Keycloak server
  • cloudstack_instance.keycloak - The Keycloak virtual machine
  • cloudstack_ipaddress.keycloak_ip - Public IP address
  • cloudstack_port_forward.keycloak_http - HTTP port forwarding rule
  • cloudstack_port_forward.keycloak_https - HTTPS port forwarding rule
  • cloudstack_firewall.keycloak_firewall - Firewall rules for security

Step 6: Deploy the Infrastructure

Apply the configuration to create the resources:

terraform apply

Type yes when prompted to confirm the deployment. Terraform will:

  1. Create the network infrastructure
  2. Provision the virtual machine with Keycloak
  3. Assign a public IP address
  4. Configure port forwarding and firewall rules
  5. Execute the cloud-init script for automated setup

The deployment typically takes 5-10 minutes to complete.

Step 7: Access Your Keycloak Server

Once Terraform completes the deployment:

Access the Admin Console

Once deployment completes, Terraform will output the access information:

keycloak_public_ip = "203.0.113.123"
keycloak_admin_url = "http://203.0.113.123/admin"
keycloak_login_url = "http://203.0.113.123/"
  1. Open your web browser and navigate to the admin URL from the output
  2. Log in with:
    • Username: admin
    • Password: The password you set in terraform.tfvars

Verify the Setup

  1. Check that you can access the Keycloak admin console
  2. Verify the server status in the admin panel
  3. Test creating a test realm and user

Understanding the Configuration

Main Components

Virtual Machine Configuration (main.tf):

resource "cloudstack_instance" "keycloak" {
  name             = "keycloak-server"
  display_name     = "Keycloak IAM Server"
  service_offering = var.instance_service_offering
  template         = var.instance_template
  zone             = var.zone
  user_data        = templatefile("${path.module}/cloud-init.yaml", {...})
  tags = {
    Name        = "keycloak-server"
    Environment = var.environment
    Purpose     = "IAM"
  }
}

Note: All network configuration including public IP, port forwarding, and firewall rules is handled automatically by Terraform.

Network Setup:

resource "cloudstack_network" "keycloak_network" {
  name             = "keycloak-network"
  display_text     = "Network for Keycloak IAM Server"
  cidr             = "10.1.0.0/24"
  network_offering = var.network_offering
  zone             = var.zone
}

Port Forwarding:

resource "cloudstack_port_forward" "keycloak_http" {
  ip_address_id = cloudstack_ipaddress.keycloak_ip.id
  forward {
    protocol     = "tcp"
    public_port   = 80
    private_port  = 8080
    virtual_machine_id = cloudstack_instance.keycloak.id
  }
}

Cloud-Init Automation

The cloud-init.yaml script handles:

  • Package installation (Java, PostgreSQL, Keycloak)
  • Database setup and configuration
  • Keycloak service configuration
  • Admin user creation
  • Nginx reverse proxy setup

Customization Options

Scaling Resources

Modify the terraform.tfvars file to adjust resources:

# For higher performance
instance_service_offering = "Atlas.a6"  # More CPU/RAM

Custom Domain

To use a custom domain, update the cloud-init script:

# In cloud-init.yaml
hostname=your-domain.com

SSL/TLS Configuration

For production deployments, configure HTTPS:

  1. Obtain SSL certificates
  2. Update Nginx configuration
  3. Modify Keycloak to use HTTPS

Managing Your Deployment

Check Status

# Check Terraform state
terraform show
 
# List all resources
terraform state list

Update Configuration

  1. Modify your .tf files or terraform.tfvars
  2. Run terraform plan to see changes
  3. Apply updates with terraform apply

Backup and Restore

Backup Terraform State:

cp terraform.tfstate terraform.tfstate.backup

Backup Keycloak Data:

# SSH into the server
ssh ubuntu@<your-public-ip>
 
# Backup PostgreSQL
sudo -u postgres pg_dump keycloak > keycloak_backup.sql

Cleanup

To remove all resources and avoid charges:

terraform destroy

Type yes to confirm the destruction of all created resources.

Best Practices

  1. Version Control: Store your Terraform code in Git
  2. State Management: Use remote state storage for team collaboration
  3. Security: Use environment variables for sensitive data
  4. Monitoring: Set up monitoring for your Keycloak instance
  5. Backups: Regularly backup both Terraform state and Keycloak data

Congratulations! You’ve successfully deployed Keycloak using Terraform on Atlas Cloud 🎉