diff --git a/.gitignore b/.gitignore
index 7a3e2fd..7f122c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,6 @@ override.tf.json
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
+
+# Terraform Provider lock file
+.terraform.lock.hcl
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..34726a8
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,29 @@
+repos:
+- repo: https://github.com/antonbabenko/pre-commit-terraform
+ rev: v1.68.1 # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases
+ hooks:
+ - id: terraform_fmt
+ - id: terraform_docs
+ args:
+ - '--args=--lockfile=false'
+ - id: terraform_validate
+ - id: terraform_tflint
+ args:
+ - '--args=--only=terraform_deprecated_interpolation'
+ - '--args=--only=terraform_deprecated_index'
+ - '--args=--only=terraform_unused_declarations'
+ - '--args=--only=terraform_comment_syntax'
+ - '--args=--only=terraform_documented_outputs'
+ - '--args=--only=terraform_documented_variables'
+ - '--args=--only=terraform_typed_variables'
+ - '--args=--only=terraform_module_pinned_source'
+ - '--args=--only=terraform_naming_convention'
+ - '--args=--only=terraform_required_version'
+ - '--args=--only=terraform_required_providers'
+ - '--args=--only=terraform_standard_module_structure'
+ - '--args=--only=terraform_workspace_remote'
+- repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.2.0
+ hooks:
+ - id: check-merge-conflict
+ - id: end-of-file-fixer
diff --git a/README.md b/README.md
index e42a6de..7ecc3d0 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,69 @@
# terraform-tfe-workspace
Terraform module to provision and manage Terraform Cloud workspaces
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 0.14.0 |
+| [tfe](#requirement\_tfe) | ~> 0.31.0 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [tfe](#provider\_tfe) | ~> 0.31.0 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [tfe_run_trigger.this](https://registry.terraform.io/providers/hashicorp/tfe/latest/docs/resources/run_trigger) | resource |
+| [tfe_variable.this](https://registry.terraform.io/providers/hashicorp/tfe/latest/docs/resources/variable) | resource |
+| [tfe_workspace.this](https://registry.terraform.io/providers/hashicorp/tfe/latest/docs/resources/workspace) | resource |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [allow\_destroy\_plan](#input\_allow\_destroy\_plan) | (Optional) Whether destroy plans can be queued on the workspace | `bool` | `false` | no |
+| [auto\_apply](#input\_auto\_apply) | (Optional) Whether to automatically apply changes when a Terraform plan is successful | `bool` | `false` | no |
+| [description](#input\_description) | (Optional) A description for the workspace | `string` | `""` | no |
+| [environment\_sensitive\_variables](#input\_environment\_sensitive\_variables) | (Optional) Map of sensitive variables of 'environment' category used in the workspace
Item syntax:
{
variable1\_name = value1
variable2\_name = value2
...
} | `map(any)` | `{}` | no |
+| [environment\_variables](#input\_environment\_variables) | (Optional) Map of variables of 'environment' category used in the workspace
Item syntax:
{
variable1\_name = value1
variable2\_name = value2
...
} | `map(any)` | `{}` | no |
+| [execution\_mode](#input\_execution\_mode) | (Optional) Which execution mode to use | `string` | `"remote"` | no |
+| [file\_triggers\_enabled](#input\_file\_triggers\_enabled) | (Optional) Whether to filter runs based on the changed files in a VCS push | `bool` | `true` | no |
+| [global\_remote\_state](#input\_global\_remote\_state) | (Optional) Whether the workspace allows all workspaces in the organization to access its state data during runs | `bool` | `false` | no |
+| [name](#input\_name) | (Required) Name of the workspace | `string` | n/a | yes |
+| [oauth\_token\_id](#input\_oauth\_token\_id) | (Optional) The token ID of the VCS connection to use | `string` | `""` | no |
+| [organization](#input\_organization) | (Required) Name of the organization | `string` | n/a | yes |
+| [queue\_all\_runs](#input\_queue\_all\_runs) | (Optional) Whether the workspace should start automatically performing runs immediately after its creation | `bool` | `true` | no |
+| [remote\_state\_consumer\_ids](#input\_remote\_state\_consumer\_ids) | (Optional) The set of workspace IDs set as explicit remote state consumers for the given workspace | `list(string)` | `[]` | no |
+| [run\_triggers](#input\_run\_triggers) | List of source workspaces IDs that trigger runs in this workspace | `list(string)` | `[]` | no |
+| [speculative\_enabled](#input\_speculative\_enabled) | (Optional) Whether this workspace allows speculative plans | `bool` | `true` | no |
+| [ssh\_key\_id](#input\_ssh\_key\_id) | (Optional) The ID of an SSH key to assign to the workspace | `string` | `null` | no |
+| [structured\_run\_output\_enabled](#input\_structured\_run\_output\_enabled) | (Optional) Whether this workspace should show output from Terraform runs using the enhanced UI when available | `bool` | `true` | no |
+| [tag\_names](#input\_tag\_names) | (Optional) A list of tag names for this workspace | `list(string)` | `[]` | no |
+| [terraform\_hcl\_sensitive\_variables](#input\_terraform\_hcl\_sensitive\_variables) | (Optional) Map of sensitive variables in HCL format of 'Terraform' category used in the workspace
Item syntax:
{
variable1\_name = value1
variable2\_name = value2
...
}
NOTE: you can specifies values in HCL format directly, like this:
{
variable\_list = ["item1","item2"]
variable\_map = {
key1 = value1
key2 = value2
}
} | `any` | `{}` | no |
+| [terraform\_hcl\_variables](#input\_terraform\_hcl\_variables) | (Optional) Map of variables in HCL format of 'Terraform' category used in the workspace
Item syntax:
{
variable1\_name = value1
variable2\_name = value2
...
}
NOTE: you can specifies values in HCL format directly, like this:
{
variable\_list = ["item1","item2"]
variable\_map = {
key1 = value1
key2 = value2
}
}
} | `any` | `{}` | no |
+| [terraform\_sensitive\_variables](#input\_terraform\_sensitive\_variables) | (Optional) Map of sensitive variables of 'Terraform' category used in the workspace
Item syntax:
{
variable1\_name = value1
variable2\_name = value2
...
} | `map(any)` | `{}` | no |
+| [terraform\_variables](#input\_terraform\_variables) | (Optional) Map of variables of 'Terraform' category used in the workspace
Item syntax:
{
variable1\_name = value1
variable2\_name = value2
...
} | `map(any)` | `{}` | no |
+| [terraform\_version](#input\_terraform\_version) | (Required) The version of Terraform to use for this workspace | `string` | n/a | yes |
+| [trigger\_prefixes](#input\_trigger\_prefixes) | (Optional) List of repository-root-relative paths which describe all locations to be tracked for changes | `list(string)` | `[]` | no |
+| [variables\_descriptions](#input\_variables\_descriptions) | (Optional) Map of descriptions applied to workspace variables
Item syntax:
{
variable1\_name = "description"
variable2\_name = "description"
...
} | `map(string)` | `{}` | no |
+| [vcs\_repository\_branch](#input\_vcs\_repository\_branch) | (Optional) The repository branch that Terraform will execute from | `string` | `""` | no |
+| [vcs\_repository\_identifier](#input\_vcs\_repository\_identifier) | (Optional) A reference to your VCS repository in the format / where and refer to the organization and repository in your VCS provider. The format for Azure DevOps is //\_git/ | `string` | `""` | no |
+| [vcs\_repository\_ingress\_submodules](#input\_vcs\_repository\_ingress\_submodules) | (Optional) Whether submodules should be fetched when cloning the VCS repository | `bool` | `false` | no |
+| [working\_directory](#input\_working\_directory) | (Optional) A relative path that Terraform will execute within | `string` | `null` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [id](#output\_id) | The workspace ID |
+
diff --git a/examples/simple-workspace/README.md b/examples/simple-workspace/README.md
new file mode 100644
index 0000000..6a010ae
--- /dev/null
+++ b/examples/simple-workspace/README.md
@@ -0,0 +1,57 @@
+# terraform-tfe-workspace
+
+This example will manage a simple Terraform Cloud/Enterprise workspace without
+connecting it to the GitHub repository.
+
+## Usage
+
+To run this example, you need to execute the following commands:
+
+```shell
+$ terraform init
+$ terraform plan
+$ terraform apply
+```
+
+:memo: **Note:** You will need a Terraform Cloud/Enterprise API token for authentication.
+You'll be prompted to insert it to provide a value for "tfc_token" variable.
+See [here](https://www.terraform.io/cloud-docs/users-teams-organizations/api-tokens)
+for further information.
+
+:warning: **Warning:** This example may create resources that cost money. Execute the command
+`terraform destroy` when the resources are no longer needed.
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | ~> 1.1.0 |
+| [tfe](#requirement\_tfe) | ~>0.31.0 |
+
+## Providers
+
+No providers.
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [simple\_workspace](#module\_simple\_workspace) | ../../ | n/a |
+
+## Resources
+
+No resources.
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [tfc\_token](#input\_tfc\_token) | Token for Terraform Cloud Authentication | `string` | n/a | yes |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [workspace\_id](#output\_workspace\_id) | The ID of Terraform Cloud/Enterprise workspace |
+
diff --git a/examples/simple-workspace/main.tf b/examples/simple-workspace/main.tf
new file mode 100644
index 0000000..66c4521
--- /dev/null
+++ b/examples/simple-workspace/main.tf
@@ -0,0 +1,35 @@
+terraform {
+ required_version = "~> 1.1.0"
+
+ required_providers {
+ tfe = {
+ source = "hashicorp/tfe"
+ version = "~>0.31.0"
+ }
+ }
+}
+
+provider "tfe" {
+ token = var.tfc_token
+}
+
+module "simple_workspace" {
+ source = "../../"
+
+ name = "simple-workspace"
+ organization = "myorg"
+ description = "A simple Terraform Cloud/Enterprise workspace"
+ terraform_version = "1.1.9"
+
+ terraform_variables = {
+ string_variable = "stringvalue"
+ number_variable = 1
+ bool_variable = true
+ }
+
+ variables_descriptions = {
+ string_variable = "A variable containing a value in string format"
+ number_variable = "A variable containing a value in number format"
+ bool_variable = "A variable containing a value in boolean format"
+ }
+}
diff --git a/examples/simple-workspace/outputs.tf b/examples/simple-workspace/outputs.tf
new file mode 100644
index 0000000..a9b3f39
--- /dev/null
+++ b/examples/simple-workspace/outputs.tf
@@ -0,0 +1,4 @@
+output "workspace_id" {
+ description = "The ID of Terraform Cloud/Enterprise workspace"
+ value = module.simple_workspace.id
+}
diff --git a/examples/simple-workspace/variables.tf b/examples/simple-workspace/variables.tf
new file mode 100644
index 0000000..5d77a40
--- /dev/null
+++ b/examples/simple-workspace/variables.tf
@@ -0,0 +1,5 @@
+variable "tfc_token" {
+ description = "Token for Terraform Cloud Authentication"
+ type = string
+ sensitive = true
+}
diff --git a/main.tf b/main.tf
new file mode 100644
index 0000000..ca7aa51
--- /dev/null
+++ b/main.tf
@@ -0,0 +1,115 @@
+locals {
+ terraform_variables = { for k, v in var.terraform_variables : k =>
+ {
+ value = v
+ category = "terraform"
+ description = lookup(var.variables_descriptions, k, null)
+ }
+ }
+
+ terraform_hcl_variables = { for k, v in var.terraform_hcl_variables : k =>
+ {
+ #NOTE: using @osterman trick https://github.com/hashicorp/terraform-provider-tfe/issues/188#issuecomment-700212045
+ value = replace(jsonencode(v), "/(\".*?\"):/", "$1 = ")
+ category = "terraform"
+ hcl = true
+ description = lookup(var.variables_descriptions, k, null)
+ }
+ }
+
+ terraform_sensitive_variables = { for k, v in var.terraform_sensitive_variables : k =>
+ {
+ value = v
+ category = "terraform"
+ description = lookup(var.variables_descriptions, k, null)
+ sensitive = true
+ }
+ }
+
+ terraform_hcl_sensitive_variables = { for k, v in var.terraform_hcl_sensitive_variables : k =>
+ {
+ #NOTE: using @osterman trick https://github.com/hashicorp/terraform-provider-tfe/issues/188#issuecomment-700212045
+ value = replace(jsonencode(v), "/(\".*?\"):/", "$1 = ")
+ category = "terraform"
+ description = lookup(var.variables_descriptions, k, null)
+ hcl = true
+ sensitive = true
+ }
+ }
+
+ environment_variables = { for k, v in var.environment_variables : k =>
+ {
+ value = v
+ category = "env"
+ description = lookup(var.variables_descriptions, k, null)
+ }
+ }
+
+ environment_sensitive_variables = { for k, v in var.environment_sensitive_variables : k =>
+ {
+ value = v
+ category = "env"
+ description = lookup(var.variables_descriptions, k, null)
+ sensitive = true
+ }
+ }
+
+ all_variables = merge(
+ local.terraform_variables,
+ local.terraform_hcl_variables,
+ local.terraform_sensitive_variables,
+ local.terraform_hcl_sensitive_variables,
+ local.environment_variables,
+ local.environment_sensitive_variables
+ )
+}
+
+resource "tfe_workspace" "this" {
+ name = var.name
+ organization = var.organization
+ description = var.description
+ allow_destroy_plan = var.allow_destroy_plan
+ auto_apply = var.auto_apply
+ execution_mode = var.execution_mode
+ file_triggers_enabled = var.file_triggers_enabled
+ global_remote_state = var.global_remote_state
+ remote_state_consumer_ids = var.remote_state_consumer_ids
+ queue_all_runs = var.queue_all_runs
+ speculative_enabled = var.speculative_enabled
+ structured_run_output_enabled = var.structured_run_output_enabled
+ ssh_key_id = var.ssh_key_id
+ terraform_version = var.terraform_version
+ trigger_prefixes = var.trigger_prefixes
+ tag_names = var.tag_names
+ working_directory = var.working_directory
+
+ dynamic "vcs_repo" {
+ for_each = length(var.vcs_repository_identifier) > 0 && length(var.oauth_token_id) > 0 ? [1] : []
+
+ content {
+ identifier = var.vcs_repository_identifier
+ branch = var.vcs_repository_branch
+ ingress_submodules = var.vcs_repository_ingress_submodules
+ oauth_token_id = var.oauth_token_id
+ }
+ }
+}
+
+resource "tfe_variable" "this" {
+ for_each = local.all_variables
+
+ key = each.key
+ value = each.value.value
+ hcl = try(each.value.hcl, null)
+ category = each.value.category
+ description = try(each.value.description, null)
+ sensitive = try(each.value.sensitive, false)
+ workspace_id = tfe_workspace.this.id
+}
+
+resource "tfe_run_trigger" "this" {
+ count = length(var.run_triggers)
+
+ workspace_id = tfe_workspace.this.id
+ sourceable_id = var.run_triggers[count.index]
+}
diff --git a/outputs.tf b/outputs.tf
new file mode 100644
index 0000000..fcd6e1f
--- /dev/null
+++ b/outputs.tf
@@ -0,0 +1,4 @@
+output "id" {
+ description = "The workspace ID"
+ value = tfe_workspace.this.id
+}
diff --git a/variables.tf b/variables.tf
new file mode 100644
index 0000000..2f87375
--- /dev/null
+++ b/variables.tf
@@ -0,0 +1,268 @@
+variable "name" {
+ description = "(Required) Name of the workspace"
+ type = string
+}
+
+variable "organization" {
+ description = "(Required) Name of the organization"
+ type = string
+}
+
+variable "description" {
+ description = "(Optional) A description for the workspace"
+ type = string
+ default = ""
+}
+
+variable "allow_destroy_plan" {
+ description = "(Optional) Whether destroy plans can be queued on the workspace"
+ type = bool
+ default = false
+}
+
+variable "auto_apply" {
+ description = "(Optional) Whether to automatically apply changes when a Terraform plan is successful"
+ type = bool
+ default = false
+}
+
+variable "execution_mode" {
+ description = "(Optional) Which execution mode to use"
+ type = string
+ default = "remote"
+
+ validation {
+ condition = can(regex("agent|local|remote", var.execution_mode))
+ error_message = "ERROR: Allowed values are \"remote\", \"local\" or \"agent\"."
+ }
+}
+
+variable "file_triggers_enabled" {
+ description = "(Optional) Whether to filter runs based on the changed files in a VCS push"
+ type = bool
+ default = true
+}
+
+variable "global_remote_state" {
+ description = "(Optional) Whether the workspace allows all workspaces in the organization to access its state data during runs"
+ type = bool
+ default = false
+}
+
+variable "remote_state_consumer_ids" {
+ description = "(Optional) The set of workspace IDs set as explicit remote state consumers for the given workspace"
+ type = list(string)
+ default = []
+}
+
+variable "queue_all_runs" {
+ description = "(Optional) Whether the workspace should start automatically performing runs immediately after its creation"
+ type = bool
+ default = true
+}
+
+variable "speculative_enabled" {
+ description = "(Optional) Whether this workspace allows speculative plans"
+ type = bool
+ default = true
+}
+
+variable "structured_run_output_enabled" {
+ description = "(Optional) Whether this workspace should show output from Terraform runs using the enhanced UI when available"
+ type = bool
+ default = true
+}
+
+variable "ssh_key_id" {
+ description = "(Optional) The ID of an SSH key to assign to the workspace"
+ type = string
+ default = null
+}
+
+variable "terraform_version" {
+ description = "(Required) The version of Terraform to use for this workspace"
+ type = string
+}
+
+variable "trigger_prefixes" {
+ description = "(Optional) List of repository-root-relative paths which describe all locations to be tracked for changes"
+ type = list(string)
+ default = []
+}
+
+variable "tag_names" {
+ description = "(Optional) A list of tag names for this workspace"
+ type = list(string)
+ default = []
+}
+
+variable "working_directory" {
+ description = "(Optional) A relative path that Terraform will execute within"
+ type = string
+ default = null
+}
+
+variable "vcs_repository_identifier" {
+ description = "(Optional) A reference to your VCS repository in the format / where and refer to the organization and repository in your VCS provider. The format for Azure DevOps is //_git/"
+ type = string
+ default = ""
+}
+
+variable "vcs_repository_branch" {
+ description = "(Optional) The repository branch that Terraform will execute from"
+ type = string
+ default = ""
+}
+
+variable "vcs_repository_ingress_submodules" {
+ description = "(Optional) Whether submodules should be fetched when cloning the VCS repository"
+ type = bool
+ default = false
+}
+
+variable "oauth_token_id" {
+ description = "(Optional) The token ID of the VCS connection to use"
+ type = string
+ default = ""
+}
+
+variable "terraform_variables" {
+ description = <