Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions modules/cicd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# CICD Keycloak Automation Client Submodule

This submodule creates a Keycloak OpenID client named `automation-client` in an existing realm. It does not require admin access to a Kubernetes cluster with the downside of requiring configuring the keycloak variables.

Comment thread
sellisd marked this conversation as resolved.
## Module Input Variables

- Cosmotech Platform specific variables
- `tenant`: CosmoTech platform tenant name
- `cluster_domain`: The cluster location
- Variables for keycloak
- `keycloak_url` Keycloak server URL
- `keycloak_client_id` Keycloak client id (with admin rights)
- `keycloak_username` Keycloak admin username
- `keycloak_password` Keycloak admin password
- `keycloak_realm` Keycloak realm name

## Usage
```
module "cicd" {
source = "./modules/cicd"

tenant = "tenant-test"
cluster_domain = "localhost"
keycloak_url = "http://localhost:8080"
keycloak_client_id = "admin-cli"
keycloak_username = "admin"
keycloak_password = "admin"
keycloak_realm = "master"
}
```

## Testing
Start the keycloak container
```shell
cd tests
docker compose up -d
```
A keycloak instance will start and will be configured with a realm and roles loaded.
Run the terraform test:
```shell
terraform test --var-file keycloak.local.tfvars
```
59 changes: 59 additions & 0 deletions modules/cicd/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
terraform {
required_providers {
keycloak = {
source = "keycloak/keycloak"
version = "5.7.0"
Comment thread
sellisd marked this conversation as resolved.
}
}
}

provider "keycloak" {
url = var.keycloak_url
client_id = var.keycloak_client_id
username = var.keycloak_username
password = var.keycloak_password
realm = var.keycloak_realm
}


Comment thread
sellisd marked this conversation as resolved.
# get realm for current tenant
data "keycloak_realm" "realm" {
realm = var.tenant
Comment thread
sellisd marked this conversation as resolved.
}

# automation client
resource "keycloak_openid_client" "automation-client" {
realm_id = data.keycloak_realm.realm.id
client_id = "automation-client"
name = "automation-client"
Comment thread
sellisd marked this conversation as resolved.
enabled = true
standard_flow_enabled = false
access_type = "CONFIDENTIAL"
service_accounts_enabled = true
login_theme = "keycloak"
root_url = "https://${var.cluster_domain}"
full_scope_allowed = true
}

resource "keycloak_generic_protocol_mapper" "automation_realm_roles_mapper" {
realm_id = data.keycloak_realm.realm.id
client_id = keycloak_openid_client.automation-client.id
name = "realm roles"
Comment thread
sellisd marked this conversation as resolved.
Comment thread
sellisd marked this conversation as resolved.
protocol = "openid-connect"
protocol_mapper = "oidc-usermodel-realm-role-mapper"
config = {
"id.token.claim" : "true",
"access.token.claim" : "true",
"claim.name" : "userRoles",
"jsonType.label" : "String",
"multivalued" : "true",
"userinfo.token.claim" : "true",
"introspection.token.claim" : "true"
}
}

resource "keycloak_openid_client_service_account_realm_role" "automation_client_service_account_role" {
realm_id = data.keycloak_realm.realm.id
service_account_user_id = keycloak_openid_client.automation-client.service_account_user_id
role = "Platform.Admin"
Comment thread
sellisd marked this conversation as resolved.
}
12 changes: 12 additions & 0 deletions modules/cicd/tests/config/tenant-test-realm.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"realm": "tenant-test",
"enabled": true,
"roles": {
"realm": [
{
"name": "Platform.Admin",
"description": "Administration role"
}
]
}
}
21 changes: 21 additions & 0 deletions modules/cicd/tests/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
services:
keycloak:
image: keycloak/keycloak:26.5
environment:
KC_BOOTSTRAP_ADMIN_USERNAME: "admin"
KC_BOOTSTRAP_ADMIN_PASSWORD: "admin"
KC_HTTP_ENABLED: "true"
KC_HOSTNAME_STRICT: "false"
ports:
- "8080:8080"
volumes:
- ./config/:/opt/keycloak/data/import/
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health/ready"]
interval: 10s
timeout: 5s
retries: 5
command:
- start-dev
- --import-realm
- --verbose
7 changes: 7 additions & 0 deletions modules/cicd/tests/keycloak.local.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
keycloak_url = "http://localhost:8080"
keycloak_client_id = "admin-cli"
keycloak_username = "admin"
keycloak_password = "admin"
Comment thread
sellisd marked this conversation as resolved.
keycloak_realm = "master"
tenant = "tenant-test"
cluster_domain = "localhost"
26 changes: 26 additions & 0 deletions modules/cicd/tests/keycloak.tftest.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
provider "keycloak" {
url = "http://localhost:8080"
username = "admin"
password = "admin"
initial_login = true
client_id = "admin-cli"
realm = "master"
tls_insecure_skip_verify = true
}

variables {
tenant = "tenant-test"
cluster_domain = "localhost"
}

run "verify_automation_client_creation" {
command = plan
variables {
tenant = var.tenant
cluster_domain = var.cluster_domain
Comment thread
sellisd marked this conversation as resolved.
}
assert {
condition = keycloak_openid_client.automation-client.client_id == "automation-client"
Comment thread
sellisd marked this conversation as resolved.
error_message = "Keycloak automation client should be created"
}
}
37 changes: 37 additions & 0 deletions modules/cicd/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
## CosmoTech Platform specifics
variable "tenant" {
description = "CosmoTech platform tenant name (e.g. tenant-sphinx)"
Comment thread
sellisd marked this conversation as resolved.
type = string
}

variable "cluster_domain" {
description = "The DNS name of the API, e.g. cluster.azure.platform.cosmotech.com"
type = string
}

## General variables
variable "keycloak_url" {
description = "Keycloak server URL"
type = string
}

variable "keycloak_client_id" {
description = "Keycloak client ID"
type = string
}

variable "keycloak_username" {
description = "Keycloak admin username"
type = string
}

variable "keycloak_password" {
description = "Keycloak admin password"
type = string
sensitive = true
}

variable "keycloak_realm" {
description = "Keycloak realm name"
type = string
}