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-deploymentCreate 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:
- Log in to sky.runatlas.is
- Click your profile in the top-right corner
- Wait 10 seconds for your API keys to appear in the user card
- If no keys appear, click “Generate new API/Secret keys”
- 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 initThis will download the CloudStack provider plugin and prepare the environment.
Step 5: Review the Deployment Plan
Generate and review the execution plan:
terraform planTerraform will show you what resources will be created:
cloudstack_network.keycloak_network- Network for the Keycloak servercloudstack_instance.keycloak- The Keycloak virtual machinecloudstack_ipaddress.keycloak_ip- Public IP addresscloudstack_port_forward.keycloak_http- HTTP port forwarding rulecloudstack_port_forward.keycloak_https- HTTPS port forwarding rulecloudstack_firewall.keycloak_firewall- Firewall rules for security
Step 6: Deploy the Infrastructure
Apply the configuration to create the resources:
terraform applyType yes when prompted to confirm the deployment. Terraform will:
- Create the network infrastructure
- Provision the virtual machine with Keycloak
- Assign a public IP address
- Configure port forwarding and firewall rules
- 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/"- Open your web browser and navigate to the admin URL from the output
- Log in with:
- Username:
admin - Password: The password you set in
terraform.tfvars
- Username:
Verify the Setup
- Check that you can access the Keycloak admin console
- Verify the server status in the admin panel
- 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/RAMCustom Domain
To use a custom domain, update the cloud-init script:
# In cloud-init.yaml
hostname=your-domain.comSSL/TLS Configuration
For production deployments, configure HTTPS:
- Obtain SSL certificates
- Update Nginx configuration
- Modify Keycloak to use HTTPS
Managing Your Deployment
Check Status
# Check Terraform state
terraform show
# List all resources
terraform state listUpdate Configuration
- Modify your
.tffiles orterraform.tfvars - Run
terraform planto see changes - Apply updates with
terraform apply
Backup and Restore
Backup Terraform State:
cp terraform.tfstate terraform.tfstate.backupBackup Keycloak Data:
# SSH into the server
ssh ubuntu@<your-public-ip>
# Backup PostgreSQL
sudo -u postgres pg_dump keycloak > keycloak_backup.sqlCleanup
To remove all resources and avoid charges:
terraform destroyType yes to confirm the destruction of all created resources.
Best Practices
- Version Control: Store your Terraform code in Git
- State Management: Use remote state storage for team collaboration
- Security: Use environment variables for sensitive data
- Monitoring: Set up monitoring for your Keycloak instance
- Backups: Regularly backup both Terraform state and Keycloak data
Congratulations! You’ve successfully deployed Keycloak using Terraform on Atlas Cloud 🎉