Define Genesys Cloud Queues with Skills Using Terraform CX as Code

Define Genesys Cloud Queues with Skills Using Terraform CX as Code

What You Will Build

  • You will provision a Genesys Cloud queue with specific skills, routing strategies, and member assignments using Infrastructure as Code.
  • This tutorial uses the genesyscloud Terraform provider (CX as Code) to manage queue resources declaratively.
  • The implementation covers Terraform HCL syntax, provider authentication, and state management for production-grade deployments.

Prerequisites

  • Terraform Version: 1.5+
  • Genesys Cloud Provider: v1.100.0+
  • Genesys Cloud Environment: A tenant with appropriate permissions to create queues and assign skills.
  • Required Permissions:
    • queue:create or queue:edit
    • routing:skillgroup:create or routing:skillgroup:edit (if creating skills dynamically)
    • routing:skill:create or routing:skill:edit
  • Authentication: Service Account credentials (Client ID and Client Secret) or Personal Access Token.
  • External Dependencies: None beyond Terraform and the provider plugin.

Authentication Setup

The Genesys Cloud Terraform provider supports multiple authentication methods. For CI/CD pipelines and production environments, using a Service Account via environment variables is the standard approach. This avoids storing secrets in code.

You must set the following environment variables before running terraform init or terraform apply:

# Linux/MacOS
export GENESYS_CLOUD_REGION="mypurecloud.com"
export GENESYS_CLOUD_CLIENT_ID="your-client-id"
export GENESYS_CLOUD_CLIENT_SECRET="your-client-secret"

# Windows PowerShell
$env:GENESYS_CLOUD_REGION="mypurecloud.com"
$env:GENESYS_CLOUD_CLIENT_ID="your-client-id"
$env:GENESYS_CLOUD_CLIENT_SECRET="your-client-secret"

If you are testing locally with a Personal Access Token, you can use:

export GENESYS_CLOUD_TOKEN_TYPE="personal"
export GENESYS_CLOUD_TOKEN="your-personal-access-token"

The provider configuration in main.tf is minimal when environment variables are used. The provider automatically detects the credentials.

terraform {
  required_providers {
    genesyscloud = {
      source  = "genesys/cloud"
      version = "~> 1.100.0"
    }
  }
}

provider "genesyscloud" {
  # No explicit credentials needed if env vars are set.
  # The provider will auto-detect from GENESYS_CLOUD_* variables.
}

Implementation

Step 1: Define Skills and Skill Groups

Queues in Genesys Cloud do not hold skills directly; they reference Skill Groups. Skills are defined at the global tenant level or within specific Skill Groups. To ensure your queue works correctly, you must first define the skills that agents will be assigned to.

In this example, we create a skill called billing_support and assign it to a Skill Group.

# Define a global skill
resource "genesyscloud_routing_skill" "billing_support" {
  name        = "Billing Support"
  description = "Skill for handling billing inquiries"
}

# Define a Skill Group (optional but recommended for organization)
resource "genesyscloud_routing_skillgroup" "finance_group" {
  name        = "Finance Skills"
  description = "Group containing all finance-related skills"
  
  # Assign the skill to this group
  skills {
    skill_id = genesyscloud_routing_skill.billing_support.id
    name     = genesyscloud_routing_skill.billing_support.name
  }
}

Key Parameters:

  • name: The display name of the skill. This is what appears in the admin console.
  • description: Optional metadata for documentation.
  • skills block inside routing_skillgroup: Links the global skill to the group.

Error Handling:
If the skill name already exists, Terraform will fail with a 409 Conflict error. To prevent this during iterative development, use the genesyscloud_routing_skill data source to look up existing skills if they might already exist, or ensure your naming convention is unique.

Step 2: Configure the Queue Resource

The core resource is genesyscloud_routing_queue. This resource maps directly to the /api/v2/routing/queues endpoint. You must define the queue name, description, and crucially, the skills that determine who can answer calls routed to this queue.

