├── .editorconfig ├── .gitignore ├── .header.md ├── .markdownlint.json ├── .pre-commit-config.yaml ├── .terraform-docs.yaml ├── .tflint.hcl ├── .tfsec ├── launch_configuration_imdsv2_tfchecks.json ├── launch_template_imdsv2_tfchecks.json ├── no_launch_config_tfchecks.json ├── sg_no_embedded_egress_rules_tfchecks.json └── sg_no_embedded_ingress_rules_tfchecks.json ├── .vscode └── settings.json ├── CHANGELOG.md ├── CODEOWNERS ├── LICENSE ├── NOTICE.txt ├── README.md ├── examples ├── complete │ ├── bot_and_lambda_associations.tf │ ├── contact-flows │ │ ├── inbound.json.tftpl │ │ └── quick_connect.json.tftpl │ ├── contact_flows.tf │ ├── hours_of_operations.tf │ ├── instance_storage_configs.tf │ ├── lambda-function │ │ └── index.js │ ├── main.tf │ ├── outputs.tf │ ├── queues.tf │ ├── quick_connects.tf │ ├── routing_security_profiles.tf │ ├── users_groups_structure.tf │ ├── versions.tf │ └── vocabularies.tf ├── hours-of-operations │ ├── main.tf │ ├── outputs.tf │ └── versions.tf ├── instance-storage-config-kinesis │ ├── main.tf │ ├── outputs.tf │ └── versions.tf ├── instance-storage-config-s3 │ ├── main.tf │ ├── outputs.tf │ └── versions.tf ├── lex-bot-association │ ├── main.tf │ ├── outputs.tf │ └── versions.tf ├── queue │ ├── main.tf │ ├── outputs.tf │ └── versions.tf └── simple │ ├── main.tf │ ├── outputs.tf │ └── versions.tf ├── main.tf ├── outputs.tf ├── test ├── complete_connect_feature_test.go ├── complete_example_test.go ├── connect_additional_feature_test │ ├── contact-flows │ │ ├── inbound.json.tftpl │ │ ├── inboundflow.json.tftpl │ │ └── quick_connect.json.tftpl │ ├── contact_flows.tf │ ├── lambda-function │ │ └── index.js │ ├── lambda_associations.tf │ ├── main.tf │ ├── outputs.tf │ ├── quick_connects.tf │ ├── routing_security_profiles.tf │ ├── users_groups_structure.tf │ ├── variables.tf │ └── versions.tf ├── hoop_example_test.go ├── instance_storage_kinesis_test.go ├── instance_storage_s3_test.go ├── lex_bot_association_example_test.go ├── queue_example_test.go └── simple_example_test.go ├── variables.tf └── versions.tf /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | # Uses editorconfig to maintain consistent coding styles 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | 7 | # Unix-style newlines with a newline ending every file 8 | [*] 9 | charset = utf-8 10 | end_of_line = lf 11 | indent_size = 2 12 | indent_style = space 13 | insert_final_newline = true 14 | max_line_length = 80 15 | trim_trailing_whitespace = true 16 | 17 | [*.md] 18 | max_line_length = 0 19 | trim_trailing_whitespace = false 20 | 21 | [*.go] 22 | indent_style = tab 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | plan.out 3 | plan.out.json 4 | 5 | # Local .terraform directories 6 | .terraform/ 7 | 8 | # .tfstate files 9 | *.tfstate 10 | *.tfstate.* 11 | 12 | # Crash log files 13 | crash.log 14 | 15 | # Exclude all .tfvars files, which are likely to contain sentitive data, such as 16 | # password, private keys, and other secrets. These should not be part of version 17 | # control as they are data points which are potentially sensitive and subject 18 | # to change depending on the environment. 19 | # 20 | *.tfvars 21 | 22 | # Ignore override files as they are usually used to override resources locally and so 23 | # are not checked in 24 | override.tf 25 | override.tf.json 26 | *_override.tf 27 | *_override.tf.json 28 | 29 | # Include override files you do wish to add to version control using negated pattern 30 | # 31 | # !example_override.tf 32 | 33 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 34 | # example: *tfplan* 35 | 36 | # Ignore CLI configuration files 37 | .terraformrc 38 | terraform.rc 39 | .terraform.lock.hcl 40 | 41 | go.mod 42 | go.sum 43 | 44 | .DS_Store 45 | 46 | # Archives 47 | *.zip 48 | -------------------------------------------------------------------------------- /.header.md: -------------------------------------------------------------------------------- 1 | # Amazon Connect Module 2 | 3 | This module can be used to deploy an Amazon Connect instance and all supporting resources, such as Hours of Operation, queues, etc (full list below). It also supports passing in an existing instance ID, and creating supporting resources associated to it. Common deployment examples can be found in the [./examples](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples) directory. 4 | 5 | **NOTE: At this time, due to limitations in the Amazon Connect API certain operations are not supported, such as deleting a queue. If you have created these resources with Terraform, and wish to destroy the instance, you must first remove them from the Terraform state with `terraform state rm`.** 6 | 7 | **Specifically with queues, if you delete them via Terraform and get a duplicate name error when trying to create them again, you will need to rename or import them into the Terraform state.** 8 | 9 | ## Usage 10 | 11 | The example below is the basic usage of this module and will create an Amazon Connect instance. 12 | 13 | ```hcl 14 | module "amazon_connect" { 15 | source = "aws-ia/amazonconnect/aws" 16 | version = "~> 0.0.1" 17 | 18 | instance_identity_management_type = "CONNECT_MANAGED" 19 | instance_inbound_calls_enabled = true 20 | instance_outbound_calls_enabled = true 21 | instance_alias = "my-instance-alias" 22 | } 23 | ``` 24 | 25 | ## Usage Examples 26 | 27 | * [Simple](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/simple/main.tf) 28 | * [Instance w/ S3 Storage Configuration](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/instance-storage-config-s3/main.tf) 29 | * [Instance w/ Kinesis Storage Configuration](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/instance-storage-config-kinesis/main.tf) 30 | * [Instance w/ Hours of Operations](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/hours-of-operations/main.tf) 31 | * [Instance w/ Queue](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/queue/main.tf) 32 | * [Instance w/ Lex Bot Association](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/lex-bot-association/main.tf) 33 | * [Complete](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/complete/main.tf) 34 | 35 | ## Dependent Resources 36 | 37 | Many resources within Amazon Connect have dependencies. A basic example is if you are creating a Queue that depends on an Hour of Operation. If you were not using this module, this would look straightforward: 38 | 39 | Without module: 40 | 41 | ```hcl 42 | resource "aws_connect_hours_of_operation" "example" { ... } 43 | 44 | resource "aws_connect_queue" "example" { 45 | ... 46 | hours_of_operation_id = aws_connect_hours_of_operation.example.hours_of_operation_id 47 | } 48 | ``` 49 | 50 | With this module, you can do the same thing in a single use of the module. It's possible by using the modules outputs as values for its variables/inputs. At first glance, this might not seem intuitive/possible, but since the Terraform plan phase "flattens" everything to resolve the DAG/order of operations for the deployment, it is completely fine. 51 | 52 | With module: 53 | 54 | ```hcl 55 | module "amazon_connect" { 56 | ... 57 | hours_of_operations = { 58 | example = { ... } 59 | } 60 | queues = { 61 | example = { 62 | hours_of_operation_id = try(module.amazon_connect.hours_of_operations["example"].hours_of_operation_id, null) 63 | } 64 | } 65 | } 66 | ``` 67 | 68 | ### Important note for Amazon Connect User Hierarchy Group 69 | 70 | The one place where this is not possible is for User Hierarchy Group resources, which have a circular dependency through `parent_group_id`. In the module, resources are created through a single resource combined with a `for_each` loop. Because of this, it would create a circular reference for Terraform to have one iteration reference itself. Instead, if you need a child/parent group relationship to be created, make a second module call for the parent group. 71 | 72 | ❌ Invalid example: 73 | 74 | ```hcl 75 | module "amazon_connect" { 76 | ... 77 | user_hierarchy_groups = { 78 | parent = { ... } 79 | child = { 80 | parent_group_id = module.amazon_connect.user_hierarchy_groups["parent"].hierarchy_group_id 81 | } 82 | } 83 | } 84 | ``` 85 | 86 | ✔️ Valid example: 87 | 88 | ```hcl 89 | module "amazon_connect" { 90 | ... 91 | user_hierarchy_groups = { 92 | child = { 93 | parent_group_id = try(module.amazon_connect_parent_group.user_hierarchy_groups["parent"].hierarchy_group_id, null) 94 | } 95 | } 96 | } 97 | 98 | module "amazon_connect_parent_group" { 99 | ... 100 | create_instance = false 101 | instance_id = module.amazon_connect.instance.id 102 | user_hierarchy_groups = { 103 | parent = {} 104 | } 105 | } 106 | ``` 107 | 108 | ## Creating/Exporting Contact Flow JSON 109 | 110 | Terraform and the Amazon Connect API expect Contact Flows and Contact Flow Modules to be provided in JSON format. Currently, the easiest way to do that is to first create the Contact Flow in the Amazon Connect management console as desired, and then retrieve the JSON format using the AWS CLI or AWS Tools for PowerShell. 111 | 112 | AWS CLI: 113 | 114 | ```shell 115 | aws connect describe-contact-flow --instance-id --contact-flow-id 116 | aws connect describe-contact-flow-module --instance-id --contact-flow-id 117 | ``` 118 | 119 | AWS Tools for PowerShell 120 | 121 | ```powershell 122 | Get-CONNContactFlow -ContactFlowId -InstanceId 123 | Get-CONNContactFlowModule -ContactFlowId -InstanceId 124 | ``` 125 | 126 | ## Module Outputs 127 | 128 | With the exception of `instance_id`, which returns the Amazon Connect Instance ID that was created or passed in, all outputs of this module return the entire resource, or collection or resources. This methodology allows the consumer of the module to access all resource attributes created, but does require some HCL if you'd like to transform it to a different structure. 129 | 130 | As an example, if you want to get a list of the queue ARNs: 131 | 132 | ```hcl 133 | module "amazon_connect" { ... } 134 | 135 | locals { 136 | queue_arns = [ for k, v in module.amazon_connect.queues : v.arn ] 137 | } 138 | ``` 139 | 140 | ## License 141 | 142 | Apache 2 Licensed. See [LICENSE](./LICENSE) for full details. 143 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "line-length": false, 3 | "no-inline-html": false 4 | } 5 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fail_fast: false 3 | minimum_pre_commit_version: "2.6.0" 4 | repos: 5 | - 6 | repo: https://github.com/aws-ia/pre-commit-configs 7 | # To update run: 8 | # pre-commit autoupdate --freeze 9 | rev: 80ed3f0a164f282afaac0b6aec70e20f7e541932 # frozen: v1.5.0 10 | hooks: 11 | - id: aws-ia-meta-hook 12 | -------------------------------------------------------------------------------- /.terraform-docs.yaml: -------------------------------------------------------------------------------- 1 | formatter: markdown 2 | header-from: .header.md 3 | 4 | settings: 5 | anchor: true 6 | color: true 7 | default: true 8 | escape: true 9 | html: true 10 | indent: 2 11 | required: true 12 | sensitive: true 13 | type: true 14 | lockfile: false 15 | 16 | sort: 17 | enabled: true 18 | by: required 19 | 20 | output: 21 | file: README.md 22 | mode: replace 23 | -------------------------------------------------------------------------------- /.tflint.hcl: -------------------------------------------------------------------------------- 1 | # https://github.com/terraform-linters/tflint/blob/master/docs/user-guide/module-inspection.md 2 | # borrowed & modified indefinitely from https://github.com/ksatirli/building-infrastructure-you-can-mostly-trust/blob/main/.tflint.hcl 3 | 4 | plugin "aws" { 5 | enabled = true 6 | version = "0.14.0" 7 | source = "github.com/terraform-linters/tflint-ruleset-aws" 8 | } 9 | 10 | config { 11 | module = true 12 | force = false 13 | } 14 | 15 | rule "terraform_required_providers" { 16 | enabled = true 17 | } 18 | 19 | rule "terraform_required_version" { 20 | enabled = true 21 | } 22 | 23 | rule "terraform_naming_convention" { 24 | enabled = true 25 | format = "snake_case" 26 | } 27 | 28 | rule "terraform_typed_variables" { 29 | enabled = true 30 | } 31 | 32 | rule "terraform_unused_declarations" { 33 | enabled = true 34 | } 35 | 36 | rule "terraform_comment_syntax" { 37 | enabled = true 38 | } 39 | 40 | rule "terraform_deprecated_index" { 41 | enabled = true 42 | } 43 | 44 | rule "terraform_deprecated_interpolation" { 45 | enabled = true 46 | } 47 | 48 | rule "terraform_documented_outputs" { 49 | enabled = true 50 | } 51 | 52 | rule "terraform_documented_variables" { 53 | enabled = true 54 | } 55 | 56 | rule "terraform_module_pinned_source" { 57 | enabled = true 58 | } 59 | 60 | rule "terraform_standard_module_structure" { 61 | enabled = true 62 | } 63 | 64 | rule "terraform_workspace_remote" { 65 | enabled = true 66 | } 67 | -------------------------------------------------------------------------------- /.tfsec/launch_configuration_imdsv2_tfchecks.json: -------------------------------------------------------------------------------- 1 | { 2 | "checks": [ 3 | { 4 | "code": "CUS002", 5 | "description": "Check to IMDSv2 is required on EC2 instances created by this Launch Template", 6 | "impact": "Instance metadata service can be interacted with freely", 7 | "resolution": "Enable HTTP token requirement for IMDS", 8 | "requiredTypes": [ 9 | "resource" 10 | ], 11 | "requiredLabels": [ 12 | "aws_launch_configuration" 13 | ], 14 | "severity": "CRITICAL", 15 | "matchSpec": { 16 | "action": "isPresent", 17 | "name": "metadata_options", 18 | "subMatch": { 19 | "action": "and", 20 | "predicateMatchSpec": [ 21 | { 22 | "action": "equals", 23 | "name": "http_tokens", 24 | "value": "required" 25 | 26 | } 27 | ] 28 | } 29 | }, 30 | 31 | "errorMessage": "is missing `metadata_options` block - it is required with `http_tokens` set to `required` to make Instance Metadata Service more secure.", 32 | "relatedLinks": [ 33 | "https://tfsec.dev/docs/aws/ec2/enforce-http-token-imds#aws/ec2", 34 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_configuration#metadata-options", 35 | "https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service" 36 | ] 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /.tfsec/launch_template_imdsv2_tfchecks.json: -------------------------------------------------------------------------------- 1 | { 2 | "checks": [ 3 | { 4 | "code": "CUS001", 5 | "description": "Check to IMDSv2 is required on EC2 instances created by this Launch Template", 6 | "impact": "Instance metadata service can be interacted with freely", 7 | "resolution": "Enable HTTP token requirement for IMDS", 8 | "requiredTypes": [ 9 | "resource" 10 | ], 11 | "requiredLabels": [ 12 | "aws_launch_template" 13 | ], 14 | "severity": "CRITICAL", 15 | "matchSpec": { 16 | "action": "isPresent", 17 | "name": "metadata_options", 18 | "subMatch": { 19 | "action": "and", 20 | "predicateMatchSpec": [ 21 | { 22 | "action": "equals", 23 | "name": "http_tokens", 24 | "value": "required" 25 | 26 | } 27 | ] 28 | } 29 | }, 30 | 31 | "errorMessage": "is missing `metadata_options` block - it is required with `http_tokens` set to `required` to make Instance Metadata Service more secure.", 32 | "relatedLinks": [ 33 | "https://tfsec.dev/docs/aws/ec2/enforce-http-token-imds#aws/ec2", 34 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template#metadata-options", 35 | "https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service" 36 | ] 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /.tfsec/no_launch_config_tfchecks.json: -------------------------------------------------------------------------------- 1 | { 2 | "checks": [ 3 | { 4 | "code": "CUS003", 5 | "description": "Use `aws_launch_template` over `aws_launch_configuration", 6 | "impact": "Launch configurations are not capable of versions", 7 | "resolution": "Convert resource type and attributes to `aws_launch_template`", 8 | "requiredTypes": [ 9 | "resource" 10 | ], 11 | "requiredLabels": [ 12 | "aws_launch_configuration" 13 | ], 14 | "severity": "MEDIUM", 15 | "matchSpec": { 16 | "action": "notPresent", 17 | "name": "image_id" 18 | }, 19 | 20 | "errorMessage": "should be changed to `aws_launch_template` since the functionality is the same but templates can be versioned.", 21 | "relatedLinks": [ 22 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template", 23 | "https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service" 24 | ] 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /.tfsec/sg_no_embedded_egress_rules_tfchecks.json: -------------------------------------------------------------------------------- 1 | { 2 | "checks": [ 3 | { 4 | "code": "CUS005", 5 | "description": "Security group rules should be defined with `aws_security_group_rule` instead of embedded.", 6 | "impact": "Embedded security group rules can cause issues during configuration updates.", 7 | "resolution": "Move `egress` rules to `aws_security_group_rule` and attach to `aws_security_group`.", 8 | "requiredTypes": [ 9 | "resource" 10 | ], 11 | "requiredLabels": [ 12 | "aws_security_group" 13 | ], 14 | "severity": "MEDIUM", 15 | "matchSpec": { 16 | "action": "notPresent", 17 | "name": "egress" 18 | }, 19 | 20 | "errorMessage": "`egress` rules should be moved to `aws_security_group_rule` and attached to `aws_security_group` instead of embedded.", 21 | "relatedLinks": [ 22 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule", 23 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group" 24 | ] 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /.tfsec/sg_no_embedded_ingress_rules_tfchecks.json: -------------------------------------------------------------------------------- 1 | { 2 | "checks": [ 3 | { 4 | "code": "CUS004", 5 | "description": "Security group rules should be defined with `aws_security_group_rule` instead of embedded.", 6 | "impact": "Embedded security group rules can cause issues during configuration updates.", 7 | "resolution": "Move `ingress` rules to `aws_security_group_rule` and attach to `aws_security_group`.", 8 | "requiredTypes": [ 9 | "resource" 10 | ], 11 | "requiredLabels": [ 12 | "aws_security_group" 13 | ], 14 | "severity": "MEDIUM", 15 | "matchSpec": { 16 | "action": "notPresent", 17 | "name": "ingress" 18 | }, 19 | 20 | "errorMessage": "`ingress` rules should be moved to `aws_security_group_rule` and attached to `aws_security_group` instead of embedded.", 21 | "relatedLinks": [ 22 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule", 23 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group" 24 | ] 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.json.tftpl": "json" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [1.0.0] - 2022-10-11 9 | 10 | ### Added 11 | 12 | - Initial release 13 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @aws-ia/aws-ia -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016-2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at 4 | 5 | http://aws.amazon.com/apache2.0/ 6 | 7 | or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Amazon Connect Module 3 | 4 | This module can be used to deploy an Amazon Connect instance and all supporting resources, such as Hours of Operation, queues, etc (full list below). It also supports passing in an existing instance ID, and creating supporting resources associated to it. Common deployment examples can be found in the [./examples](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples) directory. 5 | 6 | **NOTE: At this time, due to limitations in the Amazon Connect API certain operations are not supported, such as deleting a queue. If you have created these resources with Terraform, and wish to destroy the instance, you must first remove them from the Terraform state with `terraform state rm`.** 7 | 8 | **Specifically with queues, if you delete them via Terraform and get a duplicate name error when trying to create them again, you will need to rename or import them into the Terraform state.** 9 | 10 | ## Usage 11 | 12 | The example below is the basic usage of this module and will create an Amazon Connect instance. 13 | 14 | ```hcl 15 | module "amazon_connect" { 16 | source = "aws-ia/amazonconnect/aws" 17 | version = "~> 0.0.1" 18 | 19 | instance_identity_management_type = "CONNECT_MANAGED" 20 | instance_inbound_calls_enabled = true 21 | instance_outbound_calls_enabled = true 22 | instance_alias = "my-instance-alias" 23 | } 24 | ``` 25 | 26 | ## Usage Examples 27 | 28 | * [Simple](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/simple/main.tf) 29 | * [Instance w/ S3 Storage Configuration](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/instance-storage-config-s3/main.tf) 30 | * [Instance w/ Kinesis Storage Configuration](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/instance-storage-config-kinesis/main.tf) 31 | * [Instance w/ Hours of Operations](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/hours-of-operations/main.tf) 32 | * [Instance w/ Queue](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/queue/main.tf) 33 | * [Instance w/ Lex Bot Association](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/lex-bot-association/main.tf) 34 | * [Complete](https://github.com/aws-ia/terraform-aws-amazonconnect/tree/main/examples/complete/main.tf) 35 | 36 | ## Dependent Resources 37 | 38 | Many resources within Amazon Connect have dependencies. A basic example is if you are creating a Queue that depends on an Hour of Operation. If you were not using this module, this would look straightforward: 39 | 40 | Without module: 41 | 42 | ```hcl 43 | resource "aws_connect_hours_of_operation" "example" { ... } 44 | 45 | resource "aws_connect_queue" "example" { 46 | ... 47 | hours_of_operation_id = aws_connect_hours_of_operation.example.hours_of_operation_id 48 | } 49 | ``` 50 | 51 | With this module, you can do the same thing in a single use of the module. It's possible by using the modules outputs as values for its variables/inputs. At first glance, this might not seem intuitive/possible, but since the Terraform plan phase "flattens" everything to resolve the DAG/order of operations for the deployment, it is completely fine. 52 | 53 | With module: 54 | 55 | ```hcl 56 | module "amazon_connect" { 57 | ... 58 | hours_of_operations = { 59 | example = { ... } 60 | } 61 | queues = { 62 | example = { 63 | hours_of_operation_id = try(module.amazon_connect.hours_of_operations["example"].hours_of_operation_id, null) 64 | } 65 | } 66 | } 67 | ``` 68 | 69 | ### Important note for Amazon Connect User Hierarchy Group 70 | 71 | The one place where this is not possible is for User Hierarchy Group resources, which have a circular dependency through `parent_group_id`. In the module, resources are created through a single resource combined with a `for_each` loop. Because of this, it would create a circular reference for Terraform to have one iteration reference itself. Instead, if you need a child/parent group relationship to be created, make a second module call for the parent group. 72 | 73 | ❌ Invalid example: 74 | 75 | ```hcl 76 | module "amazon_connect" { 77 | ... 78 | user_hierarchy_groups = { 79 | parent = { ... } 80 | child = { 81 | parent_group_id = module.amazon_connect.user_hierarchy_groups["parent"].hierarchy_group_id 82 | } 83 | } 84 | } 85 | ``` 86 | 87 | ✔️ Valid example: 88 | 89 | ```hcl 90 | module "amazon_connect" { 91 | ... 92 | user_hierarchy_groups = { 93 | child = { 94 | parent_group_id = try(module.amazon_connect_parent_group.user_hierarchy_groups["parent"].hierarchy_group_id, null) 95 | } 96 | } 97 | } 98 | 99 | module "amazon_connect_parent_group" { 100 | ... 101 | create_instance = false 102 | instance_id = module.amazon_connect.instance.id 103 | user_hierarchy_groups = { 104 | parent = {} 105 | } 106 | } 107 | ``` 108 | 109 | ## Creating/Exporting Contact Flow JSON 110 | 111 | Terraform and the Amazon Connect API expect Contact Flows and Contact Flow Modules to be provided in JSON format. Currently, the easiest way to do that is to first create the Contact Flow in the Amazon Connect management console as desired, and then retrieve the JSON format using the AWS CLI or AWS Tools for PowerShell. 112 | 113 | AWS CLI: 114 | 115 | ```shell 116 | aws connect describe-contact-flow --instance-id --contact-flow-id 117 | aws connect describe-contact-flow-module --instance-id --contact-flow-id 118 | ``` 119 | 120 | AWS Tools for PowerShell 121 | 122 | ```powershell 123 | Get-CONNContactFlow -ContactFlowId -InstanceId 124 | Get-CONNContactFlowModule -ContactFlowId -InstanceId 125 | ``` 126 | 127 | ## Module Outputs 128 | 129 | With the exception of `instance_id`, which returns the Amazon Connect Instance ID that was created or passed in, all outputs of this module return the entire resource, or collection or resources. This methodology allows the consumer of the module to access all resource attributes created, but does require some HCL if you'd like to transform it to a different structure. 130 | 131 | As an example, if you want to get a list of the queue ARNs: 132 | 133 | ```hcl 134 | module "amazon_connect" { ... } 135 | 136 | locals { 137 | queue_arns = [ for k, v in module.amazon_connect.queues : v.arn ] 138 | } 139 | ``` 140 | 141 | ## License 142 | 143 | Apache 2 Licensed. See [LICENSE](./LICENSE) for full details. 144 | 145 | ## Requirements 146 | 147 | | Name | Version | 148 | |------|---------| 149 | | [terraform](#requirement\_terraform) | >= 1.2 | 150 | | [aws](#requirement\_aws) | >= 4.26 | 151 | 152 | ## Providers 153 | 154 | | Name | Version | 155 | |------|---------| 156 | | [aws](#provider\_aws) | >= 4.26 | 157 | 158 | ## Modules 159 | 160 | No modules. 161 | 162 | ## Resources 163 | 164 | | Name | Type | 165 | |------|------| 166 | | [aws_connect_bot_association.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_bot_association) | resource | 167 | | [aws_connect_contact_flow.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_contact_flow) | resource | 168 | | [aws_connect_contact_flow_module.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_contact_flow_module) | resource | 169 | | [aws_connect_hours_of_operation.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_hours_of_operation) | resource | 170 | | [aws_connect_instance.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_instance) | resource | 171 | | [aws_connect_instance_storage_config.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_instance_storage_config) | resource | 172 | | [aws_connect_lambda_function_association.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_lambda_function_association) | resource | 173 | | [aws_connect_queue.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_queue) | resource | 174 | | [aws_connect_quick_connect.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_quick_connect) | resource | 175 | | [aws_connect_routing_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_routing_profile) | resource | 176 | | [aws_connect_security_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_security_profile) | resource | 177 | | [aws_connect_user.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_user) | resource | 178 | | [aws_connect_user_hierarchy_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_user_hierarchy_group) | resource | 179 | | [aws_connect_user_hierarchy_structure.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_user_hierarchy_structure) | resource | 180 | | [aws_connect_vocabulary.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_vocabulary) | resource | 181 | 182 | ## Inputs 183 | 184 | | Name | Description | Type | Default | Required | 185 | |------|-------------|------|---------|:--------:| 186 | | [bot\_associations](#input\_bot\_associations) | A map of Amazon Connect Lex Bot Associations.

