Deploying with Terraform
This guide covers deploying the full suite of Operata Cloud Collector v3 stacks using Terraform.
Operata publishes pre-synthesized Terraform JSON files (.tf.json).
You download one file per stack, set a handful of variables, and run terraform apply. No CDK, npm, or other build tooling is needed - only Terraform and AWS credentials.
What you need to know before starting:
β± Each stack takes approximately 5-15 minutes to deploy.
π¦ Terraform state is stored locally by default. For team environments, configure a remote backend (e.g. S3) before deploying.
π Keep your Operata API key out of version control - pass it inline or store it in a secrets manager.
Prerequisites
Requirement | Notes |
Terraform | v1.8.x or later - install guide |
AWS credentials | In the target account, with permission to create IAM, Lambda, Kinesis, EventBridge, Secrets Manager, and CloudWatch Logs resources - and (for clrules) CloudFormation and Amazon Connect rule resources |
Operata Group ID | How to find your Operata Group ID |
Operata API key | |
Terraform .tf.json file(s) | Downloaded from the Operata S3 bucket for your environment (see below) |
Available stacks
Stack | Description | Filename pattern |
Contact Trace Records (CTR) | Ingests CTR data from your Amazon Connect Kinesis stream |
|
Contact Trace Records - VPC | VPC variant of CTR |
|
Contact Lens Data (CLD) | Ingests Contact Lens output from S3. |
|
Contact Lens Data - VPC | VPC variant of CLD |
|
Contact Flow Logs (CFL) | Ingests contact flow logs from CloudWatch |
|
Contact Flow Logs - VPC | VPC variant of CFL |
|
Lex V2 Logs | Ingests Lex V2 conversation logs from CloudWatch |
|
Lex V2 Logs - VPC | VPC variant of Lex V2 Logs |
|
Contact Lens Rules (clrules) | Creates the Amazon Connect rules that emit operata_* Contact Lens events. |
|
VPC variants: Choose the VPC variant only if your AWS account requires Lambda functions to run inside a VPC. VPC variants require two additional variables: a VPC ID and a list of private subnet IDs.
Stacks are independent and can be deployed in any combination.
How to deploy each stack
Each section below contains a complete, ready-to-run deploy block and destroy block. Substitute <bucket>, <env>, and <TAG> plus any stack-specific values before running.
β
For production, <bucket> is operata-customer-assets and <env> is prod. <TAG> is the release version.
1. Contact Trace Records (CTR)
Deploy:
shell
mkdir -p ~/operata/ctr-<env> cd ~/operata/ctr-<env> curl -O https://<bucket>.s3.amazonaws.com/cloud-collector-v3-contact-trace-records-<TAG>.tf.json cat > terraform.tfvars <<EOF aws_region = "ap-southeast-2" operata_group_id = "<your-operata-group-id>" operata_api_key = "<your-operata-api-key>" ctr_kinesis_stream_arn = "arn:aws:kinesis:ap-southeast-2:111111111111:stream/..." connect_instance_friendly_name = "Prod" agent_data_s3_bucket_name = ["", ""] EOF terraform init terraform apply
agent_data_s3_bucket_name expects a two-element list in the format ["bucket-name", "prefix/"]. If you have an agent data S3 bucket set up, enter those values. Otherwise leave it as ["", ""].
Destroy:
shell
terraform destroy
CTR - VPC variant:
shell
mkdir -p ~/operata/ctr-vpc-<env> cd ~/operata/ctr-vpc-<env> curl -O https://<bucket>.s3.amazonaws.com/cloud-collector-v3-contact-trace-records-vpc-<TAG>.tf.json cat > terraform.tfvars <<EOF aws_region = "ap-southeast-2" operata_group_id = "<your-operata-group-id>" operata_api_key = "<your-operata-api-key>" ctr_kinesis_stream_arn = "arn:aws:kinesis:ap-southeast-2:111111111111:stream/..." connect_instance_friendly_name = "Prod" agent_data_s3_bucket_name = ["", ""] enrichment_lambda_vpc_id = "vpc-0123456789abcdef0" enrichment_lambda_vpc_private_subnets = ["subnet-aaaa", "subnet-bbbb"] EOF terraform init terraform apply
2. Contact Lens Data (CLD)
Deploy:
shell
mkdir -p ~/operata/cld-<env> cd ~/operata/cld-<env> curl -O https://<bucket>.s3.amazonaws.com/cloud-collector-v3-contact-lens-data-<TAG>.tf.json cat > terraform.tfvars <<EOF aws_region = "ap-southeast-2" operata_group_id = "<your-operata-group-id>" operata_api_key = "<your-operata-api-key>" contact_lens_data_s3_bucket_name = "<your-contact-lens-bucket>" EOF terraform init terraform apply
Destroy:
shell
terraform destroy
CLD - VPC variant:
shell
mkdir -p ~/operata/cld-vpc-<env> cd ~/operata/cld-vpc-<env> curl -O https://<bucket>.s3.amazonaws.com/cloud-collector-v3-contact-lens-data-vpc-<TAG>.tf.json cat > terraform.tfvars <<EOF aws_region = "ap-southeast-2" operata_group_id = "<your-operata-group-id>" operata_api_key = "<your-operata-api-key>" contact_lens_data_s3_bucket_name = "<your-contact-lens-bucket>" s3_kinesis_integrator_lambda_vpc_id = "vpc-0123456789abcdef0" s3_kinesis_integrator_lambda_vpc_private_subnets = ["subnet-aaaa", "subnet-bbbb"] EOF terraform init terraform apply
3. Contact Flow Logs (CFL)
Deploy:
shell
mkdir -p ~/operata/cfl-<env> cd ~/operata/cfl-<env> curl -O https://<bucket>.s3.amazonaws.com/cloud-collector-v3-contact-flow-logs-<TAG>.tf.json cat > terraform.tfvars <<EOF aws_region = "ap-southeast-2" operata_group_id = "<your-operata-group-id>" operata_api_key = "<your-operata-api-key>" cf_log_group = "/aws/connect/<instance>/ContactFlowLogs" connect_instance_friendly_name = "Prod" EOF terraform init terraform apply
Destroy:
shell
terraform destroy
CFL - VPC variant:
shell
mkdir -p ~/operata/cfl-vpc-<env> cd ~/operata/cfl-vpc-<env> curl -O https://<bucket>.s3.amazonaws.com/cloud-collector-v3-contact-flow-logs-vpc-<TAG>.tf.json cat > terraform.tfvars <<EOF aws_region = "ap-southeast-2" operata_group_id = "<your-operata-group-id>" operata_api_key = "<your-operata-api-key>" cf_log_group = "/aws/connect/<instance>/ContactFlowLogs" connect_instance_friendly_name = "Prod" cfl_decoder_lambda_vpc_id = "vpc-0123456789abcdef0" cfl_decoder_lambda_vpc_private_subnets = ["subnet-aaaa", "subnet-bbbb"] EOF terraform init terraform apply
4. Lex V2 Logs
Deploy:
shell
mkdir -p ~/operata/lexv2-<env> cd ~/operata/lexv2-<env> curl -O https://<bucket>.s3.amazonaws.com/cloud-collector-v3-lexv2-logs-<TAG>.tf.json cat > terraform.tfvars <<EOF aws_region = "ap-southeast-2" operata_group_id = "<your-operata-group-id>" operata_api_key = "<your-operata-api-key>" lex_log_group_name = "/aws/lex/<your-bot>/conversation-logs" EOF terraform init terraform apply
Destroy:
shell
terraform destroy
Lex V2 Logs - VPC variant:
shell
mkdir -p ~/operata/lexv2-vpc-<env> cd ~/operata/lexv2-vpc-<env> curl -O https://<bucket>.s3.amazonaws.com/cloud-collector-v3-lexv2-logs-vpc-<TAG>.tf.json cat > terraform.tfvars <<EOF aws_region = "ap-southeast-2" operata_group_id = "<your-operata-group-id>" operata_api_key = "<your-operata-api-key>" lex_log_group_name = "/aws/lex/<your-bot>/conversation-logs" lex_enrichment_lambda_vpc_id = "vpc-0123456789abcdef0" lex_enrichment_lambda_vpc_private_subnets = ["subnet-aaaa", "subnet-bbbb"] EOF terraform init terraform apply
5. Contact Lens Rules (clrules)
clrules creates approximately 50 Amazon Connect rules that publish operata_*-prefixed Contact Lens events.
How it works: this Terraform stack wraps the Operata Contact Lens Rules CloudFormation template via an aws_cloudformation_stack resource. Running terraform apply provisions a nested CloudFormation stack named operata-contact-lens-rules-<env> in your account, which in turn creates the Connect rules.
Deploy:
shell
mkdir -p ~/operata/clrules-<env> cd ~/operata/clrules-<env> curl -O https://<bucket>.s3.amazonaws.com/cloud-collector-v3-contact-lens-rules-<TAG>.tf.json cat > terraform.tfvars <<EOF aws_region = "ap-southeast-2" amazon_connect_instance_arn = "arn:aws:connect:ap-southeast-2:111111111111:instance/<your-instance-id>" EOF terraform init terraform apply
The apply takes approximately 25 seconds. Rule creation is serialized to respect the Amazon Connect CreateRule API rate limit (2 requests per second).
Verify:
shell
aws connect list-rules \ --region ap-southeast-2 \ --instance-id <your-instance-id> \ --publish-status PUBLISHED \ --query 'RuleSummaryList[?starts_with(Name, `operata_`)] | length(@)' # Expected output: ~50
Destroy:
shell
terraform destroy
clrules creates no AWS Secrets Manager secrets. No force-deletion step is required after destroy.
Upgrading an existing stack
β οΈ If unexpected resources appear in the plan, contact Operata before proceeding.
To apply a newer Operata release to an already-deployed stack:
Download the new
<TAG>.tf.jsonfile into the same working directory, replacing the previous file.Run
terraform planto confirm only the expected resources change (typically the Lambda zip key and a few metadata fields).Run
terraform apply.
State in the working directory tracks resource identity, so the upgrade is in place. The EventBridge pipe, Kinesis stream, and IAM resources are not replaced. For clrules specifically, terraform apply triggers a CloudFormation stack update on the nested operata-contact-lens-rules-<env> stack, which diffs and updates only the changed rules.
Redeploying after a destroy
AWS Secrets Manager schedules secrets for deletion with a 30-day recovery window - they are not removed immediately after terraform destroy. If you attempt terraform apply again before the window expires, the apply will fail with:
InvalidRequestException: You can't create this secret because a secret with this name is already scheduled for deletion.
To unblock an immediate redeploy, force-delete the affected secrets after destroy:
shell
aws secretsmanager delete-secret \ --region ap-southeast-2 \ --secret-id /cloud-collector-v3/<service>/apiToken-<env> \ --force-delete-without-recovery
β οΈ --force-delete-without-recovery permanently removes the secret. Use this only when you intend to recreate it immediately.
Secrets created per stack:
Stack | Secrets |
clrules | None |
CTR |
|
CLD |
|
CFL |
|
Lex V2 Logs |
|
How to verify it's working
Place or receive a short test call on your Amazon Connect instance, then check the following.
Allow up to 5 minutes for data to appear. Remember to Close Contact if testing inbound call data.
1. Check API activity
Go to Group Settings β Integrations β API Management and confirm the API key shows a recent Last Used timestamp. This confirms your stack is actively sending data to Operata.
β
2. Check CTR data
Go to Calls and Logs β Details / Summary and confirm the test call appears.
β
3. Check Contact Lens data
Go to Dashboards β Conversational Analytics and confirm data appears. Filter by the test agent name, or sort by most recent call.
If data doesn't appear after 10 minutes, proceed to the troubleshooting steps below.
Troubleshooting
Work through the checklist below before contacting Operata support:
β
EventBridge Pipe fails to create on first apply. This is an IAM eventual-consistency issue. The role and policies are created correctly, but EventBridge performs an upfront access check before IAM has finished propagating. Re-run terraform apply - Terraform leaves the role in place and only retries the pipe.
β
Secret already scheduled for deletion. Force-delete the affected secret(s) via the CLI (see Redeploying after a destroy above), then re-apply.
β
CLD deploys cleanly but no data appears in Operata. The EventBridge Pipe shows RUNNING but no contact-lens summaries appear. Check that Contact Lens is enabled on your Amazon Connect instance and that calls are being processed with Contact Lens analysis turned on.
β
API key shows no recent activity. Check that operata_api_key and operata_group_id in your terraform.tfvars are correct. An incorrect API key causes data to be silently rejected.
β
Terraform inline_policy deprecation warning. Terraform may print a deprecation warning on aws_iam_role.inline_policy. This is a provider-level notice only. The configuration applies cleanly and has no functional impact.
Need help?
If you run into any issues, or aren't sure which stacks or environment applies to your setup, we're here to help.
β
π§ Email us at help@operata.com
π¬ Or send a message directly from within the Operata application
