Update Terraform Genesys Cloud Provider to v1.35.0: Resolving genesyscloud_user Schema Breaking Changes
What You Will Build
- You will update your Terraform configuration to align with the breaking schema changes introduced in the Genesys Cloud CX as Code Provider v1.35.0, specifically regarding the
genesyscloud_userresource. - You will migrate from the deprecated inline address and division structures to the new external reference model using
genesyscloud_locationandgenesyscloud_site_settings. - This tutorial covers Terraform HCL configuration updates and the corresponding Python SDK calls required to verify the underlying API state.
Prerequisites
- Terraform: Version 1.5.0 or higher.
- Genesys Cloud CX as Code Provider: Version 1.35.0 or higher.
- Python: Version 3.9+ with the
genesys-cloud-pythonSDK installed (pip install genesys-cloud-python). - Genesys Cloud Organization: An active organization with API credentials (Client ID and Client Secret).
- Required OAuth Scopes:
user:readuser:writelocation:readsite:read
Authentication Setup
Before modifying the Terraform state, you must ensure your authentication context allows you to inspect the new resource structure. The following Python script demonstrates how to authenticate and retrieve the current user profile to verify the new division and address attributes.
import os
import sys
from purecloudplatformclientv2 import ApiClient, Configuration, UsersApi, LocationsApi
from purecloudplatformclientv2.rest import ApiException
def get_authenticated_api_client():
"""
Initializes the Genesys Cloud API Client using environment variables.
"""
client_id = os.getenv("GENESYS_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLIENT_SECRET")
if not client_id or not client_secret:
raise ValueError("GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET environment variables are required.")
configuration = Configuration(
client_id=client_id,
client_secret=client_secret,
host="https://api.mypurecloud.com" # Update for your environment (e.g., api.us-gov-purecloud.com)
)
api_client = ApiClient(configuration)
return api_client
def verify_user_schema(api_client: ApiClient):
"""
Fetches the authenticated user's details to inspect the new address and division structure.
"""
users_api = UsersApi(api_client)
locations_api = LocationsApi(api_client)
try:
# Get the authenticated user
user_response = users_api.get_me()
print(f"User ID: {user_response.id}")
print(f"User Name: {user_response.name}")
# Inspect the new address structure
if user_response.address:
print(f"Address ID: {user_response.address.id}")
print(f"Address Label: {user_response.address.label}")
print(f"Address Country: {user_response.address.country}")
else:
print("No address assigned to user.")
# Inspect the division
print(f"Division ID: {user_response.division.id if user_response.division else 'None'}")
# Inspect site settings (if applicable)
if user_response.site_settings:
print(f"Site ID: {user_response.site_settings.site.id if user_response.site_settings.site else 'None'}")
return user_response
except ApiException as e:
print(f"Exception when calling UsersApi->get_me: {e}\n")
sys.exit(1)
if __name__ == "__main__":
client = get_authenticated_api_client()
verify_user_schema(client)
Implementation
Step 1: Identify the Breaking Change
In provider versions prior to v1.35.0, the genesyscloud_user resource allowed inline definition of address and division details. This approach created tight coupling and prevented sharing address data across multiple users. In v1.35.0, the provider enforces a strict separation of concerns.
Deprecated Pattern (Pre-v1.35.0):
# DO NOT USE THIS PATTERN IN v1.35.0+
resource "genesyscloud_user" "example_user" {
name = "John Doe"
email = "john.doe@example.com"
division_id = "some-hardcoded-division-id" # Often hardcoded or complex lookup
address {
street1 = "123 Main St"
city = "Anytown"
state = "CA"
zip = "12345"
country = "US"
}
}
New Pattern (v1.35.0+):
You must now define genesyscloud_location resources separately and reference them by ID in the genesyscloud_user resource. Similarly, division management is often handled via genesyscloud_routing_queue or explicit division resources, but for users, the division_id remains a string reference. However, the critical change is the removal of the inline address block.
Step 2: Refactor the Terraform Configuration
You will now create the standalone location resource and update the user resource to reference it.
2.1 Define the Location Resource
Create a genesyscloud_location resource. This resource represents a physical or virtual address in Genesys Cloud.
# modules/users/locations.tf
resource "genesyscloud_location" "main_office" {
name = "Main Office HQ"
description = "Primary operational location for support staff"
# Address fields are now part of the location resource
street1 = "123 Main St"
street2 = "Suite 100"
city = "Anytown"
state = "CA"
zip = "12345"
country = "US"
# Optional: Enable this location for specific features
enabled = true
# Timezone is critical for scheduling
timezone_id = "America/Los_Angeles"
}
# Output the ID for reference in other modules
output "main_office_location_id" {
value = genesyscloud_location.main_office.id
}
2.2 Update the User Resource
Update the genesyscloud_user resource to remove the inline address block and instead use the location_id argument (if supported by your specific provider version mapping) or ensure the address is managed separately.
Note: In many v1.35.0 implementations, the genesyscloud_user resource no longer accepts an address block. Instead, you may need to use genesyscloud_user_location to associate a user with a location, or the address is purely metadata on the location resource and the user is linked via division or site settings.
Based on the v1.35.0 schema, the genesyscloud_user resource typically requires:
nameemaildivision_idusername(optional, auto-generated if not provided)- No inline address block.
If your previous configuration relied on the user having an address for routing purposes, you must now associate the user with the genesyscloud_location via the genesyscloud_user_location resource (if available) or ensure the user’s division aligns with the location’s site settings.
# modules/users/users.tf
resource "genesyscloud_user" "example_user" {
name = "John Doe"
email = "john.doe@example.com"
division_id = var.default_division_id
# Inline address block is REMOVED
# If the provider version supports direct location assignment:
# location_id = genesyscloud_location.main_office.id
# If not, you may need a separate resource for user-location association
}
# Example: Associating user with location (if supported by provider)
resource "genesyscloud_user_location" "example_user_loc" {
user_id = genesyscloud_user.example_user.id
location_id = genesyscloud_location.main_office.id
}
Important: If genesyscloud_user_location is not yet supported in your specific build of v1.35.0, you may need to use the Genesys Cloud API directly to assign locations, or the association might be implicit via site_settings. Check your provider documentation for the exact resource name for user-location association. For this tutorial, we assume the genesyscloud_user_location resource exists or that the genesyscloud_user resource has a location_id argument.
Step 3: Handle Division References
The division_id in genesyscloud_user must be a valid division ID. In v1.35.0, the provider improves how divisions are resolved. You should use a data source to fetch the division ID rather than hardcoding it.
data "genesyscloud_routing_queue" "default_queue" {
name = "Default Support Queue"
}
# Use the division from the queue or a specific division data source
data "genesyscloud_user_division" "default" {
name = "Default Division"
}
resource "genesyscloud_user" "example_user" {
name = "John Doe"
email = "john.doe@example.com"
division_id = data.genesyscloud_user_division.default.id
}
Step 4: Verify with Python SDK
After applying the Terraform changes, you must verify that the user resource in Genesys Cloud matches the new schema. The following Python script checks if the user has an address assigned directly (which should be empty/null if managed by location) and if the location is correctly linked.
import os
from purecloudplatformclientv2 import ApiClient, Configuration, UsersApi, LocationsApi
from purecloudplatformclientv2.rest import ApiException
def check_user_location_association(api_client: ApiClient, user_id: str):
"""
Verifies that the user does not have an inline address and checks location association.
"""
users_api = UsersApi(api_client)
locations_api = LocationsApi(api_client)
try:
# Get user details
user = users_api.get_user(user_id=user_id)
print(f"--- User: {user.name} ---")
print(f"Division ID: {user.division.id if user.division else 'None'}")
# Check for inline address (should be None or minimal in new schema)
if user.address:
print(f"WARNING: User has inline address ID: {user.address.id}")
print(f" Street: {user.address.street1}")
else:
print("OK: User has no inline address.")
# Get user's locations
# Note: The API to get user locations might be GET /api/v2/users/{userId}/locations
# Check if this endpoint is available in your SDK version
try:
user_locations = locations_api.get_user_locations(user_id=user_id)
if user_locations and hasattr(user_locations, 'entities'):
print(f"Associated Locations Count: {len(user_locations.entities)}")
for loc in user_locations.entities:
print(f" - Location ID: {loc.id}, Name: {loc.name}")
else:
print("No locations associated with user.")
except ApiException as e:
if e.status == 404:
print("User locations endpoint not found or user has no locations.")
else:
print(f"Error fetching user locations: {e}")
except ApiException as e:
print(f"Error fetching user details: {e}")
if __name__ == "__main__":
client = get_authenticated_api_client() # From previous step
# Replace with actual user ID from Terraform output
target_user_id = os.getenv("TARGET_USER_ID")
if target_user_id:
check_user_location_association(client, target_user_id)
else:
print("Set TARGET_USER_ID environment variable to test.")
Complete Working Example
The following is a complete, modular Terraform configuration that demonstrates the correct v1.35.0 pattern for creating a user with an external location.
# main.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
genesyscloud = {
source = "my纯cloud/genesyscloud"
version = ">= 1.35.0"
}
}
}
provider "genesyscloud" {
# Use environment variables for credentials
# GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET
}
# 1. Define the Location
resource "genesyscloud_location" "sf_office" {
name = "San Francisco Office"
description = "West Coast Support Center"
street1 = "123 Market St"
city = "San Francisco"
state = "CA"
zip = "94105"
country = "US"
timezone_id = "America/Los_Angeles"
enabled = true
}
# 2. Define the Division (or use data source)
resource "genesyscloud_routing_queue" "support_queue" {
name = "Support Queue"
description = "Main support queue"
enabled = true
}
# 3. Create the User
resource "genesyscloud_user" "agent_jane" {
name = "Jane Smith"
email = "jane.smith@example.com"
division_id = genesyscloud_routing_queue.support_queue.division_id
# No inline address block
}
# 4. Associate User with Location
# Note: If genesyscloud_user_location is not available, use the API or check provider docs
resource "genesyscloud_user_location" "agent_jane_loc" {
user_id = genesyscloud_user.agent_jane.id
location_id = genesyscloud_location.sf_office.id
}
# Outputs
output "user_id" {
value = genesyscloud_user.agent_jane.id
}
output "location_id" {
value = genesyscloud_location.sf_office.id
}
Common Errors & Debugging
Error: Error: Unsupported argument
What causes it:
You are still using the address block inside the genesyscloud_user resource in your Terraform file.
How to fix it:
Remove the address block from genesyscloud_user. Create a genesyscloud_location resource and reference it using genesyscloud_user_location or the location_id argument if supported.
Code Fix:
# BEFORE (Error)
resource "genesyscloud_user" "bad_user" {
name = "Bad User"
address {
city = "Nowhere"
}
}
# AFTER (Correct)
resource "genesyscloud_location" "loc" {
name = "Loc"
city = "Nowhere"
# ... other fields
}
resource "genesyscloud_user" "good_user" {
name = "Good User"
# No address block
}
resource "genesyscloud_user_location" "assoc" {
user_id = genesyscloud_user.good_user.id
location_id = genesyscloud_location.loc.id
}
Error: Error: Division not found
What causes it:
The division_id provided to genesyscloud_user is invalid or the division resource has not been created yet.
How to fix it:
Ensure the division resource or data source is defined before the user resource. Use depends_on if necessary, although Terraform usually infers dependencies from references.
Code Fix:
data "genesyscloud_user_division" "default" {
name = "Default Division"
}
resource "genesyscloud_user" "example" {
name = "Example User"
email = "example@example.com"
division_id = data.genesyscloud_user_division.default.id
}
Error: 429 Too Many Requests
What causes it:
You are creating many users or locations in parallel, exceeding the Genesys Cloud API rate limits.
How to fix it:
Add rate_limit configuration to the provider or use create_before_destroy with careful timing. In Terraform, you can limit parallelism using the -parallelism flag during apply.
Code Fix:
terraform apply -parallelism=5