The key of the map is the Lex Bot `name`. The value is the configuration for that Lex Bot, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_bot_association) (except `name` which is the key, and `instance_id` which is created or passed in).

Example/available options:
{
= {
name = string
lex_region = optional(string)
}
}
| `any` | `{}` | no | 187 | | [contact\_flow\_module\_tags](#input\_contact\_flow\_module\_tags) | Additional tags to add to all Contact Flow Module resources. | `map(string)` | `{}` | no | 188 | | [contact\_flow\_modules](#input\_contact\_flow\_modules) | A map of Amazon Connect Contact Flow Modules.

The key of the map is the Contact Flow Module `name`. The value is the configuration for that Contact Flow, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_contact_flow_module) (except `name` which is the key, and `instance_id` which is created or passed in).

Example/available options:
{
= {
content = optional(string) # one required
content_hash = optional(string) # one required
description = optional(string)
filename = optional(string) # one required
tags = optional(map(string))
}
}
| `any` | `{}` | no | 189 | | [contact\_flow\_tags](#input\_contact\_flow\_tags) | Additional tags to add to all Contact Flow resources. | `map(string)` | `{}` | no | 190 | | [contact\_flows](#input\_contact\_flows) | A map of Amazon Connect Contact Flows.

The key of the map is the Contact Flow `name`. The value is the configuration for that Contact Flow, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_contact_flow) (except `name` which is the key, and `instance_id` which is created or passed in).

Example/available options:
{
= {
content = optional(string) # one required
content_hash = optional(string) # one required
description = optional(string)
filename = optional(string) # one required
tags = optional(map(string))
type = optional(string)
}
}
| `any` | `{}` | no | 191 | | [create\_instance](#input\_create\_instance) | Controls if the aws\_connect\_instance resource should be created. Defaults to true. | `bool` | `true` | no | 192 | | [hours\_of\_operations](#input\_hours\_of\_operations) | A map of Amazon Connect Hours of Operations.

The key of the map is the Hours of Operation `name`. The value is the configuration for that Hours of Operation, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_hours_of_operation) (except `name` which is the key, and `instance_id` which is created or passed in).

