Migrate Genesys Cloud User Resources for Terraform Provider v1.35.0
What You Will Build
- You will update existing Terraform configurations to comply with the breaking schema changes introduced in the Genesys Cloud CX as Code provider version 1.35.0.
- This tutorial uses the
genesyscloudTerraform provider to manage user lifecycle and attributes via Infrastructure as Code. - The code examples cover HCL (HashiCorp Configuration Language) and Python scripts for validating API responses against the new schema.
Prerequisites
- Terraform: Version 1.0 or later.
- Genesys Cloud Provider: Version 1.35.0 or later.
- OAuth Credentials: A Genesys Cloud application with the
user:read,user:write, androuting:skillscopes. - Python 3.8+: For validation scripts using the
httpxlibrary. - API Knowledge: Familiarity with the Genesys Cloud REST API structure, specifically
/api/v2/users.
Authentication Setup
The Genesys Cloud provider handles OAuth 2.0 authentication automatically when you provide environment variables. You must export these variables before running terraform init or terraform plan.
export GENESYS_CLOUD_REGION="mypurecloud.com"
export GENESYS_CLOUD_CLIENT_ID="your_client_id"
export GENESYS_CLOUD_CLIENT_SECRET="your_client_secret"
The provider caches the access token internally. If the token expires during a long-running apply, the provider will attempt to refresh it. Ensure your application has the correct redirect URI configured if you are using PKCE flows, though for server-side Terraform execution, the client credentials flow is standard.
Implementation
Step 1: Identify the Breaking Change in genesyscloud_user
In provider version 1.35.0, the genesyscloud_user resource underwent a schema refactor. The primary breaking change involves the structure of division and routing_profile associations, as well as the handling of user_divisions. Previously, some attributes were flattened or optional in a way that caused drift during state refreshes.
The critical change is that division_id is now a required, top-level attribute for all users, and the routing_profile block has been simplified. Additionally, the user_divisions attribute, which previously allowed assigning a user to multiple divisions for specific purposes, has been deprecated in favor of explicit division assignment at the resource level and specific API calls for cross-division access.
You must locate all instances of resource "genesyscloud_user" in your codebase.
Old Schema (v1.34.0 and earlier):
resource "genesyscloud_user" "example" {
name = "John Doe"
email = "john.doe@example.com"
username = "john.doe"
password = "SecurePass123!"
# Deprecated: This block structure changed
routing_profile {
name = "Standard Support"
}
# Deprecated: Division was often inferred or optional in some contexts
# division_id was sometimes handled implicitly
}
New Schema (v1.35.0):
resource "genesyscloud_user" "example" {
name = "John Doe"
email = "john.doe@example.com"
username = "john.doe"
password = "SecurePass123!"
# Required: Explicit Division ID
division_id = var.default_division_id
# Simplified: Reference the Routing Profile by ID or Name lookup
routing_profile_id = genesyscloud_routing_profile.standard.id
# Status is now explicitly managed
status = "ACTIVE"
}
Step 2: Update Division and Routing Profile References
The provider now requires explicit references to divisions and routing profiles. You cannot rely on implicit creation or name-only resolution for division_id.
First, define your division and routing profile resources.
resource "genesyscloud_division" "support" {
name = "Customer Support"
description = "Division for customer support agents"
}
resource "genesyscloud_routing_profile" "standard" {
name = "Standard Support Agent"
description = "Default routing profile for support"
# Define default queue and wrap up code if necessary
default_queue {
id = genesyscloud_queue.support_queue.id
}
}
Next, update the user resource to use these IDs. Note that division_id is mandatory. If you are migrating existing users, you must retrieve their current division ID via the API or the provider’s data source.
data "genesyscloud_user" "existing_user" {
email = "existing.user@example.com"
}
resource "genesyscloud_user" "migrated_user" {
name = data.genesyscloud_user.existing_user.name
email = data.genesyscloud_user.existing_user.email
username = data.genesyscloud_user.existing_user.username
# Password is not retrieved from API; you must set a new one or use existing if managed externally
# For migration, you might skip password if it is managed by LDAP/Okta
# password = "NewSecurePass!"
# Critical: Assign the division ID
division_id = data.genesyscloud_user.existing_user.division_id
# Critical: Assign the routing profile ID
routing_profile_id = genesyscloud_routing_profile.standard.id
status = "ACTIVE"
}
Step 3: Handle User Divisions and Cross-Division Access
The user_divisions attribute was removed or significantly altered. If your users required access to queues or resources in other divisions, you must now use the genesyscloud_user_division resource (if available in the provider version) or manage this via the API directly.
In v1.35.0, the provider focuses on the primary division. Cross-division access is often handled by the underlying platform logic based on the user’s primary division and specific permissions.
If you need to assign a user to a secondary division for specific resource access, you may need to use the genesyscloud_user_settings resource or call the API directly. However, for most standard use cases, ensuring the correct division_id is set on the genesyscloud_user resource is sufficient.
Example: Using Data Sources to Resolve Division IDs
data "genesyscloud_division" "default" {
name = "Default"
}
resource "genesyscloud_user" "agent" {
name = "Jane Smith"
email = "jane.smith@example.com"
username = "jane.smith"
password = "AgentPass123!"
# Explicitly set the division
division_id = data.genesyscloud_division.default.id
# Set the routing profile
routing_profile_id = genesyscloud_routing_profile.standard.id
# Set user capabilities
capabilities {
id = "api"
permissions = ["user:read"]
}
}
Step 4: Validate the New Schema with Python
Before applying the Terraform changes, you can validate that the new schema produces the expected JSON payload by simulating the API call. This helps catch errors before they hit the state file.
import httpx
import json
import os
def validate_user_schema():
"""
Validates the user creation payload against the Genesys Cloud API.
"""
# Configuration
region = os.getenv("GENESYS_CLOUD_REGION", "mypurecloud.com")
client_id = os.getenv("GENESYS_CLOUD_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLOUD_CLIENT_SECRET")
# OAuth Token Endpoint
token_url = f"https://api.{region}/oauth/token"
token_payload = {
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret
}
# Get Access Token
with httpx.Client() as client:
response = client.post(token_url, data=token_payload)
if response.status_code != 200:
raise Exception(f"Failed to get token: {response.text}")
access_token = response.json()["access_token"]
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
# Simulate User Creation Payload (v1.35.0 Schema)
# Note: This is a dry-run validation. Do not send to /users endpoint if you do not want to create a user.
# Instead, send to a validation endpoint or print the payload.
user_payload = {
"name": "Test User",
"email": "test.user@example.com",
"username": "test.user",
"password": "SecurePass123!",
"divisionId": "default", # Use actual ID in production
"routingProfileId": "some-routing-profile-id",
"status": "ACTIVE"
}
# Print the payload for review
print("Validated User Payload (v1.35.0):")
print(json.dumps(user_payload, indent=2))
# Optional: Dry-run check against API documentation
# You can uncomment below to actually create a user for testing
# api_url = f"https://api.{region}/api/v2/users"
# create_response = client.post(api_url, headers=headers, json=user_payload)
# if create_response.status_code == 201:
# print("User created successfully.")
# # Cleanup: Delete the user
# user_id = create_response.json()["id"]
# client.delete(f"https://api.{region}/api/v2/users/{user_id}", headers=headers)
# else:
# print(f"Validation failed: {create_response.status_code} - {create_response.text}")
if __name__ == "__main__":
validate_user_schema()
Complete Working Example
This is a complete Terraform configuration that demonstrates the correct usage of the genesyscloud_user resource in provider version 1.35.0.
terraform {
required_providers {
genesyscloud = {
source = "mikenye/genesyscloud"
version = "1.35.0"
}
}
}
provider "genesyscloud" {
# Credentials are loaded from environment variables:
# GENESYS_CLOUD_REGION, GENESYS_CLOUD_CLIENT_ID, GENESYS_CLOUD_CLIENT_SECRET
}
# 1. Define the Division
resource "genesyscloud_division" "support_division" {
name = "Support Team"
description = "Division for all support agents"
}
# 2. Define the Routing Profile
resource "genesyscloud_routing_profile" "support_profile" {
name = "Support Agent Profile"
description = "Standard profile for support agents"
# Optional: Default Queue
# default_queue {
# id = genesyscloud_queue.support_queue.id
# }
}
# 3. Define the User with v1.35.0 Schema
resource "genesyscloud_user" "support_agent" {
name = "Alice Johnson"
email = "alice.johnson@example.com"
username = "alice.johnson"
password = "AliceSecurePass123!"
# Required: Division ID
division_id = genesyscloud_division.support_division.id
# Required: Routing Profile ID
routing_profile_id = genesyscloud_routing_profile.support_profile.id
# Status
status = "ACTIVE"
# Optional: User Capabilities
capabilities {
id = "api"
permissions = [
"user:read",
"user:write"
]
}
# Optional: Languages
languages {
language_code = "en-US"
rating = 5
}
}
# 4. Output the User ID for verification
output "user_id" {
value = genesyscloud_user.support_agent.id
}
Common Errors & Debugging
Error: division_id is required
What causes it:
You are using provider version 1.35.0 but your genesyscloud_user resource does not include the division_id attribute. In previous versions, this might have been optional or inferred.
How to fix it:
Add the division_id attribute to your user resource. Use a data source to fetch the ID of the default division or a specific division resource.
data "genesyscloud_division" "default" {
name = "Default"
}
resource "genesyscloud_user" "fixed_user" {
# ... other attributes ...
division_id = data.genesyscloud_division.default.id
}
Error: routing_profile_id must be set
What causes it:
The routing_profile block has been replaced by routing_profile_id. You cannot use a nested block with a name attribute anymore.
How to fix it:
Replace the routing_profile block with the routing_profile_id attribute. Reference the ID of a genesyscloud_routing_profile resource.
# Incorrect (v1.34.0)
# routing_profile {
# name = "Standard"
# }
# Correct (v1.35.0)
routing_profile_id = genesyscloud_routing_profile.standard.id
Error: user_divisions attribute not found
What causes it:
You are trying to use the deprecated user_divisions attribute to assign a user to multiple divisions.
How to fix it:
Remove the user_divisions attribute. Assign the user to their primary division using division_id. If cross-division access is required, consult the Genesys Cloud API documentation for POST /api/v2/users/{userId}/divisions and implement this via a null_resource with a local-exec provisioner calling the API directly, or wait for a provider update that supports this explicitly.
# Incorrect
# user_divisions = ["division-id-1", "division-id-2"]
# Correct: Set primary division only
division_id = genesyscloud_division.primary.id