Automate Genesys Cloud Queue Provisioning with Terraform and YAML
What You Will Build
- A Terraform script that parses a YAML file containing queue definitions and uses
for_eachto provision multiple Genesys Cloud queues simultaneously. - This tutorial uses the Genesys Cloud Terraform Provider and standard Terraform functions.
- The implementation is written in HashiCorp Configuration Language (HCL).
Prerequisites
- Genesys Cloud OAuth Client: You need a Genesys Cloud OAuth client with the
admingrant type. The client requires thequeue:writescope to create queues androuting:queue:viewto read existing configurations if you plan to reference them later. - Terraform: Version 1.0 or higher installed on your machine.
- Genesys Cloud Terraform Provider: Version 1.0.0 or higher.
- YAML File: A local
queues.yamlfile containing your queue data. - Environment Variables: You must set
GENESYS_CLOUD_REGION(e.g.,my.genesys.cloud) andGENESYS_CLOUD_CLIENT_SECRET/GENESYS_CLOUD_CLIENT_IDin your shell or.envfile.
Authentication Setup
Terraform handles OAuth token acquisition automatically when you configure the provider block. You do not need to write manual OAuth code. However, you must ensure the provider is initialized with the correct credentials.
Create a file named main.tf. This file initializes the provider. The provider uses the environment variables to fetch an access token from the Genesys Cloud OAuth endpoint (/oauth/token).
terraform {
required_providers {
genesyscloud = {
source = "genesyscloud/genesyscloud"
version = ">= 1.0.0"
}
}
}
provider "genesyscloud" {
# These are automatically picked up from environment variables:
# GENESYS_CLOUD_CLIENT_ID
# GENESYS_CLOUD_CLIENT_SECRET
# GENESYS_CLOUD_REGION
}
If you run terraform init, the provider will download the necessary binaries. When you run terraform plan, the provider will execute a POST request to the OAuth endpoint to retrieve a bearer token. If the credentials are invalid, you will receive a 401 Unauthorized error. If the client lacks the queue:write scope, you will receive a 403 Forbidden error when attempting to create resources.
Implementation
Step 1: Define the YAML Data Structure
Create a file named queues.yaml. This file serves as the single source of truth for your queue configurations. We will structure it as a list of objects. Each object represents a queue.
Key fields in Genesys Cloud queues include:
- name: The display name of the queue.
- description: A text description.
- outbound_email: The email address used for outbound notifications (optional but common).
- wrap_up_policy: Controls how wrap-up time is applied (
FORCE,IGNORE,IGNORE_IF_EMPTY,USE_AGENT_SETTINGS). - skills: A list of skill names required to serve this queue.
# queues.yaml
- name: "Sales Support"
description: "General sales inquiries and new customer onboarding"
wrap_up_policy: "FORCE"
skills:
- "sales"
- "english"
outbound_email: "sales-support@example.com"
- name: "Technical Support"
description: "Tier 1 technical troubleshooting"
wrap_up_policy: "IGNORE_IF_EMPTY"
skills:
- "technical"
- "english"
outbound_email: "tech-support@example.com"
- name: "Billing Inquiries"
description: "Payment and invoice questions"
wrap_up_policy: "USE_AGENT_SETTINGS"
skills:
- "billing"
- "english"
outbound_email: "billing@example.com"
Step 2: Parse YAML and Prepare for Iteration
Terraform does not natively parse YAML files in a way that is directly usable for for_each without some transformation. We use the yamldecode function to convert the YAML content into a map of objects.
In main.tf, add a locals block to read the file and decode it.
locals {
# Read the raw content of the YAML file
queues_raw = file("${path.module}/queues.yaml")
# Decode the YAML into a Terraform list of maps
queues_list = yamldecode(local.queues_raw)
# Convert the list into a map for for_each usage.
# We use the queue name as the key. This ensures idempotency.
# If a queue name changes in the YAML, Terraform will detect a replacement.
queues_map = { for q in local.queues_list : q.name => q }
}
Why convert to a map?
The for_each meta-argument requires a map or a set of strings. Using the queue name as the key is critical because Genesys Cloud queue names must be unique within a routing language context. If you used an index (e.g., from a list), renaming a queue would cause Terraform to destroy the old queue and create a new one, potentially losing historical statistics. Using the name as the key allows Terraform to update the existing queue if other attributes change.
Step 3: Create Queues Using for_each
Now we define the genesyscloud_routing_queue resource. We use the for_each argument to iterate over local.queues_map.
resource "genesyscloud_routing_queue" "dynamic_queues" {
for_each = local.queues_map
name = each.value.name
description = each.value.description
enable_email = false # Set to true if you enable email capability on the queue
# Wrap-up policy must be one of: FORCE, IGNORE, IGNORE_IF_EMPTY, USE_AGENT_SETTINGS
wrap_up_policy = each.value.wrap_up_policy
# Outbound email configuration
outbound_email = each.value.outbound_email
# Skills configuration
# Note: Skills must exist in Genesys Cloud before they can be assigned to a queue.
# This tutorial assumes the skills listed in YAML already exist.
skills = each.value.skills
# Optional: Set the queue language. Default is usually "en-US".
# If your queues support multiple languages, you may need a more complex structure.
language_id = var.default_language_id
}
Important Note on Skills:
The skills parameter in the Terraform provider expects a list of skill names. The provider will automatically resolve these names to skill IDs. However, if a skill name in your YAML does not exist in your Genesys Cloud org, the terraform apply will fail with a 404 or validation error. Ensure your skills are provisioned via other Terraform resources or manually before running this script.
Step 4: Handling Dependencies and Language IDs
Genesys Cloud queues are associated with a routing language. You must provide a language_id. For simplicity, we assume a single default language (e.g., English US). In a production environment, you might fetch the language ID dynamically.
Add a variable for the language ID and a data source to look it up by name, ensuring the code is portable across organizations.
variable "default_language_name" {
description = "The name of the default routing language (e.g., 'English (US)')"
type = string
default = "English (US)"
}
data "genesyscloud_language" "default" {
name = var.default_language_name
}
locals {
default_language_id = data.genesyscloud_language.default.id
}
Update the queue resource to use this ID:
resource "genesyscloud_routing_queue" "dynamic_queues" {
for_each = local.queues_map
name = each.value.name
description = each.value.description
wrap_up_policy = each.value.wrap_up_policy
outbound_email = each.value.outbound_email
skills = each.value.skills
language_id = local.default_language_id
}
Complete Working Example
Below is the complete main.tf file. Save this as main.tf in the same directory as queues.yaml.
terraform {
required_providers {
genesyscloud = {
source = "genesyscloud/genesyscloud"
version = ">= 1.0.0"
}
}
}
provider "genesyscloud" {
# Credentials sourced from environment variables
}
variable "default_language_name" {
description = "The name of the default routing language"
type = string
default = "English (US)"
}
# Fetch the language ID to use in queue creation
data "genesyscloud_language" "default" {
name = var.default_language_name
}
locals {
# Read and parse the YAML file
queues_raw = file("${path.module}/queues.yaml")
queues_list = yamldecode(local.queues_raw)
# Create a map keyed by queue name for stable for_each iteration
queues_map = { for q in local.queues_list : q.name => q }
# Cache the language ID
default_language_id = data.genesyscloud_language.default.id
}
# Provision queues based on the YAML configuration
resource "genesyscloud_routing_queue" "dynamic_queues" {
for_each = local.queues_map
name = each.value.name
description = each.value.description
wrap_up_policy = each.value.wrap_up_policy
outbound_email = each.value.outbound_email
skills = each.value.skills
language_id = local.default_language_id
# Optional: Add tags if needed
# tags = ["automated-provisioning"]
}
And the corresponding queues.yaml:
- name: "Sales Support"
description: "General sales inquiries"
wrap_up_policy: "FORCE"
skills:
- "sales"
- "english"
outbound_email: "sales@example.com"
- name: "Tech Support"
description: "Technical issues"
wrap_up_policy: "IGNORE_IF_EMPTY"
skills:
- "technical"
- "english"
outbound_email: "tech@example.com"
To run this:
- Set environment variables:
export GENESYS_CLOUD_REGION="my.genesys.cloud" export GENESYS_CLOUD_CLIENT_ID="your_client_id" export GENESYS_CLOUD_CLIENT_SECRET="your_client_secret" - Initialize Terraform:
terraform init - Plan the changes:
terraform plan - Apply the changes:
terraform apply
Common Errors & Debugging
Error: Invalid function argument: yamldecode failed
- Cause: The YAML file is malformed or contains syntax errors.
- Fix: Validate your YAML syntax using a linter or online validator. Ensure indentation is consistent (spaces, not tabs). Check for missing quotes around strings that contain special characters.
Error: Skill not found: “sales”
- Cause: The skill named “sales” does not exist in your Genesys Cloud organization.
- Fix: Create the skill in Genesys Cloud first, either manually or via a separate
genesyscloud_routing_skillresource. Terraform will not create skills implicitly when assigning them to queues.
Error: 409 Conflict: Queue with name “Sales Support” already exists
- Cause: You are trying to create a queue with a name that already exists, but it is not managed by Terraform (orphaned resource).
- Fix: You have two options:
- Import the existing queue into Terraform state:
terraform import genesyscloud_routing_queue.dynamic_queues["Sales Support"] <queue_id> - Delete the existing queue manually in the Genesys Cloud admin console and re-run
terraform apply.
- Import the existing queue into Terraform state:
Error: 403 Forbidden: Insufficient permissions
- Cause: The OAuth client used for authentication lacks the
queue:writescope. - Fix: Go to the Genesys Cloud Admin Console > Platform > OAuth Clients. Select your client, edit the grants, and ensure
queue:writeandrouting:queue:vieware selected. Save and regenerate the client secret if necessary.
Error: for_each map key conflict
- Cause: Two entries in
queues.yamlhave the samename. - Fix: Ensure all queue names in the YAML file are unique. The
for_eachmap creation will fail or overwrite entries if keys are duplicated, leading to unpredictable behavior.