resource "genesyscloud_routing_queue" "billing_queue" {
  name        = "Billing Support Queue"
  description = "Queue for inbound billing calls"
  enabled     = true

  # Define the skills required to answer this queue
  skills {
    skill_id = genesyscloud_routing_skill.billing_support.id
    level    = 1 # Skill level required (1-5)
  }

  # Routing strategy
  routing_strategy = "longest_idle"

  # Wrap-up codes (optional)
  wrap_up_code_required = false

  # Member configuration
  members {
    # Reference an existing user or create one
    # For this example, we assume a user ID is known or looked up
    user_id = var.support_agent_user_id
    skills {
      skill_id = genesyscloud_routing_skill.billing_support.id
      level    = 5 # Agent's proficiency level
    }
  }

  # Outbound campaign settings (if applicable)
  outbound {
    enabled = false
  }

  # Small talk configuration (optional)
  small_talk {
    enabled = false
  }

  # Hold music
  hold_music = "default"
}

Key Parameters Explained:

  • skills: A list of objects. Each object contains skill_id (the UUID of the skill) and level (1-5). The queue requires agents to have at least this skill level to be routed to.
  • routing_strategy: Determines how the next available agent is selected. Common values: longest_idle, equalize, fewest_conversations, skill_only.
  • members: A list of agents assigned to the queue. Each member entry can specify their skill levels for the skills defined in the queue.
  • wrap_up_code_required: If true, agents must select a wrap-up code before taking the next call.

Non-Obvious Detail:
The members block in genesyscloud_routing_queue does not create the user. It only assigns the user to the queue. If the user_id does not exist, the apply will fail. You must either create the user first using genesyscloud_user or reference an existing user via a data source.

Step 3: Lookup Existing Users (Data Source)

Hardcoding user IDs is fragile. Use the genesyscloud_user data source to dynamically find the user ID based on their email or name.

data "genesyscloud_user" "support_agent" {
  email = "agent@example.com"
}

Then reference it in the queue:

resource "genesyscloud_routing_queue" "billing_queue" {
  # ... previous configuration ...

  members {
    user_id = data.genesyscloud_user.support_agent.id
    skills {
      skill_id = genesyscloud_routing_skill.billing_support.id
      level    = 5
    }
  }
}

Edge Case:
If multiple users share the same email (unlikely) or name, the data source may return multiple results. Ensure the lookup criteria are unique. If no user is found, Terraform will fail with a “Resource not found” error.

Step 4: Add Queue Members via Dedicated Resource (Alternative Pattern)

For large-scale deployments, managing members inside the queue resource can lead to large state files and complex diffs. A better pattern is to use the genesyscloud_routing_queue_member resource for each member. This allows independent management of queue membership.

# Define the queue without members first
resource "genesyscloud_routing_queue" "billing_queue" {
  name        = "Billing Support Queue"
  description = "Queue for inbound billing calls"
  enabled     = true

  skills {
    skill_id = genesyscloud_routing_skill.billing_support.id
    level    = 1
  }

  routing_strategy = "longest_idle"
  wrap_up_code_required = false
  hold_music = "default"
}

# Add members separately
resource "genesyscloud_routing_queue_member" "agent_assignment" {
  queue_id = genesyscloud_routing_queue.billing_queue.id
  user_id  = data.genesyscloud_user.support_agent.id

  skills {
    skill_id = genesyscloud_routing_skill.billing_support.id
    level    = 5
  }
}

Why Use This Pattern?

  • Modularity: You can add/remove agents without touching the queue definition.
  • Scalability: Terraform handles the member addition as separate API calls, reducing the risk of partial failures.
  • Clarity: The queue resource focuses on routing logic, while member resources focus on staffing.

Complete Working Example

Below is a complete main.tf file that creates a skill, a queue, looks up a user, and assigns them to the queue.

terraform {
  required_providers {
    genesyscloud = {
      source  = "genesys/cloud"
      version = "~> 1.100.0"
    }
  }
}