Example/available options:
{
= {
config = [
{
day = string
end_time = {
hours = number
minutes = number
}
start_time = {
hours = number
minutes = number
}
}
]
description = optional(string)
tags = optional(map(string))
time_zone = string
}
}
| `any` | `{}` | no | 193 | | [hours\_of\_operations\_tags](#input\_hours\_of\_operations\_tags) | Additional tags to add to all Hours of Operations resources. | `map(string)` | `{}` | no | 194 | | [instance\_alias](#input\_instance\_alias) | Specifies the name of the instance. Required if instance\_directory\_id not specified. | `string` | `null` | no | 195 | | [instance\_auto\_resolve\_best\_voices\_enabled](#input\_instance\_auto\_resolve\_best\_voices\_enabled) | Specifies whether auto resolve best voices is enabled. Defaults to true. | `bool` | `null` | no | 196 | | [instance\_contact\_flow\_logs\_enabled](#input\_instance\_contact\_flow\_logs\_enabled) | Specifies whether contact flow logs are enabled. Defaults to false. | `bool` | `null` | no | 197 | | [instance\_contact\_lens\_enabled](#input\_instance\_contact\_lens\_enabled) | Specifies whether contact lens is enabled. Defaults to true. | `bool` | `null` | no | 198 | | [instance\_directory\_id](#input\_instance\_directory\_id) | The identifier for the directory if instance\_identity\_management\_type is EXISTING\_DIRECTORY. | `string` | `null` | no | 199 | | [instance\_early\_media\_enabled](#input\_instance\_early\_media\_enabled) | Specifies whether early media for outbound calls is enabled. Defaults to true if instance\_outbound\_calls\_enabled is true. | `bool` | `null` | no | 200 | | [instance\_id](#input\_instance\_id) | If create\_instance is set to false, you may still create other resources and pass in an instance ID that was created outside this module. Ignored if create\_instance is true. | `string` | `null` | no | 201 | | [instance\_identity\_management\_type](#input\_instance\_identity\_management\_type) | Specifies the identity management type attached to the instance. Allowed values are: SAML, CONNECT\_MANAGED, EXISTING\_DIRECTORY. | `string` | `null` | no | 202 | | [instance\_inbound\_calls\_enabled](#input\_instance\_inbound\_calls\_enabled) | Specifies whether inbound calls are enabled. | `bool` | `null` | no | 203 | | [instance\_outbound\_calls\_enabled](#input\_instance\_outbound\_calls\_enabled) | Specifies whether outbound calls are enabled. | `bool` | `null` | no | 204 | | [instance\_storage\_configs](#input\_instance\_storage\_configs) | A map of Amazon Connect Instance Storage Configs.

The key of the map is the Instance Storage Config `resource_type`. The value is the configuration for that Instance Storage Config, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_instance_storage_config#storage_config) (except `resource_type` which is the key, and `instance_id` which is created or passed in).

Example/available options:
{
= {
kinesis_firehose_config = optional({
firehose_arn = string
})
kinesis_stream_config = optional({
stream_arn = string
})
kinesis_video_stream_config = optional({
encryption_config = {
encryption_type = string
key_id = string
}
prefix = string
retention_period_hours = number
})
s3_config = optional({
bucket_name = string
bucket_prefix = string
encryption_config = optional({
encryption_type = string
key_id = string
})
})
storage_type = string
}
}
| `any` | `{}` | no | 205 | | [lambda\_function\_associations](#input\_lambda\_function\_associations) | A map of Lambda Function ARNs to associate to the Amazon Connect Instance, the key is a static/arbitrary name and value is the Lambda ARN.

Example/available options:
{
= string
}
| `map(string)` | `{}` | no | 206 | | [queue\_tags](#input\_queue\_tags) | Additional tags to add to all Queue resources. | `map(string)` | `{}` | no | 207 | | [queues](#input\_queues) | A map of Amazon Connect Queues.

The key of the map is the Queue `name`. The value is the configuration for that Queue, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_queue) (except `name` which is the key, and `instance_id which` is created or passed in).

Example/available options:
{
= {
description = optional(string)
hours_of_operation_id = string
max_contacts = optional(number)
outbound_caller_config = optional({
outbound_caller_id_name = optional(string)
outbound_caller_id_number_id = optional(string)
outbound_flow_id = optional(string)
})
quick_connect_ids = optional(list(string))
status = optional(string)
tags = optional(map(string))
}
}
| `any` | `{}` | no | 208 | | [quick\_connect\_tags](#input\_quick\_connect\_tags) | Additional tags to add to all Quick Connect resources. | `map(string)` | `{}` | no | 209 | | [quick\_connects](#input\_quick\_connects) | A map of Amazon Connect Quick Connect.

The key of the map is the Quick Connect `name`. The value is the configuration for that Quick Connect, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_quick_connect) (except `name` which is the key, and `instance_id` which is created or passed in).

Example/available options:
{
= {
description = optional(string)
quick_connect_config = {
quick_connect_type = string
phone_config = optional({
phone_number = string
})
queue_config = optional({
contact_flow_id = string
queue_id = string
})
user_config = optional({
contact_flow_id = string
queue_id = string
})
})
tags = optional(map(string))
}
}
| `any` | `{}` | no | 210 | | [routing\_profile\_tags](#input\_routing\_profile\_tags) | Additional tags to add to all Routing Profile resources. | `map(string)` | `{}` | no | 211 | | [routing\_profiles](#input\_routing\_profiles) | A map of Amazon Connect Routing Profile.

The key of the map is the Routing Profile `name`. The value is the configuration for that Routing Profile, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_routing_profile) (except `name` which is the key, and `instance_id` which is created or passed in).

Example/available options:
{
= {
default_outbound_queue_id = string
description = string
media_concurrencies = [
{
channel = string
concurrency = number
}
]
queue_configs = optional([
{
channel = string
delay = number
priority = number
queue_id = string
}
])
tags = optional(map(string))
}
}
| `any` | `{}` | no | 212 | | [security\_profile\_tags](#input\_security\_profile\_tags) | Additional tags to add to all Security Profile resources. | `map(string)` | `{}` | no | 213 | | [security\_profiles](#input\_security\_profiles) | A map of Amazon Connect Security Profile.

The key of the map is the Security Profile `name`. The value is the configuration for that Security Profile, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_security_profile) (except `name` which is the key, and `instance_id` which is created or passed in).

Example/available options:
{
= {
description = optional(string)
permissions = optional(list(string))
tags = optional(map(string))
}
}
| `any` | `{}` | no | 214 | | [tags](#input\_tags) | A map of tags to add to all resources. | `map(string)` | `{}` | no | 215 | | [user\_hierarchy\_group\_tags](#input\_user\_hierarchy\_group\_tags) | Additional tags to add to all User Hierarchy Group resources. | `map(string)` | `{}` | no | 216 | | [user\_hierarchy\_groups](#input\_user\_hierarchy\_groups) | A map of Amazon Connect User Hierarchy Groups.

The key of the map is the User Hierarchy Group `name`. The value is the configuration for that User, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_user_hierarchy_group) (except `name` which is the key, and `instance_id` which is created or passed in).

Example/available options:
{
= {
parent_group_id = optional(string)
tags = optional(map(string))
}
}
| `any` | `{}` | no | 217 | | [user\_hierarchy\_structure](#input\_user\_hierarchy\_structure) | A map of Amazon Connect User Hierarchy Structure, containing keys for for zero or many levels: `level_one`, `level_two`, `level_three`, `level_four`, and `level_five`. The values are the `name` for that level. See [documentation here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_user_hierarchy_structure).

Example/available options:
{
level_one = string
}
| `map(string)` | `{}` | no | 218 | | [user\_tags](#input\_user\_tags) | Additional tags to add to all User resources. | `map(string)` | `{}` | no | 219 | | [users](#input\_users) | A map of Amazon Connect Users.

The key of the map is the User `name`. The value is the configuration for that User, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_user) (except `name` which is the key, and `instance_id` which is created or passed in).

Example/available options:
{
= {
directory_user_id = optional(string)
hierarchy_group_id = optional(string)
identity_info = optional({
email = optional(string)
first_name = optional(string)
last_name = optional(string)
})
password = optional(string)
phone_config = {
phone_type = string
after_contact_work_time_limit = optional(number)
auto_accept = optional(bool)
desk_phone_number = optional(string)
}
routing_profile_id = string
security_profile_ids = list(string)
tags = optional(map(string))
}
}
| `any` | `{}` | no | 220 | | [vocabularies](#input\_vocabularies) | A map of Amazon Connect Vocabularies.

The key of the map is the Vocabulary `name`. The value is the configuration for that Vocabulary, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_vocabulary) (except `name` which is the key, and `instance_id` which is created or passed in).

Example/available options:
{
= {
content = string
language_code = string
tags = optional(map(string))
}
}
| `any` | `{}` | no | 221 | | [vocabulary\_tags](#input\_vocabulary\_tags) | Additional tags to add to all Vocabulary resources. | `map(string)` | `{}` | no | 222 | 223 | ## Outputs 224 | 225 | | Name | Description | 226 | |------|-------------| 227 | | [bot\_associations](#output\_bot\_associations) | Full output attributes of aws\_connect\_bot\_association resource(s). | 228 | | [contact\_flow\_modules](#output\_contact\_flow\_modules) | Full output attributes of aws\_connect\_contact\_flow\_module resource(s). | 229 | | [contact\_flows](#output\_contact\_flows) | Full output attributes of aws\_connect\_contact\_flow resource(s). | 230 | | [hours\_of\_operations](#output\_hours\_of\_operations) | Full output attributes of aws\_connect\_hours\_of\_operation resource(s). | 231 | | [instance](#output\_instance) | Full output attributes of aws\_connect\_instance resource. | 232 | | [instance\_id](#output\_instance\_id) | Amazon Connect instance ID. If create\_instance = false, var.instance\_id is returned. | 233 | | [instance\_storage\_configs](#output\_instance\_storage\_configs) | Full output attributes of aws\_connect\_instance\_storage\_config resource(s). | 234 | | [lambda\_function\_associations](#output\_lambda\_function\_associations) | Full output attributes of aws\_connect\_lambda\_function\_association resource(s). | 235 | | [queues](#output\_queues) | Full output attributes of aws\_connect\_queue resource(s). | 236 | | [quick\_connects](#output\_quick\_connects) | Full output attributes of aws\_connect\_quick\_connect resource(s). | 237 | | [routing\_profiles](#output\_routing\_profiles) | Full output attributes of aws\_connect\_routing\_profile resource(s). | 238 | | [security\_profiles](#output\_security\_profiles) | Full output attributes of aws\_connect\_security\_profile resource(s). | 239 | | [user\_hierarchy\_groups](#output\_user\_hierarchy\_groups) | Full output attributes of aws\_connect\_user\_hierarchy\_group resource(s). | 240 | | [user\_hierarchy\_structure](#output\_user\_hierarchy\_structure) | Full output attributes of aws\_connect\_user\_hierarchy\_structure resource(s). | 241 | | [users](#output\_users) | Full output attributes of aws\_connect\_user resource(s). | 242 | | [vocabularies](#output\_vocabularies) | Full output attributes of aws\_connect\_vocabulary resource(s). | 243 | -------------------------------------------------------------------------------- /examples/complete/bot_and_lambda_associations.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | bot_associations = { 3 | example = { 4 | name = aws_lex_bot.example.name 5 | } 6 | } 7 | 8 | lambda_function_associations = { 9 | example = aws_lambda_function.example.arn 10 | } 11 | } 12 | 13 | # Lex Bot 14 | resource "aws_lex_bot" "example" { 15 | name = "example" 16 | child_directed = false 17 | process_behavior = "BUILD" 18 | 19 | intent { 20 | intent_name = aws_lex_intent.example.name 21 | intent_version = aws_lex_intent.example.version 22 | } 23 | 24 | abort_statement { 25 | message { 26 | content = "Sorry, I am not able to assist at this time." 27 | content_type = "PlainText" 28 | } 29 | } 30 | 31 | clarification_prompt { 32 | max_attempts = 2 33 | 34 | message { 35 | content = "I didn't understand you, what would you like to do?" 36 | content_type = "PlainText" 37 | } 38 | } 39 | } 40 | 41 | resource "aws_lex_intent" "example" { 42 | name = "example" 43 | 44 | fulfillment_activity { 45 | type = "ReturnIntent" 46 | } 47 | 48 | sample_utterances = [ 49 | "example one", 50 | "example two" 51 | ] 52 | } 53 | 54 | # Lambda Function 55 | resource "aws_lambda_function" "example" { 56 | function_name = "example" 57 | role = aws_iam_role.lambda_function.arn 58 | filename = data.archive_file.lambda_function.output_path 59 | handler = "index.handler" 60 | runtime = "nodejs16.x" 61 | 62 | environment { 63 | variables = { 64 | CONNECT_INSTANCE_ID = try(module.amazon_connect.instance.id, null) 65 | } 66 | } 67 | 68 | tracing_config { 69 | mode = "Active" 70 | } 71 | } 72 | 73 | data "archive_file" "lambda_function" { 74 | type = "zip" 75 | source_file = "${path.module}/lambda-function/index.js" 76 | output_path = "${path.module}/lambda-function/function.zip" 77 | output_file_mode = "0666" 78 | } 79 | 80 | resource "aws_iam_role" "lambda_function" { 81 | assume_role_policy = data.aws_iam_policy_document.lambda_function_assume_role_policy.json 82 | } 83 | 84 | data "aws_iam_policy_document" "lambda_function_assume_role_policy" { 85 | statement { 86 | principals { 87 | type = "Service" 88 | identifiers = ["lambda.amazonaws.com"] 89 | } 90 | 91 | actions = ["sts:AssumeRole"] 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /examples/complete/contact-flows/inbound.json.tftpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2019-10-30", 3 | "StartAction": "7b32763b-3c5c-49a2-9fa5-2328a5aa825a", 4 | "Metadata": { 5 | "entryPointPosition": { 6 | "x": 28.75, 7 | "y": 15 8 | }, 9 | "snapToGrid": false, 10 | "ActionMetadata": { 11 | "2c27f89c-81c0-4110-98c3-db2e2fa15ab7": { 12 | "position": { 13 | "x": 1540, 14 | "y": 324 15 | } 16 | }, 17 | "8cd02574-cbfa-4deb-9dd7-8a7298d6c094": { 18 | "position": { 19 | "x": 1025, 20 | "y": 706 21 | }, 22 | "useDynamic": false 23 | }, 24 | "683156f6-12af-4093-9a12-4fcada9e39a6": { 25 | "position": { 26 | "x": 736, 27 | "y": 514 28 | }, 29 | "useDynamic": false, 30 | "queue": { 31 | "id": "${support_queue_arn}", 32 | "text": "terraform-test-queue-support" 33 | } 34 | }, 35 | "47480c3a-fb85-45d3-bcbd-42ded95b3724": { 36 | "position": { 37 | "x": 1081, 38 | "y": 54 39 | }, 40 | "useDynamic": false 41 | }, 42 | "0ada9849-cd1d-485b-bce4-6e620317c4b1": { 43 | "position": { 44 | "x": 1323, 45 | "y": 307 46 | }, 47 | "useDynamic": false 48 | }, 49 | "2bfec059-0c2c-45c2-bc42-dfed3501ca2e": { 50 | "position": { 51 | "x": 501, 52 | "y": 137 53 | } 54 | }, 55 | "15f177a3-2dff-4c09-a61f-b7588dc7e606": { 56 | "position": { 57 | "x": 765, 58 | "y": 94 59 | }, 60 | "useDynamic": false, 61 | "queue": { 62 | "id": "${sales_queue_arn}", 63 | "text": "terraform-test-queue-sales" 64 | } 65 | }, 66 | "c836969c-bf0b-4208-a694-c21e8f8bac0a": { 67 | "position": { 68 | "x": 261, 69 | "y": 172 70 | }, 71 | "dynamicMetadata": {}, 72 | "useDynamic": false 73 | }, 74 | "7b32763b-3c5c-49a2-9fa5-2328a5aa825a": { 75 | "position": { 76 | "x": 19, 77 | "y": 179 78 | } 79 | }, 80 | "5f7359b1-7293-40a0-9440-d4e4068df41d": { 81 | "position": { 82 | "x": 34, 83 | "y": 413 84 | }, 85 | "conditionMetadata": [ 86 | { 87 | "id": "83a62f6b-ae30-4272-83a7-561283e45d83", 88 | "value": "Sales" 89 | }, 90 | { 91 | "id": "70bf1f47-b4b4-46d5-93fd-2fb6d48c2d97", 92 | "value": "Support" 93 | } 94 | ], 95 | "useDynamic": false, 96 | "dynamicMetadata": {}, 97 | "useLexBotDropdown": true, 98 | "useDynamicLexBotArn": false, 99 | "lexV2BotName": "${lex_bot_name}" 100 | } 101 | } 102 | }, 103 | "Actions": [ 104 | { 105 | "Identifier": "2c27f89c-81c0-4110-98c3-db2e2fa15ab7", 106 | "Type": "DisconnectParticipant", 107 | "Parameters": {}, 108 | "Transitions": {} 109 | }, 110 | { 111 | "Identifier": "8cd02574-cbfa-4deb-9dd7-8a7298d6c094", 112 | "Transitions": { 113 | "NextAction": "0ada9849-cd1d-485b-bce4-6e620317c4b1", 114 | "Errors": [ 115 | { 116 | "NextAction": "0ada9849-cd1d-485b-bce4-6e620317c4b1", 117 | "ErrorType": "NoMatchingError" 118 | }, 119 | { 120 | "NextAction": "2c27f89c-81c0-4110-98c3-db2e2fa15ab7", 121 | "ErrorType": "QueueAtCapacity" 122 | } 123 | ], 124 | "Conditions": [] 125 | }, 126 | "Type": "TransferContactToQueue" 127 | }, 128 | { 129 | "Identifier": "683156f6-12af-4093-9a12-4fcada9e39a6", 130 | "Parameters": { 131 | "QueueId": "${support_queue_arn}" 132 | }, 133 | "Transitions": { 134 | "NextAction": "8cd02574-cbfa-4deb-9dd7-8a7298d6c094", 135 | "Errors": [ 136 | { 137 | "NextAction": "0ada9849-cd1d-485b-bce4-6e620317c4b1", 138 | "ErrorType": "NoMatchingError" 139 | } 140 | ], 141 | "Conditions": [] 142 | }, 143 | "Type": "UpdateContactTargetQueue" 144 | }, 145 | { 146 | "Identifier": "47480c3a-fb85-45d3-bcbd-42ded95b3724", 147 | "Transitions": { 148 | "NextAction": "0ada9849-cd1d-485b-bce4-6e620317c4b1", 149 | "Errors": [ 150 | { 151 | "NextAction": "0ada9849-cd1d-485b-bce4-6e620317c4b1", 152 | "ErrorType": "NoMatchingError" 153 | }, 154 | { 155 | "NextAction": "0ada9849-cd1d-485b-bce4-6e620317c4b1", 156 | "ErrorType": "QueueAtCapacity" 157 | } 158 | ], 159 | "Conditions": [] 160 | }, 161 | "Type": "TransferContactToQueue" 162 | }, 163 | { 164 | "Identifier": "0ada9849-cd1d-485b-bce4-6e620317c4b1", 165 | "Parameters": { 166 | "Text": "We are not able to take your call right now. Goodbye." 167 | }, 168 | "Transitions": { 169 | "NextAction": "2c27f89c-81c0-4110-98c3-db2e2fa15ab7", 170 | "Errors": [], 171 | "Conditions": [] 172 | }, 173 | "Type": "MessageParticipant" 174 | }, 175 | { 176 | "Identifier": "2bfec059-0c2c-45c2-bc42-dfed3501ca2e", 177 | "Parameters": {}, 178 | "Transitions": { 179 | "NextAction": "0ada9849-cd1d-485b-bce4-6e620317c4b1", 180 | "Errors": [ 181 | { 182 | "NextAction": "0ada9849-cd1d-485b-bce4-6e620317c4b1", 183 | "ErrorType": "NoMatchingError" 184 | } 185 | ], 186 | "Conditions": [ 187 | { 188 | "NextAction": "15f177a3-2dff-4c09-a61f-b7588dc7e606", 189 | "Condition": { 190 | "Operator": "Equals", 191 | "Operands": [ 192 | "True" 193 | ] 194 | } 195 | }, 196 | { 197 | "NextAction": "683156f6-12af-4093-9a12-4fcada9e39a6", 198 | "Condition": { 199 | "Operator": "Equals", 200 | "Operands": [ 201 | "False" 202 | ] 203 | } 204 | } 205 | ] 206 | }, 207 | "Type": "CheckHoursOfOperation" 208 | }, 209 | { 210 | "Identifier": "15f177a3-2dff-4c09-a61f-b7588dc7e606", 211 | "Parameters": { 212 | "QueueId": "${sales_queue_arn}" 213 | }, 214 | "Transitions": { 215 | "NextAction": "47480c3a-fb85-45d3-bcbd-42ded95b3724", 216 | "Errors": [ 217 | { 218 | "NextAction": "0ada9849-cd1d-485b-bce4-6e620317c4b1", 219 | "ErrorType": "NoMatchingError" 220 | } 221 | ], 222 | "Conditions": [] 223 | }, 224 | "Type": "UpdateContactTargetQueue" 225 | }, 226 | { 227 | "Identifier": "c836969c-bf0b-4208-a694-c21e8f8bac0a", 228 | "Parameters": { 229 | "LambdaFunctionARN": "${lambda_arn}", 230 | "InvocationTimeLimitSeconds": "3" 231 | }, 232 | "Transitions": { 233 | "NextAction": "2bfec059-0c2c-45c2-bc42-dfed3501ca2e", 234 | "Errors": [ 235 | { 236 | "NextAction": "2bfec059-0c2c-45c2-bc42-dfed3501ca2e", 237 | "ErrorType": "NoMatchingError" 238 | } 239 | ], 240 | "Conditions": [] 241 | }, 242 | "Type": "InvokeLambdaFunction" 243 | }, 244 | { 245 | "Identifier": "7b32763b-3c5c-49a2-9fa5-2328a5aa825a", 246 | "Parameters": { 247 | "FlowLoggingBehavior": "Enabled" 248 | }, 249 | "Transitions": { 250 | "NextAction": "5f7359b1-7293-40a0-9440-d4e4068df41d", 251 | "Errors": [], 252 | "Conditions": [] 253 | }, 254 | "Type": "UpdateFlowLoggingBehavior" 255 | }, 256 | { 257 | "Identifier": "5f7359b1-7293-40a0-9440-d4e4068df41d", 258 | "Parameters": { 259 | "Text": "Welcome to terraform deployed connect instance, how can I help you?", 260 | "LexBot": { 261 | "Name": "IntentandRoute", 262 | "Region": "us-west-2", 263 | "Alias": "$LATEST" 264 | } 265 | }, 266 | "Transitions": { 267 | "NextAction": "683156f6-12af-4093-9a12-4fcada9e39a6", 268 | "Errors": [ 269 | { 270 | "NextAction": "683156f6-12af-4093-9a12-4fcada9e39a6", 271 | "ErrorType": "NoMatchingError" 272 | }, 273 | { 274 | "NextAction": "683156f6-12af-4093-9a12-4fcada9e39a6", 275 | "ErrorType": "NoMatchingCondition" 276 | } 277 | ], 278 | "Conditions": [ 279 | { 280 | "NextAction": "c836969c-bf0b-4208-a694-c21e8f8bac0a", 281 | "Condition": { 282 | "Operator": "Equals", 283 | "Operands": [ 284 | "Sales" 285 | ] 286 | } 287 | }, 288 | { 289 | "NextAction": "683156f6-12af-4093-9a12-4fcada9e39a6", 290 | "Condition": { 291 | "Operator": "Equals", 292 | "Operands": [ 293 | "Support" 294 | ] 295 | } 296 | } 297 | ] 298 | }, 299 | "Type": "ConnectParticipantWithLexBot" 300 | } 301 | ] 302 | } 303 | -------------------------------------------------------------------------------- /examples/complete/contact-flows/quick_connect.json.tftpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2019-10-30", 3 | "StartAction": "bf85bd5c-8cd3-4135-9f1b-30bdb914eeb6", 4 | "Metadata": { 5 | "entryPointPosition": { 6 | "x": 20, 7 | "y": 20 8 | }, 9 | "snapToGrid": false, 10 | "ActionMetadata": { 11 | "8c760cd1-ac5c-43b2-a20b-94787495b8c9": { 12 | "position": { 13 | "x": 966.3333740234375, 14 | "y": 366.3500061035156 15 | } 16 | }, 17 | "84ceaa1a-834e-4c84-a792-e8b08d746343": { 18 | "position": { 19 | "x": 714, 20 | "y": 178 21 | }, 22 | "useDynamic": false 23 | }, 24 | "2762e0a2-18a8-41df-861e-9b26bb32d133": { 25 | "position": { 26 | "x": 701, 27 | "y": 488 28 | }, 29 | "useDynamic": false 30 | }, 31 | "bf85bd5c-8cd3-4135-9f1b-30bdb914eeb6": { 32 | "position": { 33 | "x": 159.33334350585938, 34 | "y": 333.8500061035156 35 | } 36 | }, 37 | "f02bb569-c225-4b2c-9de6-28487c981eaa": { 38 | "position": { 39 | "x": 433.3333435058594, 40 | "y": 201.85000610351562 41 | }, 42 | "useDynamic": false, 43 | "queue": { 44 | "id": "${support_queue_arn}", 45 | "text": "terraform-test-queue-sales" 46 | } 47 | } 48 | } 49 | }, 50 | "Actions": [ 51 | { 52 | "Identifier": "8c760cd1-ac5c-43b2-a20b-94787495b8c9", 53 | "Type": "DisconnectParticipant", 54 | "Parameters": {}, 55 | "Transitions": {} 56 | }, 57 | { 58 | "Identifier": "84ceaa1a-834e-4c84-a792-e8b08d746343", 59 | "Transitions": { 60 | "NextAction": "8c760cd1-ac5c-43b2-a20b-94787495b8c9", 61 | "Errors": [ 62 | { 63 | "NextAction": "8c760cd1-ac5c-43b2-a20b-94787495b8c9", 64 | "ErrorType": "NoMatchingError" 65 | }, 66 | { 67 | "NextAction": "8c760cd1-ac5c-43b2-a20b-94787495b8c9", 68 | "ErrorType": "QueueAtCapacity" 69 | } 70 | ], 71 | "Conditions": [] 72 | }, 73 | "Type": "TransferContactToQueue" 74 | }, 75 | { 76 | "Identifier": "2762e0a2-18a8-41df-861e-9b26bb32d133", 77 | "Parameters": { 78 | "Text": "Unable to set working queue" 79 | }, 80 | "Transitions": { 81 | "NextAction": "8c760cd1-ac5c-43b2-a20b-94787495b8c9", 82 | "Errors": [ 83 | { 84 | "NextAction": "8c760cd1-ac5c-43b2-a20b-94787495b8c9", 85 | "ErrorType": "NoMatchingError" 86 | } 87 | ], 88 | "Conditions": [] 89 | }, 90 | "Type": "MessageParticipant" 91 | }, 92 | { 93 | "Identifier": "bf85bd5c-8cd3-4135-9f1b-30bdb914eeb6", 94 | "Parameters": { 95 | "FlowLoggingBehavior": "Enabled" 96 | }, 97 | "Transitions": { 98 | "NextAction": "f02bb569-c225-4b2c-9de6-28487c981eaa", 99 | "Errors": [], 100 | "Conditions": [] 101 | }, 102 | "Type": "UpdateFlowLoggingBehavior" 103 | }, 104 | { 105 | "Identifier": "f02bb569-c225-4b2c-9de6-28487c981eaa", 106 | "Parameters": { 107 | "QueueId": "${support_queue_arn}" 108 | }, 109 | "Transitions": { 110 | "NextAction": "84ceaa1a-834e-4c84-a792-e8b08d746343", 111 | "Errors": [ 112 | { 113 | "NextAction": "2762e0a2-18a8-41df-861e-9b26bb32d133", 114 | "ErrorType": "NoMatchingError" 115 | } 116 | ], 117 | "Conditions": [] 118 | }, 119 | "Type": "UpdateContactTargetQueue" 120 | } 121 | ] 122 | } 123 | -------------------------------------------------------------------------------- /examples/complete/contact_flows.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | contact_flows = { 3 | inbound = { 4 | content = templatefile( 5 | "${path.module}/contact-flows/inbound.json.tftpl", 6 | { 7 | sales_queue_arn = try(module.amazon_connect.queues["sales"].arn, "") 8 | support_queue_arn = try(module.amazon_connect.queues["support"].arn, "") 9 | lambda_arn = aws_lambda_function.example.arn 10 | lex_bot_name = aws_lex_bot.example.name 11 | } 12 | ) 13 | } 14 | quick_connect = { 15 | type = "QUEUE_TRANSFER" 16 | content = templatefile( 17 | "${path.module}/contact-flows/quick_connect.json.tftpl", 18 | { 19 | support_queue_arn = try(module.amazon_connect.queues["support"].arn, "") 20 | } 21 | ) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/complete/hours_of_operations.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | time_zone = "EST" 3 | sales_weekdays = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"] 4 | support_weekdays = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"] 5 | support_weekends = ["SUNDAY", "SATURDAY"] 6 | 7 | hours_of_operations = { 8 | sales = { 9 | description = "HOOP for Sales" 10 | time_zone = local.time_zone 11 | config = [ 12 | for w in local.sales_weekdays : { 13 | day = w 14 | start_time = { 15 | hours = 8 # 8 AM 16 | minutes = 0 17 | } 18 | end_time = { 19 | hours = 18 # 6 PM 20 | minutes = 0 21 | } 22 | } 23 | ] 24 | } 25 | support = { 26 | description = "HOOP for Support" 27 | time_zone = local.time_zone 28 | config = flatten([ 29 | [ 30 | for w in local.support_weekdays : { 31 | day = w 32 | start_time = { 33 | hours = 8 # 8 AM 34 | minutes = 0 35 | } 36 | end_time = { 37 | hours = 18 # 6 PM 38 | minutes = 0 39 | } 40 | } 41 | ], 42 | flatten([ 43 | for w in local.support_weekends : [ 44 | # Second loop, need two start/end times per day, with break in between 45 | # 9 AM - 12PM, 1 PM - 5 PM 46 | for t in [{ start = 9, end = 12 }, { start = 13, end = 17 }] : { 47 | day = w 48 | start_time = { 49 | hours = t.start 50 | minutes = 0 51 | } 52 | end_time = { 53 | hours = t.end 54 | minutes = 0 55 | } 56 | } 57 | ] 58 | ]) 59 | ]) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/complete/instance_storage_configs.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | instance_storage_configs = { 3 | # S3 4 | CALL_RECORDINGS = { 5 | storage_type = "S3" 6 | 7 | s3_config = { 8 | bucket_name = aws_s3_bucket.example.id 9 | bucket_prefix = "CALL_RECORDINGS" 10 | 11 | encryption_config = { 12 | encryption_type = "KMS" 13 | key_id = aws_kms_key.example.arn 14 | } 15 | } 16 | } 17 | CHAT_TRANSCRIPTS = { 18 | storage_type = "S3" 19 | 20 | s3_config = { 21 | bucket_name = aws_s3_bucket.example.id 22 | bucket_prefix = "CHAT_TRANSCRIPTS" 23 | 24 | encryption_config = { 25 | encryption_type = "KMS" 26 | key_id = aws_kms_key.example.arn 27 | } 28 | } 29 | } 30 | SCHEDULED_REPORTS = { 31 | storage_type = "S3" 32 | 33 | s3_config = { 34 | bucket_name = aws_s3_bucket.example.id 35 | bucket_prefix = "SCHEDULED_REPORTS" 36 | 37 | encryption_config = { 38 | encryption_type = "KMS" 39 | key_id = aws_kms_key.example.arn 40 | } 41 | } 42 | } 43 | 44 | # Kinesis 45 | AGENT_EVENTS = { 46 | storage_type = "KINESIS_STREAM" 47 | 48 | kinesis_stream_config = { 49 | stream_arn = aws_kinesis_stream.example.arn 50 | } 51 | } 52 | CONTACT_TRACE_RECORDS = { 53 | storage_type = "KINESIS_FIREHOSE" 54 | 55 | kinesis_firehose_config = { 56 | firehose_arn = aws_kinesis_firehose_delivery_stream.example.arn 57 | } 58 | } 59 | MEDIA_STREAMS = { 60 | storage_type = "KINESIS_VIDEO_STREAM" 61 | 62 | kinesis_video_stream_config = { 63 | prefix = "MEDIA_STREAMS" 64 | retention_period_hours = 24 65 | 66 | encryption_config = { 67 | encryption_type = "KMS" 68 | key_id = aws_kms_key.example.arn 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | # S3 76 | resource "aws_s3_bucket" "example" { 77 | force_destroy = true 78 | } 79 | 80 | resource "aws_s3_bucket_acl" "example" { 81 | bucket = aws_s3_bucket.example.id 82 | acl = "private" 83 | } 84 | 85 | resource "aws_s3_bucket_logging" "example" { 86 | bucket = aws_s3_bucket.example.id 87 | target_bucket = aws_s3_bucket.logging.id 88 | target_prefix = "${aws_s3_bucket.example.id}/logs/" 89 | } 90 | 91 | resource "aws_s3_bucket_public_access_block" "example" { 92 | bucket = aws_s3_bucket.example.id 93 | block_public_acls = true 94 | block_public_policy = true 95 | ignore_public_acls = true 96 | restrict_public_buckets = true 97 | } 98 | 99 | resource "aws_s3_bucket_server_side_encryption_configuration" "example" { 100 | bucket = aws_s3_bucket.example.id 101 | 102 | rule { 103 | apply_server_side_encryption_by_default { 104 | kms_master_key_id = aws_kms_key.example.arn 105 | sse_algorithm = "aws:kms" 106 | } 107 | } 108 | } 109 | 110 | resource "aws_s3_bucket_versioning" "example" { 111 | bucket = aws_s3_bucket.example.id 112 | 113 | versioning_configuration { 114 | status = "Enabled" 115 | } 116 | } 117 | 118 | # S3 Logging 119 | resource "aws_s3_bucket" "logging" { 120 | force_destroy = true 121 | } 122 | 123 | resource "aws_s3_bucket_acl" "logging" { 124 | bucket = aws_s3_bucket.logging.id 125 | acl = "log-delivery-write" 126 | } 127 | 128 | resource "aws_s3_bucket_public_access_block" "logging" { 129 | bucket = aws_s3_bucket.logging.id 130 | block_public_acls = true 131 | block_public_policy = true 132 | ignore_public_acls = true 133 | restrict_public_buckets = true 134 | } 135 | 136 | resource "aws_s3_bucket_server_side_encryption_configuration" "logging" { 137 | bucket = aws_s3_bucket.logging.id 138 | 139 | rule { 140 | apply_server_side_encryption_by_default { 141 | sse_algorithm = "AES256" 142 | } 143 | } 144 | } 145 | 146 | resource "aws_s3_bucket_versioning" "logging" { 147 | bucket = aws_s3_bucket.logging.id 148 | 149 | versioning_configuration { 150 | status = "Enabled" 151 | } 152 | } 153 | 154 | # Kinesis 155 | resource "aws_kinesis_stream" "example" { 156 | name = module.amazon_connect.instance.id 157 | encryption_type = "KMS" 158 | kms_key_id = aws_kms_key.example.id 159 | 160 | stream_mode_details { 161 | stream_mode = "ON_DEMAND" 162 | } 163 | } 164 | 165 | resource "aws_kinesis_firehose_delivery_stream" "example" { 166 | name = module.amazon_connect.instance.id 167 | destination = "extended_s3" 168 | 169 | server_side_encryption { 170 | enabled = true 171 | key_type = "CUSTOMER_MANAGED_CMK" 172 | key_arn = aws_kms_key.example.arn 173 | } 174 | 175 | extended_s3_configuration { 176 | bucket_arn = aws_s3_bucket.example.arn 177 | prefix = "CONTACT_TRACE_RECORDS/" 178 | role_arn = aws_iam_role.firehose.arn 179 | } 180 | } 181 | 182 | # IAM 183 | resource "aws_iam_role" "firehose" { 184 | assume_role_policy = data.aws_iam_policy_document.firehose_assume_role_policy.json 185 | } 186 | 187 | data "aws_iam_policy_document" "firehose_assume_role_policy" { 188 | statement { 189 | principals { 190 | type = "Service" 191 | identifiers = ["firehose.amazonaws.com"] 192 | } 193 | 194 | actions = ["sts:AssumeRole"] 195 | } 196 | } 197 | 198 | resource "aws_iam_role_policy" "firehose" { 199 | role = aws_iam_role.firehose.id 200 | policy = data.aws_iam_policy_document.firehose_role_policy.json 201 | } 202 | 203 | data "aws_iam_policy_document" "firehose_role_policy" { 204 | statement { 205 | actions = [ 206 | "s3:GetBucket", 207 | "s3:GetBucketAcl", 208 | "s3:ListBucket", 209 | "s3:ListBucketAcl" 210 | ] 211 | resources = [aws_s3_bucket.example.arn] 212 | } 213 | 214 | statement { 215 | actions = [ 216 | "s3:AbortMultipartUpload", 217 | "s3:GetObject", 218 | "s3:GetObjectAcl", 219 | "s3:GetObjectVersion", 220 | "s3:GetObjectVersionAcl", 221 | "s3:ListMultipartUploadParts", 222 | "s3:PutObject", 223 | "s3:PutObjectAcl", 224 | "s3:PutObjectVersionAcl" 225 | ] 226 | resources = ["${aws_s3_bucket.example.arn}/CONTACT_TRACE_RECORDS/*"] 227 | } 228 | } 229 | 230 | 231 | # KMS Key 232 | resource "aws_kms_key" "example" { 233 | policy = data.aws_iam_policy_document.example_key_policy.json 234 | enable_key_rotation = true 235 | } 236 | 237 | data "aws_iam_policy_document" "example_key_policy" { 238 | # Key Administrators 239 | statement { 240 | principals { 241 | type = "AWS" 242 | identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] 243 | } 244 | 245 | actions = ["kms:*"] 246 | resources = ["*"] 247 | } 248 | 249 | # Key Users 250 | statement { 251 | principals { 252 | type = "AWS" 253 | 254 | identifiers = [ 255 | module.amazon_connect.instance.service_role, 256 | aws_iam_role.firehose.arn 257 | ] 258 | } 259 | 260 | actions = [ 261 | "kms:Decrypt", 262 | "kms:DescribeKey", 263 | "kms:Encrypt", 264 | "kms:GenerateDataKey*", 265 | "kms:ReEncrypt*" 266 | ] 267 | resources = ["*"] 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /examples/complete/lambda-function/index.js: -------------------------------------------------------------------------------- 1 | exports.handler = async (event) => { 2 | const response = { 3 | statusCode: 200, 4 | body: JSON.stringify('Hello from Lambda!'), 5 | }; 6 | return response; 7 | }; 8 | -------------------------------------------------------------------------------- /examples/complete/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_caller_identity" "current" {} 2 | 3 | resource "random_string" "instance_alias" { 4 | length = 32 5 | special = false 6 | numeric = false 7 | upper = false 8 | } 9 | 10 | module "amazon_connect" { 11 | # source = "aws-ia/amazonconnect/aws" 12 | # version = ">= 0.0.1" 13 | 14 | source = "../../" 15 | 16 | # Instance 17 | instance_alias = random_string.instance_alias.result 18 | instance_identity_management_type = "CONNECT_MANAGED" 19 | instance_inbound_calls_enabled = true 20 | instance_outbound_calls_enabled = true 21 | instance_contact_flow_logs_enabled = true 22 | 23 | # Instance Storage Configuration 24 | instance_storage_configs = local.instance_storage_configs 25 | 26 | # Hours of Operations 27 | hours_of_operations = local.hours_of_operations 28 | hours_of_operations_tags = { 29 | example = "foo" 30 | } 31 | 32 | # Contact Flows / Modules 33 | contact_flows = local.contact_flows 34 | 35 | # Queues 36 | queues = local.queues 37 | 38 | # Quick Connects 39 | quick_connects = local.quick_connects 40 | 41 | # Routing / Security Profiles 42 | routing_profiles = local.routing_profiles 43 | security_profiles = local.security_profiles 44 | 45 | # Vocabularies 46 | vocabularies = local.vocabularies 47 | 48 | # Lex Bot / Lambda Function Associations 49 | bot_associations = local.bot_associations 50 | lambda_function_associations = local.lambda_function_associations 51 | 52 | # Users / Hierarchy Group / Structure 53 | users = local.users 54 | user_hierarchy_groups = local.user_hierarchy_groups 55 | user_hierarchy_structure = local.user_hierarchy_structure 56 | 57 | # Tags 58 | tags = { 59 | example1 = "bar" 60 | example2 = "baz" 61 | } 62 | } 63 | 64 | module "amazon_connect_parent_group" { 65 | source = "../../" 66 | 67 | create_instance = false 68 | instance_id = module.amazon_connect.instance.id 69 | 70 | # Parent Hierarchy Group 71 | user_hierarchy_groups = { 72 | parent = {} 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /examples/complete/outputs.tf: -------------------------------------------------------------------------------- 1 | output "amazon_connect" { 2 | value = { for k, v in module.amazon_connect : k => v if k != "users" } 3 | } 4 | -------------------------------------------------------------------------------- /examples/complete/queues.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | queues = { 3 | sales = { 4 | hours_of_operation_id = try(module.amazon_connect.hours_of_operations["sales"].hours_of_operation_id, null) 5 | max_contacts = 5 6 | } 7 | support = { 8 | hours_of_operation_id = try(module.amazon_connect.hours_of_operations["support"].hours_of_operation_id, null) 9 | max_contacts = 9 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/complete/quick_connects.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | quick_connects = { 3 | phone_number = { 4 | quick_connect_config = { 5 | quick_connect_type = "PHONE_NUMBER" 6 | 7 | phone_config = { 8 | phone_number = "+18885551212" 9 | } 10 | } 11 | } 12 | queue = { 13 | quick_connect_config = { 14 | quick_connect_type = "QUEUE" 15 | 16 | queue_config = { 17 | contact_flow_id = try(module.amazon_connect.contact_flows["quick_connect"].contact_flow_id, null) 18 | queue_id = try(module.amazon_connect.queues["sales"].queue_id, null) 19 | } 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/complete/routing_security_profiles.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | routing_profiles = { 3 | sales = { 4 | description = "Routing profile for Sales" 5 | default_outbound_queue_id = try(module.amazon_connect.queues["sales"].queue_id, null) 6 | 7 | media_concurrencies = [ 8 | { 9 | channel = "VOICE" 10 | concurrency = 1 // Always 1 for Voice 11 | }, 12 | { 13 | channel = "CHAT" 14 | concurrency = 2 // between 1 and 5 15 | } 16 | ] 17 | 18 | queue_configs = [ 19 | { 20 | channel = "VOICE" 21 | delay = 0 22 | priority = 1 23 | queue_id = try(module.amazon_connect.queues["sales"].queue_id, null) 24 | }, 25 | { 26 | channel = "CHAT" 27 | delay = 0 28 | priority = 1 29 | queue_id = try(module.amazon_connect.queues["sales"].queue_id, null) 30 | } 31 | ] 32 | } 33 | } 34 | 35 | security_profiles = { 36 | example = { 37 | description = "Example security profile" 38 | 39 | permissions = [ 40 | "BasicAgentAccess", 41 | "OutboundCallAccess", 42 | ] 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/complete/users_groups_structure.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | users = { 3 | sales_agent = { 4 | password = "SomeSecurePassword!1234" # Recommended to be passed in through variables if used 5 | hierarchy_group_id = try(module.amazon_connect.user_hierarchy_groups["child"].hierarchy_group_id, null) 6 | routing_profile_id = try(module.amazon_connect.routing_profiles["sales"].routing_profile_id, null) 7 | security_profile_ids = try([module.amazon_connect.security_profiles["example"].security_profile_id], []) 8 | 9 | identity_info = { 10 | email = "sales@example.com" 11 | first_name = "Sales" 12 | last_name = "Agent" 13 | } 14 | 15 | phone_config = { 16 | phone_type = "SOFT_PHONE" 17 | after_contact_work_time_limit = 5 18 | auto_accept = false 19 | } 20 | } 21 | } 22 | 23 | user_hierarchy_groups = { 24 | child = { 25 | parent_group_id = try(module.amazon_connect_parent_group.user_hierarchy_groups["parent"].hierarchy_group_id, null) 26 | } 27 | } 28 | 29 | user_hierarchy_structure = { 30 | level_one = "level-1" 31 | level_two = "level-2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/complete/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.2" 3 | 4 | required_providers { 5 | archive = { 6 | source = "hashicorp/archive" 7 | version = "~> 2.2.0" 8 | } 9 | aws = { 10 | source = "hashicorp/aws" 11 | version = "~> 4.30.0" 12 | } 13 | random = { 14 | source = "hashicorp/random" 15 | version = "~> 3.4.3" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/complete/vocabularies.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | vocabularies = { 3 | example_us = { 4 | content = "Phrase\tIPA\tSoundsLike\tDisplayAs\nLos-Angeles\t\t\tLos Angeles\nF.B.I.\tɛ f b i aɪ\t\tFBI\nEtienne\t\teh-tee-en\t" 5 | language_code = "en-US" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/hours-of-operations/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | time_zone = "EST" 3 | weekdays = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"] 4 | weekends = ["SUNDAY", "SATURDAY"] 5 | } 6 | 7 | resource "random_string" "instance_alias" { 8 | length = 32 9 | special = false 10 | numeric = false 11 | upper = false 12 | } 13 | 14 | module "amazon_connect" { 15 | # source = "aws-ia/amazonconnect/aws" 16 | # version = ">= 0.0.1" 17 | 18 | source = "../../" 19 | 20 | instance_identity_management_type = "CONNECT_MANAGED" 21 | instance_inbound_calls_enabled = true 22 | instance_outbound_calls_enabled = true 23 | instance_alias = random_string.instance_alias.result 24 | 25 | # Hours of Operations 26 | hours_of_operations = { 27 | weekday = { 28 | description = "HOOP for weekdays" 29 | time_zone = local.time_zone 30 | config = [ 31 | for w in local.weekdays : { 32 | day = w 33 | start_time = { 34 | hours = 8 # 8 AM 35 | minutes = 0 36 | } 37 | end_time = { 38 | hours = 18 # 6 PM 39 | minutes = 0 40 | } 41 | } 42 | ] 43 | } 44 | weekend_with_lunch_break = { 45 | description = "HOOP for weekends with a lunch break" 46 | time_zone = local.time_zone 47 | config = flatten([ 48 | for w in local.weekends : [ 49 | # Second loop, need two start/end times per day, with break in between 50 | # 9 AM - 12PM, 1 PM - 5 PM 51 | for t in [{ start = 9, end = 12 }, { start = 13, end = 17 }] : { 52 | day = w 53 | start_time = { 54 | hours = t.start 55 | minutes = 0 56 | } 57 | end_time = { 58 | hours = t.end 59 | minutes = 0 60 | } 61 | } 62 | ] 63 | ]) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /examples/hours-of-operations/outputs.tf: -------------------------------------------------------------------------------- 1 | output "amazon_connect" { 2 | value = { for k, v in module.amazon_connect : k => v if k != "users" } 3 | } 4 | -------------------------------------------------------------------------------- /examples/hours-of-operations/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.2" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.30.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = "~> 3.4.3" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/instance-storage-config-kinesis/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_caller_identity" "current" {} 2 | 3 | resource "random_string" "instance_alias" { 4 | length = 32 5 | special = false 6 | numeric = false 7 | upper = false 8 | } 9 | 10 | module "amazon_connect" { 11 | # source = "aws-ia/amazonconnect/aws" 12 | # version = ">= 0.0.1" 13 | 14 | source = "../../" 15 | 16 | instance_identity_management_type = "CONNECT_MANAGED" 17 | instance_inbound_calls_enabled = true 18 | instance_outbound_calls_enabled = true 19 | instance_alias = random_string.instance_alias.result 20 | 21 | # Instance Storage Configuration 22 | instance_storage_configs = { 23 | MEDIA_STREAMS = { 24 | storage_type = "KINESIS_VIDEO_STREAM" 25 | 26 | kinesis_video_stream_config = { 27 | prefix = "encrypted-media" 28 | retention_period_hours = 24 29 | 30 | encryption_config = { 31 | encryption_type = "KMS" 32 | key_id = aws_kms_key.example.arn 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | # KMS Key 40 | resource "aws_kms_key" "example" { 41 | policy = data.aws_iam_policy_document.example_key_policy.json 42 | enable_key_rotation = true 43 | } 44 | 45 | data "aws_iam_policy_document" "example_key_policy" { 46 | # Key Administrators 47 | statement { 48 | principals { 49 | type = "AWS" 50 | identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] 51 | } 52 | 53 | actions = ["kms:*"] 54 | resources = ["*"] 55 | } 56 | 57 | # Key Users 58 | statement { 59 | principals { 60 | type = "AWS" 61 | identifiers = [module.amazon_connect.instance.service_role] 62 | } 63 | 64 | actions = [ 65 | "kms:Decrypt", 66 | "kms:DescribeKey", 67 | "kms:Encrypt", 68 | "kms:GenerateDataKey*", 69 | "kms:ReEncrypt*" 70 | ] 71 | resources = ["*"] 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/instance-storage-config-kinesis/outputs.tf: -------------------------------------------------------------------------------- 1 | output "amazon_connect" { 2 | value = { for k, v in module.amazon_connect : k => v if k != "users" } 3 | } 4 | -------------------------------------------------------------------------------- /examples/instance-storage-config-kinesis/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.2" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.30.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = "~> 3.4.3" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/instance-storage-config-s3/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_caller_identity" "current" {} 2 | 3 | resource "random_string" "instance_alias" { 4 | length = 32 5 | special = false 6 | numeric = false 7 | upper = false 8 | } 9 | 10 | module "amazon_connect" { 11 | # source = "aws-ia/amazonconnect/aws" 12 | # version = ">= 0.0.1" 13 | 14 | source = "../../" 15 | 16 | instance_identity_management_type = "CONNECT_MANAGED" 17 | instance_inbound_calls_enabled = true 18 | instance_outbound_calls_enabled = true 19 | instance_alias = random_string.instance_alias.result 20 | 21 | # Instance Storage Configuration 22 | instance_storage_configs = { 23 | CALL_RECORDINGS = { 24 | storage_type = "S3" 25 | 26 | s3_config = { 27 | bucket_name = aws_s3_bucket.example.id 28 | bucket_prefix = "CALL_RECORDINGS" 29 | 30 | encryption_config = { 31 | encryption_type = "KMS" 32 | key_id = aws_kms_key.example.arn 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | # S3 40 | resource "aws_s3_bucket" "example" { 41 | force_destroy = true 42 | } 43 | 44 | resource "aws_s3_bucket_acl" "example" { 45 | bucket = aws_s3_bucket.example.id 46 | acl = "private" 47 | } 48 | 49 | resource "aws_s3_bucket_logging" "example" { 50 | bucket = aws_s3_bucket.example.id 51 | target_bucket = aws_s3_bucket.logging.id 52 | target_prefix = "${aws_s3_bucket.example.id}/logs/" 53 | } 54 | 55 | resource "aws_s3_bucket_public_access_block" "example" { 56 | bucket = aws_s3_bucket.example.id 57 | block_public_acls = true 58 | block_public_policy = true 59 | ignore_public_acls = true 60 | restrict_public_buckets = true 61 | } 62 | 63 | resource "aws_s3_bucket_server_side_encryption_configuration" "example" { 64 | bucket = aws_s3_bucket.example.id 65 | 66 | rule { 67 | apply_server_side_encryption_by_default { 68 | kms_master_key_id = aws_kms_key.example.arn 69 | sse_algorithm = "aws:kms" 70 | } 71 | } 72 | } 73 | 74 | resource "aws_s3_bucket_versioning" "example" { 75 | bucket = aws_s3_bucket.example.id 76 | 77 | versioning_configuration { 78 | status = "Enabled" 79 | } 80 | } 81 | 82 | # S3 Logging 83 | resource "aws_s3_bucket" "logging" { 84 | force_destroy = true 85 | } 86 | 87 | resource "aws_s3_bucket_acl" "logging" { 88 | bucket = aws_s3_bucket.logging.id 89 | acl = "log-delivery-write" 90 | } 91 | 92 | resource "aws_s3_bucket_public_access_block" "logging" { 93 | bucket = aws_s3_bucket.logging.id 94 | block_public_acls = true 95 | block_public_policy = true 96 | ignore_public_acls = true 97 | restrict_public_buckets = true 98 | } 99 | 100 | resource "aws_s3_bucket_server_side_encryption_configuration" "logging" { 101 | bucket = aws_s3_bucket.logging.id 102 | 103 | rule { 104 | apply_server_side_encryption_by_default { 105 | sse_algorithm = "AES256" 106 | } 107 | } 108 | } 109 | 110 | resource "aws_s3_bucket_versioning" "logging" { 111 | bucket = aws_s3_bucket.logging.id 112 | 113 | versioning_configuration { 114 | status = "Enabled" 115 | } 116 | } 117 | 118 | # KMS Key 119 | resource "aws_kms_key" "example" { 120 | policy = data.aws_iam_policy_document.example_key_policy.json 121 | enable_key_rotation = true 122 | } 123 | 124 | data "aws_iam_policy_document" "example_key_policy" { 125 | # Key Administrators 126 | statement { 127 | principals { 128 | type = "AWS" 129 | identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] 130 | } 131 | 132 | actions = ["kms:*"] 133 | resources = ["*"] 134 | } 135 | 136 | # Key Users 137 | statement { 138 | principals { 139 | type = "AWS" 140 | identifiers = [module.amazon_connect.instance.service_role] 141 | } 142 | 143 | actions = [ 144 | "kms:Decrypt", 145 | "kms:DescribeKey", 146 | "kms:Encrypt", 147 | "kms:GenerateDataKey*", 148 | "kms:ReEncrypt*" 149 | ] 150 | resources = ["*"] 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /examples/instance-storage-config-s3/outputs.tf: -------------------------------------------------------------------------------- 1 | output "amazon_connect" { 2 | value = { for k, v in module.amazon_connect : k => v if k != "users" } 3 | } 4 | -------------------------------------------------------------------------------- /examples/instance-storage-config-s3/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.2" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.30.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = "~> 3.4.3" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/lex-bot-association/main.tf: -------------------------------------------------------------------------------- 1 | resource "random_string" "instance_alias" { 2 | length = 32 3 | special = false 4 | numeric = false 5 | upper = false 6 | } 7 | 8 | module "amazon_connect" { 9 | # source = "aws-ia/amazonconnect/aws" 10 | # version = ">= 0.0.1" 11 | 12 | source = "../../" 13 | 14 | instance_identity_management_type = "CONNECT_MANAGED" 15 | instance_inbound_calls_enabled = true 16 | instance_outbound_calls_enabled = true 17 | instance_alias = random_string.instance_alias.result 18 | 19 | # Lex Bot Associations 20 | bot_associations = { 21 | example = { 22 | name = aws_lex_bot.example.name 23 | } 24 | } 25 | } 26 | 27 | # Lex Bot 28 | resource "aws_lex_bot" "example" { 29 | name = "example" 30 | child_directed = false 31 | process_behavior = "BUILD" 32 | 33 | intent { 34 | intent_name = aws_lex_intent.example.name 35 | intent_version = aws_lex_intent.example.version 36 | } 37 | 38 | abort_statement { 39 | message { 40 | content = "Sorry, I am not able to assist at this time." 41 | content_type = "PlainText" 42 | } 43 | } 44 | 45 | clarification_prompt { 46 | max_attempts = 2 47 | 48 | message { 49 | content = "I didn't understand you, what would you like to do?" 50 | content_type = "PlainText" 51 | } 52 | } 53 | } 54 | 55 | resource "aws_lex_intent" "example" { 56 | name = "example" 57 | 58 | fulfillment_activity { 59 | type = "ReturnIntent" 60 | } 61 | 62 | sample_utterances = [ 63 | "example one", 64 | "example two" 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /examples/lex-bot-association/outputs.tf: -------------------------------------------------------------------------------- 1 | output "amazon_connect" { 2 | value = { for k, v in module.amazon_connect : k => v if k != "users" } 3 | } 4 | -------------------------------------------------------------------------------- /examples/lex-bot-association/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.2" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.30.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = "~> 3.4.3" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/queue/main.tf: -------------------------------------------------------------------------------- 1 | resource "random_string" "instance_alias" { 2 | length = 32 3 | special = false 4 | numeric = false 5 | upper = false 6 | } 7 | 8 | module "amazon_connect" { 9 | # source = "aws-ia/amazonconnect/aws" 10 | # version = ">= 0.0.1" 11 | 12 | source = "../../" 13 | 14 | instance_identity_management_type = "CONNECT_MANAGED" 15 | instance_inbound_calls_enabled = true 16 | instance_outbound_calls_enabled = true 17 | instance_alias = random_string.instance_alias.result 18 | 19 | # Queues 20 | queues = { 21 | example = { 22 | hours_of_operation_id = try(module.amazon_connect.hours_of_operations["weekday"].hours_of_operation_id, null) 23 | max_contacts = 5 24 | } 25 | } 26 | 27 | # Hours of Operations 28 | hours_of_operations = { 29 | weekday = { 30 | description = "HOOP for weekdays" 31 | time_zone = "EST" 32 | config = [ 33 | for d in ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"] : { 34 | day = d 35 | start_time = { 36 | hours = 8 # 8 AM 37 | minutes = 0 38 | } 39 | end_time = { 40 | hours = 18 # 6 PM 41 | minutes = 0 42 | } 43 | } 44 | ] 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/queue/outputs.tf: -------------------------------------------------------------------------------- 1 | output "amazon_connect" { 2 | value = { for k, v in module.amazon_connect : k => v if k != "users" } 3 | } 4 | -------------------------------------------------------------------------------- /examples/queue/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.2" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.30.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = "~> 3.4.3" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/simple/main.tf: -------------------------------------------------------------------------------- 1 | resource "random_string" "instance_alias" { 2 | length = 32 3 | special = false 4 | numeric = false 5 | upper = false 6 | } 7 | 8 | module "amazon_connect" { 9 | # source = "aws-ia/amazonconnect/aws" 10 | # version = ">= 0.0.1" 11 | 12 | source = "../../" 13 | 14 | instance_identity_management_type = "CONNECT_MANAGED" 15 | instance_inbound_calls_enabled = true 16 | instance_outbound_calls_enabled = true 17 | instance_alias = random_string.instance_alias.result 18 | } 19 | -------------------------------------------------------------------------------- /examples/simple/outputs.tf: -------------------------------------------------------------------------------- 1 | output "amazon_connect" { 2 | value = { for k, v in module.amazon_connect : k => v if k != "users" } 3 | } 4 | -------------------------------------------------------------------------------- /examples/simple/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.2" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.30.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = "~> 3.4.3" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | locals { 5 | instance_id = var.create_instance ? aws_connect_instance.this[0].id : var.instance_id 6 | } 7 | 8 | ################################################################################ 9 | # Instance 10 | ################################################################################ 11 | resource "aws_connect_instance" "this" { 12 | count = var.create_instance ? 1 : 0 13 | 14 | # required 15 | identity_management_type = var.instance_identity_management_type 16 | inbound_calls_enabled = var.instance_inbound_calls_enabled 17 | outbound_calls_enabled = var.instance_outbound_calls_enabled 18 | 19 | # optional 20 | auto_resolve_best_voices_enabled = var.instance_auto_resolve_best_voices_enabled 21 | contact_flow_logs_enabled = var.instance_contact_flow_logs_enabled 22 | contact_lens_enabled = var.instance_contact_lens_enabled 23 | directory_id = var.instance_directory_id 24 | early_media_enabled = var.instance_early_media_enabled 25 | instance_alias = var.instance_alias 26 | 27 | lifecycle { 28 | precondition { 29 | condition = var.create_instance ? var.instance_identity_management_type != null : true 30 | error_message = "When create_instance is true, instance_identity_management_type is required." 31 | } 32 | 33 | precondition { 34 | condition = var.create_instance ? var.instance_inbound_calls_enabled != null : true 35 | error_message = "When create_instance is true, instance_inbound_calls_enabled is required." 36 | } 37 | 38 | precondition { 39 | condition = var.create_instance ? var.instance_outbound_calls_enabled != null : true 40 | error_message = "When create_instance is true, instance_outbound_calls_enabled is required." 41 | } 42 | } 43 | } 44 | 45 | ################################################################################ 46 | # Instance Storage Config 47 | ################################################################################ 48 | resource "aws_connect_instance_storage_config" "this" { 49 | for_each = var.instance_storage_configs 50 | 51 | # required 52 | instance_id = local.instance_id 53 | resource_type = each.key 54 | 55 | storage_config { 56 | storage_type = each.value.storage_type 57 | 58 | # optional 59 | dynamic "kinesis_firehose_config" { 60 | for_each = contains(keys(each.value), "kinesis_firehose_config") ? [1] : [] 61 | 62 | content { 63 | firehose_arn = each.value.kinesis_firehose_config.firehose_arn 64 | } 65 | } 66 | 67 | dynamic "kinesis_stream_config" { 68 | for_each = contains(keys(each.value), "kinesis_stream_config") ? [1] : [] 69 | 70 | content { 71 | stream_arn = each.value.kinesis_stream_config.stream_arn 72 | } 73 | } 74 | 75 | dynamic "kinesis_video_stream_config" { 76 | for_each = contains(keys(each.value), "kinesis_video_stream_config") ? [1] : [] 77 | 78 | content { 79 | encryption_config { 80 | encryption_type = each.value.kinesis_video_stream_config.encryption_config.encryption_type 81 | key_id = each.value.kinesis_video_stream_config.encryption_config.key_id 82 | } 83 | 84 | prefix = each.value.kinesis_video_stream_config.prefix 85 | retention_period_hours = each.value.kinesis_video_stream_config.retention_period_hours 86 | } 87 | } 88 | 89 | dynamic "s3_config" { 90 | for_each = contains(keys(each.value), "s3_config") ? [1] : [] 91 | 92 | content { 93 | bucket_name = each.value.s3_config.bucket_name 94 | bucket_prefix = each.value.s3_config.bucket_prefix 95 | 96 | dynamic "encryption_config" { 97 | for_each = contains(keys(each.value.s3_config), "encryption_config") ? [1] : [] 98 | 99 | content { 100 | encryption_type = each.value.s3_config.encryption_config.encryption_type 101 | key_id = each.value.s3_config.encryption_config.key_id 102 | } 103 | } 104 | } 105 | } 106 | } 107 | } 108 | 109 | ################################################################################ 110 | # Hours of Operation 111 | ################################################################################ 112 | resource "aws_connect_hours_of_operation" "this" { 113 | for_each = var.hours_of_operations 114 | 115 | # required 116 | dynamic "config" { 117 | for_each = each.value.config 118 | 119 | content { 120 | day = config.value.day 121 | 122 | end_time { 123 | hours = config.value.end_time.hours 124 | minutes = config.value.end_time.minutes 125 | } 126 | 127 | start_time { 128 | hours = config.value.start_time.hours 129 | minutes = config.value.start_time.minutes 130 | } 131 | } 132 | } 133 | 134 | instance_id = local.instance_id 135 | name = each.key 136 | time_zone = each.value.time_zone 137 | 138 | # optional 139 | description = try(each.value.description, null) 140 | 141 | # tags 142 | tags = merge( 143 | { Name = each.key }, 144 | var.tags, 145 | var.hours_of_operations_tags, 146 | try(each.value.tags, {}) 147 | ) 148 | } 149 | 150 | ################################################################################ 151 | # Contact Flow / Module 152 | ################################################################################ 153 | resource "aws_connect_contact_flow" "this" { 154 | for_each = var.contact_flows 155 | 156 | # required 157 | instance_id = local.instance_id 158 | name = each.key 159 | 160 | # content (one required) 161 | content = try(each.value.content, null) 162 | content_hash = try(each.value.content_hash, null) 163 | filename = try(each.value.filename, null) 164 | 165 | # optional 166 | description = try(each.value.description, null) 167 | type = try(each.value.type, null) 168 | 169 | # tags 170 | tags = merge( 171 | { Name = each.key }, 172 | var.tags, 173 | var.contact_flow_tags, 174 | try(each.value.tags, {}) 175 | ) 176 | } 177 | 178 | resource "aws_connect_contact_flow_module" "this" { 179 | for_each = var.contact_flow_modules 180 | 181 | # required 182 | instance_id = local.instance_id 183 | name = each.key 184 | 185 | # content (one required) 186 | content = try(each.value.content, null) 187 | content_hash = try(each.value.content_hash, null) 188 | filename = try(each.value.filename, null) 189 | 190 | # optional 191 | description = try(each.value.description, null) 192 | 193 | # tags 194 | tags = merge( 195 | { Name = each.key }, 196 | var.tags, 197 | var.contact_flow_module_tags, 198 | try(each.value.tags, {}) 199 | ) 200 | } 201 | 202 | ################################################################################ 203 | # Queue 204 | ################################################################################ 205 | resource "aws_connect_queue" "this" { 206 | for_each = var.queues 207 | 208 | # required 209 | hours_of_operation_id = each.value.hours_of_operation_id 210 | instance_id = local.instance_id 211 | name = each.key 212 | 213 | # optional 214 | description = try(each.value.description, null) 215 | max_contacts = try(each.value.max_contacts, null) 216 | 217 | dynamic "outbound_caller_config" { 218 | for_each = contains(keys(each.value), "outbound_caller_config") ? [1] : [] 219 | 220 | content { 221 | outbound_caller_id_name = try(each.value.outbound_caller_config.outbound_caller_id_name, null) 222 | outbound_caller_id_number_id = try(each.value.outbound_caller_config.outbound_caller_id_number_id, null) 223 | outbound_flow_id = try(each.value.outbound_caller_config.outbound_flow_id, null) 224 | } 225 | } 226 | 227 | quick_connect_ids = try(each.value.quick_connect_ids, null) 228 | status = try(each.value.status, null) 229 | 230 | # tags 231 | tags = merge( 232 | { Name = each.key }, 233 | var.tags, 234 | var.queue_tags, 235 | try(each.value.tags, {}) 236 | ) 237 | } 238 | 239 | ################################################################################ 240 | # Quick Connect 241 | ################################################################################ 242 | resource "aws_connect_quick_connect" "this" { 243 | for_each = var.quick_connects 244 | 245 | # required 246 | instance_id = local.instance_id 247 | name = each.key 248 | 249 | quick_connect_config { 250 | quick_connect_type = each.value.quick_connect_config.quick_connect_type 251 | 252 | # optional 253 | dynamic "phone_config" { 254 | for_each = contains(keys(each.value.quick_connect_config), "phone_config") ? [1] : [] 255 | 256 | content { 257 | phone_number = each.value.quick_connect_config.phone_config.phone_number 258 | } 259 | } 260 | 261 | dynamic "queue_config" { 262 | for_each = contains(keys(each.value.quick_connect_config), "queue_config") ? [1] : [] 263 | 264 | content { 265 | contact_flow_id = each.value.quick_connect_config.queue_config.contact_flow_id 266 | queue_id = each.value.quick_connect_config.queue_config.queue_id 267 | } 268 | } 269 | 270 | dynamic "user_config" { 271 | for_each = contains(keys(each.value.quick_connect_config), "user_config") ? [1] : [] 272 | 273 | content { 274 | contact_flow_id = each.value.quick_connect_config.user_config.contact_flow_id 275 | user_id = each.value.quick_connect_config.user_config.user_id 276 | } 277 | } 278 | } 279 | 280 | # optional 281 | description = try(each.value.description, null) 282 | 283 | # tags 284 | tags = merge( 285 | { Name = each.key }, 286 | var.tags, 287 | var.quick_connect_tags, 288 | try(each.value.tags, {}) 289 | ) 290 | } 291 | 292 | ################################################################################ 293 | # Routing / Security Profiles 294 | ################################################################################ 295 | resource "aws_connect_routing_profile" "this" { 296 | for_each = var.routing_profiles 297 | 298 | # required 299 | default_outbound_queue_id = each.value.default_outbound_queue_id 300 | description = each.value.description 301 | instance_id = local.instance_id 302 | 303 | dynamic "media_concurrencies" { 304 | for_each = each.value.media_concurrencies 305 | 306 | content { 307 | channel = media_concurrencies.value.channel 308 | concurrency = media_concurrencies.value.concurrency 309 | } 310 | } 311 | 312 | name = each.key 313 | 314 | # optional 315 | dynamic "queue_configs" { 316 | for_each = contains(keys(each.value), "queue_configs") ? each.value.queue_configs : [] 317 | 318 | content { 319 | channel = queue_configs.value.channel 320 | delay = queue_configs.value.delay 321 | priority = queue_configs.value.priority 322 | queue_id = queue_configs.value.queue_id 323 | } 324 | } 325 | 326 | # tags 327 | tags = merge( 328 | { Name = each.key }, 329 | var.tags, 330 | var.routing_profile_tags, 331 | try(each.value.tags, {}) 332 | ) 333 | } 334 | 335 | resource "aws_connect_security_profile" "this" { 336 | for_each = var.security_profiles 337 | 338 | # required 339 | instance_id = local.instance_id 340 | name = each.key 341 | 342 | # optional 343 | description = try(each.value.description, null) 344 | permissions = try(each.value.permissions, null) 345 | 346 | # tags 347 | tags = merge( 348 | { Name = each.key }, 349 | var.tags, 350 | var.security_profile_tags, 351 | try(each.value.tags, {}) 352 | ) 353 | } 354 | 355 | ################################################################################ 356 | # Vocabulary 357 | ################################################################################ 358 | resource "aws_connect_vocabulary" "this" { 359 | for_each = var.vocabularies 360 | 361 | # required 362 | content = each.value.content 363 | instance_id = local.instance_id 364 | language_code = each.value.language_code 365 | name = each.key 366 | 367 | # tags 368 | tags = merge( 369 | { Name = each.key }, 370 | var.tags, 371 | var.vocabulary_tags, 372 | try(each.value.tags, {}) 373 | ) 374 | } 375 | 376 | ################################################################################ 377 | # Lex Bot / Lambda Function Associations 378 | ################################################################################ 379 | resource "aws_connect_bot_association" "this" { 380 | for_each = var.bot_associations 381 | 382 | # required 383 | instance_id = local.instance_id 384 | 385 | lex_bot { 386 | name = each.value.name 387 | 388 | # optional 389 | lex_region = try(each.value.lex_region, null) 390 | } 391 | } 392 | 393 | resource "aws_connect_lambda_function_association" "this" { 394 | for_each = var.lambda_function_associations 395 | 396 | function_arn = each.value 397 | instance_id = local.instance_id 398 | } 399 | 400 | ################################################################################ 401 | # Users / Hierarchy Group / Structure 402 | ################################################################################ 403 | resource "aws_connect_user" "this" { 404 | for_each = var.users 405 | 406 | # required 407 | instance_id = local.instance_id 408 | name = each.key 409 | 410 | phone_config { 411 | phone_type = each.value.phone_config.phone_type 412 | 413 | # optional 414 | after_contact_work_time_limit = try(each.value.phone_config.after_contact_work_time_limit, null) 415 | auto_accept = try(each.value.phone_config.auto_accept, null) 416 | desk_phone_number = try(each.value.phone_config.desk_phone_number, null) 417 | } 418 | 419 | routing_profile_id = each.value.routing_profile_id 420 | security_profile_ids = each.value.security_profile_ids 421 | 422 | # optional 423 | directory_user_id = try(each.value.directory_user_id, null) 424 | hierarchy_group_id = try(each.value.hierarchy_group_id, null) 425 | 426 | dynamic "identity_info" { 427 | for_each = contains(keys(each.value), "identity_info") ? [1] : [] 428 | 429 | content { 430 | email = try(each.value.identity_info.email, null) 431 | first_name = try(each.value.identity_info.first_name, null) 432 | last_name = try(each.value.identity_info.last_name, null) 433 | } 434 | } 435 | 436 | password = try(each.value.password, null) 437 | 438 | # tags 439 | tags = merge( 440 | { Name = each.key }, 441 | var.tags, 442 | var.user_tags, 443 | try(each.value.tags, {}) 444 | ) 445 | } 446 | 447 | resource "aws_connect_user_hierarchy_group" "this" { 448 | for_each = var.user_hierarchy_groups 449 | 450 | # required 451 | instance_id = local.instance_id 452 | name = each.key 453 | 454 | # optional 455 | parent_group_id = try(each.value.parent_group_id, null) 456 | 457 | # tags 458 | tags = merge( 459 | { Name = each.key }, 460 | var.tags, 461 | var.user_hierarchy_group_tags, 462 | try(each.value.tags, {}) 463 | ) 464 | } 465 | 466 | resource "aws_connect_user_hierarchy_structure" "this" { 467 | count = length(keys(var.user_hierarchy_structure)) > 0 ? 1 : 0 468 | 469 | # required 470 | instance_id = local.instance_id 471 | 472 | hierarchy_structure { 473 | dynamic "level_one" { 474 | for_each = contains(keys(var.user_hierarchy_structure), "level_one") ? [1] : [] 475 | 476 | content { 477 | name = var.user_hierarchy_structure.level_one 478 | } 479 | } 480 | 481 | # optional 482 | dynamic "level_two" { 483 | for_each = contains(keys(var.user_hierarchy_structure), "level_two") ? [1] : [] 484 | 485 | content { 486 | name = var.user_hierarchy_structure.level_two 487 | } 488 | } 489 | 490 | dynamic "level_three" { 491 | for_each = contains(keys(var.user_hierarchy_structure), "level_three") ? [1] : [] 492 | 493 | content { 494 | name = var.user_hierarchy_structure.level_three 495 | } 496 | } 497 | 498 | dynamic "level_four" { 499 | for_each = contains(keys(var.user_hierarchy_structure), "level_four") ? [1] : [] 500 | 501 | content { 502 | name = var.user_hierarchy_structure.level_four 503 | } 504 | } 505 | 506 | dynamic "level_five" { 507 | for_each = contains(keys(var.user_hierarchy_structure), "level_five") ? [1] : [] 508 | 509 | content { 510 | name = var.user_hierarchy_structure.level_five 511 | } 512 | } 513 | } 514 | } 515 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | ################################################################################ 5 | # Instance 6 | ################################################################################ 7 | output "instance" { 8 | description = "Full output attributes of aws_connect_instance resource." 9 | value = one(aws_connect_instance.this[*]) 10 | } 11 | 12 | output "instance_id" { 13 | description = "Amazon Connect instance ID. If create_instance = false, var.instance_id is returned." 14 | value = local.instance_id 15 | } 16 | 17 | ################################################################################ 18 | # Instance Storage Config 19 | ################################################################################ 20 | output "instance_storage_configs" { 21 | description = "Full output attributes of aws_connect_instance_storage_config resource(s)." 22 | value = aws_connect_instance_storage_config.this 23 | } 24 | 25 | ################################################################################ 26 | # Hours of Operation 27 | ################################################################################ 28 | output "hours_of_operations" { 29 | description = "Full output attributes of aws_connect_hours_of_operation resource(s)." 30 | value = aws_connect_hours_of_operation.this 31 | } 32 | 33 | ################################################################################ 34 | # Contact Flow / Module 35 | ################################################################################ 36 | output "contact_flows" { 37 | description = "Full output attributes of aws_connect_contact_flow resource(s)." 38 | value = aws_connect_contact_flow.this 39 | } 40 | 41 | output "contact_flow_modules" { 42 | description = "Full output attributes of aws_connect_contact_flow_module resource(s)." 43 | value = aws_connect_contact_flow_module.this 44 | } 45 | 46 | ################################################################################ 47 | # Queue 48 | ################################################################################ 49 | output "queues" { 50 | description = "Full output attributes of aws_connect_queue resource(s)." 51 | value = aws_connect_queue.this 52 | } 53 | 54 | ################################################################################ 55 | # Quick Connect 56 | ################################################################################ 57 | output "quick_connects" { 58 | description = "Full output attributes of aws_connect_quick_connect resource(s)." 59 | value = aws_connect_quick_connect.this 60 | } 61 | 62 | ################################################################################ 63 | # Routing / Security Profiles 64 | ################################################################################ 65 | output "routing_profiles" { 66 | description = "Full output attributes of aws_connect_routing_profile resource(s)." 67 | value = aws_connect_routing_profile.this 68 | } 69 | 70 | output "security_profiles" { 71 | description = "Full output attributes of aws_connect_security_profile resource(s)." 72 | value = aws_connect_security_profile.this 73 | } 74 | 75 | ################################################################################ 76 | # Vocabulary 77 | ################################################################################ 78 | output "vocabularies" { 79 | description = "Full output attributes of aws_connect_vocabulary resource(s)." 80 | value = aws_connect_vocabulary.this 81 | } 82 | 83 | ################################################################################ 84 | # Lex Bot / Lambda Function Associations 85 | ################################################################################ 86 | output "bot_associations" { 87 | description = "Full output attributes of aws_connect_bot_association resource(s)." 88 | value = aws_connect_bot_association.this 89 | } 90 | 91 | output "lambda_function_associations" { 92 | description = "Full output attributes of aws_connect_lambda_function_association resource(s)." 93 | value = aws_connect_lambda_function_association.this 94 | } 95 | 96 | ################################################################################ 97 | # Users / Hierarchy Group / Structure 98 | ################################################################################ 99 | output "users" { 100 | description = "Full output attributes of aws_connect_user resource(s)." 101 | value = aws_connect_user.this 102 | sensitive = true 103 | } 104 | 105 | output "user_hierarchy_groups" { 106 | description = "Full output attributes of aws_connect_user_hierarchy_group resource(s)." 107 | value = aws_connect_user_hierarchy_group.this 108 | } 109 | 110 | output "user_hierarchy_structure" { 111 | description = "Full output attributes of aws_connect_user_hierarchy_structure resource(s)." 112 | value = one(aws_connect_user_hierarchy_structure.this[*]) 113 | } 114 | -------------------------------------------------------------------------------- /test/complete_connect_feature_test.go: -------------------------------------------------------------------------------- 1 | // The Go test file which tests all the Amazon connect features not covered in the examples 2 | 3 | package test 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/gruntwork-io/terratest/modules/terraform" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestCompleteConnectAdditionalFeature(t *testing.T) { 13 | // sut 14 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ 15 | TerraformDir: "connect_additional_feature_test/", 16 | }) 17 | 18 | defer terraform.Destroy(t, terraformOptions) 19 | terraform.InitAndApply(t, terraformOptions) 20 | terraform.ApplyAndIdempotent(t, terraformOptions) 21 | 22 | // assertions 23 | moduleOutputs := terraform.OutputMapOfObjects(t, terraformOptions, "amazon_connect") 24 | 25 | // contact flow module config 26 | contactflowmoduleconfig := moduleOutputs["contact_flow_modules"].(map[string]interface{}) 27 | contactflowmodulename := contactflowmoduleconfig["inbound"].(map[string]interface{}) 28 | 29 | assert.NotEmpty(t, contactflowmodulename["id"]) 30 | assert.NotEmpty(t, contactflowmodulename["arn"]) 31 | assert.Equal(t, "inbound", contactflowmodulename["name"]) 32 | 33 | // Contact flow config 34 | contactflowconfig := moduleOutputs["contact_flows"].(map[string]interface{}) 35 | contactflowvalue := contactflowconfig["inbound"].(map[string]interface{}) 36 | 37 | assert.Equal(t, "CONTACT_FLOW", contactflowvalue["type"]) 38 | assert.Equal(t, "inbound", contactflowvalue["name"]) 39 | 40 | // Lambda association config 41 | lambdaassociation := moduleOutputs["lambda_function_associations"].(map[string]interface{}) 42 | lambdaassociationname := lambdaassociation["example"].(map[string]interface{}) 43 | 44 | assert.NotEmpty(t, lambdaassociationname["function_arn"]) 45 | assert.NotEmpty(t, lambdaassociationname["id"]) 46 | 47 | // Quick Connect config 48 | quickconnectconfig := moduleOutputs["quick_connects"].(map[string]interface{}) 49 | quickconnectconfigid := quickconnectconfig["phone_number"].(map[string]interface{}) 50 | 51 | assert.NotEmpty(t, quickconnectconfigid["quick_connect_id"]) 52 | 53 | // Routing Profile config 54 | routingprofileconfig := moduleOutputs["routing_profiles"].(map[string]interface{}) 55 | routingprofileconfigid := routingprofileconfig["sales"].(map[string]interface{}) 56 | 57 | assert.Equal(t, "sales", routingprofileconfigid["name"]) 58 | assert.NotEmpty(t, routingprofileconfigid["routing_profile_id"]) 59 | 60 | } 61 | -------------------------------------------------------------------------------- /test/complete_example_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | NOTE: This example is currently not testable. The Connect API currently does not support deleting a Queue. 3 | For this reason, Terraform Destroy will always fail. When resolved, these tests can be written/run. 4 | The Queue example has been manually tested successfully. 5 | */ 6 | 7 | package test 8 | 9 | // import ( 10 | // "testing" 11 | 12 | // "github.com/gruntwork-io/terratest/modules/terraform" 13 | // ) 14 | 15 | // func TestCompleteExample(t *testing.T) { 16 | 17 | // // sut 18 | // terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ 19 | // TerraformDir: "../examples/complete", 20 | // }) 21 | 22 | // defer terraform.Destroy(t, terraformOptions) 23 | // terraform.InitAndApply(t, terraformOptions) 24 | // terraform.ApplyAndIdempotent(t, terraformOptions) 25 | 26 | // // assertions 27 | // moduleOutputs := terraform.OutputMapOfObjects(t, terraformOptions, "amazon_connect") 28 | // } 29 | -------------------------------------------------------------------------------- /test/connect_additional_feature_test/contact-flows/inbound.json.tftpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2019-10-30", 3 | "StartAction": "c94cb722-33fc-4bce-9bb5-589f0bcaaeef", 4 | "Metadata": { 5 | "entryPointPosition": { 6 | "x": 20, 7 | "y": 20 8 | }, 9 | "snapToGrid": false, 10 | "ActionMetadata": { 11 | "c94cb722-33fc-4bce-9bb5-589f0bcaaeef": { 12 | "position": { 13 | "x": 238, 14 | "y": 102 15 | } 16 | }, 17 | "a84352ae-03da-4460-a7ac-8804c4790040": { 18 | "position": { 19 | "x": 585, 20 | "y": 120 21 | }, 22 | "dynamicMetadata": {}, 23 | "useDynamic": false 24 | }, 25 | "c3a2d449-38d5-4ee9-a144-a029f1c8c157": { 26 | "position": { 27 | "x": 939, 28 | "y": 408 29 | }, 30 | "useDynamic": false 31 | }, 32 | "e5716fc3-6c37-4490-b728-0c8d3f8b88c8": { 33 | "position": { 34 | "x": 1555, 35 | "y": 183 36 | } 37 | }, 38 | "1e837f30-776e-427a-9250-1adab51d181c": { 39 | "position": { 40 | "x": 924, 41 | "y": 158 42 | }, 43 | "useDynamic": true 44 | }, 45 | "1e14d5e6-41b0-4c16-bae1-043738245baa": { 46 | "position": { 47 | "x": 1197, 48 | "y": 299 49 | }, 50 | "useDynamic": false 51 | } 52 | } 53 | }, 54 | "Actions": [ 55 | { 56 | "Identifier": "c94cb722-33fc-4bce-9bb5-589f0bcaaeef", 57 | "Parameters": { 58 | "FlowLoggingBehavior": "Enabled" 59 | }, 60 | "Transitions": { 61 | "NextAction": "a84352ae-03da-4460-a7ac-8804c4790040", 62 | "Errors": [], 63 | "Conditions": [] 64 | }, 65 | "Type": "UpdateFlowLoggingBehavior" 66 | }, 67 | { 68 | "Identifier": "a84352ae-03da-4460-a7ac-8804c4790040", 69 | "Parameters": { 70 | "LambdaFunctionARN": "${lambda_arn}", 71 | "InvocationTimeLimitSeconds": "3" 72 | }, 73 | "Transitions": { 74 | "NextAction": "1e837f30-776e-427a-9250-1adab51d181c", 75 | "Errors": [ 76 | { 77 | "NextAction": "c3a2d449-38d5-4ee9-a144-a029f1c8c157", 78 | "ErrorType": "NoMatchingError" 79 | } 80 | ], 81 | "Conditions": [] 82 | }, 83 | "Type": "InvokeLambdaFunction" 84 | }, 85 | { 86 | "Identifier": "c3a2d449-38d5-4ee9-a144-a029f1c8c157", 87 | "Parameters": { 88 | "Text": "There was an error with the return prompt Lambda. Please try again later." 89 | }, 90 | "Transitions": { 91 | "NextAction": "e5716fc3-6c37-4490-b728-0c8d3f8b88c8", 92 | "Errors": [ 93 | { 94 | "NextAction": "1e14d5e6-41b0-4c16-bae1-043738245baa", 95 | "ErrorType": "NoMatchingError" 96 | } 97 | ], 98 | "Conditions": [] 99 | }, 100 | "Type": "MessageParticipant" 101 | }, 102 | { 103 | "Identifier": "e5716fc3-6c37-4490-b728-0c8d3f8b88c8", 104 | "Type": "DisconnectParticipant", 105 | "Parameters": {}, 106 | "Transitions": {} 107 | }, 108 | { 109 | "Identifier": "1e837f30-776e-427a-9250-1adab51d181c", 110 | "Parameters": { 111 | "Text": "$.External.prompt" 112 | }, 113 | "Transitions": { 114 | "NextAction": "e5716fc3-6c37-4490-b728-0c8d3f8b88c8", 115 | "Errors": [ 116 | { 117 | "NextAction": "1e14d5e6-41b0-4c16-bae1-043738245baa", 118 | "ErrorType": "NoMatchingError" 119 | } 120 | ], 121 | "Conditions": [] 122 | }, 123 | "Type": "MessageParticipant" 124 | }, 125 | { 126 | "Identifier": "1e14d5e6-41b0-4c16-bae1-043738245baa", 127 | "Parameters": { 128 | "Text": "There was an error playing the prompt returned by Lambda." 129 | }, 130 | "Transitions": { 131 | "NextAction": "e5716fc3-6c37-4490-b728-0c8d3f8b88c8", 132 | "Errors": [ 133 | { 134 | "NextAction": "e5716fc3-6c37-4490-b728-0c8d3f8b88c8", 135 | "ErrorType": "NoMatchingError" 136 | } 137 | ], 138 | "Conditions": [] 139 | }, 140 | "Type": "MessageParticipant" 141 | } 142 | ] 143 | } -------------------------------------------------------------------------------- /test/connect_additional_feature_test/contact-flows/inboundflow.json.tftpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2019-10-30", 3 | "StartAction": "12345678-1234-1234-1234-123456789012", 4 | "Actions": [ 5 | { 6 | "Identifier": "12345678-1234-1234-1234-123456789012", 7 | "Parameters": { 8 | "Text": "Hello contact flow module" 9 | }, 10 | "Transitions": { 11 | "NextAction": "abcdef-abcd-abcd-abcd-abcdefghijkl", 12 | "Errors": [], 13 | "Conditions": [] 14 | }, 15 | "Type": "MessageParticipant" 16 | }, 17 | { 18 | "Identifier": "abcdef-abcd-abcd-abcd-abcdefghijkl", 19 | "Type": "DisconnectParticipant", 20 | "Parameters": {}, 21 | "Transitions": {} 22 | } 23 | ], 24 | "Settings": { 25 | "InputParameters": [], 26 | "OutputParameters": [], 27 | "Transitions": [ 28 | { 29 | "DisplayName": "Success", 30 | "ReferenceName": "Success", 31 | "Description": "" 32 | }, 33 | { 34 | "DisplayName": "Error", 35 | "ReferenceName": "Error", 36 | "Description": "" 37 | } 38 | ] 39 | } 40 | } -------------------------------------------------------------------------------- /test/connect_additional_feature_test/contact-flows/quick_connect.json.tftpl: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2019-10-30", 3 | "StartAction": "bf85bd5c-8cd3-4135-9f1b-30bdb914eeb6", 4 | "Metadata": { 5 | "entryPointPosition": { 6 | "x": 20, 7 | "y": 20 8 | }, 9 | "snapToGrid": false, 10 | "ActionMetadata": { 11 | "8c760cd1-ac5c-43b2-a20b-94787495b8c9": { 12 | "position": { 13 | "x": 966.3333740234375, 14 | "y": 366.3500061035156 15 | } 16 | }, 17 | "84ceaa1a-834e-4c84-a792-e8b08d746343": { 18 | "position": { 19 | "x": 714, 20 | "y": 178 21 | }, 22 | "useDynamic": false 23 | }, 24 | "2762e0a2-18a8-41df-861e-9b26bb32d133": { 25 | "position": { 26 | "x": 701, 27 | "y": 488 28 | }, 29 | "useDynamic": false 30 | }, 31 | "bf85bd5c-8cd3-4135-9f1b-30bdb914eeb6": { 32 | "position": { 33 | "x": 159.33334350585938, 34 | "y": 333.8500061035156 35 | } 36 | }, 37 | "f02bb569-c225-4b2c-9de6-28487c981eaa": { 38 | "position": { 39 | "x": 433.3333435058594, 40 | "y": 201.85000610351562 41 | }, 42 | "useDynamic": false, 43 | "queue": { 44 | "id": "${support_queue_arn}", 45 | "text": "terraform-test-queue-sales" 46 | } 47 | } 48 | } 49 | }, 50 | "Actions": [ 51 | { 52 | "Identifier": "8c760cd1-ac5c-43b2-a20b-94787495b8c9", 53 | "Type": "DisconnectParticipant", 54 | "Parameters": {}, 55 | "Transitions": {} 56 | }, 57 | { 58 | "Identifier": "84ceaa1a-834e-4c84-a792-e8b08d746343", 59 | "Transitions": { 60 | "NextAction": "8c760cd1-ac5c-43b2-a20b-94787495b8c9", 61 | "Errors": [ 62 | { 63 | "NextAction": "8c760cd1-ac5c-43b2-a20b-94787495b8c9", 64 | "ErrorType": "NoMatchingError" 65 | }, 66 | { 67 | "NextAction": "8c760cd1-ac5c-43b2-a20b-94787495b8c9", 68 | "ErrorType": "QueueAtCapacity" 69 | } 70 | ], 71 | "Conditions": [] 72 | }, 73 | "Type": "TransferContactToQueue" 74 | }, 75 | { 76 | "Identifier": "2762e0a2-18a8-41df-861e-9b26bb32d133", 77 | "Parameters": { 78 | "Text": "Unable to set working queue" 79 | }, 80 | "Transitions": { 81 | "NextAction": "8c760cd1-ac5c-43b2-a20b-94787495b8c9", 82 | "Errors": [ 83 | { 84 | "NextAction": "8c760cd1-ac5c-43b2-a20b-94787495b8c9", 85 | "ErrorType": "NoMatchingError" 86 | } 87 | ], 88 | "Conditions": [] 89 | }, 90 | "Type": "MessageParticipant" 91 | }, 92 | { 93 | "Identifier": "bf85bd5c-8cd3-4135-9f1b-30bdb914eeb6", 94 | "Parameters": { 95 | "FlowLoggingBehavior": "Enabled" 96 | }, 97 | "Transitions": { 98 | "NextAction": "f02bb569-c225-4b2c-9de6-28487c981eaa", 99 | "Errors": [], 100 | "Conditions": [] 101 | }, 102 | "Type": "UpdateFlowLoggingBehavior" 103 | }, 104 | { 105 | "Identifier": "f02bb569-c225-4b2c-9de6-28487c981eaa", 106 | "Parameters": { 107 | "QueueId": "${support_queue_arn}" 108 | }, 109 | "Transitions": { 110 | "NextAction": "84ceaa1a-834e-4c84-a792-e8b08d746343", 111 | "Errors": [ 112 | { 113 | "NextAction": "2762e0a2-18a8-41df-861e-9b26bb32d133", 114 | "ErrorType": "NoMatchingError" 115 | } 116 | ], 117 | "Conditions": [] 118 | }, 119 | "Type": "UpdateContactTargetQueue" 120 | } 121 | ] 122 | } -------------------------------------------------------------------------------- /test/connect_additional_feature_test/contact_flows.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | contact_flows = { 3 | inbound = { 4 | content = templatefile( 5 | "${path.module}/contact-flows/inbound.json.tftpl", 6 | { 7 | lambda_arn = aws_lambda_function.example.arn 8 | } 9 | ) 10 | } 11 | } 12 | contact_flow_modules = { 13 | inbound = { 14 | filename = "${path.module}/contact-flows/inboundflow.json.tftpl" 15 | content_hash = filebase64sha256("${path.module}/contact-flows/inboundflow.json.tftpl") 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/connect_additional_feature_test/lambda-function/index.js: -------------------------------------------------------------------------------- 1 | exports.handler = async (event) => { 2 | const response = { 3 | statusCode: 200, 4 | body: JSON.stringify('Hello from Lambda!'), 5 | }; 6 | return response; 7 | }; 8 | -------------------------------------------------------------------------------- /test/connect_additional_feature_test/lambda_associations.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | lambda_function_associations = { 3 | example = aws_lambda_function.example.arn 4 | } 5 | } 6 | 7 | # Lambda Function 8 | resource "aws_lambda_function" "example" { 9 | function_name = "example" 10 | role = aws_iam_role.lambda_function.arn 11 | filename = data.archive_file.lambda_function.output_path 12 | handler = "index.handler" 13 | runtime = "nodejs16.x" 14 | 15 | environment { 16 | variables = { 17 | CONNECT_INSTANCE_ID = try(module.amazon_connect.instance.id, null) 18 | } 19 | } 20 | 21 | tracing_config { 22 | mode = "Active" 23 | } 24 | } 25 | 26 | data "archive_file" "lambda_function" { 27 | type = "zip" 28 | source_file = "${path.module}/lambda-function/index.js" 29 | output_path = "${path.module}/lambda-function/function.zip" 30 | output_file_mode = "0666" 31 | } 32 | 33 | resource "aws_iam_role" "lambda_function" { 34 | assume_role_policy = data.aws_iam_policy_document.lambda_function_assume_role_policy.json 35 | } 36 | 37 | data "aws_iam_policy_document" "lambda_function_assume_role_policy" { 38 | statement { 39 | principals { 40 | type = "Service" 41 | identifiers = ["lambda.amazonaws.com"] 42 | } 43 | 44 | actions = ["sts:AssumeRole"] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/connect_additional_feature_test/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_connect_queue" "example" { 2 | instance_id = module.amazon_connect.instance.id 3 | name = "BasicQueue" 4 | } 5 | 6 | resource "random_string" "instance_alias" { 7 | length = 32 8 | special = false 9 | numeric = false 10 | upper = false 11 | } 12 | 13 | module "amazon_connect" { 14 | # source = "aws-ia/amazonconnect/aws" 15 | # version = ">= 0.0.1" 16 | 17 | source = "../../" 18 | 19 | # Instance 20 | instance_alias = random_string.instance_alias.result 21 | instance_identity_management_type = "CONNECT_MANAGED" 22 | instance_inbound_calls_enabled = true 23 | instance_outbound_calls_enabled = true 24 | instance_contact_flow_logs_enabled = true 25 | 26 | # Contact Flows / Modules 27 | contact_flows = local.contact_flows 28 | contact_flow_modules = local.contact_flow_modules 29 | 30 | # Quick Connects 31 | quick_connects = local.quick_connects 32 | 33 | # Routing / Security Profiles 34 | routing_profiles = local.routing_profiles 35 | security_profiles = local.security_profiles 36 | 37 | # Lambda Function Associations 38 | lambda_function_associations = local.lambda_function_associations 39 | 40 | # Users / Hierarchy Group / Structure 41 | users = local.users 42 | } 43 | -------------------------------------------------------------------------------- /test/connect_additional_feature_test/outputs.tf: -------------------------------------------------------------------------------- 1 | /* 2 | The security profile is excluded from the outputs as go lang was unable to parse the map object output from 3 | security profile as it contains list and was throwing the error "Type switching to map[string]interface{} failed." 4 | */ 5 | 6 | output "amazon_connect" { 7 | value = { for k, v in module.amazon_connect : k => v if !contains(["users", "security_profiles"], k) } 8 | description = "Amazon Connect module outputs" 9 | } 10 | -------------------------------------------------------------------------------- /test/connect_additional_feature_test/quick_connects.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | quick_connects = { 3 | phone_number = { 4 | quick_connect_config = { 5 | quick_connect_type = "PHONE_NUMBER" 6 | 7 | phone_config = { 8 | phone_number = "+18885551212" 9 | } 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/connect_additional_feature_test/routing_security_profiles.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | routing_profiles = { 3 | sales = { 4 | description = "Routing profile for Sales" 5 | default_outbound_queue_id = data.aws_connect_queue.example.queue_id 6 | 7 | media_concurrencies = [ 8 | { 9 | channel = "VOICE" 10 | concurrency = 1 11 | }, 12 | { 13 | channel = "CHAT" 14 | concurrency = 2 15 | } 16 | ] 17 | } 18 | } 19 | 20 | security_profiles = { 21 | example = { 22 | description = "Example security profile" 23 | 24 | permissions = [ 25 | "BasicAgentAccess", 26 | "OutboundCallAccess", 27 | ] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/connect_additional_feature_test/users_groups_structure.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | users = { 3 | sales_agent = { 4 | password = "SomeSecurePassword!1234" # Recommended to be passed in through variables if used 5 | hierarchy_group_id = try(module.amazon_connect.user_hierarchy_groups["child"].hierarchy_group_id, null) 6 | routing_profile_id = try(module.amazon_connect.routing_profiles["sales"].routing_profile_id, null) 7 | security_profile_ids = try([module.amazon_connect.security_profiles["example"].security_profile_id], []) 8 | 9 | identity_info = { 10 | email = "sales@example.com" 11 | first_name = "Sales" 12 | last_name = "Agent" 13 | } 14 | 15 | phone_config = { 16 | phone_type = "SOFT_PHONE" 17 | after_contact_work_time_limit = 5 18 | auto_accept = false 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/connect_additional_feature_test/variables.tf: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/connect_additional_feature_test/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.2" 3 | 4 | required_providers { 5 | archive = { 6 | source = "hashicorp/archive" 7 | version = "~> 2.2.0" 8 | } 9 | aws = { 10 | source = "hashicorp/aws" 11 | version = "~> 4.30.0" 12 | } 13 | random = { 14 | source = "hashicorp/random" 15 | version = "~> 3.4.3" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/hoop_example_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/gruntwork-io/terratest/modules/terraform" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestHoopExample(t *testing.T) { 11 | // sut 12 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ 13 | TerraformDir: "../examples/hours-of-operations", 14 | }) 15 | 16 | defer terraform.Destroy(t, terraformOptions) 17 | terraform.InitAndApply(t, terraformOptions) 18 | terraform.ApplyAndIdempotent(t, terraformOptions) 19 | 20 | // assertions 21 | moduleOutputs := terraform.OutputMapOfObjects(t, terraformOptions, "amazon_connect") 22 | 23 | // instance 24 | instance := moduleOutputs["instance"].(map[string]interface{}) 25 | 26 | assert.NotEmpty(t, instance["id"]) 27 | assert.Equal(t, "CONNECT_MANAGED", instance["identity_management_type"]) 28 | assert.Equal(t, true, instance["inbound_calls_enabled"]) 29 | assert.Equal(t, true, instance["outbound_calls_enabled"]) 30 | assert.Len(t, instance["instance_alias"], 32) 31 | 32 | // hoops 33 | hoops := moduleOutputs["hours_of_operations"].(map[string]interface{}) 34 | 35 | assert.Contains(t, hoops, "weekday") 36 | assert.Contains(t, hoops, "weekend_with_lunch_break") 37 | 38 | weekday := hoops["weekday"].(map[string]interface{}) 39 | weekend := hoops["weekend_with_lunch_break"].(map[string]interface{}) 40 | 41 | assert.Equal(t, "HOOP for weekdays", weekday["description"]) 42 | assert.Equal(t, "EST", weekday["time_zone"]) 43 | assert.Equal(t, 5, len(weekday["config"].([]map[string]interface{}))) 44 | 45 | assert.Equal(t, "HOOP for weekends with a lunch break", weekend["description"]) 46 | assert.Equal(t, "EST", weekend["time_zone"]) 47 | assert.Equal(t, 4, len(weekend["config"].([]map[string]interface{}))) 48 | } 49 | -------------------------------------------------------------------------------- /test/instance_storage_kinesis_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/gruntwork-io/terratest/modules/terraform" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestInstanceStorageKinesisExample(t *testing.T) { 11 | // sut 12 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ 13 | TerraformDir: "../examples/instance-storage-config-kinesis", 14 | }) 15 | 16 | defer terraform.Destroy(t, terraformOptions) 17 | terraform.InitAndApply(t, terraformOptions) 18 | terraform.ApplyAndIdempotent(t, terraformOptions) 19 | 20 | // assertions 21 | moduleOutputs := terraform.OutputMapOfObjects(t, terraformOptions, "amazon_connect") 22 | 23 | // storage config 24 | instanceStorageConfig := moduleOutputs["instance_storage_configs"].(map[string]interface{}) 25 | mediaStreams := instanceStorageConfig["MEDIA_STREAMS"].(map[string]interface{}) 26 | storageConfig := mediaStreams["storage_config"].([]map[string]interface{})[0] 27 | kinesisVideoStreamConfig := storageConfig["kinesis_video_stream_config"].([]map[string]interface{})[0] 28 | encryptionConfig := kinesisVideoStreamConfig["encryption_config"].([]map[string]interface{})[0] 29 | 30 | assert.NotEmpty(t, mediaStreams["association_id"]) 31 | assert.Equal(t, "KINESIS_VIDEO_STREAM", storageConfig["storage_type"]) 32 | assert.Equal(t, "KMS", encryptionConfig["encryption_type"]) 33 | assert.NotEmpty(t, encryptionConfig["key_id"]) 34 | } 35 | -------------------------------------------------------------------------------- /test/instance_storage_s3_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/gruntwork-io/terratest/modules/terraform" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestS3StorageAssociationExample(t *testing.T) { 11 | // sut 12 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ 13 | TerraformDir: "../examples/instance-storage-config-s3", 14 | }) 15 | 16 | defer terraform.Destroy(t, terraformOptions) 17 | terraform.InitAndApply(t, terraformOptions) 18 | terraform.ApplyAndIdempotent(t, terraformOptions) 19 | 20 | // assertions 21 | moduleOutputs := terraform.OutputMapOfObjects(t, terraformOptions, "amazon_connect") 22 | 23 | // storage config 24 | instanceStorageConfig := moduleOutputs["instance_storage_configs"].(map[string]interface{}) 25 | callRecordings := instanceStorageConfig["CALL_RECORDINGS"].(map[string]interface{}) 26 | storageConfig := callRecordings["storage_config"].([]map[string]interface{})[0] 27 | s3Config := storageConfig["s3_config"].([]map[string]interface{})[0] 28 | 29 | assert.NotEmpty(t, callRecordings["association_id"]) 30 | assert.Equal(t, "S3", storageConfig["storage_type"]) 31 | assert.NotEmpty(t, s3Config["bucket_name"]) 32 | } 33 | -------------------------------------------------------------------------------- /test/lex_bot_association_example_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/gruntwork-io/terratest/modules/terraform" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestLexBotAssociationExample(t *testing.T) { 11 | // sut 12 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ 13 | TerraformDir: "../examples/lex-bot-association", 14 | }) 15 | 16 | defer terraform.Destroy(t, terraformOptions) 17 | terraform.InitAndApply(t, terraformOptions) 18 | terraform.ApplyAndIdempotent(t, terraformOptions) 19 | 20 | // assertions 21 | moduleOutputs := terraform.OutputMapOfObjects(t, terraformOptions, "amazon_connect") 22 | 23 | // instance 24 | instance := moduleOutputs["instance"].(map[string]interface{}) 25 | 26 | assert.NotEmpty(t, instance["id"]) 27 | assert.Equal(t, "CONNECT_MANAGED", instance["identity_management_type"]) 28 | assert.Equal(t, true, instance["inbound_calls_enabled"]) 29 | assert.Equal(t, true, instance["outbound_calls_enabled"]) 30 | assert.Len(t, instance["instance_alias"], 32) 31 | 32 | // bot associations 33 | botAssociations := moduleOutputs["bot_associations"].(map[string]interface{}) 34 | botAssociationexample := botAssociations["example"].(map[string]interface{}) 35 | lexBot := botAssociationexample["lex_bot"].([]map[string]interface{})[0] 36 | 37 | assert.NotEmpty(t, botAssociationexample["id"]) 38 | assert.Equal(t, instance["id"], botAssociationexample["instance_id"]) 39 | assert.Equal(t, "example", lexBot["name"]) 40 | } 41 | -------------------------------------------------------------------------------- /test/queue_example_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | NOTE: This example is currently not testable. The Connect API currently does not support deleting a Queue. 3 | For this reason, Terraform Destroy will always fail. When resolved, these tests can be written/run. 4 | The Queue example has been manually tested successfully. 5 | */ 6 | 7 | package test 8 | 9 | // import ( 10 | // "testing" 11 | 12 | // "github.com/gruntwork-io/terratest/modules/terraform" 13 | // ) 14 | 15 | // func TestQueueExample(t *testing.T) { 16 | // // sut 17 | // terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ 18 | // TerraformDir: "../examples/queue", 19 | // }) 20 | 21 | // defer terraform.Destroy(t, terraformOptions) 22 | // terraform.InitAndApply(t, terraformOptions) 23 | // terraform.ApplyAndIdempotent(t, terraformOptions) 24 | 25 | // // assertions 26 | // moduleOutputs := terraform.OutputMapOfObjects(t, terraformOptions, "amazon_connect") 27 | // } 28 | -------------------------------------------------------------------------------- /test/simple_example_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/gruntwork-io/terratest/modules/terraform" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestSimpleExample(t *testing.T) { 11 | // sut 12 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ 13 | TerraformDir: "../examples/simple", 14 | }) 15 | 16 | defer terraform.Destroy(t, terraformOptions) 17 | terraform.InitAndApply(t, terraformOptions) 18 | terraform.ApplyAndIdempotent(t, terraformOptions) 19 | 20 | // assertions 21 | moduleOutputs := terraform.OutputMapOfObjects(t, terraformOptions, "amazon_connect") 22 | 23 | // instance 24 | instance := moduleOutputs["instance"].(map[string]interface{}) 25 | 26 | assert.NotEmpty(t, instance["id"]) 27 | assert.Equal(t, "CONNECT_MANAGED", instance["identity_management_type"]) 28 | assert.Equal(t, true, instance["inbound_calls_enabled"]) 29 | assert.Equal(t, true, instance["outbound_calls_enabled"]) 30 | assert.Len(t, instance["instance_alias"], 32) 31 | } 32 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | ################################################################################ 5 | # Instance 6 | ################################################################################ 7 | # required (when create_instance = true) 8 | variable "instance_identity_management_type" { 9 | type = string 10 | default = null 11 | description = "Specifies the identity management type attached to the instance. Allowed values are: SAML, CONNECT_MANAGED, EXISTING_DIRECTORY." 12 | } 13 | 14 | variable "instance_inbound_calls_enabled" { 15 | type = bool 16 | default = null 17 | description = "Specifies whether inbound calls are enabled." 18 | } 19 | 20 | variable "instance_outbound_calls_enabled" { 21 | type = bool 22 | default = null 23 | description = "Specifies whether outbound calls are enabled." 24 | } 25 | 26 | # optional 27 | variable "create_instance" { 28 | type = bool 29 | default = true 30 | description = "Controls if the aws_connect_instance resource should be created. Defaults to true." 31 | } 32 | 33 | variable "instance_id" { 34 | type = string 35 | default = null 36 | description = "If create_instance is set to false, you may still create other resources and pass in an instance ID that was created outside this module. Ignored if create_instance is true." 37 | } 38 | 39 | variable "instance_auto_resolve_best_voices_enabled" { 40 | type = bool 41 | default = null 42 | description = "Specifies whether auto resolve best voices is enabled. Defaults to true." 43 | } 44 | 45 | variable "instance_contact_flow_logs_enabled" { 46 | type = bool 47 | default = null 48 | description = "Specifies whether contact flow logs are enabled. Defaults to false." 49 | } 50 | 51 | variable "instance_contact_lens_enabled" { 52 | type = bool 53 | default = null 54 | description = "Specifies whether contact lens is enabled. Defaults to true." 55 | } 56 | 57 | variable "instance_directory_id" { 58 | type = string 59 | default = null 60 | description = "The identifier for the directory if instance_identity_management_type is EXISTING_DIRECTORY." 61 | } 62 | 63 | variable "instance_early_media_enabled" { 64 | type = bool 65 | default = null 66 | description = "Specifies whether early media for outbound calls is enabled. Defaults to true if instance_outbound_calls_enabled is true." 67 | } 68 | 69 | variable "instance_alias" { 70 | type = string 71 | default = null 72 | description = "Specifies the name of the instance. Required if instance_directory_id not specified." 73 | } 74 | 75 | ################################################################################ 76 | # Instance Storage Config 77 | ################################################################################ 78 | variable "instance_storage_configs" { 79 | type = any 80 | default = {} 81 | description = <<-EOF 82 | A map of Amazon Connect Instance Storage Configs. 83 | 84 | The key of the map is the Instance Storage Config `resource_type`. The value is the configuration for that Instance Storage Config, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_instance_storage_config#storage_config) (except `resource_type` which is the key, and `instance_id` which is created or passed in). 85 | 86 | Example/available options: 87 | ``` 88 | { 89 | = { 90 | kinesis_firehose_config = optional({ 91 | firehose_arn = string 92 | }) 93 | kinesis_stream_config = optional({ 94 | stream_arn = string 95 | }) 96 | kinesis_video_stream_config = optional({ 97 | encryption_config = { 98 | encryption_type = string 99 | key_id = string 100 | } 101 | prefix = string 102 | retention_period_hours = number 103 | }) 104 | s3_config = optional({ 105 | bucket_name = string 106 | bucket_prefix = string 107 | encryption_config = optional({ 108 | encryption_type = string 109 | key_id = string 110 | }) 111 | }) 112 | storage_type = string 113 | } 114 | } 115 | ``` 116 | EOF 117 | } 118 | 119 | ################################################################################ 120 | # Hours of Operation 121 | ################################################################################ 122 | variable "hours_of_operations" { 123 | type = any 124 | default = {} 125 | description = <<-EOF 126 | A map of Amazon Connect Hours of Operations. 127 | 128 | The key of the map is the Hours of Operation `name`. The value is the configuration for that Hours of Operation, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_hours_of_operation) (except `name` which is the key, and `instance_id` which is created or passed in). 129 | 130 | Example/available options: 131 | ``` 132 | { 133 | = { 134 | config = [ 135 | { 136 | day = string 137 | end_time = { 138 | hours = number 139 | minutes = number 140 | } 141 | start_time = { 142 | hours = number 143 | minutes = number 144 | } 145 | } 146 | ] 147 | description = optional(string) 148 | tags = optional(map(string)) 149 | time_zone = string 150 | } 151 | } 152 | ``` 153 | EOF 154 | } 155 | 156 | variable "hours_of_operations_tags" { 157 | type = map(string) 158 | default = {} 159 | description = "Additional tags to add to all Hours of Operations resources." 160 | } 161 | 162 | ################################################################################ 163 | # Contact Flow / Module 164 | ################################################################################ 165 | variable "contact_flows" { 166 | type = any 167 | default = {} 168 | description = <<-EOF 169 | A map of Amazon Connect Contact Flows. 170 | 171 | The key of the map is the Contact Flow `name`. The value is the configuration for that Contact Flow, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_contact_flow) (except `name` which is the key, and `instance_id` which is created or passed in). 172 | 173 | Example/available options: 174 | ``` 175 | { 176 | = { 177 | content = optional(string) # one required 178 | content_hash = optional(string) # one required 179 | description = optional(string) 180 | filename = optional(string) # one required 181 | tags = optional(map(string)) 182 | type = optional(string) 183 | } 184 | } 185 | ``` 186 | EOF 187 | } 188 | 189 | variable "contact_flow_tags" { 190 | type = map(string) 191 | default = {} 192 | description = "Additional tags to add to all Contact Flow resources." 193 | } 194 | 195 | variable "contact_flow_modules" { 196 | type = any 197 | default = {} 198 | description = <<-EOF 199 | A map of Amazon Connect Contact Flow Modules. 200 | 201 | The key of the map is the Contact Flow Module `name`. The value is the configuration for that Contact Flow, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_contact_flow_module) (except `name` which is the key, and `instance_id` which is created or passed in). 202 | 203 | Example/available options: 204 | ``` 205 | { 206 | = { 207 | content = optional(string) # one required 208 | content_hash = optional(string) # one required 209 | description = optional(string) 210 | filename = optional(string) # one required 211 | tags = optional(map(string)) 212 | } 213 | } 214 | ``` 215 | EOF 216 | } 217 | 218 | variable "contact_flow_module_tags" { 219 | type = map(string) 220 | default = {} 221 | description = "Additional tags to add to all Contact Flow Module resources." 222 | } 223 | 224 | ################################################################################ 225 | # Queue 226 | ################################################################################ 227 | variable "queues" { 228 | type = any 229 | default = {} 230 | description = <<-EOF 231 | A map of Amazon Connect Queues. 232 | 233 | The key of the map is the Queue `name`. The value is the configuration for that Queue, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_queue) (except `name` which is the key, and `instance_id which` is created or passed in). 234 | 235 | Example/available options: 236 | ``` 237 | { 238 | = { 239 | description = optional(string) 240 | hours_of_operation_id = string 241 | max_contacts = optional(number) 242 | outbound_caller_config = optional({ 243 | outbound_caller_id_name = optional(string) 244 | outbound_caller_id_number_id = optional(string) 245 | outbound_flow_id = optional(string) 246 | }) 247 | quick_connect_ids = optional(list(string)) 248 | status = optional(string) 249 | tags = optional(map(string)) 250 | } 251 | } 252 | ``` 253 | EOF 254 | } 255 | 256 | variable "queue_tags" { 257 | type = map(string) 258 | default = {} 259 | description = "Additional tags to add to all Queue resources." 260 | } 261 | 262 | ################################################################################ 263 | # Quick Connect 264 | ################################################################################ 265 | variable "quick_connects" { 266 | type = any 267 | default = {} 268 | description = <<-EOF 269 | A map of Amazon Connect Quick Connect. 270 | 271 | The key of the map is the Quick Connect `name`. The value is the configuration for that Quick Connect, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_quick_connect) (except `name` which is the key, and `instance_id` which is created or passed in). 272 | 273 | Example/available options: 274 | ``` 275 | { 276 | = { 277 | description = optional(string) 278 | quick_connect_config = { 279 | quick_connect_type = string 280 | phone_config = optional({ 281 | phone_number = string 282 | }) 283 | queue_config = optional({ 284 | contact_flow_id = string 285 | queue_id = string 286 | }) 287 | user_config = optional({ 288 | contact_flow_id = string 289 | queue_id = string 290 | }) 291 | }) 292 | tags = optional(map(string)) 293 | } 294 | } 295 | ``` 296 | EOF 297 | } 298 | 299 | variable "quick_connect_tags" { 300 | type = map(string) 301 | default = {} 302 | description = "Additional tags to add to all Quick Connect resources." 303 | } 304 | 305 | ################################################################################ 306 | # Routing / Security Profiles 307 | ################################################################################ 308 | variable "routing_profiles" { 309 | type = any 310 | default = {} 311 | description = <<-EOF 312 | A map of Amazon Connect Routing Profile. 313 | 314 | The key of the map is the Routing Profile `name`. The value is the configuration for that Routing Profile, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_routing_profile) (except `name` which is the key, and `instance_id` which is created or passed in). 315 | 316 | Example/available options: 317 | ``` 318 | { 319 | = { 320 | default_outbound_queue_id = string 321 | description = string 322 | media_concurrencies = [ 323 | { 324 | channel = string 325 | concurrency = number 326 | } 327 | ] 328 | queue_configs = optional([ 329 | { 330 | channel = string 331 | delay = number 332 | priority = number 333 | queue_id = string 334 | } 335 | ]) 336 | tags = optional(map(string)) 337 | } 338 | } 339 | ``` 340 | EOF 341 | } 342 | 343 | variable "routing_profile_tags" { 344 | type = map(string) 345 | default = {} 346 | description = "Additional tags to add to all Routing Profile resources." 347 | } 348 | 349 | variable "security_profiles" { 350 | type = any 351 | default = {} 352 | description = <<-EOF 353 | A map of Amazon Connect Security Profile. 354 | 355 | The key of the map is the Security Profile `name`. The value is the configuration for that Security Profile, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_security_profile) (except `name` which is the key, and `instance_id` which is created or passed in). 356 | 357 | Example/available options: 358 | ``` 359 | { 360 | = { 361 | description = optional(string) 362 | permissions = optional(list(string)) 363 | tags = optional(map(string)) 364 | } 365 | } 366 | ``` 367 | EOF 368 | } 369 | 370 | variable "security_profile_tags" { 371 | type = map(string) 372 | default = {} 373 | description = "Additional tags to add to all Security Profile resources." 374 | } 375 | 376 | ################################################################################ 377 | # Vocabulary 378 | ################################################################################ 379 | variable "vocabularies" { 380 | type = any 381 | default = {} 382 | description = <<-EOF 383 | A map of Amazon Connect Vocabularies. 384 | 385 | The key of the map is the Vocabulary `name`. The value is the configuration for that Vocabulary, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_vocabulary) (except `name` which is the key, and `instance_id` which is created or passed in). 386 | 387 | Example/available options: 388 | ``` 389 | { 390 | = { 391 | content = string 392 | language_code = string 393 | tags = optional(map(string)) 394 | } 395 | } 396 | ``` 397 | EOF 398 | } 399 | 400 | variable "vocabulary_tags" { 401 | type = map(string) 402 | default = {} 403 | description = "Additional tags to add to all Vocabulary resources." 404 | } 405 | 406 | ################################################################################ 407 | # Lex Bot / Lambda Function Associations 408 | ################################################################################ 409 | variable "bot_associations" { 410 | type = any 411 | default = {} 412 | description = <<-EOF 413 | A map of Amazon Connect Lex Bot Associations. 414 | 415 | The key of the map is the Lex Bot `name`. The value is the configuration for that Lex Bot, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_bot_association) (except `name` which is the key, and `instance_id` which is created or passed in). 416 | 417 | Example/available options: 418 | ``` 419 | { 420 | = { 421 | name = string 422 | lex_region = optional(string) 423 | } 424 | } 425 | ``` 426 | EOF 427 | } 428 | 429 | variable "lambda_function_associations" { 430 | type = map(string) 431 | default = {} 432 | description = <<-EOF 433 | A map of Lambda Function ARNs to associate to the Amazon Connect Instance, the key is a static/arbitrary name and value is the Lambda ARN. 434 | 435 | Example/available options: 436 | ``` 437 | { 438 | = string 439 | } 440 | ``` 441 | EOF 442 | } 443 | 444 | ################################################################################ 445 | # Users / Hierarchy Group / Structure 446 | ################################################################################ 447 | variable "users" { 448 | type = any 449 | default = {} 450 | description = <<-EOF 451 | A map of Amazon Connect Users. 452 | 453 | The key of the map is the User `name`. The value is the configuration for that User, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_user) (except `name` which is the key, and `instance_id` which is created or passed in). 454 | 455 | Example/available options: 456 | ``` 457 | { 458 | = { 459 | directory_user_id = optional(string) 460 | hierarchy_group_id = optional(string) 461 | identity_info = optional({ 462 | email = optional(string) 463 | first_name = optional(string) 464 | last_name = optional(string) 465 | }) 466 | password = optional(string) 467 | phone_config = { 468 | phone_type = string 469 | after_contact_work_time_limit = optional(number) 470 | auto_accept = optional(bool) 471 | desk_phone_number = optional(string) 472 | } 473 | routing_profile_id = string 474 | security_profile_ids = list(string) 475 | tags = optional(map(string)) 476 | } 477 | } 478 | ``` 479 | EOF 480 | } 481 | 482 | variable "user_tags" { 483 | type = map(string) 484 | default = {} 485 | description = "Additional tags to add to all User resources." 486 | } 487 | 488 | variable "user_hierarchy_groups" { 489 | type = any 490 | default = {} 491 | description = <<-EOF 492 | A map of Amazon Connect User Hierarchy Groups. 493 | 494 | The key of the map is the User Hierarchy Group `name`. The value is the configuration for that User, supporting all arguments [documented here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_user_hierarchy_group) (except `name` which is the key, and `instance_id` which is created or passed in). 495 | 496 | Example/available options: 497 | ``` 498 | { 499 | = { 500 | parent_group_id = optional(string) 501 | tags = optional(map(string)) 502 | } 503 | } 504 | ``` 505 | EOF 506 | } 507 | 508 | variable "user_hierarchy_group_tags" { 509 | type = map(string) 510 | default = {} 511 | description = "Additional tags to add to all User Hierarchy Group resources." 512 | } 513 | 514 | variable "user_hierarchy_structure" { 515 | type = map(string) 516 | default = {} 517 | description = <<-EOF 518 | A map of Amazon Connect User Hierarchy Structure, containing keys for for zero or many levels: `level_one`, `level_two`, `level_three`, `level_four`, and `level_five`. The values are the `name` for that level. See [documentation here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/connect_user_hierarchy_structure). 519 | 520 | Example/available options: 521 | ``` 522 | { 523 | level_one = string 524 | } 525 | ``` 526 | EOF 527 | } 528 | 529 | ################################################################################ 530 | # Tags 531 | ################################################################################ 532 | variable "tags" { 533 | type = map(string) 534 | default = {} 535 | description = "A map of tags to add to all resources." 536 | } 537 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | terraform { 5 | required_version = ">= 1.2" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = ">= 4.26" 11 | } 12 | } 13 | } 14 | --------------------------------------------------------------------------------