provider "genesyscloud" {
  # Authentication handled via environment variables
}

# Variable for user email
variable "support_agent_email" {
  description = "Email of the support agent to assign to the queue"
  type        = string
  default     = "agent@example.com"
}

# 1. Create the Skill
resource "genesyscloud_routing_skill" "billing_support" {
  name        = "Billing Support"
  description = "Skill for handling billing inquiries"
}

# 2. Lookup the User
data "genesyscloud_user" "support_agent" {
  email = var.support_agent_email
}

# 3. Create the Queue
resource "genesyscloud_routing_queue" "billing_queue" {
  name        = "Billing Support Queue"
  description = "Queue for inbound billing calls"
  enabled     = true

  # Skills required for the queue
  skills {
    skill_id = genesyscloud_routing_skill.billing_support.id
    level    = 1
  }

  # Routing configuration
  routing_strategy = "longest_idle"
  wrap_up_code_required = false
  hold_music = "default"
  
  # Outbound settings
  outbound {
    enabled = false
  }
}

# 4. Assign the User to the Queue
resource "genesyscloud_routing_queue_member" "agent_assignment" {
  queue_id = genesyscloud_routing_queue.billing_queue.id
  user_id  = data.genesyscloud_user.support_agent.id

  # Agent's skill level for this queue
  skills {
    skill_id = genesyscloud_routing_skill.billing_support.id
    level    = 5
  }
}

# Output the Queue ID for verification
output "queue_id" {
  value = genesyscloud_routing_queue.billing_queue.id
}

output "queue_name" {
  value = genesyscloud_routing_queue.billing_queue.name
}

How to Run:

  1. Save the code to main.tf.
  2. Set your environment variables (GENESYS_CLOUD_REGION, GENESYS_CLOUD_CLIENT_ID, GENESYS_CLOUD_CLIENT_SECRET).
  3. Run terraform init to download the provider.
  4. Run terraform plan to preview the changes.
  5. Run terraform apply to create the resources.

Common Errors & Debugging

Error: 409 Conflict (Resource Already Exists)

What causes it:
You attempted to create a skill or queue with a name that already exists in your Genesys Cloud tenant. Genesys Cloud enforces unique names for skills and queues within certain scopes.

How to fix it:

  1. Check the existing resources in the Genesys Cloud Admin Console.
  2. Update the name in your Terraform code to be unique.
  3. Alternatively, use the genesyscloud_routing_skill data source to import the existing resource into your Terraform state if you want to manage it via code.
# Import existing skill
import {
  to = genesyscloud_routing_skill.existing_billing
  id = "existing-skill-uuid"
}

resource "genesyscloud_routing_skill" "existing_billing" {
  name = "Billing Support" # Must match existing name
}

Error: 404 Not Found (User Not Found)

What causes it:
The genesyscloud_user data source could not find a user with the specified email. This usually happens if the email is misspelled or the user does not exist in the tenant.

How to fix it:

  1. Verify the email address in the Genesys Cloud Admin Console.
  2. Ensure the user is not deactivated.
  3. If the user does not exist, create them using genesyscloud_user resource before referencing them.

Error: 400 Bad Request (Invalid Skill Level)

What causes it:
The level parameter in the skills block is outside the valid range (1-5).

How to fix it:
Ensure all level values in skills blocks are integers between 1 and 5.

# Invalid
skills {
  skill_id = genesyscloud_routing_skill.billing_support.id
  level    = 10 # Error: Level must be 1-5
}

# Valid
skills {
  skill_id = genesyscloud_routing_skill.billing_support.id
  level    = 5
}

Error: 429 Too Many Requests

What causes it:
Genesys Cloud API rate limits have been exceeded. This can happen during large-scale deployments with many resources.

How to fix it:
The Terraform provider includes built-in retry logic for 429 errors. However, if you are creating many resources, consider adding a delay in your Terraform configuration or breaking the deployment into smaller batches.

Official References