diff --git a/docs-examples/datasources/stack_template/datasource.tf b/docs-examples/datasources/stack_template/datasource.tf
new file mode 100644
index 0000000..8cca881
--- /dev/null
+++ b/docs-examples/datasources/stack_template/datasource.tf
@@ -0,0 +1,7 @@
+data "stackguardian_stack_template" "example" {
+ id = "my-stack-template"
+}
+
+output "stack_template_output" {
+ value = data.stackguardian_stack_template.example.description
+}
diff --git a/docs-examples/datasources/stack_template_revision/datasource.tf b/docs-examples/datasources/stack_template_revision/datasource.tf
new file mode 100644
index 0000000..326d234
--- /dev/null
+++ b/docs-examples/datasources/stack_template_revision/datasource.tf
@@ -0,0 +1,7 @@
+data "stackguardian_stack_template_revision" "example" {
+ id = "my-stack-template:1"
+}
+
+output "stack_template_revision_output" {
+ value = data.stackguardian_stack_template_revision.example.notes
+}
diff --git a/docs-examples/resources/stack_template/resource.tf b/docs-examples/resources/stack_template/resource.tf
new file mode 100644
index 0000000..0b701a2
--- /dev/null
+++ b/docs-examples/resources/stack_template/resource.tf
@@ -0,0 +1,7 @@
+# Example 1: Basic stack template
+resource "stackguardian_stack_template" "basic" {
+ template_name = "my-stack-template"
+ source_config_kind = "TERRAFORM"
+ is_public = "0"
+ tags = ["terraform", "production"]
+}
diff --git a/docs-examples/resources/stack_template_revision/resource.tf b/docs-examples/resources/stack_template_revision/resource.tf
new file mode 100644
index 0000000..682edf0
--- /dev/null
+++ b/docs-examples/resources/stack_template_revision/resource.tf
@@ -0,0 +1,67 @@
+resource "stackguardian_stack_template" "example" {
+ template_name = "my-stack-template"
+ source_config_kind = "TERRAFORM"
+ is_public = "0"
+ tags = ["terraform", "production"]
+}
+
+# Example 1: Basic stack template revision
+resource "stackguardian_stack_template_revision" "basic" {
+ parent_template_id = stackguardian_stack_template.example.id
+ alias = "v1"
+ notes = "Initial revision"
+ description = "First revision of the stack template"
+ source_config_kind = "TERRAFORM"
+
+ workflows_config = {
+ workflows = [
+ {
+ id = "d8dfaf15-2ad9-da29-8af0-c6b288b12089"
+ template_id = "my-workflow-template"
+ resource_name = "wf-1"
+
+ terraform_config = {
+ managed_terraform_state = true
+ terraform_version = "1.5.7"
+ }
+ }
+ ]
+ }
+}
+
+# Example 2: Stack template revision with VCS config
+resource "stackguardian_stack_template_revision" "with_vcs" {
+ parent_template_id = stackguardian_stack_template.example.id
+ alias = "v2"
+ notes = "Revision with VCS configuration"
+ description = "Stack template revision with VCS and input data"
+ source_config_kind = "TERRAFORM"
+
+ workflows_config = {
+ workflows = [
+ {
+ id = "d8dfaf15-2ad9-da29-8af0-c6b288b12089"
+ template_id = "my-workflow-template"
+ resource_name = "wf-1"
+
+ vcs_config = {
+ iac_vcs_config = {
+ use_marketplace_template = true
+ iac_template_id = "my-workflow-template"
+ }
+ iac_input_data = {
+ schema_type = "RAW_JSON"
+ data = jsonencode({
+ bucket_region = "eu-central-1"
+ })
+ }
+ }
+
+ terraform_config = {
+ managed_terraform_state = true
+ terraform_version = "1.5.7"
+ }
+ }
+ ]
+ }
+}
diff --git a/docs-templates/data-sources/stack_template.md.tmpl b/docs-templates/data-sources/stack_template.md.tmpl
new file mode 100644
index 0000000..2298e0b
--- /dev/null
+++ b/docs-templates/data-sources/stack_template.md.tmpl
@@ -0,0 +1,19 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "stackguardian_stack_template Data Source - terraform-provider-stackguardian"
+subcategory: ""
+description: |-
+
+---
+
+# stackguardian_stack_template (Data Source)
+
+
+⚠️ This feature is currently in BETA.
+
+
+## Example Usage
+
+{{tffile "docs-examples/datasources/stack_template/datasource.tf"}}
+
+{{ .SchemaMarkdown }}
diff --git a/docs-templates/data-sources/stack_template_revision.md.tmpl b/docs-templates/data-sources/stack_template_revision.md.tmpl
new file mode 100644
index 0000000..44f55db
--- /dev/null
+++ b/docs-templates/data-sources/stack_template_revision.md.tmpl
@@ -0,0 +1,19 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "stackguardian_stack_template_revision Data Source - terraform-provider-stackguardian"
+subcategory: ""
+description: |-
+
+---
+
+# stackguardian_stack_template_revision (Data Source)
+
+
+⚠️ This feature is currently in BETA.
+
+
+## Example Usage
+
+{{tffile "docs-examples/datasources/stack_template_revision/datasource.tf"}}
+
+{{ .SchemaMarkdown }}
diff --git a/docs-templates/resources/stack_template.md.tmpl b/docs-templates/resources/stack_template.md.tmpl
new file mode 100644
index 0000000..ea3da32
--- /dev/null
+++ b/docs-templates/resources/stack_template.md.tmpl
@@ -0,0 +1,36 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "stackguardian_stack_template Resource - terraform-provider-stackguardian"
+subcategory: ""
+description: |-
+
+---
+
+# stackguardian_stack_template (Resource)
+
+
+⚠️ This feature is currently in BETA.
+
+
+## Example Usage
+
+{{tffile "docs-examples/resources/stack_template/resource.tf"}}
+
+{{ .SchemaMarkdown }}
+
+## Import
+
+Import existing resource
+
+### Using Import block (terraform v1.5.0 and later)
+```terraform
+import {
+ to = stackguardian_stack_template.example
+ id = "template-name"
+}
+```
+
+### Using CLI
+```bash
+terraform import stackguardian_stack_template.example template-name
+```
diff --git a/docs-templates/resources/stack_template_revision.md.tmpl b/docs-templates/resources/stack_template_revision.md.tmpl
new file mode 100644
index 0000000..94f4a60
--- /dev/null
+++ b/docs-templates/resources/stack_template_revision.md.tmpl
@@ -0,0 +1,36 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "stackguardian_stack_template_revision Resource - terraform-provider-stackguardian"
+subcategory: ""
+description: |-
+
+---
+
+# stackguardian_stack_template_revision (Resource)
+
+
+⚠️ This feature is currently in BETA.
+
+
+## Example Usage
+
+{{tffile "docs-examples/resources/stack_template_revision/resource.tf"}}
+
+{{ .SchemaMarkdown }}
+
+## Import
+
+Import existing resource
+
+### Using Import block (terraform v1.5.0 and later)
+```terraform
+import {
+ to = stackguardian_stack_template_revision.example
+ id = "template-name:revision-version"
+}
+```
+
+### Using CLI
+```bash
+terraform import stackguardian_stack_template_revision.example template-name:revision-version
+```
diff --git a/docs/data-sources/stack_template.md b/docs/data-sources/stack_template.md
new file mode 100644
index 0000000..0dea1d7
--- /dev/null
+++ b/docs/data-sources/stack_template.md
@@ -0,0 +1,58 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "stackguardian_stack_template Data Source - terraform-provider-stackguardian"
+subcategory: ""
+description: |-
+
+---
+
+# stackguardian_stack_template (Data Source)
+
+
+⚠️ This feature is currently in BETA.
+
+
+## Example Usage
+
+```terraform
+data "stackguardian_stack_template" "example" {
+ id = "my-stack-template"
+}
+
+output "stack_template_output" {
+ value = data.stackguardian_stack_template.example.description
+}
+```
+
+
+## Schema
+
+### Required
+
+- `id` (String) ID of the resource. Should be used to import the resource.
+
+### Read-Only
+
+- `context_tags` (Map of String) Context tags for stack template
+- `description` (String) A brief description of the stack template. Must be less than 256 characters.
+- `is_active` (String) Whether the stack template is active. Valid values:
+ 0 (false),
+ 1 (true)
+- `is_public` (String) Whether the stack template is publicly available. Valid values:
+ 0 (false),
+ 1 (true)
+- `owner_org` (String) Organization that owns the stack template.
+- `shared_orgs_list` (List of String) List of organization IDs with which this template is shared.
+- `source_config_kind` (String) Source configuration kind for the stack template. Valid values:
+ TERRAFORM,
+ OPENTOFU,
+ ANSIBLE_PLAYBOOK,
+ HELM,
+ KUBECTL,
+ CLOUDFORMATION,
+ MIXED,
+ CUSTOM
+- `tags` (List of String) A list of tags associated with the stack template. A maximum of 10 tags are allowed.
+- `template_name` (String) Name of the stack template. Must be less than 100 characters.
+
+
diff --git a/docs/data-sources/stack_template_revision.md b/docs/data-sources/stack_template_revision.md
new file mode 100644
index 0000000..ab78099
--- /dev/null
+++ b/docs/data-sources/stack_template_revision.md
@@ -0,0 +1,642 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "stackguardian_stack_template_revision Data Source - terraform-provider-stackguardian"
+subcategory: ""
+description: |-
+
+---
+
+# stackguardian_stack_template_revision (Data Source)
+
+
+⚠️ This feature is currently in BETA.
+
+
+## Example Usage
+
+```terraform
+data "stackguardian_stack_template_revision" "example" {
+ id = "my-stack-template:1"
+}
+
+output "stack_template_revision_output" {
+ value = data.stackguardian_stack_template_revision.example.notes
+}
+```
+
+
+## Schema
+
+### Required
+
+- `id` (String) ID of the resource. Should be used to import the resource.
+
+### Read-Only
+
+- `actions` (Attributes Map) JSON-encoded map of actions for the stack template revision. (see [below for nested schema](#nestedatt--actions))
+- `alias` (String) Human-readable alias for the revision (e.g., `v1.0.0`).
+- `context_tags` (Map of String) Context tags for stack template revision
+- `deprecation` (Attributes) Deprecation information for this resource. (see [below for nested schema](#nestedatt--deprecation))
+- `description` (String) Long description for the stack template revision.
+- `is_active` (String) Whether the stack template is active. Valid values:
+ 0 (false),
+ 1 (true)
+- `is_public` (String) Whether the stack template is publicly available. Valid values:
+ 0 (false),
+ 1 (true)
+- `notes` (String) Release notes or changelog for this revision.
+- `source_config_kind` (String) Source configuration kind for the stack template. Valid values:
+ TERRAFORM,
+ OPENTOFU,
+ ANSIBLE_PLAYBOOK,
+ HELM,
+ KUBECTL,
+ CLOUDFORMATION,
+ MIXED,
+ CUSTOM
+- `tags` (List of String) A list of tags associated with the stack template revision. A maximum of 10 tags are allowed.
+- `template_id` (String) ID of the parent stack template.
+- `workflows_config` (Attributes) JSON-encoded workflows configuration for the stack template revision. (see [below for nested schema](#nestedatt--workflows_config))
+
+
+### Nested Schema for `actions`
+
+Read-Only:
+
+- `default` (Boolean) Whether this is the default action.
+- `description` (String) Description of the action.
+- `name` (String) Name of the action.
+- `order` (Attributes Map) Ordered map of workflow IDs to their action configurations. (see [below for nested schema](#nestedatt--actions--order))
+
+
+### Nested Schema for `actions.order`
+
+Read-Only:
+
+- `dependencies` (Attributes List) List of workflow dependencies that must complete before this step runs. (see [below for nested schema](#nestedatt--actions--order--dependencies))
+- `parameters` (Attributes) Run configuration parameters for the action step. (see [below for nested schema](#nestedatt--actions--order--parameters))
+
+
+### Nested Schema for `actions.order.dependencies`
+
+Read-Only:
+
+- `condition` (Attributes) Condition that must be met by the dependency. (see [below for nested schema](#nestedatt--actions--order--dependencies--condition))
+- `id` (String) ID of the dependent workflow.
+
+
+### Nested Schema for `actions.order.dependencies.condition`
+
+Read-Only:
+
+- `latest_status` (String) Required latest status of the dependency (e.g., COMPLETED).
+
+
+
+
+### Nested Schema for `actions.order.parameters`
+
+Read-Only:
+
+- `deployment_platform_config` (Attributes List) Deployment platform configuration. (see [below for nested schema](#nestedatt--actions--order--parameters--deployment_platform_config))
+- `environment_variables` (Attributes List) Environment variables. (see [below for nested schema](#nestedatt--actions--order--parameters--environment_variables))
+- `terraform_action` (Attributes) Terraform-specific action parameters. (see [below for nested schema](#nestedatt--actions--order--parameters--terraform_action))
+- `wf_steps_config` (Attributes List) Workflow steps configuration. (see [below for nested schema](#nestedatt--actions--order--parameters--wf_steps_config))
+
+
+### Nested Schema for `actions.order.parameters.deployment_platform_config`
+
+Read-Only:
+
+- `config` (String) Deployment platform configuration details. (JSON string)
+- `kind` (String) Deployment platform kind. Options: AWS_STATIC, AWS_RBAC, AWS_OIDC, AZURE_STATIC, AZURE_OIDC, GCP_STATIC, GCP_OIDC
+
+
+
+### Nested Schema for `actions.order.parameters.environment_variables`
+
+Read-Only:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--actions--order--parameters--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `actions.order.parameters.environment_variables.config`
+
+Read-Only:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+- `var_name` (String) Name of the variable.
+
+
+
+
+### Nested Schema for `actions.order.parameters.terraform_action`
+
+Read-Only:
+
+- `action` (String) Terraform action to execute. E.g., "apply", "plan", "destroy".
+
+
+
+### Nested Schema for `actions.order.parameters.wf_steps_config`
+
+Read-Only:
+
+- `approval` (Boolean) Enable approval for the workflow step.
+- `cmd_override` (String) Override command for the step.
+- `environment_variables` (Attributes List) Environment variables for the workflow steps. (see [below for nested schema](#nestedatt--actions--order--parameters--wf_steps_config--environment_variables))
+- `mount_points` (Attributes List) Mount points for the step. (see [below for nested schema](#nestedatt--actions--order--parameters--wf_steps_config--mount_points))
+- `name` (String) Step name.
+- `timeout` (Number) Workflow step execution timeout in seconds.
+- `wf_step_input_data` (Attributes) Workflow step input data (JSON string) (see [below for nested schema](#nestedatt--actions--order--parameters--wf_steps_config--wf_step_input_data))
+- `wf_step_template_id` (String) Workflow step template ID.
+
+
+### Nested Schema for `actions.order.parameters.wf_steps_config.environment_variables`
+
+Read-Only:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--actions--order--parameters--wf_steps_config--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `actions.order.parameters.wf_steps_config.environment_variables.config`
+
+Read-Only:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+- `var_name` (String) Name of the variable.
+
+
+
+
+### Nested Schema for `actions.order.parameters.wf_steps_config.mount_points`
+
+Read-Only:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+### Nested Schema for `actions.order.parameters.wf_steps_config.wf_step_input_data`
+
+Read-Only:
+
+- `data` (String) Input data (JSON).
+- `schema_type` (String) Schema type for the input data. Options: FORM_JSONSCHEMA
+
+
+
+
+
+
+
+### Nested Schema for `deprecation`
+
+Read-Only:
+
+- `effective_date` (String) Effective date for after which revision will be deprecated
+- `message` (String) Marking a template revision for deprecation
+
+
+
+### Nested Schema for `workflows_config`
+
+Read-Only:
+
+- `workflows` (Attributes List) List of workflows that make up the stack. (see [below for nested schema](#nestedatt--workflows_config--workflows))
+
+
+### Nested Schema for `workflows_config.workflows`
+
+Read-Only:
+
+- `approvers` (List of String) List of approvers.
+- `deployment_platform_config` (Attributes List) Deployment platform configuration. (see [below for nested schema](#nestedatt--workflows_config--workflows--deployment_platform_config))
+- `description` (String) Description of this workflow.
+- `environment_variables` (Attributes List) Environment variables for the workflow. (see [below for nested schema](#nestedatt--workflows_config--workflows--environment_variables))
+- `is_active` (String) Whether the stack template is active. Valid values:
+ 0 (false),
+ 1 (true)
+- `number_of_approvals_required` (Number) Number of approvals required.
+- `resource_name` (String) Name of the workflow resource within the stack.
+- `runner_constraints` (Attributes) Runner constraints for the workflow. (see [below for nested schema](#nestedatt--workflows_config--workflows--runner_constraints))
+- `tags` (List of String) Tags for the workflow.
+- `terraform_config` (Attributes) Terraform configuration. Valid only for terraform type template (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config))
+- `user_job_cpu` (Number) CPU limit for the user job.
+- `user_job_memory` (Number) Memory limit for the user job.
+- `user_schedules` (Attributes List) Scheduled run configuration. (see [below for nested schema](#nestedatt--workflows_config--workflows--user_schedules))
+- `vcs_config` (Attributes) VCS (version control) configuration for the workflow. (see [below for nested schema](#nestedatt--workflows_config--workflows--vcs_config))
+- `wf_steps_config` (Attributes List) Workflow steps configuration. (see [below for nested schema](#nestedatt--workflows_config--workflows--wf_steps_config))
+- `wf_type` (String) Workflow type. Valid values: TERRAFORM, OPENTOFU, CUSTOM
+
+
+### Nested Schema for `workflows_config.workflows.deployment_platform_config`
+
+Read-Only:
+
+- `config` (String) Deployment platform configuration details. (JSON string)
+- `kind` (String) Deployment platform kind. Options: AWS_STATIC, AWS_RBAC, AWS_OIDC, AZURE_STATIC, AZURE_OIDC, GCP_STATIC, GCP_OIDC
+
+
+
+### Nested Schema for `workflows_config.workflows.environment_variables`
+
+Read-Only:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--workflows_config--workflows--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `workflows_config.workflows.environment_variables.config`
+
+Read-Only:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+- `var_name` (String) Name of the variable.
+
+
+
+
+### Nested Schema for `workflows_config.workflows.runner_constraints`
+
+Read-Only:
+
+- `names` (List of String) Id of the runner group
+- `type` (String) Type of runner. Valid options: shared or external
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config`
+
+Read-Only:
+
+- `approval_pre_apply` (Boolean) Require approval before apply.
+- `drift_check` (Boolean) Enable drift check.
+- `drift_cron` (String) Cron expression for drift check.
+- `managed_terraform_state` (Boolean) Enable stackguardian managed terraform state.
+- `post_apply_hooks` (List of String) Hooks to run after apply.
+- `post_apply_wf_steps_config` (Attributes List) Workflow steps configuration to run after apply. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_apply_wf_steps_config))
+- `post_plan_hooks` (List of String) Hooks to run after plan.
+- `post_plan_wf_steps_config` (Attributes List) Workflow steps configuration to run after plan. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_plan_wf_steps_config))
+- `pre_apply_hooks` (List of String) Hooks to run before apply.
+- `pre_apply_wf_steps_config` (Attributes List) Workflow steps configuration to run before apply. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_apply_wf_steps_config))
+- `pre_init_hooks` (List of String) Hooks to run before init.
+- `pre_plan_hooks` (List of String) Hooks to run before plan.
+- `pre_plan_wf_steps_config` (Attributes List) Workflow steps configuration to run before plan. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_plan_wf_steps_config))
+- `run_pre_init_hooks_on_drift` (Boolean) Run pre-init hooks on drift detection.
+- `terraform_bin_path` (Attributes List) Mount points for terraform binary. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--terraform_bin_path))
+- `terraform_init_options` (String) Additional options for terraform init.
+- `terraform_plan_options` (String) Additional options for terraform plan.
+- `terraform_version` (String) Terraform version to use.
+- `timeout` (Number) Timeout for terraform operations in seconds.
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_apply_wf_steps_config`
+
+Read-Only:
+
+- `approval` (Boolean) Enable approval for the workflow step.
+- `cmd_override` (String) Override command for the step.
+- `environment_variables` (Attributes List) Environment variables for the workflow steps. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_apply_wf_steps_config--environment_variables))
+- `mount_points` (Attributes List) Mount points for the step. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_apply_wf_steps_config--mount_points))
+- `name` (String) Step name.
+- `timeout` (Number) Workflow step execution timeout in seconds.
+- `wf_step_input_data` (Attributes) Workflow step input data (JSON string) (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_apply_wf_steps_config--wf_step_input_data))
+- `wf_step_template_id` (String) Workflow step template ID.
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_apply_wf_steps_config.environment_variables`
+
+Read-Only:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_apply_wf_steps_config--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_apply_wf_steps_config.environment_variables.config`
+
+Read-Only:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+- `var_name` (String) Name of the variable.
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_apply_wf_steps_config.mount_points`
+
+Read-Only:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_apply_wf_steps_config.wf_step_input_data`
+
+Read-Only:
+
+- `data` (String) Input data (JSON).
+- `schema_type` (String) Schema type for the input data. Options: FORM_JSONSCHEMA
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_plan_wf_steps_config`
+
+Read-Only:
+
+- `approval` (Boolean) Enable approval for the workflow step.
+- `cmd_override` (String) Override command for the step.
+- `environment_variables` (Attributes List) Environment variables for the workflow steps. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_plan_wf_steps_config--environment_variables))
+- `mount_points` (Attributes List) Mount points for the step. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_plan_wf_steps_config--mount_points))
+- `name` (String) Step name.
+- `timeout` (Number) Workflow step execution timeout in seconds.
+- `wf_step_input_data` (Attributes) Workflow step input data (JSON string) (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_plan_wf_steps_config--wf_step_input_data))
+- `wf_step_template_id` (String) Workflow step template ID.
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_plan_wf_steps_config.environment_variables`
+
+Read-Only:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_plan_wf_steps_config--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_plan_wf_steps_config.environment_variables.config`
+
+Read-Only:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+- `var_name` (String) Name of the variable.
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_plan_wf_steps_config.mount_points`
+
+Read-Only:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_plan_wf_steps_config.wf_step_input_data`
+
+Read-Only:
+
+- `data` (String) Input data (JSON).
+- `schema_type` (String) Schema type for the input data. Options: FORM_JSONSCHEMA
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_apply_wf_steps_config`
+
+Read-Only:
+
+- `approval` (Boolean) Enable approval for the workflow step.
+- `cmd_override` (String) Override command for the step.
+- `environment_variables` (Attributes List) Environment variables for the workflow steps. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_apply_wf_steps_config--environment_variables))
+- `mount_points` (Attributes List) Mount points for the step. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_apply_wf_steps_config--mount_points))
+- `name` (String) Step name.
+- `timeout` (Number) Workflow step execution timeout in seconds.
+- `wf_step_input_data` (Attributes) Workflow step input data (JSON string) (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_apply_wf_steps_config--wf_step_input_data))
+- `wf_step_template_id` (String) Workflow step template ID.
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_apply_wf_steps_config.environment_variables`
+
+Read-Only:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_apply_wf_steps_config--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_apply_wf_steps_config.environment_variables.config`
+
+Read-Only:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+- `var_name` (String) Name of the variable.
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_apply_wf_steps_config.mount_points`
+
+Read-Only:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_apply_wf_steps_config.wf_step_input_data`
+
+Read-Only:
+
+- `data` (String) Input data (JSON).
+- `schema_type` (String) Schema type for the input data. Options: FORM_JSONSCHEMA
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_plan_wf_steps_config`
+
+Read-Only:
+
+- `approval` (Boolean) Enable approval for the workflow step.
+- `cmd_override` (String) Override command for the step.
+- `environment_variables` (Attributes List) Environment variables for the workflow steps. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_plan_wf_steps_config--environment_variables))
+- `mount_points` (Attributes List) Mount points for the step. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_plan_wf_steps_config--mount_points))
+- `name` (String) Step name.
+- `timeout` (Number) Workflow step execution timeout in seconds.
+- `wf_step_input_data` (Attributes) Workflow step input data (JSON string) (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_plan_wf_steps_config--wf_step_input_data))
+- `wf_step_template_id` (String) Workflow step template ID.
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_plan_wf_steps_config.environment_variables`
+
+Read-Only:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_plan_wf_steps_config--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_plan_wf_steps_config.environment_variables.config`
+
+Read-Only:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+- `var_name` (String) Name of the variable.
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_plan_wf_steps_config.mount_points`
+
+Read-Only:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_plan_wf_steps_config.wf_step_input_data`
+
+Read-Only:
+
+- `data` (String) Input data (JSON).
+- `schema_type` (String) Schema type for the input data. Options: FORM_JSONSCHEMA
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.terraform_bin_path`
+
+Read-Only:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+
+### Nested Schema for `workflows_config.workflows.user_schedules`
+
+Read-Only:
+
+- `cron` (String) Cron expression defining the schedule.
+- `desc` (String)
+- `name` (String)
+- `state` (String) State of the schedule. Options: ENABLED, DISABLED
+
+
+
+### Nested Schema for `workflows_config.workflows.vcs_config`
+
+Read-Only:
+
+- `iac_input_data` (Attributes) IaC input data for the workflow. (see [below for nested schema](#nestedatt--workflows_config--workflows--vcs_config--iac_input_data))
+- `iac_vcs_config` (Attributes) IaC VCS configuration. (see [below for nested schema](#nestedatt--workflows_config--workflows--vcs_config--iac_vcs_config))
+
+
+### Nested Schema for `workflows_config.workflows.vcs_config.iac_input_data`
+
+Read-Only:
+
+- `data` (String) Input data as a JSON string.
+- `schema_id` (String)
+- `schema_type` (String)
+
+
+
+### Nested Schema for `workflows_config.workflows.vcs_config.iac_vcs_config`
+
+Read-Only:
+
+- `custom_source` (Attributes) Custom source configuration. (see [below for nested schema](#nestedatt--workflows_config--workflows--vcs_config--iac_vcs_config--custom_source))
+- `iac_template_id` (String) ID of the IaC template from the marketplace.
+- `use_marketplace_template` (Boolean) Whether to use a marketplace template.
+
+
+### Nested Schema for `workflows_config.workflows.vcs_config.iac_vcs_config.custom_source`
+
+Read-Only:
+
+- `config` (Attributes) Source configuration details. (see [below for nested schema](#nestedatt--workflows_config--workflows--vcs_config--iac_vcs_config--custom_source--config))
+- `source_config_dest_kind` (String) Destination kind for the source configuration. Options: GITHUB_COM, GITHUB_APP_CUSTOM, GITLAB_OAUTH_SSH, GITLAB_COM, AZURE_DEVOPS
+
+
+### Nested Schema for `workflows_config.workflows.vcs_config.iac_vcs_config.custom_source.config`
+
+Read-Only:
+
+- `auth` (String, Sensitive)
+- `git_core_auto_crlf` (Boolean)
+- `git_sparse_checkout_config` (String)
+- `include_sub_module` (Boolean)
+- `is_private` (Boolean)
+- `ref` (String)
+- `repo` (String)
+- `working_dir` (String)
+
+
+
+
+
+
+### Nested Schema for `workflows_config.workflows.wf_steps_config`
+
+Read-Only:
+
+- `approval` (Boolean) Enable approval for the workflow step.
+- `cmd_override` (String) Override command for the step.
+- `environment_variables` (Attributes List) Environment variables for the workflow steps. (see [below for nested schema](#nestedatt--workflows_config--workflows--wf_steps_config--environment_variables))
+- `mount_points` (Attributes List) Mount points for the step. (see [below for nested schema](#nestedatt--workflows_config--workflows--wf_steps_config--mount_points))
+- `name` (String) Step name.
+- `timeout` (Number) Workflow step execution timeout in seconds.
+- `wf_step_input_data` (Attributes) Workflow step input data (JSON string) (see [below for nested schema](#nestedatt--workflows_config--workflows--wf_steps_config--wf_step_input_data))
+- `wf_step_template_id` (String) Workflow step template ID.
+
+
+### Nested Schema for `workflows_config.workflows.wf_steps_config.environment_variables`
+
+Read-Only:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--workflows_config--workflows--wf_steps_config--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `workflows_config.workflows.wf_steps_config.environment_variables.config`
+
+Read-Only:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+- `var_name` (String) Name of the variable.
+
+
+
+
+### Nested Schema for `workflows_config.workflows.wf_steps_config.mount_points`
+
+Read-Only:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+### Nested Schema for `workflows_config.workflows.wf_steps_config.wf_step_input_data`
+
+Read-Only:
+
+- `data` (String) Input data (JSON).
+- `schema_type` (String) Schema type for the input data. Options: FORM_JSONSCHEMA
+
+
+
+
+
+
diff --git a/docs/resources/stack_template.md b/docs/resources/stack_template.md
new file mode 100644
index 0000000..ffe7279
--- /dev/null
+++ b/docs/resources/stack_template.md
@@ -0,0 +1,78 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "stackguardian_stack_template Resource - terraform-provider-stackguardian"
+subcategory: ""
+description: |-
+
+---
+
+# stackguardian_stack_template (Resource)
+
+
+⚠️ This feature is currently in BETA.
+
+
+## Example Usage
+
+```terraform
+# Example 1: Basic stack template
+resource "stackguardian_stack_template" "basic" {
+ template_name = "my-stack-template"
+ source_config_kind = "TERRAFORM"
+ is_public = "0"
+ tags = ["terraform", "production"]
+}
+```
+
+
+## Schema
+
+### Required
+
+- `source_config_kind` (String) Source configuration kind for the stack template. Valid values:
+ TERRAFORM,
+ OPENTOFU,
+ ANSIBLE_PLAYBOOK,
+ HELM,
+ KUBECTL,
+ CLOUDFORMATION,
+ MIXED,
+ CUSTOM
+- `template_name` (String) Name of the stack template. Must be less than 100 characters.
+
+### Optional
+
+- `context_tags` (Map of String) Context tags for stack template
+- `description` (String) A brief description of the stack template. Must be less than 256 characters.
+- `id` (String) ID of the resource — Use this attribute: - Set the Id of the resource manually
- To reference the resource in other resources. The `resource_name` attribute is still available but its use is discouraged and may not work in some cases.
+- `is_active` (String) Whether the stack template is active. Valid values:
+ 0 (false),
+ 1 (true)
+- `is_public` (String) Whether the stack template is publicly available. Valid values:
+ 0 (false),
+ 1 (true)
+- `shared_orgs_list` (List of String) List of organization IDs with which this template is shared.
+- `tags` (List of String) A list of tags associated with the stack template. A maximum of 10 tags are allowed.
+
+### Read-Only
+
+- `owner_org` (String) Organization that owns the stack template.
+
+
+
+## Import
+
+Import existing resource
+
+### Using Import block (terraform v1.5.0 and later)
+```terraform
+import {
+ to = stackguardian_stack_template.example
+ id = "template-name"
+}
+```
+
+### Using CLI
+```bash
+terraform import stackguardian_stack_template.example template-name
+```
diff --git a/docs/resources/stack_template_revision.md b/docs/resources/stack_template_revision.md
new file mode 100644
index 0000000..01f9c4b
--- /dev/null
+++ b/docs/resources/stack_template_revision.md
@@ -0,0 +1,1003 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "stackguardian_stack_template_revision Resource - terraform-provider-stackguardian"
+subcategory: ""
+description: |-
+
+---
+
+# stackguardian_stack_template_revision (Resource)
+
+
+⚠️ This feature is currently in BETA.
+
+
+## Example Usage
+
+```terraform
+resource "stackguardian_stack_template" "example" {
+ template_name = "my-stack-template"
+ source_config_kind = "TERRAFORM"
+ is_public = "0"
+ tags = ["terraform", "production"]
+}
+
+# Example 1: Basic stack template revision
+resource "stackguardian_stack_template_revision" "basic" {
+ parent_template_id = stackguardian_stack_template.example.id
+ alias = "v1"
+ notes = "Initial revision"
+ description = "First revision of the stack template"
+ source_config_kind = "TERRAFORM"
+
+ workflows_config = {
+ workflows = [
+ {
+ id = "d8dfaf15-2ad9-da29-8af0-c6b288b12089"
+ template_id = "my-workflow-template"
+ resource_name = "wf-1"
+
+ terraform_config = {
+ managed_terraform_state = true
+ terraform_version = "1.5.7"
+ }
+ }
+ ]
+ }
+}
+
+# Example 2: Stack template revision with VCS config
+resource "stackguardian_stack_template_revision" "with_vcs" {
+ parent_template_id = stackguardian_stack_template.example.id
+ alias = "v2"
+ notes = "Revision with VCS configuration"
+ description = "Stack template revision with VCS and input data"
+ source_config_kind = "TERRAFORM"
+
+ workflows_config = {
+ workflows = [
+ {
+ id = "d8dfaf15-2ad9-da29-8af0-c6b288b12089"
+ template_id = "my-workflow-template"
+ resource_name = "wf-1"
+
+ vcs_config = {
+ iac_vcs_config = {
+ use_marketplace_template = true
+ iac_template_id = "my-workflow-template"
+ }
+ iac_input_data = {
+ schema_type = "RAW_JSON"
+ data = jsonencode({
+ bucket_region = "eu-central-1"
+ })
+ }
+ }
+
+ terraform_config = {
+ managed_terraform_state = true
+ terraform_version = "1.5.7"
+ }
+ }
+ ]
+ }
+}
+```
+
+
+## Schema
+
+### Required
+
+- `parent_template_id` (String) ID of the parent stack template to create the revision under.
+- `source_config_kind` (String) Source configuration kind for the stack template. Valid values:
+ TERRAFORM,
+ OPENTOFU,
+ ANSIBLE_PLAYBOOK,
+ HELM,
+ KUBECTL,
+ CLOUDFORMATION,
+ MIXED,
+ CUSTOM
+
+### Optional
+
+- `actions` (Attributes Map) JSON-encoded map of actions for the stack template revision. (see [below for nested schema](#nestedatt--actions))
+- `alias` (String) Human-readable alias for the revision (e.g., `v1.0.0`).
+- `context_tags` (Map of String) Context tags for stack template revision
+- `deprecation` (Attributes) Deprecation information for this resource. (see [below for nested schema](#nestedatt--deprecation))
+- `description` (String) Long description for the stack template revision.
+- `is_active` (String) Whether the stack template is active. Valid values:
+ 0 (false),
+ 1 (true)
+- `is_public` (String) Whether the stack template is publicly available. Valid values:
+ 0 (false),
+ 1 (true)
+- `notes` (String) Release notes or changelog for this revision.
+- `tags` (List of String) A list of tags associated with the stack template revision. A maximum of 10 tags are allowed.
+- `workflows_config` (Attributes) JSON-encoded workflows configuration for the stack template revision. (see [below for nested schema](#nestedatt--workflows_config))
+
+### Read-Only
+
+- `id` (String) Unique identifier of the stack template revision.
+- `template_id` (String) ID of the parent stack template.
+
+
+### Nested Schema for `actions`
+
+Optional:
+
+- `default` (Boolean) Whether this is the default action.
+- `description` (String) Description of the action.
+- `name` (String) Name of the action.
+- `order` (Attributes Map) Ordered map of workflow IDs to their action configurations. (see [below for nested schema](#nestedatt--actions--order))
+
+
+### Nested Schema for `actions.order`
+
+Optional:
+
+- `dependencies` (Attributes List) List of workflow dependencies that must complete before this step runs. (see [below for nested schema](#nestedatt--actions--order--dependencies))
+- `parameters` (Attributes) Run configuration parameters for the action step. (see [below for nested schema](#nestedatt--actions--order--parameters))
+
+
+### Nested Schema for `actions.order.dependencies`
+
+Required:
+
+- `id` (String) ID of the dependent workflow.
+
+Optional:
+
+- `condition` (Attributes) Condition that must be met by the dependency. (see [below for nested schema](#nestedatt--actions--order--dependencies--condition))
+
+
+### Nested Schema for `actions.order.dependencies.condition`
+
+Required:
+
+- `latest_status` (String) Required latest status of the dependency (e.g., COMPLETED).
+
+
+
+
+### Nested Schema for `actions.order.parameters`
+
+Optional:
+
+- `deployment_platform_config` (Attributes List) Deployment platform configuration. (see [below for nested schema](#nestedatt--actions--order--parameters--deployment_platform_config))
+- `environment_variables` (Attributes List) Environment variables. (see [below for nested schema](#nestedatt--actions--order--parameters--environment_variables))
+- `terraform_action` (Attributes) Terraform-specific action parameters. (see [below for nested schema](#nestedatt--actions--order--parameters--terraform_action))
+- `wf_steps_config` (Attributes List) Workflow steps configuration. (see [below for nested schema](#nestedatt--actions--order--parameters--wf_steps_config))
+
+
+### Nested Schema for `actions.order.parameters.deployment_platform_config`
+
+Required:
+
+- `kind` (String) Deployment platform kind. Options: AWS_STATIC, AWS_RBAC, AWS_OIDC, AZURE_STATIC, AZURE_OIDC, GCP_STATIC, GCP_OIDC
+
+Optional:
+
+- `config` (String) Deployment platform configuration details. (JSON string)
+
+
+
+### Nested Schema for `actions.order.parameters.environment_variables`
+
+Required:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--actions--order--parameters--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `actions.order.parameters.environment_variables.config`
+
+Required:
+
+- `var_name` (String) Name of the variable.
+
+Optional:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+
+
+
+
+### Nested Schema for `actions.order.parameters.terraform_action`
+
+Read-Only:
+
+- `action` (String) Terraform action to execute. E.g., "apply", "plan", "destroy".
+
+
+
+### Nested Schema for `actions.order.parameters.wf_steps_config`
+
+Required:
+
+- `name` (String) Step name.
+- `wf_step_template_id` (String) Workflow step template ID.
+
+Optional:
+
+- `approval` (Boolean) Enable approval for the workflow step.
+- `cmd_override` (String) Override command for the step.
+- `environment_variables` (Attributes List) Environment variables for the workflow steps. (see [below for nested schema](#nestedatt--actions--order--parameters--wf_steps_config--environment_variables))
+- `mount_points` (Attributes List) Mount points for the step. (see [below for nested schema](#nestedatt--actions--order--parameters--wf_steps_config--mount_points))
+- `timeout` (Number) Workflow step execution timeout in seconds.
+- `wf_step_input_data` (Attributes) Workflow step input data (JSON string) (see [below for nested schema](#nestedatt--actions--order--parameters--wf_steps_config--wf_step_input_data))
+
+
+### Nested Schema for `actions.order.parameters.wf_steps_config.environment_variables`
+
+Required:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--actions--order--parameters--wf_steps_config--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `actions.order.parameters.wf_steps_config.environment_variables.config`
+
+Required:
+
+- `var_name` (String) Name of the variable.
+
+Optional:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+
+
+
+
+### Nested Schema for `actions.order.parameters.wf_steps_config.mount_points`
+
+Optional:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+### Nested Schema for `actions.order.parameters.wf_steps_config.wf_step_input_data`
+
+Optional:
+
+- `data` (String) Input data (JSON).
+- `schema_type` (String) Schema type for the input data. Options: FORM_JSONSCHEMA
+
+
+
+
+
+
+
+### Nested Schema for `deprecation`
+
+Optional:
+
+- `effective_date` (String) Effective date for after which revision will be deprecated
+- `message` (String) Marking a template revision for deprecation
+
+
+
+### Nested Schema for `workflows_config`
+
+Optional:
+
+- `workflows` (Attributes List) List of workflows that make up the stack. (see [below for nested schema](#nestedatt--workflows_config--workflows))
+
+
+### Nested Schema for `workflows_config.workflows`
+
+Required:
+
+- `id` (String) UUID identifying the workflow within the stack template.
+- `template_id` (String) ID of the workflow template that this workflow is based on.
+
+Optional:
+
+- `approvers` (List of String) List of approvers.
+- `deployment_platform_config` (Attributes List) Deployment platform configuration. (see [below for nested schema](#nestedatt--workflows_config--workflows--deployment_platform_config))
+- `environment_variables` (Attributes List) Environment variables for the workflow. (see [below for nested schema](#nestedatt--workflows_config--workflows--environment_variables))
+- `iac_input_data` (Attributes) Top-level IaC input data for this workflow, used when the workflow is instantiated from a workflow template (`template_id`). (see [below for nested schema](#nestedatt--workflows_config--workflows--iac_input_data))
+- `input_schemas` (Attributes List) Input schema definitions for this workflow. (see [below for nested schema](#nestedatt--workflows_config--workflows--input_schemas))
+- `mini_steps` (Attributes) Actions that are required to be performed once workflow execution is complete (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps))
+- `number_of_approvals_required` (Number) Number of approvals required.
+- `resource_name` (String) Name of the workflow resource within the stack.
+- `runner_constraints` (Attributes) Runner constraints for the workflow. (see [below for nested schema](#nestedatt--workflows_config--workflows--runner_constraints))
+- `terraform_config` (Attributes) Terraform configuration. Valid only for terraform type template (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config))
+- `user_job_cpu` (Number) CPU limit for the user job.
+- `user_job_memory` (Number) Memory limit for the user job.
+- `user_schedules` (Attributes List) Scheduled run configuration. (see [below for nested schema](#nestedatt--workflows_config--workflows--user_schedules))
+- `vcs_config` (Attributes) VCS (version control) configuration for the workflow. (see [below for nested schema](#nestedatt--workflows_config--workflows--vcs_config))
+- `wf_steps_config` (Attributes List) Workflow steps configuration. (see [below for nested schema](#nestedatt--workflows_config--workflows--wf_steps_config))
+
+
+### Nested Schema for `workflows_config.workflows.deployment_platform_config`
+
+Required:
+
+- `kind` (String) Deployment platform kind. Options: AWS_STATIC, AWS_RBAC, AWS_OIDC, AZURE_STATIC, AZURE_OIDC, GCP_STATIC, GCP_OIDC
+
+Optional:
+
+- `config` (String) Deployment platform configuration details. (JSON string)
+
+
+
+### Nested Schema for `workflows_config.workflows.environment_variables`
+
+Required:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--workflows_config--workflows--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `workflows_config.workflows.environment_variables.config`
+
+Required:
+
+- `var_name` (String) Name of the variable.
+
+Optional:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+
+
+
+
+### Nested Schema for `workflows_config.workflows.iac_input_data`
+
+Required:
+
+- `schema_type` (String) Schema type for the input data (e.g. RAW_JSON).
+
+Optional:
+
+- `data` (String) Input data as a JSON string.
+
+
+
+### Nested Schema for `workflows_config.workflows.input_schemas`
+
+Required:
+
+- `type` (String) Schema type (e.g. FORM_JSONSCHEMA).
+
+Optional:
+
+- `description` (String)
+- `encoded_data` (String) Base64-encoded schema data.
+- `id` (String)
+- `is_committed` (Boolean)
+- `name` (String)
+- `ui_schema_data` (String)
+
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps`
+
+Optional:
+
+- `notifications` (Attributes) Configuration for notifications to be sent on workflow completion (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--notifications))
+- `webhooks` (Attributes) Configuration for webhooks to be triggered on completion. Statuses on which webhooks can be sent: approval_required, cancelled, completed, drift_detected, errored (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--webhooks))
+- `wf_chaining` (Attributes) Configuration for other workflows to be triggered on completion. Statuses on which workflows can be chained: completed, errored (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--wf_chaining))
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.notifications`
+
+Optional:
+
+- `email` (Attributes) Configuration for email notifications to be sent on completion. Statuses on which notifications can be sent: approval_required, cancelled, completed, drift_detected, errored (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--notifications--email))
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.notifications.email`
+
+Optional:
+
+- `approval_required` (Attributes List) (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--notifications--email--approval_required))
+- `cancelled` (Attributes List) (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--notifications--email--cancelled))
+- `completed` (Attributes List) (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--notifications--email--completed))
+- `drift_detected` (Attributes List) (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--notifications--email--drift_detected))
+- `errored` (Attributes List) (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--notifications--email--errored))
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.notifications.email.approval_required`
+
+Optional:
+
+- `recipients` (List of String) List of emails
+
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.notifications.email.cancelled`
+
+Optional:
+
+- `recipients` (List of String) List of emails
+
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.notifications.email.completed`
+
+Optional:
+
+- `recipients` (List of String) List of emails
+
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.notifications.email.drift_detected`
+
+Optional:
+
+- `recipients` (List of String) List of emails
+
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.notifications.email.errored`
+
+Optional:
+
+- `recipients` (List of String) List of emails
+
+
+
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.webhooks`
+
+Optional:
+
+- `approval_required` (Attributes List) (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--webhooks--approval_required))
+- `cancelled` (Attributes List) (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--webhooks--cancelled))
+- `completed` (Attributes List) (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--webhooks--completed))
+- `drift_detected` (Attributes List) (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--webhooks--drift_detected))
+- `errored` (Attributes List) (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--webhooks--errored))
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.webhooks.approval_required`
+
+Required:
+
+- `webhook_name` (String) Webhook name
+- `webhook_url` (String) Webhook URL
+
+Optional:
+
+- `webhook_secret` (String) Secret to be sent with API request to webhook url
+
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.webhooks.cancelled`
+
+Required:
+
+- `webhook_name` (String) Webhook name
+- `webhook_url` (String) Webhook URL
+
+Optional:
+
+- `webhook_secret` (String) Secret to be sent with API request to webhook url
+
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.webhooks.completed`
+
+Required:
+
+- `webhook_name` (String) Webhook name
+- `webhook_url` (String) Webhook URL
+
+Optional:
+
+- `webhook_secret` (String) Secret to be sent with API request to webhook url
+
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.webhooks.drift_detected`
+
+Required:
+
+- `webhook_name` (String) Webhook name
+- `webhook_url` (String) Webhook URL
+
+Optional:
+
+- `webhook_secret` (String) Secret to be sent with API request to webhook url
+
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.webhooks.errored`
+
+Required:
+
+- `webhook_name` (String) Webhook name
+- `webhook_url` (String) Webhook URL
+
+Optional:
+
+- `webhook_secret` (String) Secret to be sent with API request to webhook url
+
+
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.wf_chaining`
+
+Optional:
+
+- `completed` (Attributes List) (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--wf_chaining--completed))
+- `errored` (Attributes List) (see [below for nested schema](#nestedatt--workflows_config--workflows--mini_steps--wf_chaining--errored))
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.wf_chaining.completed`
+
+Required:
+
+- `workflow_group_id` (String) Workflow group id for the workflow.
+
+Optional:
+
+- `stack_id` (String) Stack id for the stack to be triggered.
+- `stack_run_payload` (String) JSON string specifying overrides for the stack to be triggered
+- `workflow_id` (String) Workflow id for the workflow to be triggered
+- `workflow_run_payload` (String) JSON string specifying overrides for the workflow to be triggered
+
+
+
+### Nested Schema for `workflows_config.workflows.mini_steps.wf_chaining.errored`
+
+Required:
+
+- `workflow_group_id` (String) Workflow group id for the workflow.
+
+Optional:
+
+- `stack_id` (String) Stack id for the stack to be triggered.
+- `stack_run_payload` (String) JSON string specifying overrides for the stack to be triggered
+- `workflow_id` (String) Workflow id for the workflow to be triggered
+- `workflow_run_payload` (String) JSON string specifying overrides for the workflow to be triggered
+
+
+
+
+
+### Nested Schema for `workflows_config.workflows.runner_constraints`
+
+Required:
+
+- `type` (String) Type of runner. Valid options: shared or external
+
+Optional:
+
+- `names` (List of String) Id of the runner group
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config`
+
+Optional:
+
+- `approval_pre_apply` (Boolean) Require approval before apply.
+- `drift_check` (Boolean) Enable drift check.
+- `drift_cron` (String) Cron expression for drift check.
+- `managed_terraform_state` (Boolean) Enable stackguardian managed terraform state.
+- `post_apply_hooks` (List of String) Hooks to run after apply.
+- `post_apply_wf_steps_config` (Attributes List) Workflow steps configuration to run after apply. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_apply_wf_steps_config))
+- `post_plan_hooks` (List of String) Hooks to run after plan.
+- `post_plan_wf_steps_config` (Attributes List) Workflow steps configuration to run after plan. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_plan_wf_steps_config))
+- `pre_apply_hooks` (List of String) Hooks to run before apply.
+- `pre_apply_wf_steps_config` (Attributes List) Workflow steps configuration to run before apply. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_apply_wf_steps_config))
+- `pre_init_hooks` (List of String) Hooks to run before init.
+- `pre_plan_hooks` (List of String) Hooks to run before plan.
+- `pre_plan_wf_steps_config` (Attributes List) Workflow steps configuration to run before plan. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_plan_wf_steps_config))
+- `run_pre_init_hooks_on_drift` (Boolean) Run pre-init hooks on drift detection.
+- `terraform_bin_path` (Attributes List) Mount points for terraform binary. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--terraform_bin_path))
+- `terraform_init_options` (String) Additional options for terraform init.
+- `terraform_plan_options` (String) Additional options for terraform plan.
+- `terraform_version` (String) Terraform version to use.
+- `timeout` (Number) Timeout for terraform operations in seconds.
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_apply_wf_steps_config`
+
+Required:
+
+- `name` (String) Step name.
+- `wf_step_template_id` (String) Workflow step template ID.
+
+Optional:
+
+- `approval` (Boolean) Enable approval for the workflow step.
+- `cmd_override` (String) Override command for the step.
+- `environment_variables` (Attributes List) Environment variables for the workflow steps. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_apply_wf_steps_config--environment_variables))
+- `mount_points` (Attributes List) Mount points for the step. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_apply_wf_steps_config--mount_points))
+- `timeout` (Number) Workflow step execution timeout in seconds.
+- `wf_step_input_data` (Attributes) Workflow step input data (JSON string) (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_apply_wf_steps_config--wf_step_input_data))
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_apply_wf_steps_config.environment_variables`
+
+Required:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_apply_wf_steps_config--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_apply_wf_steps_config.environment_variables.config`
+
+Required:
+
+- `var_name` (String) Name of the variable.
+
+Optional:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_apply_wf_steps_config.mount_points`
+
+Optional:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_apply_wf_steps_config.wf_step_input_data`
+
+Optional:
+
+- `data` (String) Input data (JSON).
+- `schema_type` (String) Schema type for the input data. Options: FORM_JSONSCHEMA
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_plan_wf_steps_config`
+
+Required:
+
+- `name` (String) Step name.
+- `wf_step_template_id` (String) Workflow step template ID.
+
+Optional:
+
+- `approval` (Boolean) Enable approval for the workflow step.
+- `cmd_override` (String) Override command for the step.
+- `environment_variables` (Attributes List) Environment variables for the workflow steps. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_plan_wf_steps_config--environment_variables))
+- `mount_points` (Attributes List) Mount points for the step. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_plan_wf_steps_config--mount_points))
+- `timeout` (Number) Workflow step execution timeout in seconds.
+- `wf_step_input_data` (Attributes) Workflow step input data (JSON string) (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_plan_wf_steps_config--wf_step_input_data))
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_plan_wf_steps_config.environment_variables`
+
+Required:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--post_plan_wf_steps_config--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_plan_wf_steps_config.environment_variables.config`
+
+Required:
+
+- `var_name` (String) Name of the variable.
+
+Optional:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_plan_wf_steps_config.mount_points`
+
+Optional:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.post_plan_wf_steps_config.wf_step_input_data`
+
+Optional:
+
+- `data` (String) Input data (JSON).
+- `schema_type` (String) Schema type for the input data. Options: FORM_JSONSCHEMA
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_apply_wf_steps_config`
+
+Required:
+
+- `name` (String) Step name.
+- `wf_step_template_id` (String) Workflow step template ID.
+
+Optional:
+
+- `approval` (Boolean) Enable approval for the workflow step.
+- `cmd_override` (String) Override command for the step.
+- `environment_variables` (Attributes List) Environment variables for the workflow steps. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_apply_wf_steps_config--environment_variables))
+- `mount_points` (Attributes List) Mount points for the step. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_apply_wf_steps_config--mount_points))
+- `timeout` (Number) Workflow step execution timeout in seconds.
+- `wf_step_input_data` (Attributes) Workflow step input data (JSON string) (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_apply_wf_steps_config--wf_step_input_data))
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_apply_wf_steps_config.environment_variables`
+
+Required:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_apply_wf_steps_config--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_apply_wf_steps_config.environment_variables.config`
+
+Required:
+
+- `var_name` (String) Name of the variable.
+
+Optional:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_apply_wf_steps_config.mount_points`
+
+Optional:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_apply_wf_steps_config.wf_step_input_data`
+
+Optional:
+
+- `data` (String) Input data (JSON).
+- `schema_type` (String) Schema type for the input data. Options: FORM_JSONSCHEMA
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_plan_wf_steps_config`
+
+Required:
+
+- `name` (String) Step name.
+- `wf_step_template_id` (String) Workflow step template ID.
+
+Optional:
+
+- `approval` (Boolean) Enable approval for the workflow step.
+- `cmd_override` (String) Override command for the step.
+- `environment_variables` (Attributes List) Environment variables for the workflow steps. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_plan_wf_steps_config--environment_variables))
+- `mount_points` (Attributes List) Mount points for the step. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_plan_wf_steps_config--mount_points))
+- `timeout` (Number) Workflow step execution timeout in seconds.
+- `wf_step_input_data` (Attributes) Workflow step input data (JSON string) (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_plan_wf_steps_config--wf_step_input_data))
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_plan_wf_steps_config.environment_variables`
+
+Required:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--workflows_config--workflows--terraform_config--pre_plan_wf_steps_config--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_plan_wf_steps_config.environment_variables.config`
+
+Required:
+
+- `var_name` (String) Name of the variable.
+
+Optional:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_plan_wf_steps_config.mount_points`
+
+Optional:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.pre_plan_wf_steps_config.wf_step_input_data`
+
+Optional:
+
+- `data` (String) Input data (JSON).
+- `schema_type` (String) Schema type for the input data. Options: FORM_JSONSCHEMA
+
+
+
+
+### Nested Schema for `workflows_config.workflows.terraform_config.terraform_bin_path`
+
+Optional:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+
+### Nested Schema for `workflows_config.workflows.user_schedules`
+
+Required:
+
+- `cron` (String) Cron expression defining the schedule.
+- `state` (String) State of the schedule. Options: ENABLED, DISABLED
+
+Optional:
+
+- `desc` (String)
+- `name` (String)
+
+
+
+### Nested Schema for `workflows_config.workflows.vcs_config`
+
+Optional:
+
+- `iac_input_data` (Attributes) IaC input data for the workflow. (see [below for nested schema](#nestedatt--workflows_config--workflows--vcs_config--iac_input_data))
+- `iac_vcs_config` (Attributes) IaC VCS configuration. (see [below for nested schema](#nestedatt--workflows_config--workflows--vcs_config--iac_vcs_config))
+
+
+### Nested Schema for `workflows_config.workflows.vcs_config.iac_input_data`
+
+Required:
+
+- `schema_type` (String)
+
+Optional:
+
+- `data` (String) Input data as a JSON string.
+- `schema_id` (String)
+
+
+
+### Nested Schema for `workflows_config.workflows.vcs_config.iac_vcs_config`
+
+Optional:
+
+- `custom_source` (Attributes) Custom source configuration. (see [below for nested schema](#nestedatt--workflows_config--workflows--vcs_config--iac_vcs_config--custom_source))
+- `iac_template_id` (String) ID of the IaC template from the marketplace.
+- `use_marketplace_template` (Boolean) Whether to use a marketplace template.
+
+
+### Nested Schema for `workflows_config.workflows.vcs_config.iac_vcs_config.custom_source`
+
+Required:
+
+- `source_config_dest_kind` (String) Destination kind for the source configuration. Options: GITHUB_COM, GITHUB_APP_CUSTOM, GITLAB_OAUTH_SSH, GITLAB_COM, AZURE_DEVOPS
+
+Optional:
+
+- `config` (Attributes) Source configuration details. (see [below for nested schema](#nestedatt--workflows_config--workflows--vcs_config--iac_vcs_config--custom_source--config))
+
+
+### Nested Schema for `workflows_config.workflows.vcs_config.iac_vcs_config.custom_source.config`
+
+Optional:
+
+- `auth` (String, Sensitive)
+- `git_core_auto_crlf` (Boolean)
+- `git_sparse_checkout_config` (String)
+- `include_sub_module` (Boolean)
+- `is_private` (Boolean)
+- `ref` (String)
+- `repo` (String)
+- `working_dir` (String)
+
+
+
+
+
+
+### Nested Schema for `workflows_config.workflows.wf_steps_config`
+
+Required:
+
+- `name` (String) Step name.
+- `wf_step_template_id` (String) Workflow step template ID.
+
+Optional:
+
+- `approval` (Boolean) Enable approval for the workflow step.
+- `cmd_override` (String) Override command for the step.
+- `environment_variables` (Attributes List) Environment variables for the workflow steps. (see [below for nested schema](#nestedatt--workflows_config--workflows--wf_steps_config--environment_variables))
+- `mount_points` (Attributes List) Mount points for the step. (see [below for nested schema](#nestedatt--workflows_config--workflows--wf_steps_config--mount_points))
+- `timeout` (Number) Workflow step execution timeout in seconds.
+- `wf_step_input_data` (Attributes) Workflow step input data (JSON string) (see [below for nested schema](#nestedatt--workflows_config--workflows--wf_steps_config--wf_step_input_data))
+
+
+### Nested Schema for `workflows_config.workflows.wf_steps_config.environment_variables`
+
+Required:
+
+- `config` (Attributes) Configuration for the environment variable. (see [below for nested schema](#nestedatt--workflows_config--workflows--wf_steps_config--environment_variables--config))
+- `kind` (String) Kind of the environment variable. Options: TEXT, SECRET_REF
+
+
+### Nested Schema for `workflows_config.workflows.wf_steps_config.environment_variables.config`
+
+Required:
+
+- `var_name` (String) Name of the variable.
+
+Optional:
+
+- `secret_id` (String) ID of the secret (if using vault secret). Only if type is SECRET_REF
+- `text_value` (String) Text value (if using plain text). Only if type is TEXT
+
+
+
+
+### Nested Schema for `workflows_config.workflows.wf_steps_config.mount_points`
+
+Optional:
+
+- `read_only` (Boolean) If the directory is to be mounted as read only or not
+- `source` (String) Source path for mount point.
+- `target` (String) Target path for mount point.
+
+
+
+### Nested Schema for `workflows_config.workflows.wf_steps_config.wf_step_input_data`
+
+Optional:
+
+- `data` (String) Input data (JSON).
+- `schema_type` (String) Schema type for the input data. Options: FORM_JSONSCHEMA
+
+
+
+
+
+
+
+## Import
+
+Import existing resource
+
+### Using Import block (terraform v1.5.0 and later)
+```terraform
+import {
+ to = stackguardian_stack_template_revision.example
+ id = "template-name:revision-version"
+}
+```
+
+### Using CLI
+```bash
+terraform import stackguardian_stack_template_revision.example template-name:revision-version
+```
diff --git a/go.mod b/go.mod
index 217d31b..20c3bb9 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/StackGuardian/terraform-provider-stackguardian
go 1.21.4
require (
- github.com/StackGuardian/sg-sdk-go v1.3.1
+ github.com/StackGuardian/sg-sdk-go v1.4.1
github.com/hashicorp/terraform-plugin-framework v1.11.0
github.com/hashicorp/terraform-plugin-go v0.23.0
github.com/hashicorp/terraform-plugin-log v0.9.0
diff --git a/go.sum b/go.sum
index d290eea..fc1a028 100644
--- a/go.sum
+++ b/go.sum
@@ -4,8 +4,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg=
github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
-github.com/StackGuardian/sg-sdk-go v1.3.1 h1:WmuLhw1S98SJBepPS1Na+Gs9JBasdvrtFabSUudM9io=
-github.com/StackGuardian/sg-sdk-go v1.3.1/go.mod h1:iCrgHE0WbxEaaLx6ATsRe81Qjp82kahCwLGJ2t66mZ0=
+github.com/StackGuardian/sg-sdk-go v1.4.1 h1:0RIE7nxA4HRgcACaTcOqQqccVCvoq6HE/IeHHcHIcck=
+github.com/StackGuardian/sg-sdk-go v1.4.1/go.mod h1:iCrgHE0WbxEaaLx6ATsRe81Qjp82kahCwLGJ2t66mZ0=
github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
diff --git a/internal/constants/stack_template.go b/internal/constants/stack_template.go
new file mode 100644
index 0000000..9fdce5b
--- /dev/null
+++ b/internal/constants/stack_template.go
@@ -0,0 +1,45 @@
+package constants
+
+// Stack Template - Common documentation
+const (
+ StackTemplateSourceConfigKindCommon = `Source configuration kind for the stack template. Valid values:
+ TERRAFORM,
+ OPENTOFU,
+ ANSIBLE_PLAYBOOK,
+ HELM,
+ KUBECTL,
+ CLOUDFORMATION,
+ MIXED,
+ CUSTOM`
+
+ StackTemplateIsActiveCommon = `Whether the stack template is active. Valid values:
+ 0 (false),
+ 1 (true)`
+
+ StackTemplateIsPublicCommon = `Whether the stack template is publicly available. Valid values:
+ 0 (false),
+ 1 (true)`
+)
+
+// Stack Template Resource documentation
+const (
+ StackTemplateName = "Name of the stack template. Must be less than 100 characters."
+ StackTemplateOwnerOrg = "Organization that owns the stack template."
+ StackTemplateSharedOrgs = "List of organization IDs with which this template is shared."
+ StackTemplateDescription = "A brief description of the stack template."
+ StackTemplateTags = "A list of tags associated with the stack template."
+ StackTemplateContextTags = "Contextual key-value tags that provide additional context to the main tags."
+)
+
+// Stack Template Revision Resource documentation
+const (
+ StackTemplateRevisionId = "Unique identifier of the stack template revision."
+ StackTemplateRevisionTemplateId = "ID of the parent stack template."
+ StackTemplateRevisionAlias = "Human-readable alias for the revision (e.g., `v1.0.0`)."
+ StackTemplateRevisionNotes = "Release notes or changelog for this revision."
+ StackTemplateRevisionDescription = "Long description for the stack template revision."
+ StackTemplateRevisionTags = "A list of tags associated with the revision."
+ StackTemplateRevisionContextTags = "Contextual key-value tags for the revision."
+ StackTemplateRevisionWorkflowsConfig = "JSON-encoded workflows configuration for the stack template revision."
+ StackTemplateRevisionActions = "JSON-encoded map of actions for the stack template revision."
+)
diff --git a/internal/datasources/stack_template/datasource.go b/internal/datasources/stack_template/datasource.go
new file mode 100644
index 0000000..2e33fd5
--- /dev/null
+++ b/internal/datasources/stack_template/datasource.go
@@ -0,0 +1,137 @@
+package stacktemplatedatasource
+
+import (
+ "context"
+ "fmt"
+
+ sgclient "github.com/StackGuardian/sg-sdk-go/client"
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/constants"
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/customTypes"
+ stacktemplate "github.com/StackGuardian/terraform-provider-stackguardian/internal/resource/stack_template"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+var (
+ _ datasource.DataSource = &stackTemplateDataSource{}
+ _ datasource.DataSourceWithConfigure = &stackTemplateDataSource{}
+)
+
+// NewDataSource is a helper function to simplify the provider implementation.
+func NewDataSource() datasource.DataSource {
+ return &stackTemplateDataSource{}
+}
+
+type stackTemplateDataSource struct {
+ client *sgclient.Client
+ orgName string
+}
+
+func (d *stackTemplateDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_stack_template"
+}
+
+func (d *stackTemplateDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ if req.ProviderData == nil {
+ return
+ }
+
+ provInfo, ok := req.ProviderData.(*customTypes.ProviderInfo)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected *customTypes.ProviderInfo, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+ return
+ }
+
+ d.client = provInfo.Client
+ d.orgName = provInfo.Org_name
+}
+
+func (d *stackTemplateDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ MarkdownDescription: "> **Note:** This data source is currently in **BETA**. Features and behavior may change.\n\nUse this data source to read a stack template.",
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ MarkdownDescription: constants.DatasourceId,
+ Required: true,
+ },
+ "owner_org": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateOwnerOrg,
+ Computed: true,
+ },
+ "template_name": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateName,
+ Computed: true,
+ },
+ "source_config_kind": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateSourceConfigKindCommon,
+ Computed: true,
+ },
+ "is_active": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateIsActiveCommon,
+ Computed: true,
+ },
+ "is_public": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateIsPublicCommon,
+ Computed: true,
+ },
+ "description": schema.StringAttribute{
+ MarkdownDescription: fmt.Sprintf(constants.Description, "stack template"),
+ Computed: true,
+ },
+ "tags": schema.ListAttribute{
+ MarkdownDescription: fmt.Sprintf(constants.Tags, "stack template"),
+ ElementType: types.StringType,
+ Computed: true,
+ },
+ "context_tags": schema.MapAttribute{
+ MarkdownDescription: fmt.Sprintf(constants.ContextTags, "stack template"),
+ ElementType: types.StringType,
+ Computed: true,
+ },
+ "shared_orgs_list": schema.ListAttribute{
+ MarkdownDescription: constants.StackTemplateSharedOrgs,
+ ElementType: types.StringType,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func (d *stackTemplateDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ var config stacktemplate.StackTemplateResourceModel
+
+ diags := req.Config.Get(ctx, &config)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ templateID := config.Id.ValueString()
+ if templateID == "" {
+ resp.Diagnostics.AddError("id must be provided", "")
+ return
+ }
+
+ readResp, err := d.client.StackTemplates.ReadStackTemplate(ctx, d.orgName, templateID)
+ if err != nil {
+ resp.Diagnostics.AddError("Unable to read stack template.", err.Error())
+ return
+ }
+
+ if readResp == nil {
+ resp.Diagnostics.AddError("Error reading stack template", "API response is empty")
+ return
+ }
+
+ model, diags := stacktemplate.BuildAPIModelToStackTemplateModel(&readResp.Msg)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, model)...)
+}
diff --git a/internal/datasources/stack_template_revision/datasource.go b/internal/datasources/stack_template_revision/datasource.go
new file mode 100644
index 0000000..d3fca09
--- /dev/null
+++ b/internal/datasources/stack_template_revision/datasource.go
@@ -0,0 +1,610 @@
+package stacktemplaterevisiondatasource
+
+import (
+ "context"
+ "fmt"
+
+ sgclient "github.com/StackGuardian/sg-sdk-go/client"
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/constants"
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/customTypes"
+ stacktemplaterevision "github.com/StackGuardian/terraform-provider-stackguardian/internal/resource/stack_template_revision"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+var (
+ _ datasource.DataSource = &stackTemplateRevisionDataSource{}
+ _ datasource.DataSourceWithConfigure = &stackTemplateRevisionDataSource{}
+)
+
+// NewDataSource is a helper function to simplify the provider implementation.
+func NewDataSource() datasource.DataSource {
+ return &stackTemplateRevisionDataSource{}
+}
+
+type stackTemplateRevisionDataSource struct {
+ client *sgclient.Client
+ orgName string
+}
+
+func (d *stackTemplateRevisionDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_stack_template_revision"
+}
+
+func (d *stackTemplateRevisionDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ if req.ProviderData == nil {
+ return
+ }
+
+ provInfo, ok := req.ProviderData.(*customTypes.ProviderInfo)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected *customTypes.ProviderInfo, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+ return
+ }
+
+ d.client = provInfo.Client
+ d.orgName = provInfo.Org_name
+}
+
+// Datasource-scoped schema helpers (mirrors resource/stack_template_revision/schema.go using datasource/schema types).
+
+var dsEnvVarsAttrs = map[string]schema.Attribute{
+ "config": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.EnvVarConfig,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "var_name": schema.StringAttribute{
+ MarkdownDescription: constants.EnvVarConfigVarName,
+ Computed: true,
+ },
+ "secret_id": schema.StringAttribute{
+ MarkdownDescription: constants.EnvVarConfigSecretId,
+ Computed: true,
+ },
+ "text_value": schema.StringAttribute{
+ MarkdownDescription: constants.EnvVarConfigTextValue,
+ Computed: true,
+ },
+ },
+ },
+ "kind": schema.StringAttribute{
+ MarkdownDescription: constants.EnvVarKind,
+ Computed: true,
+ },
+}
+
+var dsMountPointAttrs = map[string]schema.Attribute{
+ "source": schema.StringAttribute{
+ MarkdownDescription: constants.MountPointSource,
+ Computed: true,
+ },
+ "target": schema.StringAttribute{
+ MarkdownDescription: constants.MountPointTarget,
+ Computed: true,
+ },
+ "read_only": schema.BoolAttribute{
+ MarkdownDescription: constants.MountPointReadOnly,
+ Computed: true,
+ },
+}
+
+var dsWfStepsConfigNestedObj = schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "name": schema.StringAttribute{
+ MarkdownDescription: constants.WfStepName,
+ Computed: true,
+ },
+ "environment_variables": schema.ListNestedAttribute{
+ MarkdownDescription: constants.WfStepEnvVars,
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{Attributes: dsEnvVarsAttrs},
+ },
+ "approval": schema.BoolAttribute{
+ MarkdownDescription: constants.WfStepApproval,
+ Computed: true,
+ },
+ "timeout": schema.Int64Attribute{
+ MarkdownDescription: constants.WfStepTimeout,
+ Computed: true,
+ },
+ "cmd_override": schema.StringAttribute{
+ MarkdownDescription: constants.WfStepCmdOverride,
+ Computed: true,
+ },
+ "mount_points": schema.ListNestedAttribute{
+ MarkdownDescription: constants.WfStepMountPoints,
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{Attributes: dsMountPointAttrs},
+ },
+ "wf_step_template_id": schema.StringAttribute{
+ MarkdownDescription: constants.WfStepTemplateId,
+ Computed: true,
+ },
+ "wf_step_input_data": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.WfStepInputData,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "schema_type": schema.StringAttribute{
+ MarkdownDescription: constants.WfStepInputDataSchemaType,
+ Computed: true,
+ },
+ "data": schema.StringAttribute{
+ MarkdownDescription: constants.WfStepInputDataData,
+ Computed: true,
+ },
+ },
+ },
+ },
+}
+
+var dsDeploymentPlatformConfigAttrs = map[string]schema.Attribute{
+ "kind": schema.StringAttribute{
+ MarkdownDescription: constants.DeploymentPlatformKind,
+ Computed: true,
+ },
+ "config": schema.StringAttribute{
+ MarkdownDescription: constants.DeploymentPlatformConfigDetails + " (JSON string)",
+ Computed: true,
+ },
+}
+
+var dsWorkflowInStackAttrs = map[string]schema.Attribute{
+ "resource_name": schema.StringAttribute{
+ MarkdownDescription: "Name of the workflow resource within the stack.",
+ Computed: true,
+ },
+ "description": schema.StringAttribute{
+ MarkdownDescription: "Description of this workflow.",
+ Computed: true,
+ },
+ "tags": schema.ListAttribute{
+ MarkdownDescription: "Tags for the workflow.",
+ ElementType: types.StringType,
+ Computed: true,
+ },
+ "is_active": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateIsActiveCommon,
+ Computed: true,
+ },
+ "wf_type": schema.StringAttribute{
+ MarkdownDescription: `Workflow type. Valid values: TERRAFORM, OPENTOFU, CUSTOM`,
+ Computed: true,
+ },
+ "wf_steps_config": schema.ListNestedAttribute{
+ MarkdownDescription: "Workflow steps configuration.",
+ Computed: true,
+ NestedObject: dsWfStepsConfigNestedObj,
+ },
+ "terraform_config": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.TerraformConfig,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "terraform_version": schema.StringAttribute{
+ MarkdownDescription: constants.TerraformVersion,
+ Computed: true,
+ },
+ "drift_check": schema.BoolAttribute{
+ MarkdownDescription: constants.TerraformDriftCheck,
+ Computed: true,
+ },
+ "drift_cron": schema.StringAttribute{
+ MarkdownDescription: constants.TerraformDriftCron,
+ Computed: true,
+ },
+ "managed_terraform_state": schema.BoolAttribute{
+ MarkdownDescription: constants.TerraformManagedState,
+ Computed: true,
+ },
+ "approval_pre_apply": schema.BoolAttribute{
+ MarkdownDescription: constants.TerraformApprovalPreApply,
+ Computed: true,
+ },
+ "terraform_plan_options": schema.StringAttribute{
+ MarkdownDescription: constants.TerraformPlanOptions,
+ Computed: true,
+ },
+ "terraform_init_options": schema.StringAttribute{
+ MarkdownDescription: constants.TerraformInitOptions,
+ Computed: true,
+ },
+ "terraform_bin_path": schema.ListNestedAttribute{
+ MarkdownDescription: constants.TerraformBinPath,
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{Attributes: dsMountPointAttrs},
+ },
+ "timeout": schema.Int64Attribute{
+ MarkdownDescription: constants.TerraformTimeout,
+ Computed: true,
+ },
+ "post_apply_wf_steps_config": schema.ListNestedAttribute{
+ MarkdownDescription: constants.TerraformPostApplyWfSteps,
+ Computed: true,
+ NestedObject: dsWfStepsConfigNestedObj,
+ },
+ "pre_apply_wf_steps_config": schema.ListNestedAttribute{
+ MarkdownDescription: constants.TerraformPreApplyWfSteps,
+ Computed: true,
+ NestedObject: dsWfStepsConfigNestedObj,
+ },
+ "pre_plan_wf_steps_config": schema.ListNestedAttribute{
+ MarkdownDescription: constants.TerraformPrePlanWfSteps,
+ Computed: true,
+ NestedObject: dsWfStepsConfigNestedObj,
+ },
+ "post_plan_wf_steps_config": schema.ListNestedAttribute{
+ MarkdownDescription: constants.TerraformPostPlanWfSteps,
+ Computed: true,
+ NestedObject: dsWfStepsConfigNestedObj,
+ },
+ "pre_init_hooks": schema.ListAttribute{
+ MarkdownDescription: constants.TerraformPreInitHooks,
+ Computed: true,
+ ElementType: types.StringType,
+ },
+ "pre_plan_hooks": schema.ListAttribute{
+ MarkdownDescription: constants.TerraformPrePlanHooks,
+ Computed: true,
+ ElementType: types.StringType,
+ },
+ "post_plan_hooks": schema.ListAttribute{
+ MarkdownDescription: constants.TerraformPostPlanHooks,
+ Computed: true,
+ ElementType: types.StringType,
+ },
+ "pre_apply_hooks": schema.ListAttribute{
+ MarkdownDescription: constants.TerraformPreApplyHooks,
+ Computed: true,
+ ElementType: types.StringType,
+ },
+ "post_apply_hooks": schema.ListAttribute{
+ MarkdownDescription: constants.TerraformPostApplyHooks,
+ Computed: true,
+ ElementType: types.StringType,
+ },
+ "run_pre_init_hooks_on_drift": schema.BoolAttribute{
+ MarkdownDescription: constants.TerraformRunPreInitHooksOnDrift,
+ Computed: true,
+ },
+ },
+ },
+ "environment_variables": schema.ListNestedAttribute{
+ MarkdownDescription: "Environment variables for the workflow.",
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{Attributes: dsEnvVarsAttrs},
+ },
+ "deployment_platform_config": schema.ListNestedAttribute{
+ MarkdownDescription: "Deployment platform configuration.",
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{Attributes: dsDeploymentPlatformConfigAttrs},
+ },
+ "vcs_config": schema.SingleNestedAttribute{
+ MarkdownDescription: "VCS (version control) configuration for the workflow.",
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "iac_vcs_config": schema.SingleNestedAttribute{
+ MarkdownDescription: "IaC VCS configuration.",
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "use_marketplace_template": schema.BoolAttribute{
+ MarkdownDescription: "Whether to use a marketplace template.",
+ Computed: true,
+ },
+ "iac_template_id": schema.StringAttribute{
+ MarkdownDescription: "ID of the IaC template from the marketplace.",
+ Computed: true,
+ },
+ "custom_source": schema.SingleNestedAttribute{
+ MarkdownDescription: "Custom source configuration.",
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "source_config_dest_kind": schema.StringAttribute{
+ MarkdownDescription: constants.RuntimeSourceDestKind,
+ Computed: true,
+ },
+ "config": schema.SingleNestedAttribute{
+ MarkdownDescription: "Source configuration details.",
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "is_private": schema.BoolAttribute{
+ Computed: true,
+ },
+ "auth": schema.StringAttribute{
+ Computed: true,
+ Sensitive: true,
+ },
+ "working_dir": schema.StringAttribute{
+ Computed: true,
+ },
+ "git_sparse_checkout_config": schema.StringAttribute{
+ Computed: true,
+ },
+ "git_core_auto_crlf": schema.BoolAttribute{
+ Computed: true,
+ },
+ "ref": schema.StringAttribute{
+ Computed: true,
+ },
+ "repo": schema.StringAttribute{
+ Computed: true,
+ },
+ "include_sub_module": schema.BoolAttribute{
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ "iac_input_data": schema.SingleNestedAttribute{
+ MarkdownDescription: "IaC input data for the workflow.",
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "schema_id": schema.StringAttribute{
+ Computed: true,
+ },
+ "schema_type": schema.StringAttribute{
+ Computed: true,
+ },
+ "data": schema.StringAttribute{
+ MarkdownDescription: "Input data as a JSON string.",
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ "user_schedules": schema.ListNestedAttribute{
+ MarkdownDescription: "Scheduled run configuration.",
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "name": schema.StringAttribute{
+ Computed: true,
+ },
+ "desc": schema.StringAttribute{
+ Computed: true,
+ },
+ "cron": schema.StringAttribute{
+ MarkdownDescription: constants.UserScheduleCron,
+ Computed: true,
+ },
+ "state": schema.StringAttribute{
+ MarkdownDescription: constants.UserScheduleState,
+ Computed: true,
+ },
+ },
+ },
+ },
+ "approvers": schema.ListAttribute{
+ MarkdownDescription: "List of approvers.",
+ ElementType: types.StringType,
+ Computed: true,
+ },
+ "number_of_approvals_required": schema.Int64Attribute{
+ MarkdownDescription: "Number of approvals required.",
+ Computed: true,
+ },
+ "runner_constraints": schema.SingleNestedAttribute{
+ MarkdownDescription: "Runner constraints for the workflow.",
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "type": schema.StringAttribute{
+ MarkdownDescription: constants.RunnerConstraintsType,
+ Computed: true,
+ },
+ "names": schema.ListAttribute{
+ MarkdownDescription: constants.RunnerConstraintsNames,
+ ElementType: types.StringType,
+ Computed: true,
+ },
+ },
+ },
+ "user_job_cpu": schema.Int64Attribute{
+ MarkdownDescription: "CPU limit for the user job.",
+ Computed: true,
+ },
+ "user_job_memory": schema.Int64Attribute{
+ MarkdownDescription: "Memory limit for the user job.",
+ Computed: true,
+ },
+}
+
+var dsActionOrderAttrs = map[string]schema.Attribute{
+ "parameters": schema.SingleNestedAttribute{
+ MarkdownDescription: "Run configuration parameters for the action step.",
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "terraform_action": schema.SingleNestedAttribute{
+ MarkdownDescription: "Terraform-specific action parameters.",
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "action": schema.StringAttribute{
+ MarkdownDescription: `Terraform action to execute. E.g., "apply", "plan", "destroy".`,
+ Computed: true,
+ },
+ },
+ },
+ "deployment_platform_config": schema.ListNestedAttribute{
+ MarkdownDescription: "Deployment platform configuration.",
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{Attributes: dsDeploymentPlatformConfigAttrs},
+ },
+ "wf_steps_config": schema.ListNestedAttribute{
+ MarkdownDescription: "Workflow steps configuration.",
+ Computed: true,
+ NestedObject: dsWfStepsConfigNestedObj,
+ },
+ "environment_variables": schema.ListNestedAttribute{
+ MarkdownDescription: "Environment variables.",
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{Attributes: dsEnvVarsAttrs},
+ },
+ },
+ },
+ "dependencies": schema.ListNestedAttribute{
+ MarkdownDescription: "List of workflow dependencies that must complete before this step runs.",
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ MarkdownDescription: "ID of the dependent workflow.",
+ Computed: true,
+ },
+ "condition": schema.SingleNestedAttribute{
+ MarkdownDescription: "Condition that must be met by the dependency.",
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "latest_status": schema.StringAttribute{
+ MarkdownDescription: "Required latest status of the dependency (e.g., COMPLETED).",
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ },
+}
+
+func (d *stackTemplateRevisionDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ MarkdownDescription: "> **Note:** This data source is currently in **BETA**. Features and behavior may change.\n\nUse this data source to read a stack template revision.",
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ MarkdownDescription: constants.DatasourceId,
+ Required: true,
+ },
+ "template_id": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateRevisionTemplateId,
+ Computed: true,
+ },
+ "alias": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateRevisionAlias,
+ Computed: true,
+ },
+ "notes": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateRevisionNotes,
+ Computed: true,
+ },
+ "description": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateRevisionDescription,
+ Computed: true,
+ },
+ "source_config_kind": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateSourceConfigKindCommon,
+ Computed: true,
+ },
+ "is_active": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateIsActiveCommon,
+ Computed: true,
+ },
+ "is_public": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateIsPublicCommon,
+ Computed: true,
+ },
+ "tags": schema.ListAttribute{
+ MarkdownDescription: fmt.Sprintf(constants.Tags, "stack template revision"),
+ ElementType: types.StringType,
+ Computed: true,
+ },
+ "context_tags": schema.MapAttribute{
+ MarkdownDescription: fmt.Sprintf(constants.ContextTags, "stack template revision"),
+ ElementType: types.StringType,
+ Computed: true,
+ },
+ "deprecation": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.Deprecation,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "effective_date": schema.StringAttribute{
+ MarkdownDescription: constants.TemplateRevisionDeprecationEffectiveDate,
+ Computed: true,
+ },
+ "message": schema.StringAttribute{
+ MarkdownDescription: constants.TemplateRevisionDeprecation,
+ Computed: true,
+ },
+ },
+ },
+ "workflows_config": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.StackTemplateRevisionWorkflowsConfig,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "workflows": schema.ListNestedAttribute{
+ MarkdownDescription: "List of workflows that make up the stack.",
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: dsWorkflowInStackAttrs,
+ },
+ },
+ },
+ },
+ "actions": schema.MapNestedAttribute{
+ MarkdownDescription: constants.StackTemplateRevisionActions,
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "name": schema.StringAttribute{
+ MarkdownDescription: "Name of the action.",
+ Computed: true,
+ },
+ "description": schema.StringAttribute{
+ MarkdownDescription: "Description of the action.",
+ Computed: true,
+ },
+ "default": schema.BoolAttribute{
+ MarkdownDescription: "Whether this is the default action.",
+ Computed: true,
+ },
+ "order": schema.MapNestedAttribute{
+ MarkdownDescription: "Ordered map of workflow IDs to their action configurations.",
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: dsActionOrderAttrs,
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func (d *stackTemplateRevisionDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ var config stacktemplaterevision.StackTemplateRevisionResourceModel
+
+ diags := req.Config.Get(ctx, &config)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ revisionID := config.Id.ValueString()
+ if revisionID == "" {
+ resp.Diagnostics.AddError("id must be provided", "")
+ return
+ }
+
+ readResp, err := d.client.StackTemplateRevisions.ReadStackTemplateRevision(ctx, d.orgName, revisionID)
+ if err != nil {
+ resp.Diagnostics.AddError("Unable to read stack template revision.", err.Error())
+ return
+ }
+
+ if readResp == nil {
+ resp.Diagnostics.AddError("Error reading stack template revision", "API response is empty")
+ return
+ }
+
+ model, diags := stacktemplaterevision.BuildAPIModelToStackTemplateRevisionModel(ctx, &readResp.Msg)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, model)...)
+}
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
index 1377588..1875f69 100644
--- a/internal/provider/provider.go
+++ b/internal/provider/provider.go
@@ -16,6 +16,8 @@ import (
runnergroupdatasource "github.com/StackGuardian/terraform-provider-stackguardian/internal/datasources/runner_group"
runnergrouptoken "github.com/StackGuardian/terraform-provider-stackguardian/internal/datasources/runner_group_token"
stackoutputs "github.com/StackGuardian/terraform-provider-stackguardian/internal/datasources/stack_outputs"
+ stacktemplatedatasource "github.com/StackGuardian/terraform-provider-stackguardian/internal/datasources/stack_template"
+ stacktemplaterevisiondatasource "github.com/StackGuardian/terraform-provider-stackguardian/internal/datasources/stack_template_revision"
stackworkflowoutputs "github.com/StackGuardian/terraform-provider-stackguardian/internal/datasources/stack_workflow_outputs"
workflowgroupdatasource "github.com/StackGuardian/terraform-provider-stackguardian/internal/datasources/workflow_group"
workflowoutputs "github.com/StackGuardian/terraform-provider-stackguardian/internal/datasources/workflow_outputs"
@@ -29,6 +31,8 @@ import (
roleassignment "github.com/StackGuardian/terraform-provider-stackguardian/internal/resource/role_assignment"
rolev4 "github.com/StackGuardian/terraform-provider-stackguardian/internal/resource/role_v4"
runnergroup "github.com/StackGuardian/terraform-provider-stackguardian/internal/resource/runner_group"
+ stacktemplate "github.com/StackGuardian/terraform-provider-stackguardian/internal/resource/stack_template"
+ stacktemplaterevision "github.com/StackGuardian/terraform-provider-stackguardian/internal/resource/stack_template_revision"
workflowgroup "github.com/StackGuardian/terraform-provider-stackguardian/internal/resource/workflow_group"
workflowsteptemplate "github.com/StackGuardian/terraform-provider-stackguardian/internal/resource/workflow_step_template"
workflowsteptemplaterevision "github.com/StackGuardian/terraform-provider-stackguardian/internal/resource/workflow_step_template_revision"
@@ -228,6 +232,8 @@ func (p *stackguardianProvider) DataSources(_ context.Context) []func() datasour
workflowsteptemplaterevisiondatasource.NewDataSource,
workflowtemplatedatasource.NewDataSource,
workflowtemplaterevisiondatasource.NewDataSource,
+ stacktemplatedatasource.NewDataSource,
+ stacktemplaterevisiondatasource.NewDataSource,
}
}
@@ -245,5 +251,7 @@ func (p *stackguardianProvider) Resources(_ context.Context) []func() resource.R
rolev4.NewResource,
workflowtemplate.NewResource,
workflowtemplaterevision.NewResource,
+ stacktemplate.NewResource,
+ stacktemplaterevision.NewResource,
}
}
diff --git a/internal/resource/stack_template/model.go b/internal/resource/stack_template/model.go
new file mode 100644
index 0000000..fa0e6f5
--- /dev/null
+++ b/internal/resource/stack_template/model.go
@@ -0,0 +1,203 @@
+package stacktemplate
+
+import (
+ "context"
+
+ sgsdkgo "github.com/StackGuardian/sg-sdk-go"
+ "github.com/StackGuardian/sg-sdk-go/stacktemplates"
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/expanders"
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/flatteners"
+ "github.com/hashicorp/terraform-plugin-framework/diag"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+type StackTemplateResourceModel struct {
+ Id types.String `tfsdk:"id"`
+ TemplateName types.String `tfsdk:"template_name"`
+ OwnerOrg types.String `tfsdk:"owner_org"`
+ SourceConfigKind types.String `tfsdk:"source_config_kind"`
+ IsActive types.String `tfsdk:"is_active"`
+ IsPublic types.String `tfsdk:"is_public"`
+ ShortDescription types.String `tfsdk:"description"`
+ Tags types.List `tfsdk:"tags"`
+ ContextTags types.Map `tfsdk:"context_tags"`
+ SharedOrgsList types.List `tfsdk:"shared_orgs_list"`
+}
+
+func (m *StackTemplateResourceModel) ToAPIModel(ctx context.Context) (*stacktemplates.CreateStackTemplateRequest, diag.Diagnostics) {
+ diags := diag.Diagnostics{}
+
+ apiModel := &stacktemplates.CreateStackTemplateRequest{
+ TemplateName: m.TemplateName.ValueString(),
+ ShortDescription: m.ShortDescription.ValueStringPointer(),
+ }
+
+ if !m.SourceConfigKind.IsNull() && !m.SourceConfigKind.IsUnknown() {
+ apiModel.SourceConfigKind = (*stacktemplates.StackTemplateSourceConfigKindEnum)(m.SourceConfigKind.ValueStringPointer())
+ }
+
+ if !m.IsActive.IsNull() && !m.IsActive.IsUnknown() {
+ apiModel.IsActive = (*sgsdkgo.IsPublicEnum)(m.IsActive.ValueStringPointer())
+ }
+
+ if !m.IsPublic.IsNull() && !m.IsPublic.IsUnknown() {
+ apiModel.IsPublic = (*sgsdkgo.IsPublicEnum)(m.IsPublic.ValueStringPointer())
+ }
+
+ // Convert Tags
+ if !m.Tags.IsNull() && !m.Tags.IsUnknown() {
+ tags, diagsTags := expanders.StringList(ctx, m.Tags)
+ diags.Append(diagsTags...)
+ if !diags.HasError() {
+ apiModel.Tags = tags
+ }
+ }
+
+ // Convert SharedOrgsList
+ if !m.SharedOrgsList.IsNull() && !m.SharedOrgsList.IsUnknown() {
+ sharedOrgs, diagsShared := expanders.StringList(ctx, m.SharedOrgsList)
+ diags.Append(diagsShared...)
+ if !diags.HasError() {
+ apiModel.SharedOrgsList = sharedOrgs
+ }
+ }
+
+ // Convert ContextTags
+ if !m.ContextTags.IsNull() && !m.ContextTags.IsUnknown() {
+ contextTags := make(map[string]string)
+ diagsCT := m.ContextTags.ElementsAs(ctx, &contextTags, false)
+ diags.Append(diagsCT...)
+ if !diags.HasError() && len(contextTags) > 0 {
+ apiModel.ContextTags = contextTags
+ }
+ }
+
+ return apiModel, diags
+}
+
+func (m *StackTemplateResourceModel) ToUpdateAPIModel(ctx context.Context) (*stacktemplates.UpdateStackTemplateRequest, diag.Diagnostics) {
+ diags := diag.Diagnostics{}
+
+ apiModel := &stacktemplates.UpdateStackTemplateRequest{
+ TemplateName: sgsdkgo.Optional(m.TemplateName.ValueString()),
+ }
+
+ if !m.SourceConfigKind.IsNull() && !m.SourceConfigKind.IsUnknown() {
+ apiModel.SourceConfigKind = sgsdkgo.Optional(stacktemplates.StackTemplateSourceConfigKindEnum(m.SourceConfigKind.ValueString()))
+ } else {
+ apiModel.SourceConfigKind = sgsdkgo.Null[stacktemplates.StackTemplateSourceConfigKindEnum]()
+ }
+
+ if !m.ShortDescription.IsNull() && !m.ShortDescription.IsUnknown() {
+ apiModel.ShortDescription = sgsdkgo.Optional(m.ShortDescription.ValueString())
+ } else {
+ apiModel.ShortDescription = sgsdkgo.Null[string]()
+ }
+
+ if !m.IsActive.IsNull() && !m.IsActive.IsUnknown() {
+ apiModel.IsActive = sgsdkgo.Optional(sgsdkgo.IsPublicEnum(m.IsActive.ValueString()))
+ }
+
+ if !m.IsPublic.IsNull() && !m.IsPublic.IsUnknown() {
+ apiModel.IsPublic = sgsdkgo.Optional(sgsdkgo.IsPublicEnum(m.IsPublic.ValueString()))
+ }
+
+ // Convert Tags
+ tags, diagsTags := expanders.StringList(ctx, m.Tags)
+ diags.Append(diagsTags...)
+ if tags != nil {
+ apiModel.Tags = sgsdkgo.Optional(tags)
+ } else {
+ apiModel.Tags = sgsdkgo.Null[[]string]()
+ }
+
+ // Convert ContextTags
+ contextTags, diagsCT := expanders.MapStringString(ctx, m.ContextTags)
+ diags.Append(diagsCT...)
+ if contextTags != nil {
+ apiModel.ContextTags = sgsdkgo.Optional(contextTags)
+ } else {
+ apiModel.ContextTags = sgsdkgo.Null[map[string]string]()
+ }
+
+ // Convert SharedOrgsList
+ sharedOrgsList, diagsShared := expanders.StringList(ctx, m.SharedOrgsList)
+ diags.Append(diagsShared...)
+ if sharedOrgsList != nil {
+ apiModel.SharedOrgsList = sgsdkgo.Optional(sharedOrgsList)
+ } else {
+ apiModel.SharedOrgsList = sgsdkgo.Null[[]string]()
+ }
+
+ return apiModel, diags
+}
+
+func BuildAPIModelToStackTemplateModel(apiResponse *stacktemplates.ReadStackTemplateResponse) (*StackTemplateResourceModel, diag.Diagnostics) {
+ diags := diag.Diagnostics{}
+
+ model := &StackTemplateResourceModel{
+ Id: flatteners.StringPtr(apiResponse.Id),
+ TemplateName: flatteners.StringPtr(apiResponse.TemplateName),
+ OwnerOrg: flatteners.StringPtr(apiResponse.OwnerOrg),
+ ShortDescription: flatteners.StringPtr(apiResponse.ShortDescription),
+ }
+
+ if apiResponse.SourceConfigKind != nil {
+ model.SourceConfigKind = flatteners.String(string(*apiResponse.SourceConfigKind))
+ } else {
+ model.SourceConfigKind = types.StringNull()
+ }
+
+ if apiResponse.IsActive != nil {
+ model.IsActive = flatteners.String(string(*apiResponse.IsActive))
+ } else {
+ model.IsActive = types.StringNull()
+ }
+
+ if apiResponse.IsPublic != nil {
+ model.IsPublic = flatteners.String(string(*apiResponse.IsPublic))
+ } else {
+ model.IsPublic = types.StringNull()
+ }
+
+ // Convert Tags
+ if apiResponse.Tags != nil {
+ var tags []types.String
+ for _, tag := range apiResponse.Tags {
+ tags = append(tags, flatteners.String(tag))
+ }
+ tagsList, diagsTags := types.ListValueFrom(context.Background(), types.StringType, tags)
+ diags.Append(diagsTags...)
+ model.Tags = tagsList
+ } else {
+ model.Tags = types.ListNull(types.StringType)
+ }
+
+ // Convert SharedOrgsList
+ if apiResponse.SharedOrgsList != nil {
+ var sharedOrgs []types.String
+ for _, org := range apiResponse.SharedOrgsList {
+ sharedOrgs = append(sharedOrgs, flatteners.String(org))
+ }
+ sharedOrgsList, diagsShared := types.ListValueFrom(context.Background(), types.StringType, sharedOrgs)
+ diags.Append(diagsShared...)
+ model.SharedOrgsList = sharedOrgsList
+ } else {
+ model.SharedOrgsList = types.ListNull(types.StringType)
+ }
+
+ // Convert ContextTags
+ if apiResponse.ContextTags != nil {
+ contextTags := make(map[string]types.String)
+ for k, v := range apiResponse.ContextTags {
+ contextTags[k] = flatteners.String(v)
+ }
+ contextTagsMap, diagsCT := types.MapValueFrom(context.Background(), types.StringType, contextTags)
+ diags.Append(diagsCT...)
+ model.ContextTags = contextTagsMap
+ } else {
+ model.ContextTags = types.MapNull(types.StringType)
+ }
+
+ return model, diags
+}
diff --git a/internal/resource/stack_template/resource.go b/internal/resource/stack_template/resource.go
new file mode 100644
index 0000000..e1f9644
--- /dev/null
+++ b/internal/resource/stack_template/resource.go
@@ -0,0 +1,196 @@
+package stacktemplate
+
+import (
+ "context"
+ "fmt"
+
+ sgclient "github.com/StackGuardian/sg-sdk-go/client"
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/customTypes"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+)
+
+var (
+ _ resource.Resource = &stackTemplateResource{}
+ _ resource.ResourceWithConfigure = &stackTemplateResource{}
+ _ resource.ResourceWithImportState = &stackTemplateResource{}
+)
+
+type stackTemplateResource struct {
+ client *sgclient.Client
+ org_name string
+}
+
+// NewResource is a helper function to simplify the provider implementation.
+func NewResource() resource.Resource {
+ return &stackTemplateResource{}
+}
+
+// Metadata returns the resource type name.
+func (r *stackTemplateResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_stack_template"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *stackTemplateResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+ if req.ProviderData == nil {
+ return
+ }
+
+ provider, ok := req.ProviderData.(*customTypes.ProviderInfo)
+
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected *customTypes.ProviderInfo, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+
+ return
+ }
+
+ r.client = provider.Client
+ r.org_name = provider.Org_name
+}
+
+// ImportState imports a stack template using its ID.
+func (r *stackTemplateResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), req.ID)...)
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *stackTemplateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ var plan StackTemplateResourceModel
+
+ diags := req.Plan.Get(ctx, &plan)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ payload, diags := plan.ToAPIModel(ctx)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ payload.OwnerOrg = fmt.Sprintf("/orgs/%v", r.org_name)
+
+ createResp, err := r.client.StackTemplates.CreateStackTemplate(ctx, r.org_name, false, payload)
+ if err != nil {
+ resp.Diagnostics.AddError("Error creating stack template", "Error in creating stack template API call: "+err.Error())
+ return
+ }
+
+ templateID := *createResp.Data.Parent.Id
+
+ // Call read to get the full state since create response doesn't return all values
+ readResp, err := r.client.StackTemplates.ReadStackTemplate(ctx, r.org_name, templateID)
+ if err != nil {
+ resp.Diagnostics.AddError("Error reading created stack template", "Could not read the created stack template: "+err.Error())
+ return
+ }
+
+ if readResp == nil {
+ resp.Diagnostics.AddError("Error reading stack template", "API response is empty")
+ return
+ }
+
+ templateModel, diags := BuildAPIModelToStackTemplateModel(&readResp.Msg)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &templateModel)...)
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *stackTemplateResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ var state StackTemplateResourceModel
+
+ diags := req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ templateID := state.Id.ValueString()
+
+ readResp, err := r.client.StackTemplates.ReadStackTemplate(ctx, r.org_name, templateID)
+ if err != nil {
+ resp.Diagnostics.AddError("Error reading stack template", "Error in reading stack template API call: "+err.Error())
+ return
+ }
+
+ templateModel, diags := BuildAPIModelToStackTemplateModel(&readResp.Msg)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &templateModel)...)
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *stackTemplateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ var plan StackTemplateResourceModel
+ var state StackTemplateResourceModel
+
+ diags := req.Plan.Get(ctx, &plan)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ diags = req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ templateID := state.Id.ValueString()
+
+ payload, diags := plan.ToUpdateAPIModel(ctx)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ _, err := r.client.StackTemplates.UpdateStackTemplate(ctx, r.org_name, templateID, payload)
+ if err != nil {
+ resp.Diagnostics.AddError("Error updating stack template", "Error in updating stack template API call: "+err.Error())
+ return
+ }
+
+ // Call read to get the updated state since update response doesn't return all values
+ readResp, err := r.client.StackTemplates.ReadStackTemplate(ctx, r.org_name, templateID)
+ if err != nil {
+ resp.Diagnostics.AddError("Error reading updated stack template", "Could not read the updated stack template: "+err.Error())
+ return
+ }
+
+ templateModel, diags := BuildAPIModelToStackTemplateModel(&readResp.Msg)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &templateModel)...)
+}
+
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *stackTemplateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ var state StackTemplateResourceModel
+
+ diags := req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ err := r.client.StackTemplates.DeleteStackTemplate(ctx, r.org_name, state.Id.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError("Error deleting stack template", "Error in deleting stack template API call: "+err.Error())
+ return
+ }
+}
diff --git a/internal/resource/stack_template/resource_test.go b/internal/resource/stack_template/resource_test.go
new file mode 100644
index 0000000..76e2cc2
--- /dev/null
+++ b/internal/resource/stack_template/resource_test.go
@@ -0,0 +1,71 @@
+package stacktemplate_test
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/acctest"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/tfversion"
+)
+
+var sourceConfigKind = "TERRAFORM"
+
+func TestAccStackTemplate_Basic(t *testing.T) {
+ templateName := "tf-provider-stack-template-1"
+
+ customHeader := http.Header{}
+ customHeader.Set("x-sg-internal-auth-orgid", "sg-provider-test")
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { acctest.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.SkipBelow(tfversion.Version1_1_0),
+ },
+ ProtoV6ProviderFactories: acctest.ProviderFactories(customHeader),
+ Steps: []resource.TestStep{
+ // Create and Read testing
+ {
+ Config: testAccStackTemplateConfig(templateName, sourceConfigKind),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("stackguardian_stack_template.test", "template_name", templateName),
+ resource.TestCheckResourceAttr("stackguardian_stack_template.test", "is_public", "0"),
+ resource.TestCheckResourceAttrSet("stackguardian_stack_template.test", "id"),
+ ),
+ },
+ // Update and Read testing
+ {
+ Config: testAccStackTemplateConfigUpdated(templateName, sourceConfigKind),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("stackguardian_stack_template.test", "template_name", templateName),
+ resource.TestCheckResourceAttr("stackguardian_stack_template.test", "description", "Updated description"),
+ ),
+ },
+ // Delete testing automatically occurs
+ },
+ })
+}
+
+func testAccStackTemplateConfig(name, sourceConfigKind string) string {
+ return fmt.Sprintf(`
+resource "stackguardian_stack_template" "test" {
+ template_name = "%s"
+ source_config_kind = "%s"
+ is_public = "0"
+ tags = ["test", "terraform"]
+}
+`, name, sourceConfigKind)
+}
+
+func testAccStackTemplateConfigUpdated(name, sourceConfigKind string) string {
+ return fmt.Sprintf(`
+resource "stackguardian_stack_template" "test" {
+ template_name = "%s"
+ source_config_kind = "%s"
+ is_public = "1"
+ description = "Updated description"
+ tags = ["test", "terraform", "updated"]
+}
+`, name, sourceConfigKind)
+}
diff --git a/internal/resource/stack_template/schema.go b/internal/resource/stack_template/schema.go
new file mode 100644
index 0000000..8988330
--- /dev/null
+++ b/internal/resource/stack_template/schema.go
@@ -0,0 +1,76 @@
+package stacktemplate
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/constants"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+// Schema defines the schema for the resource.
+func (r *stackTemplateResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ MarkdownDescription: "> **Note:** This resource is currently in **BETA**. Features and behavior may change.\n\nManages a stack template resource.",
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ MarkdownDescription: constants.Id,
+ Computed: true,
+ Optional: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ stringplanmodifier.RequiresReplace(),
+ },
+ },
+ "owner_org": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateOwnerOrg,
+ Computed: true,
+ },
+ "template_name": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateName,
+ Required: true,
+ },
+ "source_config_kind": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateSourceConfigKindCommon,
+ Required: true,
+ },
+ "is_active": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateIsActiveCommon,
+ Optional: true,
+ Computed: true,
+ },
+ "is_public": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateIsPublicCommon,
+ Optional: true,
+ Computed: true,
+ },
+ "description": schema.StringAttribute{
+ MarkdownDescription: fmt.Sprintf(constants.Description, "stack template"),
+ Optional: true,
+ Computed: true,
+ },
+ "tags": schema.ListAttribute{
+ MarkdownDescription: fmt.Sprintf(constants.Tags, "stack template"),
+ ElementType: types.StringType,
+ Optional: true,
+ Computed: true,
+ },
+ "context_tags": schema.MapAttribute{
+ MarkdownDescription: fmt.Sprintf(constants.ContextTags, "stack template"),
+ ElementType: types.StringType,
+ Optional: true,
+ Computed: true,
+ },
+ "shared_orgs_list": schema.ListAttribute{
+ MarkdownDescription: constants.StackTemplateSharedOrgs,
+ ElementType: types.StringType,
+ Optional: true,
+ Computed: true,
+ },
+ },
+ }
+}
diff --git a/internal/resource/stack_template_revision/model.go b/internal/resource/stack_template_revision/model.go
new file mode 100644
index 0000000..8ab783d
--- /dev/null
+++ b/internal/resource/stack_template_revision/model.go
@@ -0,0 +1,2349 @@
+package stacktemplaterevision
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ sgsdkgo "github.com/StackGuardian/sg-sdk-go"
+ "github.com/StackGuardian/sg-sdk-go/stacktemplaterevisions"
+ "github.com/StackGuardian/sg-sdk-go/stacktemplates"
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/expanders"
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/flatteners"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/diag"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+)
+
+// ---------------------------------------------------------------------------
+// Root resource model
+// ---------------------------------------------------------------------------
+
+type StackTemplateRevisionResourceModel struct {
+ Id types.String `tfsdk:"id"`
+ ParentTemplateId types.String `tfsdk:"parent_template_id"`
+ TemplateId types.String `tfsdk:"template_id"`
+ Alias types.String `tfsdk:"alias"`
+ Notes types.String `tfsdk:"notes"`
+ LongDescription types.String `tfsdk:"description"`
+ SourceConfigKind types.String `tfsdk:"source_config_kind"`
+ IsActive types.String `tfsdk:"is_active"`
+ IsPublic types.String `tfsdk:"is_public"`
+ Tags types.List `tfsdk:"tags"`
+ ContextTags types.Map `tfsdk:"context_tags"`
+ Deprecation types.Object `tfsdk:"deprecation"`
+ WorkflowsConfig types.Object `tfsdk:"workflows_config"`
+ Actions types.Map `tfsdk:"actions"`
+}
+
+// ---------------------------------------------------------------------------
+// Deprecation
+// ---------------------------------------------------------------------------
+
+type DeprecationModel struct {
+ EffectiveDate types.String `tfsdk:"effective_date"`
+ Message types.String `tfsdk:"message"`
+}
+
+func (DeprecationModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "effective_date": types.StringType,
+ "message": types.StringType,
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Environment variables
+// ---------------------------------------------------------------------------
+
+type EnvVarConfigModel struct {
+ VarName types.String `tfsdk:"var_name"`
+ SecretId types.String `tfsdk:"secret_id"`
+ TextValue types.String `tfsdk:"text_value"`
+}
+
+func (EnvVarConfigModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "var_name": types.StringType,
+ "secret_id": types.StringType,
+ "text_value": types.StringType,
+ }
+}
+
+type EnvVarModel struct {
+ Config types.Object `tfsdk:"config"`
+ Kind types.String `tfsdk:"kind"`
+}
+
+func (EnvVarModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "config": types.ObjectType{AttrTypes: EnvVarConfigModel{}.AttributeTypes()},
+ "kind": types.StringType,
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Mount points
+// ---------------------------------------------------------------------------
+
+type MountPointModel struct {
+ Source types.String `tfsdk:"source"`
+ Target types.String `tfsdk:"target"`
+ ReadOnly types.Bool `tfsdk:"read_only"`
+}
+
+func (MountPointModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "source": types.StringType,
+ "target": types.StringType,
+ "read_only": types.BoolType,
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Wf step input data
+// ---------------------------------------------------------------------------
+
+type WfStepInputDataModel struct {
+ SchemaType types.String `tfsdk:"schema_type"`
+ Data types.String `tfsdk:"data"`
+}
+
+func (WfStepInputDataModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "schema_type": types.StringType,
+ "data": types.StringType,
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Input schemas (for WorkflowsConfig workflows)
+// ---------------------------------------------------------------------------
+
+type StackInputSchemaModel struct {
+ Id types.String `tfsdk:"id"`
+ Name types.String `tfsdk:"name"`
+ Description types.String `tfsdk:"description"`
+ Type types.String `tfsdk:"type"`
+ EncodedData types.String `tfsdk:"encoded_data"`
+ UiSchemaData types.String `tfsdk:"ui_schema_data"`
+ IsCommitted types.Bool `tfsdk:"is_committed"`
+}
+
+func (StackInputSchemaModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "id": types.StringType,
+ "name": types.StringType,
+ "description": types.StringType,
+ "type": types.StringType,
+ "encoded_data": types.StringType,
+ "ui_schema_data": types.StringType,
+ "is_committed": types.BoolType,
+ }
+}
+
+// ---------------------------------------------------------------------------
+// MiniSteps
+// ---------------------------------------------------------------------------
+
+type MinistepsNotificationRecipientsModel struct {
+ Recipients types.List `tfsdk:"recipients"`
+}
+
+func (MinistepsNotificationRecipientsModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "recipients": types.ListType{ElemType: types.StringType},
+ }
+}
+
+type MinistepsWebhooksModel struct {
+ WebhookName types.String `tfsdk:"webhook_name"`
+ WebhookUrl types.String `tfsdk:"webhook_url"`
+ WebhookSecret types.String `tfsdk:"webhook_secret"`
+}
+
+func (MinistepsWebhooksModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "webhook_name": types.StringType,
+ "webhook_url": types.StringType,
+ "webhook_secret": types.StringType,
+ }
+}
+
+type MinistepsWorkflowChainingModel struct {
+ WorkflowGroupId types.String `tfsdk:"workflow_group_id"`
+ StackId types.String `tfsdk:"stack_id"`
+ StackRunPayload types.String `tfsdk:"stack_run_payload"`
+ WorkflowId types.String `tfsdk:"workflow_id"`
+ WorkflowRunPayload types.String `tfsdk:"workflow_run_payload"`
+}
+
+func (MinistepsWorkflowChainingModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "workflow_group_id": types.StringType,
+ "stack_id": types.StringType,
+ "stack_run_payload": types.StringType,
+ "workflow_id": types.StringType,
+ "workflow_run_payload": types.StringType,
+ }
+}
+
+type MinistepsEmailModel struct {
+ ApprovalRequired types.List `tfsdk:"approval_required"`
+ Cancelled types.List `tfsdk:"cancelled"`
+ Completed types.List `tfsdk:"completed"`
+ DriftDetected types.List `tfsdk:"drift_detected"`
+ Errored types.List `tfsdk:"errored"`
+}
+
+func (MinistepsEmailModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "approval_required": types.ListType{ElemType: types.ObjectType{AttrTypes: MinistepsNotificationRecipientsModel{}.AttributeTypes()}},
+ "cancelled": types.ListType{ElemType: types.ObjectType{AttrTypes: MinistepsNotificationRecipientsModel{}.AttributeTypes()}},
+ "completed": types.ListType{ElemType: types.ObjectType{AttrTypes: MinistepsNotificationRecipientsModel{}.AttributeTypes()}},
+ "drift_detected": types.ListType{ElemType: types.ObjectType{AttrTypes: MinistepsNotificationRecipientsModel{}.AttributeTypes()}},
+ "errored": types.ListType{ElemType: types.ObjectType{AttrTypes: MinistepsNotificationRecipientsModel{}.AttributeTypes()}},
+ }
+}
+
+type MinistepsNotificationsModel struct {
+ Email types.Object `tfsdk:"email"`
+}
+
+func (MinistepsNotificationsModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "email": types.ObjectType{AttrTypes: MinistepsEmailModel{}.AttributeTypes()},
+ }
+}
+
+type MinistepsWebhooksContainerModel struct {
+ ApprovalRequired types.List `tfsdk:"approval_required"`
+ Cancelled types.List `tfsdk:"cancelled"`
+ Completed types.List `tfsdk:"completed"`
+ DriftDetected types.List `tfsdk:"drift_detected"`
+ Errored types.List `tfsdk:"errored"`
+}
+
+func (MinistepsWebhooksContainerModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "approval_required": types.ListType{ElemType: types.ObjectType{AttrTypes: MinistepsWebhooksModel{}.AttributeTypes()}},
+ "cancelled": types.ListType{ElemType: types.ObjectType{AttrTypes: MinistepsWebhooksModel{}.AttributeTypes()}},
+ "completed": types.ListType{ElemType: types.ObjectType{AttrTypes: MinistepsWebhooksModel{}.AttributeTypes()}},
+ "drift_detected": types.ListType{ElemType: types.ObjectType{AttrTypes: MinistepsWebhooksModel{}.AttributeTypes()}},
+ "errored": types.ListType{ElemType: types.ObjectType{AttrTypes: MinistepsWebhooksModel{}.AttributeTypes()}},
+ }
+}
+
+type MinistepsWfChainingContainerModel struct {
+ Completed types.List `tfsdk:"completed"`
+ Errored types.List `tfsdk:"errored"`
+}
+
+func (MinistepsWfChainingContainerModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "completed": types.ListType{ElemType: types.ObjectType{AttrTypes: MinistepsWorkflowChainingModel{}.AttributeTypes()}},
+ "errored": types.ListType{ElemType: types.ObjectType{AttrTypes: MinistepsWorkflowChainingModel{}.AttributeTypes()}},
+ }
+}
+
+type MinistepsModel struct {
+ Notifications types.Object `tfsdk:"notifications"`
+ Webhooks types.Object `tfsdk:"webhooks"`
+ WfChaining types.Object `tfsdk:"wf_chaining"`
+}
+
+func (MinistepsModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "notifications": types.ObjectType{AttrTypes: MinistepsNotificationsModel{}.AttributeTypes()},
+ "webhooks": types.ObjectType{AttrTypes: MinistepsWebhooksContainerModel{}.AttributeTypes()},
+ "wf_chaining": types.ObjectType{AttrTypes: MinistepsWfChainingContainerModel{}.AttributeTypes()},
+ }
+}
+
+// ---------------------------------------------------------------------------
+// WfStepsConfig
+// ---------------------------------------------------------------------------
+
+type WfStepsConfigModel struct {
+ Name types.String `tfsdk:"name"`
+ EnvironmentVariables types.List `tfsdk:"environment_variables"`
+ Approval types.Bool `tfsdk:"approval"`
+ Timeout types.Int64 `tfsdk:"timeout"`
+ CmdOverride types.String `tfsdk:"cmd_override"`
+ MountPoints types.List `tfsdk:"mount_points"`
+ WfStepTemplateId types.String `tfsdk:"wf_step_template_id"`
+ WfStepInputData types.Object `tfsdk:"wf_step_input_data"`
+}
+
+func (WfStepsConfigModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "name": types.StringType,
+ "environment_variables": types.ListType{ElemType: types.ObjectType{AttrTypes: EnvVarModel{}.AttributeTypes()}},
+ "approval": types.BoolType,
+ "timeout": types.Int64Type,
+ "cmd_override": types.StringType,
+ "mount_points": types.ListType{ElemType: types.ObjectType{AttrTypes: MountPointModel{}.AttributeTypes()}},
+ "wf_step_template_id": types.StringType,
+ "wf_step_input_data": types.ObjectType{AttrTypes: WfStepInputDataModel{}.AttributeTypes()},
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Terraform config
+// ---------------------------------------------------------------------------
+
+type TerraformConfigModel struct {
+ TerraformVersion types.String `tfsdk:"terraform_version"`
+ DriftCheck types.Bool `tfsdk:"drift_check"`
+ DriftCron types.String `tfsdk:"drift_cron"`
+ ManagedTerraformState types.Bool `tfsdk:"managed_terraform_state"`
+ ApprovalPreApply types.Bool `tfsdk:"approval_pre_apply"`
+ TerraformPlanOptions types.String `tfsdk:"terraform_plan_options"`
+ TerraformInitOptions types.String `tfsdk:"terraform_init_options"`
+ TerraformBinPath types.List `tfsdk:"terraform_bin_path"`
+ Timeout types.Int64 `tfsdk:"timeout"`
+ PostApplyWfStepsConfig types.List `tfsdk:"post_apply_wf_steps_config"`
+ PreApplyWfStepsConfig types.List `tfsdk:"pre_apply_wf_steps_config"`
+ PrePlanWfStepsConfig types.List `tfsdk:"pre_plan_wf_steps_config"`
+ PostPlanWfStepsConfig types.List `tfsdk:"post_plan_wf_steps_config"`
+ PreInitHooks types.List `tfsdk:"pre_init_hooks"`
+ PrePlanHooks types.List `tfsdk:"pre_plan_hooks"`
+ PostPlanHooks types.List `tfsdk:"post_plan_hooks"`
+ PreApplyHooks types.List `tfsdk:"pre_apply_hooks"`
+ PostApplyHooks types.List `tfsdk:"post_apply_hooks"`
+ RunPreInitHooksOnDrift types.Bool `tfsdk:"run_pre_init_hooks_on_drift"`
+}
+
+func (TerraformConfigModel) AttributeTypes() map[string]attr.Type {
+ wfStepsListType := types.ListType{ElemType: types.ObjectType{AttrTypes: WfStepsConfigModel{}.AttributeTypes()}}
+ return map[string]attr.Type{
+ "terraform_version": types.StringType,
+ "drift_check": types.BoolType,
+ "drift_cron": types.StringType,
+ "managed_terraform_state": types.BoolType,
+ "approval_pre_apply": types.BoolType,
+ "terraform_plan_options": types.StringType,
+ "terraform_init_options": types.StringType,
+ "terraform_bin_path": types.ListType{ElemType: types.ObjectType{AttrTypes: MountPointModel{}.AttributeTypes()}},
+ "timeout": types.Int64Type,
+ "post_apply_wf_steps_config": wfStepsListType,
+ "pre_apply_wf_steps_config": wfStepsListType,
+ "pre_plan_wf_steps_config": wfStepsListType,
+ "post_plan_wf_steps_config": wfStepsListType,
+ "pre_init_hooks": types.ListType{ElemType: types.StringType},
+ "pre_plan_hooks": types.ListType{ElemType: types.StringType},
+ "post_plan_hooks": types.ListType{ElemType: types.StringType},
+ "pre_apply_hooks": types.ListType{ElemType: types.StringType},
+ "post_apply_hooks": types.ListType{ElemType: types.StringType},
+ "run_pre_init_hooks_on_drift": types.BoolType,
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Deployment platform config
+// ---------------------------------------------------------------------------
+
+type DeploymentPlatformConfigModel struct {
+ Kind types.String `tfsdk:"kind"`
+ Config types.String `tfsdk:"config"` // JSON string (map[string]interface{})
+}
+
+func (DeploymentPlatformConfigModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "kind": types.StringType,
+ "config": types.StringType,
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Runner constraints
+// ---------------------------------------------------------------------------
+
+type RunnerConstraintsModel struct {
+ Type types.String `tfsdk:"type"`
+ Names types.List `tfsdk:"names"`
+}
+
+func (RunnerConstraintsModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "type": types.StringType,
+ "names": types.ListType{ElemType: types.StringType},
+ }
+}
+
+// ---------------------------------------------------------------------------
+// User schedules
+// ---------------------------------------------------------------------------
+
+type UserSchedulesModel struct {
+ Name types.String `tfsdk:"name"`
+ Desc types.String `tfsdk:"desc"`
+ Cron types.String `tfsdk:"cron"`
+ State types.String `tfsdk:"state"`
+}
+
+func (UserSchedulesModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "name": types.StringType,
+ "desc": types.StringType,
+ "cron": types.StringType,
+ "state": types.StringType,
+ }
+}
+
+// ---------------------------------------------------------------------------
+// VCS config (for WorkflowsConfigWorkflow)
+// ---------------------------------------------------------------------------
+
+type CustomSourceConfigModel struct {
+ IsPrivate types.Bool `tfsdk:"is_private"`
+ Auth types.String `tfsdk:"auth"`
+ WorkingDir types.String `tfsdk:"working_dir"`
+ GitSparseCheckoutConfig types.String `tfsdk:"git_sparse_checkout_config"`
+ GitCoreAutoCrlf types.Bool `tfsdk:"git_core_auto_crlf"`
+ Ref types.String `tfsdk:"ref"`
+ Repo types.String `tfsdk:"repo"`
+ IncludeSubModule types.Bool `tfsdk:"include_sub_module"`
+}
+
+func (CustomSourceConfigModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "is_private": types.BoolType,
+ "auth": types.StringType,
+ "working_dir": types.StringType,
+ "git_sparse_checkout_config": types.StringType,
+ "git_core_auto_crlf": types.BoolType,
+ "ref": types.StringType,
+ "repo": types.StringType,
+ "include_sub_module": types.BoolType,
+ }
+}
+
+type CustomSourceModel struct {
+ SourceConfigDestKind types.String `tfsdk:"source_config_dest_kind"`
+ Config types.Object `tfsdk:"config"`
+}
+
+func (CustomSourceModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "source_config_dest_kind": types.StringType,
+ "config": types.ObjectType{AttrTypes: CustomSourceConfigModel{}.AttributeTypes()},
+ }
+}
+
+type IacVcsConfigModel struct {
+ UseMarketplaceTemplate types.Bool `tfsdk:"use_marketplace_template"`
+ IacTemplateId types.String `tfsdk:"iac_template_id"`
+ CustomSource types.Object `tfsdk:"custom_source"`
+}
+
+func (IacVcsConfigModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "use_marketplace_template": types.BoolType,
+ "iac_template_id": types.StringType,
+ "custom_source": types.ObjectType{AttrTypes: CustomSourceModel{}.AttributeTypes()},
+ }
+}
+
+type IacInputDataModel struct {
+ SchemaId types.String `tfsdk:"schema_id"`
+ SchemaType types.String `tfsdk:"schema_type"`
+ Data types.String `tfsdk:"data"` // JSON string
+}
+
+func (IacInputDataModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "schema_id": types.StringType,
+ "schema_type": types.StringType,
+ "data": types.StringType,
+ }
+}
+
+type VcsConfigModel struct {
+ IacVcsConfig types.Object `tfsdk:"iac_vcs_config"`
+ IacInputData types.Object `tfsdk:"iac_input_data"`
+}
+
+func (VcsConfigModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "iac_vcs_config": types.ObjectType{AttrTypes: IacVcsConfigModel{}.AttributeTypes()},
+ "iac_input_data": types.ObjectType{AttrTypes: IacInputDataModel{}.AttributeTypes()},
+ }
+}
+
+// ---------------------------------------------------------------------------
+// WorkflowsConfig
+// ---------------------------------------------------------------------------
+
+type WorkflowInStackModel struct {
+ Id types.String `tfsdk:"id"`
+ TemplateId types.String `tfsdk:"template_id"`
+ ResourceName types.String `tfsdk:"resource_name"`
+ WfStepsConfig types.List `tfsdk:"wf_steps_config"`
+ TerraformConfig types.Object `tfsdk:"terraform_config"`
+ EnvironmentVariables types.List `tfsdk:"environment_variables"`
+ DeploymentPlatformConfig types.List `tfsdk:"deployment_platform_config"`
+ VcsConfig types.Object `tfsdk:"vcs_config"`
+ IacInputData types.Object `tfsdk:"iac_input_data"`
+ UserSchedules types.List `tfsdk:"user_schedules"`
+ Approvers types.List `tfsdk:"approvers"`
+ NumberOfApprovalsRequired types.Int64 `tfsdk:"number_of_approvals_required"`
+ RunnerConstraints types.Object `tfsdk:"runner_constraints"`
+ UserJobCpu types.Int64 `tfsdk:"user_job_cpu"`
+ UserJobMemory types.Int64 `tfsdk:"user_job_memory"`
+ InputSchemas types.List `tfsdk:"input_schemas"`
+ MiniSteps types.Object `tfsdk:"mini_steps"`
+}
+
+func (WorkflowInStackModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "id": types.StringType,
+ "template_id": types.StringType,
+ "resource_name": types.StringType,
+ "wf_steps_config": types.ListType{ElemType: types.ObjectType{AttrTypes: WfStepsConfigModel{}.AttributeTypes()}},
+ "terraform_config": types.ObjectType{AttrTypes: TerraformConfigModel{}.AttributeTypes()},
+ "environment_variables": types.ListType{ElemType: types.ObjectType{AttrTypes: EnvVarModel{}.AttributeTypes()}},
+ "deployment_platform_config": types.ListType{ElemType: types.ObjectType{AttrTypes: DeploymentPlatformConfigModel{}.AttributeTypes()}},
+ "vcs_config": types.ObjectType{AttrTypes: VcsConfigModel{}.AttributeTypes()},
+ "iac_input_data": types.ObjectType{AttrTypes: WfStepInputDataModel{}.AttributeTypes()},
+ "user_schedules": types.ListType{ElemType: types.ObjectType{AttrTypes: UserSchedulesModel{}.AttributeTypes()}},
+ "approvers": types.ListType{ElemType: types.StringType},
+ "number_of_approvals_required": types.Int64Type,
+ "runner_constraints": types.ObjectType{AttrTypes: RunnerConstraintsModel{}.AttributeTypes()},
+ "user_job_cpu": types.Int64Type,
+ "user_job_memory": types.Int64Type,
+ "input_schemas": types.ListType{ElemType: types.ObjectType{AttrTypes: StackInputSchemaModel{}.AttributeTypes()}},
+ "mini_steps": types.ObjectType{AttrTypes: MinistepsModel{}.AttributeTypes()},
+ }
+}
+
+type WorkflowsConfigModel struct {
+ Workflows types.List `tfsdk:"workflows"`
+}
+
+func (WorkflowsConfigModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "workflows": types.ListType{ElemType: types.ObjectType{AttrTypes: WorkflowInStackModel{}.AttributeTypes()}},
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Actions
+// ---------------------------------------------------------------------------
+
+type TerraformActionModel struct {
+ Action types.String `tfsdk:"action"`
+}
+
+func (TerraformActionModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "action": types.StringType,
+ }
+}
+
+type StackActionParametersModel struct {
+ TerraformAction types.Object `tfsdk:"terraform_action"`
+ DeploymentPlatformConfig types.List `tfsdk:"deployment_platform_config"`
+ WfStepsConfig types.List `tfsdk:"wf_steps_config"`
+ EnvironmentVariables types.List `tfsdk:"environment_variables"`
+}
+
+func (StackActionParametersModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "terraform_action": types.ObjectType{AttrTypes: TerraformActionModel{}.AttributeTypes()},
+ "deployment_platform_config": types.ListType{ElemType: types.ObjectType{AttrTypes: DeploymentPlatformConfigModel{}.AttributeTypes()}},
+ "wf_steps_config": types.ListType{ElemType: types.ObjectType{AttrTypes: WfStepsConfigModel{}.AttributeTypes()}},
+ "environment_variables": types.ListType{ElemType: types.ObjectType{AttrTypes: EnvVarModel{}.AttributeTypes()}},
+ }
+}
+
+type ActionDependencyConditionModel struct {
+ LatestStatus types.String `tfsdk:"latest_status"`
+}
+
+func (ActionDependencyConditionModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "latest_status": types.StringType,
+ }
+}
+
+type ActionDependencyModel struct {
+ Id types.String `tfsdk:"id"`
+ Condition types.Object `tfsdk:"condition"`
+}
+
+func (ActionDependencyModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "id": types.StringType,
+ "condition": types.ObjectType{AttrTypes: ActionDependencyConditionModel{}.AttributeTypes()},
+ }
+}
+
+type ActionOrderModel struct {
+ Parameters types.Object `tfsdk:"parameters"`
+ Dependencies types.List `tfsdk:"dependencies"`
+}
+
+func (ActionOrderModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "parameters": types.ObjectType{AttrTypes: StackActionParametersModel{}.AttributeTypes()},
+ "dependencies": types.ListType{ElemType: types.ObjectType{AttrTypes: ActionDependencyModel{}.AttributeTypes()}},
+ }
+}
+
+// ActionsModel represents a single action value in the actions map.
+type ActionsModel struct {
+ Name types.String `tfsdk:"name"`
+ Description types.String `tfsdk:"description"`
+ Default types.Bool `tfsdk:"default"`
+ Order types.Map `tfsdk:"order"` // map[string]ActionOrderModel
+}
+
+func (ActionsModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "name": types.StringType,
+ "description": types.StringType,
+ "default": types.BoolType,
+ "order": types.MapType{ElemType: types.ObjectType{AttrTypes: ActionOrderModel{}.AttributeTypes()}},
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Helper: parse JSON string to map[string]interface{}
+// ---------------------------------------------------------------------------
+
+func parseJSONToMap(s string) map[string]interface{} {
+ if s == "" {
+ return nil
+ }
+ var result map[string]interface{}
+ if err := json.Unmarshal([]byte(s), &result); err != nil {
+ return nil
+ }
+ return result
+}
+
+func marshalToJSONString(v interface{}) types.String {
+ if v == nil {
+ return types.StringNull()
+ }
+ b, err := json.Marshal(v)
+ if err != nil {
+ return types.StringNull()
+ }
+ return flatteners.String(string(b))
+}
+
+// ---------------------------------------------------------------------------
+// Converters: Terraform model → SDK API types
+// ---------------------------------------------------------------------------
+
+func convertEnvVarsToAPI(ctx context.Context, list types.List) ([]sgsdkgo.EnvVars, diag.Diagnostics) {
+ if list.IsNull() || list.IsUnknown() {
+ return nil, nil
+ }
+ var models []EnvVarModel
+ if diags := list.ElementsAs(ctx, &models, false); diags.HasError() {
+ return nil, diags
+ }
+ result := make([]sgsdkgo.EnvVars, len(models))
+ for i, m := range models {
+ var cfgModel EnvVarConfigModel
+ if diags := m.Config.As(ctx, &cfgModel, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ result[i] = sgsdkgo.EnvVars{
+ Kind: sgsdkgo.EnvVarsKindEnum(m.Kind.ValueString()),
+ Config: &sgsdkgo.EnvVarConfig{
+ VarName: cfgModel.VarName.ValueString(),
+ SecretId: cfgModel.SecretId.ValueStringPointer(),
+ TextValue: cfgModel.TextValue.ValueStringPointer(),
+ },
+ }
+ }
+ return result, nil
+}
+
+func convertEnvVarPointersToAPI(ctx context.Context, list types.List) ([]*sgsdkgo.EnvVars, diag.Diagnostics) {
+ vals, diags := convertEnvVarsToAPI(ctx, list)
+ if diags.HasError() {
+ return nil, diags
+ }
+ if vals == nil {
+ return nil, nil
+ }
+ result := make([]*sgsdkgo.EnvVars, len(vals))
+ for i := range vals {
+ result[i] = &vals[i]
+ }
+ return result, nil
+}
+
+func convertMountPointsToAPI(ctx context.Context, list types.List) ([]sgsdkgo.MountPoint, diag.Diagnostics) {
+ if list.IsNull() || list.IsUnknown() {
+ return nil, nil
+ }
+ var models []MountPointModel
+ if diags := list.ElementsAs(ctx, &models, false); diags.HasError() {
+ return nil, diags
+ }
+ result := make([]sgsdkgo.MountPoint, len(models))
+ for i, m := range models {
+ result[i] = sgsdkgo.MountPoint{
+ Source: m.Source.ValueString(),
+ Target: m.Target.ValueString(),
+ ReadOnly: m.ReadOnly.ValueBoolPointer(),
+ }
+ }
+ return result, nil
+}
+
+func convertWfStepsConfigToAPI(ctx context.Context, list types.List) ([]sgsdkgo.WfStepsConfig, diag.Diagnostics) {
+ if list.IsNull() || list.IsUnknown() {
+ return nil, nil
+ }
+ var models []WfStepsConfigModel
+ if diags := list.ElementsAs(ctx, &models, false); diags.HasError() {
+ return nil, diags
+ }
+ result := make([]sgsdkgo.WfStepsConfig, len(models))
+ for i, m := range models {
+ step := sgsdkgo.WfStepsConfig{
+ Name: m.Name.ValueString(),
+ Approval: m.Approval.ValueBoolPointer(),
+ Timeout: expanders.IntPtr(m.Timeout.ValueInt64Pointer()),
+ WfStepTemplateId: m.WfStepTemplateId.ValueStringPointer(),
+ CmdOverride: m.CmdOverride.ValueStringPointer(),
+ }
+ if !m.EnvironmentVariables.IsNull() && !m.EnvironmentVariables.IsUnknown() {
+ envVars, diags := convertEnvVarsToAPI(ctx, m.EnvironmentVariables)
+ if diags.HasError() {
+ return nil, diags
+ }
+ step.EnvironmentVariables = envVars
+ }
+ if !m.MountPoints.IsNull() && !m.MountPoints.IsUnknown() {
+ mps, diags := convertMountPointsToAPI(ctx, m.MountPoints)
+ if diags.HasError() {
+ return nil, diags
+ }
+ step.MountPoints = mps
+ }
+ if !m.WfStepInputData.IsNull() && !m.WfStepInputData.IsUnknown() {
+ var idm WfStepInputDataModel
+ if diags := m.WfStepInputData.As(ctx, &idm, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ step.WfStepInputData = &sgsdkgo.WfStepInputData{
+ SchemaType: sgsdkgo.WfStepInputDataSchemaTypeEnum(idm.SchemaType.ValueString()),
+ Data: parseJSONToMap(idm.Data.ValueString()),
+ }
+ }
+ result[i] = step
+ }
+ return result, nil
+}
+
+func convertWfStepsConfigPointersToAPI(ctx context.Context, list types.List) ([]*sgsdkgo.WfStepsConfig, diag.Diagnostics) {
+ vals, diags := convertWfStepsConfigToAPI(ctx, list)
+ if diags.HasError() {
+ return nil, diags
+ }
+ if vals == nil {
+ return nil, nil
+ }
+ result := make([]*sgsdkgo.WfStepsConfig, len(vals))
+ for i := range vals {
+ result[i] = &vals[i]
+ }
+ return result, nil
+}
+
+func convertTerraformConfigToAPI(ctx context.Context, obj types.Object) (*sgsdkgo.TerraformConfig, diag.Diagnostics) {
+ if obj.IsNull() || obj.IsUnknown() {
+ return nil, nil
+ }
+ var m TerraformConfigModel
+ if diags := obj.As(ctx, &m, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ tc := &sgsdkgo.TerraformConfig{
+ TerraformVersion: m.TerraformVersion.ValueStringPointer(),
+ DriftCheck: m.DriftCheck.ValueBoolPointer(),
+ DriftCron: m.DriftCron.ValueStringPointer(),
+ ManagedTerraformState: m.ManagedTerraformState.ValueBoolPointer(),
+ ApprovalPreApply: m.ApprovalPreApply.ValueBoolPointer(),
+ TerraformPlanOptions: m.TerraformPlanOptions.ValueStringPointer(),
+ TerraformInitOptions: m.TerraformInitOptions.ValueStringPointer(),
+ Timeout: expanders.IntPtr(m.Timeout.ValueInt64Pointer()),
+ RunPreInitHooksOnDrift: m.RunPreInitHooksOnDrift.ValueBoolPointer(),
+ }
+ if !m.TerraformBinPath.IsNull() && !m.TerraformBinPath.IsUnknown() {
+ mps, diags := convertMountPointsToAPI(ctx, m.TerraformBinPath)
+ if diags.HasError() {
+ return nil, diags
+ }
+ tc.TerraformBinPath = mps
+ }
+ for _, pair := range []struct {
+ list *types.List
+ dest *[]sgsdkgo.WfStepsConfig
+ }{
+ {&m.PostApplyWfStepsConfig, &tc.PostApplyWfStepsConfig},
+ {&m.PreApplyWfStepsConfig, &tc.PreApplyWfStepsConfig},
+ {&m.PrePlanWfStepsConfig, &tc.PrePlanWfStepsConfig},
+ {&m.PostPlanWfStepsConfig, &tc.PostPlanWfStepsConfig},
+ } {
+ if !pair.list.IsNull() && !pair.list.IsUnknown() {
+ steps, diags := convertWfStepsConfigToAPI(ctx, *pair.list)
+ if diags.HasError() {
+ return nil, diags
+ }
+ *pair.dest = steps
+ }
+ }
+ for _, pair := range []struct {
+ list *types.List
+ dest *[]string
+ }{
+ {&m.PreInitHooks, &tc.PreInitHooks},
+ {&m.PrePlanHooks, &tc.PrePlanHooks},
+ {&m.PostPlanHooks, &tc.PostPlanHooks},
+ {&m.PreApplyHooks, &tc.PreApplyHooks},
+ {&m.PostApplyHooks, &tc.PostApplyHooks},
+ } {
+ if !pair.list.IsNull() && !pair.list.IsUnknown() {
+ hooks, diags := expanders.StringList(ctx, *pair.list)
+ if diags.HasError() {
+ return nil, diags
+ }
+ *pair.dest = hooks
+ }
+ }
+ return tc, nil
+}
+
+func convertDeploymentPlatformConfigToAPI(ctx context.Context, list types.List) ([]*sgsdkgo.DeploymentPlatformConfig, diag.Diagnostics) {
+ if list.IsNull() || list.IsUnknown() {
+ return nil, nil
+ }
+ var models []DeploymentPlatformConfigModel
+ if diags := list.ElementsAs(ctx, &models, false); diags.HasError() {
+ return nil, diags
+ }
+ result := make([]*sgsdkgo.DeploymentPlatformConfig, len(models))
+ for i, m := range models {
+ dpc := &sgsdkgo.DeploymentPlatformConfig{
+ Kind: sgsdkgo.DeploymentPlatformConfigKindEnum(m.Kind.ValueString()),
+ Config: parseJSONToMap(m.Config.ValueString()),
+ }
+ result[i] = dpc
+ }
+ return result, nil
+}
+
+func convertRunnerConstraintsToAPI(ctx context.Context, obj types.Object) (*sgsdkgo.RunnerConstraints, diag.Diagnostics) {
+ if obj.IsNull() || obj.IsUnknown() {
+ return nil, nil
+ }
+ var m RunnerConstraintsModel
+ if diags := obj.As(ctx, &m, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ names, diags := expanders.StringList(ctx, m.Names)
+ if diags.HasError() {
+ return nil, diags
+ }
+ return &sgsdkgo.RunnerConstraints{
+ Type: sgsdkgo.RunnerConstraintsTypeEnum(m.Type.ValueString()),
+ Names: names,
+ }, nil
+}
+
+func convertUserSchedulesToAPI(ctx context.Context, list types.List) ([]*sgsdkgo.UserSchedules, diag.Diagnostics) {
+ if list.IsNull() || list.IsUnknown() {
+ return nil, nil
+ }
+ var models []UserSchedulesModel
+ if diags := list.ElementsAs(ctx, &models, false); diags.HasError() {
+ return nil, diags
+ }
+ result := make([]*sgsdkgo.UserSchedules, len(models))
+ for i, m := range models {
+ us := &sgsdkgo.UserSchedules{
+ Name: m.Name.ValueStringPointer(),
+ Desc: m.Desc.ValueStringPointer(),
+ Cron: m.Cron.ValueString(),
+ State: sgsdkgo.StateEnum(m.State.ValueString()),
+ }
+ result[i] = us
+ }
+ return result, nil
+}
+
+func convertVcsConfigToAPI(ctx context.Context, obj types.Object, orgName string) (*sgsdkgo.VcsConfig, diag.Diagnostics) {
+ if obj.IsNull() || obj.IsUnknown() {
+ return nil, nil
+ }
+ var m VcsConfigModel
+ if diags := obj.As(ctx, &m, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ vcsConfig := &sgsdkgo.VcsConfig{}
+
+ if !m.IacVcsConfig.IsNull() && !m.IacVcsConfig.IsUnknown() {
+ var iacModel IacVcsConfigModel
+ if diags := m.IacVcsConfig.As(ctx, &iacModel, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ var prefixedIacTemplateId *string
+ if tid := iacModel.IacTemplateId.ValueStringPointer(); tid != nil {
+ v := fmt.Sprintf("/%s/%s", orgName, *tid)
+ prefixedIacTemplateId = &v
+ }
+ iacVcs := &sgsdkgo.IacvcsConfig{
+ UseMarketplaceTemplate: iacModel.UseMarketplaceTemplate.ValueBool(),
+ IacTemplateId: prefixedIacTemplateId,
+ }
+ if !iacModel.CustomSource.IsNull() && !iacModel.CustomSource.IsUnknown() {
+ var csModel CustomSourceModel
+ if diags := iacModel.CustomSource.As(ctx, &csModel, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ cs := &sgsdkgo.CustomSource{
+ SourceConfigDestKind: sgsdkgo.CustomSourceSourceConfigDestKindEnum(csModel.SourceConfigDestKind.ValueString()),
+ }
+ if !csModel.Config.IsNull() && !csModel.Config.IsUnknown() {
+ var csCfgModel CustomSourceConfigModel
+ if diags := csModel.Config.As(ctx, &csCfgModel, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ cs.Config = &sgsdkgo.CustomSourceConfig{
+ IsPrivate: csCfgModel.IsPrivate.ValueBoolPointer(),
+ Auth: csCfgModel.Auth.ValueStringPointer(),
+ WorkingDir: csCfgModel.WorkingDir.ValueStringPointer(),
+ GitSparseCheckoutConfig: csCfgModel.GitSparseCheckoutConfig.ValueStringPointer(),
+ GitCoreAutoCrlf: csCfgModel.GitCoreAutoCrlf.ValueBoolPointer(),
+ Ref: csCfgModel.Ref.ValueStringPointer(),
+ Repo: csCfgModel.Repo.ValueStringPointer(),
+ IncludeSubModule: csCfgModel.IncludeSubModule.ValueBoolPointer(),
+ }
+ }
+ iacVcs.CustomSource = cs
+ }
+ vcsConfig.IacVcsConfig = iacVcs
+ }
+
+ if !m.IacInputData.IsNull() && !m.IacInputData.IsUnknown() {
+ var idModel IacInputDataModel
+ if diags := m.IacInputData.As(ctx, &idModel, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ vcsConfig.IacInputData = &sgsdkgo.IacInputData{
+ SchemaId: idModel.SchemaId.ValueStringPointer(),
+ SchemaType: sgsdkgo.IacInputDataSchemaTypeEnum(idModel.SchemaType.ValueString()),
+ Data: parseJSONToMap(idModel.Data.ValueString()),
+ }
+ }
+
+ return vcsConfig, nil
+}
+
+// ---------------------------------------------------------------------------
+// MiniSteps converters (Terraform → SDK)
+// ---------------------------------------------------------------------------
+
+func convertNotificationRecipientsToAPI(ctx context.Context, list types.List) ([]*sgsdkgo.Notifications, diag.Diagnostics) {
+ if list.IsNull() || list.IsUnknown() {
+ return nil, nil
+ }
+ var models []MinistepsNotificationRecipientsModel
+ if diags := list.ElementsAs(ctx, &models, false); diags.HasError() {
+ return nil, diags
+ }
+ result := make([]*sgsdkgo.Notifications, len(models))
+ for i, m := range models {
+ recipients, diags := expanders.StringList(ctx, m.Recipients)
+ if diags.HasError() {
+ return nil, diags
+ }
+ result[i] = &sgsdkgo.Notifications{Recipients: recipients}
+ }
+ return result, nil
+}
+
+func convertWebhooksToAPI(ctx context.Context, list types.List) ([]map[string]interface{}, diag.Diagnostics) {
+ if list.IsNull() || list.IsUnknown() {
+ return nil, nil
+ }
+ var models []MinistepsWebhooksModel
+ if diags := list.ElementsAs(ctx, &models, false); diags.HasError() {
+ return nil, diags
+ }
+ result := make([]map[string]interface{}, len(models))
+ for i, m := range models {
+ wh := map[string]interface{}{
+ "webhookName": m.WebhookName.ValueString(),
+ "webhookUrl": m.WebhookUrl.ValueString(),
+ }
+ if !m.WebhookSecret.IsNull() && !m.WebhookSecret.IsUnknown() {
+ wh["webhookSecret"] = m.WebhookSecret.ValueString()
+ }
+ result[i] = wh
+ }
+ return result, nil
+}
+
+func convertWfChainingToAPI(ctx context.Context, list types.List) ([]*sgsdkgo.MiniSteps, diag.Diagnostics) {
+ if list.IsNull() || list.IsUnknown() {
+ return nil, nil
+ }
+ var models []MinistepsWorkflowChainingModel
+ if diags := list.ElementsAs(ctx, &models, false); diags.HasError() {
+ return nil, diags
+ }
+ result := make([]*sgsdkgo.MiniSteps, len(models))
+ for i, m := range models {
+ ms := &sgsdkgo.MiniSteps{
+ WorkflowGroupId: m.WorkflowGroupId.ValueString(),
+ WorkflowId: m.WorkflowId.ValueStringPointer(),
+ StackId: m.StackId.ValueStringPointer(),
+ }
+ if !m.WorkflowRunPayload.IsNull() && !m.WorkflowRunPayload.IsUnknown() {
+ ms.WorkflowRunPayload = parseJSONToMap(m.WorkflowRunPayload.ValueString())
+ }
+ if !m.StackRunPayload.IsNull() && !m.StackRunPayload.IsUnknown() {
+ ms.StackRunPayload = parseJSONToMap(m.StackRunPayload.ValueString())
+ }
+ result[i] = ms
+ }
+ return result, nil
+}
+
+func convertMiniStepsToAPI(ctx context.Context, obj types.Object) (*sgsdkgo.MiniStepsSchema, diag.Diagnostics) {
+ if obj.IsNull() || obj.IsUnknown() {
+ return nil, nil
+ }
+ var m MinistepsModel
+ if diags := obj.As(ctx, &m, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ result := &sgsdkgo.MiniStepsSchema{}
+
+ if !m.Notifications.IsNull() && !m.Notifications.IsUnknown() {
+ var notifModel MinistepsNotificationsModel
+ if diags := m.Notifications.As(ctx, ¬ifModel, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ notif := &sgsdkgo.NotificationTypes{}
+ if !notifModel.Email.IsNull() && !notifModel.Email.IsUnknown() {
+ var emailModel MinistepsEmailModel
+ if diags := notifModel.Email.As(ctx, &emailModel, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ email := &sgsdkgo.NotificationEmailType{}
+ for _, pair := range []struct {
+ list *types.List
+ dest *[]*sgsdkgo.Notifications
+ }{
+ {&emailModel.ApprovalRequired, &email.ApprovalRequired},
+ {&emailModel.Cancelled, &email.Cancelled},
+ {&emailModel.Completed, &email.Completed},
+ {&emailModel.DriftDetected, &email.DriftDetected},
+ {&emailModel.Errored, &email.Errored},
+ } {
+ if !pair.list.IsNull() && !pair.list.IsUnknown() {
+ vals, diags := convertNotificationRecipientsToAPI(ctx, *pair.list)
+ if diags.HasError() {
+ return nil, diags
+ }
+ *pair.dest = vals
+ }
+ }
+ notif.Email = email
+ }
+ result.Notifications = notif
+ }
+
+ if !m.Webhooks.IsNull() && !m.Webhooks.IsUnknown() {
+ var whModel MinistepsWebhooksContainerModel
+ if diags := m.Webhooks.As(ctx, &whModel, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ wh := &sgsdkgo.WebhookTypes{}
+ for _, pair := range []struct {
+ list *types.List
+ dest *[]map[string]interface{}
+ }{
+ {&whModel.ApprovalRequired, &wh.ApprovalRequired},
+ {&whModel.Cancelled, &wh.Cancelled},
+ {&whModel.Completed, &wh.Completed},
+ {&whModel.DriftDetected, &wh.DriftDetected},
+ {&whModel.Errored, &wh.Errored},
+ } {
+ if !pair.list.IsNull() && !pair.list.IsUnknown() {
+ vals, diags := convertWebhooksToAPI(ctx, *pair.list)
+ if diags.HasError() {
+ return nil, diags
+ }
+ *pair.dest = vals
+ }
+ }
+ result.Webhooks = wh
+ }
+
+ if !m.WfChaining.IsNull() && !m.WfChaining.IsUnknown() {
+ var wcModel MinistepsWfChainingContainerModel
+ if diags := m.WfChaining.As(ctx, &wcModel, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ wc := &sgsdkgo.WfChainingPayloadPayload{}
+ for _, pair := range []struct {
+ list *types.List
+ dest *[]*sgsdkgo.MiniSteps
+ }{
+ {&wcModel.Completed, &wc.Completed},
+ {&wcModel.Errored, &wc.Errored},
+ } {
+ if !pair.list.IsNull() && !pair.list.IsUnknown() {
+ vals, diags := convertWfChainingToAPI(ctx, *pair.list)
+ if diags.HasError() {
+ return nil, diags
+ }
+ *pair.dest = vals
+ }
+ }
+ result.WfChaining = wc
+ }
+
+ return result, nil
+}
+
+func convertNotificationRecipientsFromAPI(ctx context.Context, recipients []*sgsdkgo.Notifications) (types.List, diag.Diagnostics) {
+ nullObj := types.ListNull(types.ObjectType{AttrTypes: MinistepsNotificationRecipientsModel{}.AttributeTypes()})
+ if recipients == nil {
+ return nullObj, nil
+ }
+ elems := make([]MinistepsNotificationRecipientsModel, 0, len(recipients))
+ for _, r := range recipients {
+ if r == nil {
+ continue
+ }
+ recList, diags := types.ListValueFrom(ctx, types.StringType, r.Recipients)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ elems = append(elems, MinistepsNotificationRecipientsModel{Recipients: recList})
+ }
+ obj, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: MinistepsNotificationRecipientsModel{}.AttributeTypes()}, elems)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ return obj, nil
+}
+
+func convertWebhooksFromAPI(ctx context.Context, webhooks []map[string]interface{}) (types.List, diag.Diagnostics) {
+ nullObj := types.ListNull(types.ObjectType{AttrTypes: MinistepsWebhooksModel{}.AttributeTypes()})
+ if webhooks == nil {
+ return nullObj, nil
+ }
+ elems := make([]MinistepsWebhooksModel, 0, len(webhooks))
+ for _, wh := range webhooks {
+ name, _ := wh["webhookName"].(string)
+ url, _ := wh["webhookUrl"].(string)
+ secret, _ := wh["webhookSecret"].(string)
+ m := MinistepsWebhooksModel{
+ WebhookName: flatteners.String(name),
+ WebhookUrl: flatteners.String(url),
+ WebhookSecret: flatteners.String(secret),
+ }
+ elems = append(elems, m)
+ }
+ obj, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: MinistepsWebhooksModel{}.AttributeTypes()}, elems)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ return obj, nil
+}
+
+func convertWfChainingFromAPI(ctx context.Context, items []*sgsdkgo.MiniSteps) (types.List, diag.Diagnostics) {
+ nullObj := types.ListNull(types.ObjectType{AttrTypes: MinistepsWorkflowChainingModel{}.AttributeTypes()})
+ if items == nil {
+ return nullObj, nil
+ }
+ elems := make([]MinistepsWorkflowChainingModel, 0, len(items))
+ for _, item := range items {
+ if item == nil {
+ continue
+ }
+ m := MinistepsWorkflowChainingModel{
+ WorkflowGroupId: flatteners.String(item.WorkflowGroupId),
+ StackId: flatteners.StringPtr(item.StackId),
+ StackRunPayload: marshalToJSONString(item.StackRunPayload),
+ WorkflowId: flatteners.StringPtr(item.WorkflowId),
+ WorkflowRunPayload: marshalToJSONString(item.WorkflowRunPayload),
+ }
+ elems = append(elems, m)
+ }
+ obj, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: MinistepsWorkflowChainingModel{}.AttributeTypes()}, elems)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ return obj, nil
+}
+
+func miniStepsFromAPI(ctx context.Context, miniSteps *sgsdkgo.MiniStepsSchema) (types.Object, diag.Diagnostics) {
+ nullObj := types.ObjectNull(MinistepsModel{}.AttributeTypes())
+ if miniSteps == nil {
+ return nullObj, nil
+ }
+
+ var diags diag.Diagnostics
+ msModel := MinistepsModel{}
+
+ // Notifications
+ if miniSteps.Notifications != nil {
+ notifModel := MinistepsNotificationsModel{}
+ if miniSteps.Notifications.Email != nil {
+ emailModel := MinistepsEmailModel{}
+ emailModel.ApprovalRequired, diags = convertNotificationRecipientsFromAPI(ctx, miniSteps.Notifications.Email.ApprovalRequired)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ emailModel.Cancelled, diags = convertNotificationRecipientsFromAPI(ctx, miniSteps.Notifications.Email.Cancelled)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ emailModel.Completed, diags = convertNotificationRecipientsFromAPI(ctx, miniSteps.Notifications.Email.Completed)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ emailModel.DriftDetected, diags = convertNotificationRecipientsFromAPI(ctx, miniSteps.Notifications.Email.DriftDetected)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ emailModel.Errored, diags = convertNotificationRecipientsFromAPI(ctx, miniSteps.Notifications.Email.Errored)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ emailObj, d := types.ObjectValueFrom(ctx, MinistepsEmailModel{}.AttributeTypes(), emailModel)
+ if d.HasError() {
+ return nullObj, d
+ }
+ notifModel.Email = emailObj
+ } else {
+ notifModel.Email = types.ObjectNull(MinistepsEmailModel{}.AttributeTypes())
+ }
+ notifObj, d := types.ObjectValueFrom(ctx, MinistepsNotificationsModel{}.AttributeTypes(), notifModel)
+ if d.HasError() {
+ return nullObj, d
+ }
+ msModel.Notifications = notifObj
+ } else {
+ msModel.Notifications = types.ObjectNull(MinistepsNotificationsModel{}.AttributeTypes())
+ }
+
+ // Webhooks
+ if miniSteps.Webhooks != nil {
+ whModel := MinistepsWebhooksContainerModel{}
+ whModel.ApprovalRequired, diags = convertWebhooksFromAPI(ctx, miniSteps.Webhooks.ApprovalRequired)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ whModel.Cancelled, diags = convertWebhooksFromAPI(ctx, miniSteps.Webhooks.Cancelled)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ whModel.Completed, diags = convertWebhooksFromAPI(ctx, miniSteps.Webhooks.Completed)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ whModel.DriftDetected, diags = convertWebhooksFromAPI(ctx, miniSteps.Webhooks.DriftDetected)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ whModel.Errored, diags = convertWebhooksFromAPI(ctx, miniSteps.Webhooks.Errored)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ whObj, d := types.ObjectValueFrom(ctx, MinistepsWebhooksContainerModel{}.AttributeTypes(), whModel)
+ if d.HasError() {
+ return nullObj, d
+ }
+ msModel.Webhooks = whObj
+ } else {
+ msModel.Webhooks = types.ObjectNull(MinistepsWebhooksContainerModel{}.AttributeTypes())
+ }
+
+ // WfChaining
+ if miniSteps.WfChaining != nil {
+ wcModel := MinistepsWfChainingContainerModel{}
+ wcModel.Completed, diags = convertWfChainingFromAPI(ctx, miniSteps.WfChaining.Completed)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ wcModel.Errored, diags = convertWfChainingFromAPI(ctx, miniSteps.WfChaining.Errored)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ wcObj, d := types.ObjectValueFrom(ctx, MinistepsWfChainingContainerModel{}.AttributeTypes(), wcModel)
+ if d.HasError() {
+ return nullObj, d
+ }
+ msModel.WfChaining = wcObj
+ } else {
+ msModel.WfChaining = types.ObjectNull(MinistepsWfChainingContainerModel{}.AttributeTypes())
+ }
+
+ return types.ObjectValueFrom(ctx, MinistepsModel{}.AttributeTypes(), msModel)
+}
+
+func convertWorkflowsConfigToAPI(ctx context.Context, obj types.Object, orgName string) (*stacktemplaterevisions.StackTemplateRevisionWorkflowsConfig, diag.Diagnostics) {
+ if obj.IsNull() || obj.IsUnknown() {
+ return nil, nil
+ }
+ var m WorkflowsConfigModel
+ if diags := obj.As(ctx, &m, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ if m.Workflows.IsNull() || m.Workflows.IsUnknown() {
+ return &stacktemplaterevisions.StackTemplateRevisionWorkflowsConfig{}, nil
+ }
+ var wfModels []WorkflowInStackModel
+ if diags := m.Workflows.ElementsAs(ctx, &wfModels, false); diags.HasError() {
+ return nil, diags
+ }
+
+ workflows := make([]*stacktemplaterevisions.StackTemplateRevisionWorkflow, len(wfModels))
+ for i, wm := range wfModels {
+ // Prefix template_id with //
+ var prefixedTemplateId *string
+ if tid := wm.TemplateId.ValueStringPointer(); tid != nil {
+ v := fmt.Sprintf("/%s/%s", orgName, *tid)
+ prefixedTemplateId = &v
+ }
+
+ wf := &stacktemplaterevisions.StackTemplateRevisionWorkflow{
+ Id: wm.Id.ValueStringPointer(),
+ TemplateId: prefixedTemplateId,
+ ResourceName: wm.ResourceName.ValueStringPointer(),
+ NumberOfApprovalsRequired: expanders.IntPtr(wm.NumberOfApprovalsRequired.ValueInt64Pointer()),
+ UserJobCpu: expanders.IntPtr(wm.UserJobCpu.ValueInt64Pointer()),
+ UserJobMemory: expanders.IntPtr(wm.UserJobMemory.ValueInt64Pointer()),
+ }
+ if !wm.WfStepsConfig.IsNull() && !wm.WfStepsConfig.IsUnknown() {
+ steps, diags := convertWfStepsConfigPointersToAPI(ctx, wm.WfStepsConfig)
+ if diags.HasError() {
+ return nil, diags
+ }
+ wf.WfStepsConfig = steps
+ }
+ if !wm.TerraformConfig.IsNull() && !wm.TerraformConfig.IsUnknown() {
+ tc, diags := convertTerraformConfigToAPI(ctx, wm.TerraformConfig)
+ if diags.HasError() {
+ return nil, diags
+ }
+ wf.TerraformConfig = tc
+ }
+ if !wm.EnvironmentVariables.IsNull() && !wm.EnvironmentVariables.IsUnknown() {
+ envVars, diags := convertEnvVarPointersToAPI(ctx, wm.EnvironmentVariables)
+ if diags.HasError() {
+ return nil, diags
+ }
+ wf.EnvironmentVariables = envVars
+ }
+ if !wm.DeploymentPlatformConfig.IsNull() && !wm.DeploymentPlatformConfig.IsUnknown() {
+ dpcs, diags := convertDeploymentPlatformConfigToAPI(ctx, wm.DeploymentPlatformConfig)
+ if diags.HasError() {
+ return nil, diags
+ }
+ wf.DeploymentPlatformConfig = dpcs
+ }
+ if !wm.VcsConfig.IsNull() && !wm.VcsConfig.IsUnknown() {
+ vcs, diags := convertVcsConfigToAPI(ctx, wm.VcsConfig, orgName)
+ if diags.HasError() {
+ return nil, diags
+ }
+ wf.VcsConfig = vcs
+ }
+ if !wm.IacInputData.IsNull() && !wm.IacInputData.IsUnknown() {
+ var idm WfStepInputDataModel
+ if diags := wm.IacInputData.As(ctx, &idm, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ wf.IacInputData = &sgsdkgo.TemplatesIacInputData{
+ SchemaType: idm.SchemaType.ValueString(),
+ Data: parseJSONToMap(idm.Data.ValueString()),
+ }
+ }
+ if !wm.UserSchedules.IsNull() && !wm.UserSchedules.IsUnknown() {
+ us, diags := convertUserSchedulesToAPI(ctx, wm.UserSchedules)
+ if diags.HasError() {
+ return nil, diags
+ }
+ wf.UserSchedules = us
+ }
+ if !wm.Approvers.IsNull() && !wm.Approvers.IsUnknown() {
+ approvers, diags := expanders.StringList(ctx, wm.Approvers)
+ if diags.HasError() {
+ return nil, diags
+ }
+ wf.Approvers = approvers
+ }
+ if !wm.RunnerConstraints.IsNull() && !wm.RunnerConstraints.IsUnknown() {
+ rc, diags := convertRunnerConstraintsToAPI(ctx, wm.RunnerConstraints)
+ if diags.HasError() {
+ return nil, diags
+ }
+ wf.RunnerConstraints = rc
+ }
+ if !wm.InputSchemas.IsNull() && !wm.InputSchemas.IsUnknown() {
+ var isModels []StackInputSchemaModel
+ if diags := wm.InputSchemas.ElementsAs(ctx, &isModels, false); diags.HasError() {
+ return nil, diags
+ }
+ schemas := make([]*sgsdkgo.InputSchemas, len(isModels))
+ for j, ism := range isModels {
+ schemas[j] = &sgsdkgo.InputSchemas{
+ Id: ism.Id.ValueStringPointer(),
+ Name: ism.Name.ValueStringPointer(),
+ Description: ism.Description.ValueStringPointer(),
+ Type: sgsdkgo.InputSchemasTypeEnum(ism.Type.ValueString()),
+ EncodedData: ism.EncodedData.ValueStringPointer(),
+ UiSchemaData: ism.UiSchemaData.ValueStringPointer(),
+ IsCommitted: ism.IsCommitted.ValueBoolPointer(),
+ }
+ }
+ wf.InputSchemas = schemas
+ }
+ if !wm.MiniSteps.IsNull() && !wm.MiniSteps.IsUnknown() {
+ ms, diags := convertMiniStepsToAPI(ctx, wm.MiniSteps)
+ if diags.HasError() {
+ return nil, diags
+ }
+ wf.MiniSteps = ms
+ }
+ workflows[i] = wf
+ }
+ return &stacktemplaterevisions.StackTemplateRevisionWorkflowsConfig{Workflows: workflows}, nil
+}
+
+func convertActionsToAPI(ctx context.Context, actionsMap types.Map) (map[string]*sgsdkgo.Actions, diag.Diagnostics) {
+ if actionsMap.IsNull() || actionsMap.IsUnknown() {
+ return nil, nil
+ }
+ var models map[string]ActionsModel
+ if diags := actionsMap.ElementsAs(ctx, &models, false); diags.HasError() {
+ return nil, diags
+ }
+ result := make(map[string]*sgsdkgo.Actions, len(models))
+ for k, am := range models {
+ action := &sgsdkgo.Actions{
+ Name: am.Name.ValueString(),
+ Description: am.Description.ValueStringPointer(),
+ Default: am.Default.ValueBoolPointer(),
+ }
+ if !am.Order.IsNull() && !am.Order.IsUnknown() {
+ var orderModels map[string]ActionOrderModel
+ if diags := am.Order.ElementsAs(ctx, &orderModels, false); diags.HasError() {
+ return nil, diags
+ }
+ order := make(map[string]*sgsdkgo.ActionOrder, len(orderModels))
+ for wfId, om := range orderModels {
+ ao := &sgsdkgo.ActionOrder{}
+ if !om.Parameters.IsNull() && !om.Parameters.IsUnknown() {
+ var pm StackActionParametersModel
+ if diags := om.Parameters.As(ctx, &pm, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ params := &sgsdkgo.StackActionParameters{}
+ if !pm.TerraformAction.IsNull() && !pm.TerraformAction.IsUnknown() {
+ var tam TerraformActionModel
+ if diags := pm.TerraformAction.As(ctx, &tam, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ actionEnum := sgsdkgo.ActionEnum(tam.Action.ValueString())
+ params.TerraformAction = &sgsdkgo.TerraformAction{Action: &actionEnum}
+ }
+ if !pm.DeploymentPlatformConfig.IsNull() && !pm.DeploymentPlatformConfig.IsUnknown() {
+ dpcs, diags := convertDeploymentPlatformConfigToAPI(ctx, pm.DeploymentPlatformConfig)
+ if diags.HasError() {
+ return nil, diags
+ }
+ params.DeploymentPlatformConfig = dpcs
+ }
+ if !pm.WfStepsConfig.IsNull() && !pm.WfStepsConfig.IsUnknown() {
+ steps, diags := convertWfStepsConfigPointersToAPI(ctx, pm.WfStepsConfig)
+ if diags.HasError() {
+ return nil, diags
+ }
+ params.WfStepsConfig = steps
+ }
+ if !pm.EnvironmentVariables.IsNull() && !pm.EnvironmentVariables.IsUnknown() {
+ envVars, diags := convertEnvVarPointersToAPI(ctx, pm.EnvironmentVariables)
+ if diags.HasError() {
+ return nil, diags
+ }
+ params.EnvironmentVariables = envVars
+ }
+ ao.Parameters = params
+ }
+ if !om.Dependencies.IsNull() && !om.Dependencies.IsUnknown() {
+ var depModels []ActionDependencyModel
+ if diags := om.Dependencies.ElementsAs(ctx, &depModels, false); diags.HasError() {
+ return nil, diags
+ }
+ deps := make([]*sgsdkgo.ActionDependency, len(depModels))
+ for j, dm := range depModels {
+ dep := &sgsdkgo.ActionDependency{Id: dm.Id.ValueString()}
+ if !dm.Condition.IsNull() && !dm.Condition.IsUnknown() {
+ var cond ActionDependencyConditionModel
+ if diags := dm.Condition.As(ctx, &cond, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true}); diags.HasError() {
+ return nil, diags
+ }
+ dep.Condition = &sgsdkgo.ActionDependencyCondition{LatestStatus: cond.LatestStatus.ValueString()}
+ }
+ deps[j] = dep
+ }
+ ao.Dependencies = deps
+ }
+ order[wfId] = ao
+ }
+ action.Order = order
+ }
+ result[k] = action
+ }
+ return result, nil
+}
+
+// ---------------------------------------------------------------------------
+// Converters: SDK API types → Terraform model
+// ---------------------------------------------------------------------------
+
+func envVarsFromAPI(envVars []sgsdkgo.EnvVars) (types.List, diag.Diagnostics) {
+ if envVars == nil {
+ return types.ListNull(types.ObjectType{AttrTypes: EnvVarModel{}.AttributeTypes()}), nil
+ }
+ elements := make([]attr.Value, len(envVars))
+ for i, ev := range envVars {
+ cfgModel := EnvVarConfigModel{}
+ if ev.Config != nil {
+ cfgModel.VarName = flatteners.String(ev.Config.VarName)
+ cfgModel.SecretId = flatteners.StringPtr(ev.Config.SecretId)
+ cfgModel.TextValue = flatteners.StringPtr(ev.Config.TextValue)
+ }
+ cfgObj, diags := types.ObjectValueFrom(context.Background(), EnvVarConfigModel{}.AttributeTypes(), cfgModel)
+ if diags.HasError() {
+ return types.ListNull(types.ObjectType{AttrTypes: EnvVarModel{}.AttributeTypes()}), diags
+ }
+ evModel := EnvVarModel{
+ Config: cfgObj,
+ Kind: flatteners.String(string(ev.Kind)),
+ }
+ obj, diags := types.ObjectValueFrom(context.Background(), EnvVarModel{}.AttributeTypes(), evModel)
+ if diags.HasError() {
+ return types.ListNull(types.ObjectType{AttrTypes: EnvVarModel{}.AttributeTypes()}), diags
+ }
+ elements[i] = obj
+ }
+ return types.ListValue(types.ObjectType{AttrTypes: EnvVarModel{}.AttributeTypes()}, elements)
+}
+
+func envVarPointersFromAPI(envVars []*sgsdkgo.EnvVars) (types.List, diag.Diagnostics) {
+ vals := make([]sgsdkgo.EnvVars, 0, len(envVars))
+ for _, p := range envVars {
+ if p != nil {
+ vals = append(vals, *p)
+ }
+ }
+ return envVarsFromAPI(vals)
+}
+
+func mountPointsFromAPI(mps []sgsdkgo.MountPoint) (types.List, diag.Diagnostics) {
+ if mps == nil {
+ return types.ListNull(types.ObjectType{AttrTypes: MountPointModel{}.AttributeTypes()}), nil
+ }
+ elements := make([]attr.Value, len(mps))
+ for i, mp := range mps {
+ m := MountPointModel{
+ Source: flatteners.String(mp.Source),
+ Target: flatteners.String(mp.Target),
+ ReadOnly: flatteners.BoolPtr(mp.ReadOnly),
+ }
+ obj, diags := types.ObjectValueFrom(context.Background(), MountPointModel{}.AttributeTypes(), m)
+ if diags.HasError() {
+ return types.ListNull(types.ObjectType{AttrTypes: MountPointModel{}.AttributeTypes()}), diags
+ }
+ elements[i] = obj
+ }
+ return types.ListValue(types.ObjectType{AttrTypes: MountPointModel{}.AttributeTypes()}, elements)
+}
+
+func wfStepsConfigFromAPI(steps []sgsdkgo.WfStepsConfig) (types.List, diag.Diagnostics) {
+ listNull := types.ListNull(types.ObjectType{AttrTypes: WfStepsConfigModel{}.AttributeTypes()})
+ if steps == nil {
+ return listNull, nil
+ }
+ elements := make([]attr.Value, len(steps))
+ for i, s := range steps {
+ envList, diags := envVarsFromAPI(s.EnvironmentVariables)
+ if diags.HasError() {
+ return listNull, diags
+ }
+ mpList, diags := mountPointsFromAPI(s.MountPoints)
+ if diags.HasError() {
+ return listNull, diags
+ }
+ inputDataObj := types.ObjectNull(WfStepInputDataModel{}.AttributeTypes())
+ if s.WfStepInputData != nil {
+ dataStr := marshalToJSONString(s.WfStepInputData.Data)
+ idm := WfStepInputDataModel{
+ SchemaType: flatteners.String(string(s.WfStepInputData.SchemaType)),
+ Data: dataStr,
+ }
+ var diags2 diag.Diagnostics
+ inputDataObj, diags2 = types.ObjectValueFrom(context.Background(), WfStepInputDataModel{}.AttributeTypes(), idm)
+ if diags2.HasError() {
+ return listNull, diags2
+ }
+ }
+ m := WfStepsConfigModel{
+ Name: flatteners.String(s.Name),
+ EnvironmentVariables: envList,
+ Approval: flatteners.BoolPtr(s.Approval),
+ Timeout: flatteners.Int64Ptr(s.Timeout),
+ CmdOverride: flatteners.StringPtr(s.CmdOverride),
+ MountPoints: mpList,
+ WfStepTemplateId: flatteners.StringPtr(s.WfStepTemplateId),
+ WfStepInputData: inputDataObj,
+ }
+ obj, diags2 := types.ObjectValueFrom(context.Background(), WfStepsConfigModel{}.AttributeTypes(), m)
+ if diags2.HasError() {
+ return listNull, diags2
+ }
+ elements[i] = obj
+ }
+ return types.ListValue(types.ObjectType{AttrTypes: WfStepsConfigModel{}.AttributeTypes()}, elements)
+}
+
+func wfStepsConfigPointersFromAPI(steps []*sgsdkgo.WfStepsConfig) (types.List, diag.Diagnostics) {
+ vals := make([]sgsdkgo.WfStepsConfig, 0, len(steps))
+ for _, p := range steps {
+ if p != nil {
+ vals = append(vals, *p)
+ }
+ }
+ return wfStepsConfigFromAPI(vals)
+}
+
+func terraformConfigFromAPI(tc *sgsdkgo.TerraformConfig) (types.Object, diag.Diagnostics) {
+ nullObj := types.ObjectNull(TerraformConfigModel{}.AttributeTypes())
+ if tc == nil {
+ return nullObj, nil
+ }
+ strListNull := types.ListNull(types.StringType)
+
+ binPath, diags := mountPointsFromAPI(tc.TerraformBinPath)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+
+ makeWfStepsList := func(steps []sgsdkgo.WfStepsConfig) (types.List, diag.Diagnostics) {
+ return wfStepsConfigFromAPI(steps)
+ }
+
+ postApply, diags := makeWfStepsList(tc.PostApplyWfStepsConfig)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ preApply, diags := makeWfStepsList(tc.PreApplyWfStepsConfig)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ prePlan, diags := makeWfStepsList(tc.PrePlanWfStepsConfig)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ postPlan, diags := makeWfStepsList(tc.PostPlanWfStepsConfig)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+
+ makeStringList := func(hooks []string) (types.List, diag.Diagnostics) {
+ if hooks == nil {
+ return strListNull, nil
+ }
+ elems := make([]attr.Value, len(hooks))
+ for i, h := range hooks {
+ elems[i] = flatteners.String(h)
+ }
+ return types.ListValue(types.StringType, elems)
+ }
+
+ preInit, diags := makeStringList(tc.PreInitHooks)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ prePlan2, diags := makeStringList(tc.PrePlanHooks)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ postPlan2, diags := makeStringList(tc.PostPlanHooks)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ preApply2, diags := makeStringList(tc.PreApplyHooks)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ postApply2, diags := makeStringList(tc.PostApplyHooks)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+
+ m := TerraformConfigModel{
+ TerraformVersion: flatteners.StringPtr(tc.TerraformVersion),
+ DriftCheck: flatteners.BoolPtr(tc.DriftCheck),
+ DriftCron: flatteners.StringPtr(tc.DriftCron),
+ ManagedTerraformState: flatteners.BoolPtr(tc.ManagedTerraformState),
+ ApprovalPreApply: flatteners.BoolPtr(tc.ApprovalPreApply),
+ TerraformPlanOptions: flatteners.StringPtr(tc.TerraformPlanOptions),
+ TerraformInitOptions: flatteners.StringPtr(tc.TerraformInitOptions),
+ TerraformBinPath: binPath,
+ Timeout: flatteners.Int64Ptr(tc.Timeout),
+ PostApplyWfStepsConfig: postApply,
+ PreApplyWfStepsConfig: preApply,
+ PrePlanWfStepsConfig: prePlan,
+ PostPlanWfStepsConfig: postPlan,
+ PreInitHooks: preInit,
+ PrePlanHooks: prePlan2,
+ PostPlanHooks: postPlan2,
+ PreApplyHooks: preApply2,
+ PostApplyHooks: postApply2,
+ RunPreInitHooksOnDrift: flatteners.BoolPtr(tc.RunPreInitHooksOnDrift),
+ }
+ return types.ObjectValueFrom(context.Background(), TerraformConfigModel{}.AttributeTypes(), m)
+}
+
+func deploymentPlatformConfigFromAPI(dpcs []*sgsdkgo.DeploymentPlatformConfig) (types.List, diag.Diagnostics) {
+ listNull := types.ListNull(types.ObjectType{AttrTypes: DeploymentPlatformConfigModel{}.AttributeTypes()})
+ if dpcs == nil {
+ return listNull, nil
+ }
+ elements := make([]attr.Value, 0, len(dpcs))
+ for _, dpc := range dpcs {
+ if dpc == nil {
+ continue
+ }
+ m := DeploymentPlatformConfigModel{
+ Kind: flatteners.String(string(dpc.Kind)),
+ Config: marshalToJSONString(dpc.Config),
+ }
+ obj, diags := types.ObjectValueFrom(context.Background(), DeploymentPlatformConfigModel{}.AttributeTypes(), m)
+ if diags.HasError() {
+ return listNull, diags
+ }
+ elements = append(elements, obj)
+ }
+ return types.ListValue(types.ObjectType{AttrTypes: DeploymentPlatformConfigModel{}.AttributeTypes()}, elements)
+}
+
+func runnerConstraintsFromAPI(rc *sgsdkgo.RunnerConstraints) (types.Object, diag.Diagnostics) {
+ nullObj := types.ObjectNull(RunnerConstraintsModel{}.AttributeTypes())
+ if rc == nil {
+ return nullObj, nil
+ }
+ namesList, diags := types.ListValueFrom(context.Background(), types.StringType, rc.Names)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ m := RunnerConstraintsModel{
+ Type: flatteners.String(string(rc.Type)),
+ Names: namesList,
+ }
+ return types.ObjectValueFrom(context.Background(), RunnerConstraintsModel{}.AttributeTypes(), m)
+}
+
+func userSchedulesFromAPI(uss []*sgsdkgo.UserSchedules) (types.List, diag.Diagnostics) {
+ listNull := types.ListNull(types.ObjectType{AttrTypes: UserSchedulesModel{}.AttributeTypes()})
+ if uss == nil {
+ return listNull, nil
+ }
+ elements := make([]attr.Value, 0, len(uss))
+ for _, us := range uss {
+ if us == nil {
+ continue
+ }
+ m := UserSchedulesModel{
+ Name: flatteners.StringPtr(us.Name),
+ Desc: flatteners.StringPtr(us.Desc),
+ Cron: flatteners.String(us.Cron),
+ State: flatteners.String(string(us.State)),
+ }
+ obj, diags := types.ObjectValueFrom(context.Background(), UserSchedulesModel{}.AttributeTypes(), m)
+ if diags.HasError() {
+ return listNull, diags
+ }
+ elements = append(elements, obj)
+ }
+ return types.ListValue(types.ObjectType{AttrTypes: UserSchedulesModel{}.AttributeTypes()}, elements)
+}
+
+func vcsConfigFromAPI(vc *sgsdkgo.VcsConfig) (types.Object, diag.Diagnostics) {
+ nullObj := types.ObjectNull(VcsConfigModel{}.AttributeTypes())
+ if vc == nil {
+ return nullObj, nil
+ }
+ m := VcsConfigModel{}
+
+ iacVcsNull := types.ObjectNull(IacVcsConfigModel{}.AttributeTypes())
+ if vc.IacVcsConfig != nil {
+ var strippedIacTemplateId *string
+ if vc.IacVcsConfig.IacTemplateId != nil {
+ parts := strings.Split(*vc.IacVcsConfig.IacTemplateId, "/")
+ base := parts[len(parts)-1]
+ strippedIacTemplateId = &base
+ }
+ iacM := IacVcsConfigModel{
+ UseMarketplaceTemplate: types.BoolValue(vc.IacVcsConfig.UseMarketplaceTemplate),
+ IacTemplateId: flatteners.StringPtr(strippedIacTemplateId),
+ CustomSource: types.ObjectNull(CustomSourceModel{}.AttributeTypes()),
+ }
+ if vc.IacVcsConfig.CustomSource != nil {
+ cs := vc.IacVcsConfig.CustomSource
+ csM := CustomSourceModel{
+ SourceConfigDestKind: flatteners.String(string(cs.SourceConfigDestKind)),
+ Config: types.ObjectNull(CustomSourceConfigModel{}.AttributeTypes()),
+ }
+ if cs.Config != nil {
+ csCfgM := CustomSourceConfigModel{
+ IsPrivate: flatteners.BoolPtr(cs.Config.IsPrivate),
+ Auth: flatteners.StringPtr(cs.Config.Auth),
+ WorkingDir: flatteners.StringPtr(cs.Config.WorkingDir),
+ GitSparseCheckoutConfig: flatteners.StringPtr(cs.Config.GitSparseCheckoutConfig),
+ GitCoreAutoCrlf: flatteners.BoolPtr(cs.Config.GitCoreAutoCrlf),
+ Ref: flatteners.StringPtr(cs.Config.Ref),
+ Repo: flatteners.StringPtr(cs.Config.Repo),
+ IncludeSubModule: flatteners.BoolPtr(cs.Config.IncludeSubModule),
+ }
+ cfgObj, diags := types.ObjectValueFrom(context.Background(), CustomSourceConfigModel{}.AttributeTypes(), csCfgM)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ csM.Config = cfgObj
+ }
+ csObj, diags := types.ObjectValueFrom(context.Background(), CustomSourceModel{}.AttributeTypes(), csM)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ iacM.CustomSource = csObj
+ }
+ var diags diag.Diagnostics
+ iacVcsNull, diags = types.ObjectValueFrom(context.Background(), IacVcsConfigModel{}.AttributeTypes(), iacM)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ }
+ m.IacVcsConfig = iacVcsNull
+
+ iacInputNull := types.ObjectNull(IacInputDataModel{}.AttributeTypes())
+ if vc.IacInputData != nil {
+ idM := IacInputDataModel{
+ SchemaId: flatteners.StringPtr(vc.IacInputData.SchemaId),
+ SchemaType: flatteners.String(string(vc.IacInputData.SchemaType)),
+ Data: marshalToJSONString(vc.IacInputData.Data),
+ }
+ var diags diag.Diagnostics
+ iacInputNull, diags = types.ObjectValueFrom(context.Background(), IacInputDataModel{}.AttributeTypes(), idM)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ }
+ m.IacInputData = iacInputNull
+
+ return types.ObjectValueFrom(context.Background(), VcsConfigModel{}.AttributeTypes(), m)
+}
+
+func workflowsConfigFromAPI(wc *stacktemplaterevisions.StackTemplateRevisionWorkflowsConfig) (types.Object, diag.Diagnostics) {
+ nullObj := types.ObjectNull(WorkflowsConfigModel{}.AttributeTypes())
+ if wc == nil {
+ return nullObj, nil
+ }
+ wfListNull := types.ListNull(types.ObjectType{AttrTypes: WorkflowInStackModel{}.AttributeTypes()})
+ if wc.Workflows == nil {
+ m := WorkflowsConfigModel{Workflows: wfListNull}
+ return types.ObjectValueFrom(context.Background(), WorkflowsConfigModel{}.AttributeTypes(), m)
+ }
+
+ elements := make([]attr.Value, 0, len(wc.Workflows))
+ for _, wf := range wc.Workflows {
+ if wf == nil {
+ continue
+ }
+ wfSteps, diags := wfStepsConfigPointersFromAPI(wf.WfStepsConfig)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ tcObj, diags := terraformConfigFromAPI(wf.TerraformConfig)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ envVars, diags := envVarPointersFromAPI(wf.EnvironmentVariables)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ dpcs, diags := deploymentPlatformConfigFromAPI(wf.DeploymentPlatformConfig)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ vcs, diags := vcsConfigFromAPI(wf.VcsConfig)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ // IacInputData (TemplatesIacInputData)
+ iacInputDataObj := types.ObjectNull(WfStepInputDataModel{}.AttributeTypes())
+ if wf.IacInputData != nil {
+ idm := WfStepInputDataModel{
+ SchemaType: flatteners.String(wf.IacInputData.SchemaType),
+ Data: marshalToJSONString(wf.IacInputData.Data),
+ }
+ iacInputDataObj, diags = types.ObjectValueFrom(context.Background(), WfStepInputDataModel{}.AttributeTypes(), idm)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ }
+ us, diags := userSchedulesFromAPI(wf.UserSchedules)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ approvers, diags := types.ListValueFrom(context.Background(), types.StringType, wf.Approvers)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ rc, diags := runnerConstraintsFromAPI(wf.RunnerConstraints)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ // MiniSteps
+ msObj, diags := miniStepsFromAPI(context.Background(), wf.MiniSteps)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+
+ // InputSchemas
+ inputSchemasNull := types.ListNull(types.ObjectType{AttrTypes: StackInputSchemaModel{}.AttributeTypes()})
+ if wf.InputSchemas != nil {
+ isElems := make([]attr.Value, 0, len(wf.InputSchemas))
+ for _, is := range wf.InputSchemas {
+ if is == nil {
+ continue
+ }
+ ism := StackInputSchemaModel{
+ Id: flatteners.StringPtr(is.Id),
+ Name: flatteners.StringPtr(is.Name),
+ Description: flatteners.StringPtr(is.Description),
+ Type: flatteners.String(string(is.Type)),
+ EncodedData: flatteners.StringPtr(is.EncodedData),
+ UiSchemaData: flatteners.StringPtr(is.UiSchemaData),
+ IsCommitted: flatteners.BoolPtr(is.IsCommitted),
+ }
+ isObj, diags2 := types.ObjectValueFrom(context.Background(), StackInputSchemaModel{}.AttributeTypes(), ism)
+ if diags2.HasError() {
+ return nullObj, diags2
+ }
+ isElems = append(isElems, isObj)
+ }
+ inputSchemasNull, diags = types.ListValue(types.ObjectType{AttrTypes: StackInputSchemaModel{}.AttributeTypes()}, isElems)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ }
+
+ // Strip // prefix from template_id returned by the API
+ var strippedTemplateId *string
+ if wf.TemplateId != nil {
+ parts := strings.Split(*wf.TemplateId, "/")
+ base := parts[len(parts)-1]
+ strippedTemplateId = &base
+ }
+
+ wm := WorkflowInStackModel{
+ Id: flatteners.StringPtr(wf.Id),
+ TemplateId: flatteners.StringPtr(strippedTemplateId),
+ ResourceName: flatteners.StringPtr(wf.ResourceName),
+ WfStepsConfig: wfSteps,
+ TerraformConfig: tcObj,
+ EnvironmentVariables: envVars,
+ DeploymentPlatformConfig: dpcs,
+ VcsConfig: vcs,
+ IacInputData: iacInputDataObj,
+ UserSchedules: us,
+ Approvers: approvers,
+ NumberOfApprovalsRequired: flatteners.Int64Ptr(wf.NumberOfApprovalsRequired),
+ RunnerConstraints: rc,
+ UserJobCpu: flatteners.Int64Ptr(wf.UserJobCpu),
+ UserJobMemory: flatteners.Int64Ptr(wf.UserJobMemory),
+ InputSchemas: inputSchemasNull,
+ MiniSteps: msObj,
+ }
+ obj, diags := types.ObjectValueFrom(context.Background(), WorkflowInStackModel{}.AttributeTypes(), wm)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ elements = append(elements, obj)
+ }
+ wfList, diags := types.ListValue(types.ObjectType{AttrTypes: WorkflowInStackModel{}.AttributeTypes()}, elements)
+ if diags.HasError() {
+ return nullObj, diags
+ }
+ m := WorkflowsConfigModel{Workflows: wfList}
+ return types.ObjectValueFrom(context.Background(), WorkflowsConfigModel{}.AttributeTypes(), m)
+}
+
+func actionsFromAPI(actions map[string]*sgsdkgo.Actions) (types.Map, diag.Diagnostics) {
+ mapNull := types.MapNull(types.ObjectType{AttrTypes: ActionsModel{}.AttributeTypes()})
+ if actions == nil {
+ return mapNull, nil
+ }
+ elements := make(map[string]attr.Value, len(actions))
+ for k, a := range actions {
+ if a == nil {
+ continue
+ }
+ orderNull := types.MapNull(types.ObjectType{AttrTypes: ActionOrderModel{}.AttributeTypes()})
+ if a.Order != nil {
+ orderElements := make(map[string]attr.Value, len(a.Order))
+ for wfId, ao := range a.Order {
+ if ao == nil {
+ continue
+ }
+ // Parameters
+ paramsNull := types.ObjectNull(StackActionParametersModel{}.AttributeTypes())
+ if ao.Parameters != nil {
+ p := ao.Parameters
+ // TerraformAction
+ taNull := types.ObjectNull(TerraformActionModel{}.AttributeTypes())
+ if p.TerraformAction != nil && p.TerraformAction.Action != nil {
+ tam := TerraformActionModel{Action: flatteners.String(string(*p.TerraformAction.Action))}
+ var diags2 diag.Diagnostics
+ taNull, diags2 = types.ObjectValueFrom(context.Background(), TerraformActionModel{}.AttributeTypes(), tam)
+ if diags2.HasError() {
+ return mapNull, diags2
+ }
+ }
+ // DPC
+ dpcList, diags2 := deploymentPlatformConfigFromAPI(p.DeploymentPlatformConfig)
+ if diags2.HasError() {
+ return mapNull, diags2
+ }
+ // WfStepsConfig
+ wfStepsList, diags2 := wfStepsConfigPointersFromAPI(p.WfStepsConfig)
+ if diags2.HasError() {
+ return mapNull, diags2
+ }
+ // EnvVars
+ envList, diags2 := envVarPointersFromAPI(p.EnvironmentVariables)
+ if diags2.HasError() {
+ return mapNull, diags2
+ }
+ pm := StackActionParametersModel{
+ TerraformAction: taNull,
+ DeploymentPlatformConfig: dpcList,
+ WfStepsConfig: wfStepsList,
+ EnvironmentVariables: envList,
+ }
+ var diags3 diag.Diagnostics
+ paramsNull, diags3 = types.ObjectValueFrom(context.Background(), StackActionParametersModel{}.AttributeTypes(), pm)
+ if diags3.HasError() {
+ return mapNull, diags3
+ }
+ }
+
+ // Dependencies
+ depListNull := types.ListNull(types.ObjectType{AttrTypes: ActionDependencyModel{}.AttributeTypes()})
+ if ao.Dependencies != nil {
+ depElems := make([]attr.Value, 0, len(ao.Dependencies))
+ for _, dep := range ao.Dependencies {
+ if dep == nil {
+ continue
+ }
+ condNull := types.ObjectNull(ActionDependencyConditionModel{}.AttributeTypes())
+ if dep.Condition != nil {
+ condM := ActionDependencyConditionModel{LatestStatus: flatteners.String(dep.Condition.LatestStatus)}
+ var diags2 diag.Diagnostics
+ condNull, diags2 = types.ObjectValueFrom(context.Background(), ActionDependencyConditionModel{}.AttributeTypes(), condM)
+ if diags2.HasError() {
+ return mapNull, diags2
+ }
+ }
+ dm := ActionDependencyModel{Id: flatteners.String(dep.Id), Condition: condNull}
+ depObj, diags2 := types.ObjectValueFrom(context.Background(), ActionDependencyModel{}.AttributeTypes(), dm)
+ if diags2.HasError() {
+ return mapNull, diags2
+ }
+ depElems = append(depElems, depObj)
+ }
+ var diags2 diag.Diagnostics
+ depListNull, diags2 = types.ListValue(types.ObjectType{AttrTypes: ActionDependencyModel{}.AttributeTypes()}, depElems)
+ if diags2.HasError() {
+ return mapNull, diags2
+ }
+ }
+ aoM := ActionOrderModel{Parameters: paramsNull, Dependencies: depListNull}
+ aoObj, diags2 := types.ObjectValueFrom(context.Background(), ActionOrderModel{}.AttributeTypes(), aoM)
+ if diags2.HasError() {
+ return mapNull, diags2
+ }
+ orderElements[wfId] = aoObj
+ }
+ var diags2 diag.Diagnostics
+ orderNull, diags2 = types.MapValue(types.ObjectType{AttrTypes: ActionOrderModel{}.AttributeTypes()}, orderElements)
+ if diags2.HasError() {
+ return mapNull, diags2
+ }
+ }
+ am := ActionsModel{
+ Name: flatteners.String(a.Name),
+ Description: flatteners.StringPtr(a.Description),
+ Default: flatteners.BoolPtr(a.Default),
+ Order: orderNull,
+ }
+ obj, diags2 := types.ObjectValueFrom(context.Background(), ActionsModel{}.AttributeTypes(), am)
+ if diags2.HasError() {
+ return mapNull, diags2
+ }
+ elements[k] = obj
+ }
+ return types.MapValue(types.ObjectType{AttrTypes: ActionsModel{}.AttributeTypes()}, elements)
+}
+
+// ---------------------------------------------------------------------------
+// ToAPIModel / ToUpdateAPIModel
+// ---------------------------------------------------------------------------
+
+func (m *StackTemplateRevisionResourceModel) ToAPIModel(ctx context.Context, orgName string) (*stacktemplaterevisions.CreateStackTemplateRevisionRequest, diag.Diagnostics) {
+ diags := diag.Diagnostics{}
+ apiModel := &stacktemplaterevisions.CreateStackTemplateRevisionRequest{}
+
+ if !m.Alias.IsNull() && !m.Alias.IsUnknown() {
+ apiModel.Alias = m.Alias.ValueString()
+ }
+ if !m.Notes.IsNull() && !m.Notes.IsUnknown() {
+ apiModel.Notes = m.Notes.ValueString()
+ }
+ if !m.LongDescription.IsNull() && !m.LongDescription.IsUnknown() {
+ apiModel.LongDescription = m.LongDescription.ValueStringPointer()
+ }
+ if !m.SourceConfigKind.IsNull() && !m.SourceConfigKind.IsUnknown() {
+ apiModel.SourceConfigKind = (*stacktemplates.StackTemplateSourceConfigKindEnum)(m.SourceConfigKind.ValueStringPointer())
+ }
+ if !m.IsActive.IsNull() && !m.IsActive.IsUnknown() {
+ apiModel.IsActive = (*sgsdkgo.IsPublicEnum)(m.IsActive.ValueStringPointer())
+ }
+ if !m.IsPublic.IsNull() && !m.IsPublic.IsUnknown() {
+ apiModel.IsPublic = (*sgsdkgo.IsPublicEnum)(m.IsPublic.ValueStringPointer())
+ }
+
+ tags, diagsTags := expanders.StringList(ctx, m.Tags)
+ diags.Append(diagsTags...)
+ if !diags.HasError() {
+ apiModel.Tags = tags
+ }
+
+ if !m.ContextTags.IsNull() && !m.ContextTags.IsUnknown() {
+ contextTags := make(map[string]string)
+ diags.Append(m.ContextTags.ElementsAs(ctx, &contextTags, false)...)
+ if !diags.HasError() {
+ apiModel.ContextTags = contextTags
+ }
+ }
+
+ if !m.Deprecation.IsNull() && !m.Deprecation.IsUnknown() {
+ var dep DeprecationModel
+ diags.Append(m.Deprecation.As(ctx, &dep, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true})...)
+ if !diags.HasError() {
+ apiModel.Deprecation = &stacktemplaterevisions.Deprecation{
+ EffectiveDate: dep.EffectiveDate.ValueStringPointer(),
+ Message: dep.Message.ValueStringPointer(),
+ }
+ }
+ }
+
+ if !m.WorkflowsConfig.IsNull() && !m.WorkflowsConfig.IsUnknown() {
+ wc, diagsWC := convertWorkflowsConfigToAPI(ctx, m.WorkflowsConfig, orgName)
+ diags.Append(diagsWC...)
+ if !diags.HasError() {
+ apiModel.WorkflowsConfig = wc
+ }
+ }
+
+ if !m.Actions.IsNull() && !m.Actions.IsUnknown() {
+ acts, diagsActs := convertActionsToAPI(ctx, m.Actions)
+ diags.Append(diagsActs...)
+ if !diags.HasError() {
+ apiModel.Actions = acts
+ }
+ }
+
+ return apiModel, diags
+}
+
+func (m *StackTemplateRevisionResourceModel) ToUpdateAPIModel(ctx context.Context, orgName string) (*stacktemplaterevisions.UpdateStackTemplateRevisionRequest, diag.Diagnostics) {
+ diags := diag.Diagnostics{}
+ apiModel := &stacktemplaterevisions.UpdateStackTemplateRevisionRequest{}
+
+ if !m.Alias.IsNull() && !m.Alias.IsUnknown() {
+ apiModel.Alias = sgsdkgo.Optional(m.Alias.ValueString())
+ } else {
+ apiModel.Alias = sgsdkgo.Null[string]()
+ }
+ if !m.Notes.IsNull() && !m.Notes.IsUnknown() {
+ apiModel.Notes = sgsdkgo.Optional(m.Notes.ValueString())
+ } else {
+ apiModel.Notes = sgsdkgo.Null[string]()
+ }
+ if !m.LongDescription.IsNull() && !m.LongDescription.IsUnknown() {
+ apiModel.LongDescription = sgsdkgo.Optional(m.LongDescription.ValueString())
+ } else {
+ apiModel.LongDescription = sgsdkgo.Null[string]()
+ }
+ if !m.SourceConfigKind.IsNull() && !m.SourceConfigKind.IsUnknown() {
+ apiModel.SourceConfigKind = sgsdkgo.Optional(stacktemplates.StackTemplateSourceConfigKindEnum(m.SourceConfigKind.ValueString()))
+ } else {
+ apiModel.SourceConfigKind = sgsdkgo.Null[stacktemplates.StackTemplateSourceConfigKindEnum]()
+ }
+ if !m.IsActive.IsNull() && !m.IsActive.IsUnknown() {
+ apiModel.IsActive = sgsdkgo.Optional(sgsdkgo.IsPublicEnum(m.IsActive.ValueString()))
+ }
+ if !m.IsPublic.IsNull() && !m.IsPublic.IsUnknown() {
+ apiModel.IsPublic = sgsdkgo.Optional(sgsdkgo.IsPublicEnum(m.IsPublic.ValueString()))
+ }
+
+ tags, diagsTags := expanders.StringList(ctx, m.Tags)
+ diags.Append(diagsTags...)
+ if tags != nil {
+ apiModel.Tags = sgsdkgo.Optional(tags)
+ } else {
+ apiModel.Tags = sgsdkgo.Null[[]string]()
+ }
+
+ contextTags, diagsCT := expanders.MapStringString(ctx, m.ContextTags)
+ diags.Append(diagsCT...)
+ if contextTags != nil {
+ apiModel.ContextTags = sgsdkgo.Optional(contextTags)
+ } else {
+ apiModel.ContextTags = sgsdkgo.Null[map[string]string]()
+ }
+
+ if !m.Deprecation.IsNull() && !m.Deprecation.IsUnknown() {
+ var dep DeprecationModel
+ diags.Append(m.Deprecation.As(ctx, &dep, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true})...)
+ if !diags.HasError() {
+ apiModel.Deprecation = sgsdkgo.Optional(stacktemplaterevisions.Deprecation{
+ EffectiveDate: dep.EffectiveDate.ValueStringPointer(),
+ Message: dep.Message.ValueStringPointer(),
+ })
+ }
+ } else {
+ apiModel.Deprecation = sgsdkgo.Null[stacktemplaterevisions.Deprecation]()
+ }
+
+ if !m.WorkflowsConfig.IsNull() && !m.WorkflowsConfig.IsUnknown() {
+ wc, diagsWC := convertWorkflowsConfigToAPI(ctx, m.WorkflowsConfig, orgName)
+ diags.Append(diagsWC...)
+ if !diags.HasError() && wc != nil {
+ apiModel.WorkflowsConfig = sgsdkgo.Optional(*wc)
+ }
+ } else {
+ apiModel.WorkflowsConfig = sgsdkgo.Null[stacktemplaterevisions.StackTemplateRevisionWorkflowsConfig]()
+ }
+
+ if !m.Actions.IsNull() && !m.Actions.IsUnknown() {
+ acts, diagsActs := convertActionsToAPI(ctx, m.Actions)
+ diags.Append(diagsActs...)
+ if !diags.HasError() && acts != nil {
+ apiModel.Actions = sgsdkgo.Optional(acts)
+ }
+ } else {
+ apiModel.Actions = sgsdkgo.Null[map[string]*sgsdkgo.Actions]()
+ }
+
+ return apiModel, diags
+}
+
+// ---------------------------------------------------------------------------
+// BuildAPIModelToStackTemplateRevisionModel
+// ---------------------------------------------------------------------------
+
+func BuildAPIModelToStackTemplateRevisionModel(ctx context.Context, apiResponse *stacktemplaterevisions.ReadStackTemplateRevisionModel) (*StackTemplateRevisionResourceModel, diag.Diagnostics) {
+ diags := diag.Diagnostics{}
+
+ model := &StackTemplateRevisionResourceModel{
+ Id: flatteners.StringPtr(apiResponse.Id),
+ TemplateId: flatteners.String(apiResponse.TemplateId),
+ Alias: flatteners.String(apiResponse.Alias),
+ Notes: flatteners.String(apiResponse.Notes),
+ LongDescription: flatteners.StringPtr(apiResponse.LongDescription),
+ IsPublic: flatteners.StringPtr((*string)(apiResponse.IsPublic)),
+ }
+
+ if apiResponse.SourceConfigKind != nil {
+ model.SourceConfigKind = flatteners.String(string(*apiResponse.SourceConfigKind))
+ } else {
+ model.SourceConfigKind = types.StringNull()
+ }
+ if apiResponse.IsActive != nil {
+ model.IsActive = flatteners.String(string(*apiResponse.IsActive))
+ } else {
+ model.IsActive = types.StringNull()
+ }
+
+ // Tags
+ if apiResponse.Tags != nil {
+ tagsList, diagsTags := types.ListValueFrom(context.Background(), types.StringType, apiResponse.Tags)
+ diags.Append(diagsTags...)
+ model.Tags = tagsList
+ } else {
+ model.Tags = types.ListNull(types.StringType)
+ }
+
+ // ContextTags
+ if apiResponse.ContextTags != nil {
+ contextTags := make(map[string]types.String)
+ for k, v := range apiResponse.ContextTags {
+ contextTags[k] = flatteners.String(v)
+ }
+ contextTagsMap, diagsCT := types.MapValueFrom(context.Background(), types.StringType, contextTags)
+ diags.Append(diagsCT...)
+ model.ContextTags = contextTagsMap
+ } else {
+ model.ContextTags = types.MapNull(types.StringType)
+ }
+
+ // Deprecation
+ if apiResponse.Deprecation != nil {
+ depM := DeprecationModel{
+ EffectiveDate: flatteners.StringPtr(apiResponse.Deprecation.EffectiveDate),
+ Message: flatteners.StringPtr(apiResponse.Deprecation.Message),
+ }
+ depObj, diagsDep := types.ObjectValueFrom(context.Background(), DeprecationModel{}.AttributeTypes(), depM)
+ diags.Append(diagsDep...)
+ model.Deprecation = depObj
+ } else {
+ model.Deprecation = types.ObjectNull(DeprecationModel{}.AttributeTypes())
+ }
+
+ // WorkflowsConfig
+ wcObj, diagsWC := workflowsConfigFromAPI(apiResponse.WorkflowsConfig)
+ diags.Append(diagsWC...)
+ model.WorkflowsConfig = wcObj
+
+ // Actions
+ actsMap, diagsActs := actionsFromAPI(apiResponse.Actions)
+ diags.Append(diagsActs...)
+ model.Actions = actsMap
+
+ return model, diags
+}
diff --git a/internal/resource/stack_template_revision/resource.go b/internal/resource/stack_template_revision/resource.go
new file mode 100644
index 0000000..3420cfa
--- /dev/null
+++ b/internal/resource/stack_template_revision/resource.go
@@ -0,0 +1,222 @@
+package stacktemplaterevision
+
+import (
+ "context"
+ "fmt"
+
+ sgsdkgo "github.com/StackGuardian/sg-sdk-go"
+ sgclient "github.com/StackGuardian/sg-sdk-go/client"
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/customTypes"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+)
+
+var (
+ _ resource.Resource = &stackTemplateRevisionResource{}
+ _ resource.ResourceWithConfigure = &stackTemplateRevisionResource{}
+ _ resource.ResourceWithImportState = &stackTemplateRevisionResource{}
+)
+
+type stackTemplateRevisionResource struct {
+ client *sgclient.Client
+ org_name string
+}
+
+// NewResource is a helper function to simplify the provider implementation.
+func NewResource() resource.Resource {
+ return &stackTemplateRevisionResource{}
+}
+
+// Metadata returns the resource type name.
+func (r *stackTemplateRevisionResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_stack_template_revision"
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *stackTemplateRevisionResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+ if req.ProviderData == nil {
+ return
+ }
+
+ provider, ok := req.ProviderData.(*customTypes.ProviderInfo)
+
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected *customTypes.ProviderInfo, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+
+ return
+ }
+
+ r.client = provider.Client
+ r.org_name = provider.Org_name
+}
+
+// ImportState imports a stack template revision using its ID.
+func (r *stackTemplateRevisionResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), req.ID)...)
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *stackTemplateRevisionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ var plan StackTemplateRevisionResourceModel
+
+ diags := req.Plan.Get(ctx, &plan)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ ownerOrg := fmt.Sprintf("/orgs/%s", r.org_name)
+
+ payload, diags := plan.ToAPIModel(ctx, r.org_name)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ templateID := plan.ParentTemplateId.ValueString()
+ payload.OwnerOrg = ownerOrg
+
+ createResp, err := r.client.StackTemplateRevisions.CreateStackTemplateRevision(ctx, r.org_name, templateID, payload)
+ if err != nil {
+ resp.Diagnostics.AddError("Error creating stack template revision", "Error in creating stack template revision API call: "+err.Error())
+ return
+ }
+
+ revisionID := createResp.Data.Revision.Id
+
+ // Call read to get the full state since create response doesn't return all values
+ readResp, err := r.client.StackTemplateRevisions.ReadStackTemplateRevision(ctx, r.org_name, revisionID)
+ if err != nil {
+ resp.Diagnostics.AddError("Error reading created stack template revision", "Could not read the created stack template revision: "+err.Error())
+ return
+ }
+
+ revisionModel, diags := BuildAPIModelToStackTemplateRevisionModel(ctx, &readResp.Msg)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Preserve the user-provided parent_template_id from the plan
+ revisionModel.ParentTemplateId = plan.ParentTemplateId
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &revisionModel)...)
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *stackTemplateRevisionResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ var state StackTemplateRevisionResourceModel
+
+ diags := req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ revisionID := state.Id.ValueString()
+ if revisionID == "" {
+ resp.Diagnostics.AddError("Error reading stack template revision", "Revision ID is empty")
+ return
+ }
+
+ readResp, err := r.client.StackTemplateRevisions.ReadStackTemplateRevision(ctx, r.org_name, revisionID)
+ if err != nil {
+ resp.Diagnostics.AddError("Error reading stack template revision", "Error in reading stack template revision API call: "+err.Error())
+ return
+ }
+
+ if readResp == nil {
+ resp.Diagnostics.AddError("Error reading stack template revision", "API response is empty")
+ return
+ }
+
+ revisionModel, diags := BuildAPIModelToStackTemplateRevisionModel(ctx, &readResp.Msg)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Preserve parent_template_id from prior state
+ revisionModel.ParentTemplateId = state.ParentTemplateId
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &revisionModel)...)
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *stackTemplateRevisionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ var plan StackTemplateRevisionResourceModel
+ var state StackTemplateRevisionResourceModel
+
+ diags := req.Plan.Get(ctx, &plan)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ diags = req.State.Get(ctx, &state)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ revisionID := state.Id.ValueString()
+ ownerOrg := fmt.Sprintf("/orgs/%s", r.org_name)
+
+ payload, diags := plan.ToUpdateAPIModel(ctx, r.org_name)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ payload.OwnerOrg = sgsdkgo.Optional(ownerOrg)
+
+ _, err := r.client.StackTemplateRevisions.UpdateStackTemplateRevision(ctx, r.org_name, revisionID, payload)
+ if err != nil {
+ resp.Diagnostics.AddError("Error updating stack template revision", "Error in updating stack template revision API call: "+err.Error())
+ return
+ }
+
+ // Call read to get the updated state since update response doesn't return all values
+ readResp, err := r.client.StackTemplateRevisions.ReadStackTemplateRevision(ctx, r.org_name, revisionID)
+ if err != nil {
+ resp.Diagnostics.AddError("Error reading updated stack template revision", "Could not read the updated stack template revision: "+err.Error())
+ return
+ }
+
+ revisionModel, diags := BuildAPIModelToStackTemplateRevisionModel(ctx, &readResp.Msg)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Preserve parent_template_id from plan
+ revisionModel.ParentTemplateId = plan.ParentTemplateId
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &revisionModel)...)
+}
+
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *stackTemplateRevisionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ var state StackTemplateRevisionResourceModel
+
+ diags := req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ revisionID := state.Id.ValueString()
+ if revisionID == "" {
+ resp.Diagnostics.AddError("Error deleting stack template revision", "Revision ID is empty")
+ return
+ }
+
+ err := r.client.StackTemplateRevisions.DeleteStackTemplateRevision(ctx, r.org_name, revisionID, true)
+ if err != nil {
+ resp.Diagnostics.AddError("Error deleting stack template revision", "Error in deleting stack template revision API call: "+err.Error())
+ return
+ }
+}
diff --git a/internal/resource/stack_template_revision/resource_test.go b/internal/resource/stack_template_revision/resource_test.go
new file mode 100644
index 0000000..706a377
--- /dev/null
+++ b/internal/resource/stack_template_revision/resource_test.go
@@ -0,0 +1,278 @@
+package stacktemplaterevision_test
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "os"
+ "testing"
+
+ sgsdkgo "github.com/StackGuardian/sg-sdk-go"
+ sgclient "github.com/StackGuardian/sg-sdk-go/client"
+ sgoption "github.com/StackGuardian/sg-sdk-go/option"
+ "github.com/StackGuardian/sg-sdk-go/stacktemplates"
+ "github.com/StackGuardian/sg-sdk-go/workflowtemplates"
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/acctest"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/tfversion"
+)
+
+var org = os.Getenv("STACKGUARDIAN_ORG_NAME")
+
+func getClient() *sgclient.Client {
+ customHeader := http.Header{}
+ customHeader.Set("x-sg-internal-auth-orgid", "sg-provider-test")
+
+ return sgclient.NewClient(
+ sgoption.WithApiKey(fmt.Sprintf("apikey %s", os.Getenv("STACKGUARDIAN_API_KEY"))),
+ sgoption.WithBaseURL(os.Getenv("STACKGUARDIAN_API_URI")),
+ sgoption.WithHTTPHeader(customHeader),
+ )
+}
+
+// --- Fixture helpers: create resources via SDK ---
+
+func createWorkflowTemplateFixture(templateName, sourceConfigKind string) error {
+ client := getClient()
+ kind := workflowtemplates.WorkflowTemplateSourceConfigKindEnum(sourceConfigKind)
+ _, err := client.WorkflowTemplates.CreateWorkflowTemplate(context.TODO(), org, false, &workflowtemplates.CreateWorkflowTemplateRequest{
+ Id: &templateName,
+ TemplateName: templateName,
+ SourceConfigKind: &kind,
+ TemplateType: sgsdkgo.TemplateTypeEnumIac,
+ IsPublic: sgsdkgo.IsPublicEnumZero.Ptr(),
+ OwnerOrg: fmt.Sprintf("/orgs/%s", org),
+ })
+ return err
+}
+
+func createStackTemplateFixture(templateName, sourceConfigKind string) error {
+ client := getClient()
+ kind := stacktemplates.StackTemplateSourceConfigKindEnum(sourceConfigKind)
+ _, err := client.StackTemplates.CreateStackTemplate(context.TODO(), org, false, &stacktemplates.CreateStackTemplateRequest{
+ Id: &templateName,
+ TemplateName: templateName,
+ SourceConfigKind: &kind,
+ TemplateType: sgsdkgo.TemplateTypeEnumIacGroup,
+ IsPublic: sgsdkgo.IsPublicEnumZero.Ptr(),
+ OwnerOrg: fmt.Sprintf("/orgs/%s", org),
+ })
+ return err
+}
+
+// --- Fixture helpers: delete resources via SDK (best-effort, for cleanup) ---
+
+func deleteWorkflowTemplateFixture(templateId string) {
+ client := getClient()
+ client.WorkflowTemplates.DeleteWorkflowTemplate(context.TODO(), org, templateId)
+}
+
+func deleteStackTemplateFixture(templateId string) {
+ client := getClient()
+ client.StackTemplates.DeleteStackTemplate(context.TODO(), org, templateId)
+}
+
+func deleteStackTemplateRevisionFixture(revisionId string) {
+ client := getClient()
+ client.StackTemplateRevisions.DeleteStackTemplateRevision(context.TODO(), org, revisionId, true)
+}
+
+// --- Terraform config generators ---
+
+func testAccStackTemplateRevisionConfig(stackTemplateID, wfTemplateID, alias string) string {
+ return fmt.Sprintf(`
+resource "stackguardian_stack_template_revision" "test" {
+ parent_template_id = "%s"
+ alias = "%s"
+ notes = "Test revision notes"
+ description = "Test revision description"
+ source_config_kind = "TERRAFORM"
+
+ workflows_config = {
+ workflows = [
+ {
+ id = "d8dfaf15-2ad9-da29-8af0-c6b288b12089"
+ template_id = "%s"
+ resource_name = "wf-1"
+
+ terraform_config = {
+ managed_terraform_state = true
+ terraform_version = "1.5.7"
+ }
+ }
+ ]
+ }
+}
+`, stackTemplateID, alias, wfTemplateID)
+}
+
+func testAccStackTemplateRevisionConfigUpdated(stackTemplateID, wfTemplateID, alias string) string {
+ return fmt.Sprintf(`
+resource "stackguardian_stack_template_revision" "test" {
+ parent_template_id = "%s"
+ alias = "%s"
+ notes = "Updated revision notes"
+ description = "Updated revision description"
+ source_config_kind = "TERRAFORM"
+
+ workflows_config = {
+ workflows = [
+ {
+ id = "d8dfaf15-2ad9-da29-8af0-c6b288b12089"
+ template_id = "%s"
+ resource_name = "wf-1"
+
+ terraform_config = {
+ managed_terraform_state = true
+ terraform_version = "1.5.7"
+ }
+ }
+ ]
+ }
+}
+`, stackTemplateID, alias, wfTemplateID)
+}
+
+func testAccStackTemplateRevisionWithWorkflowsConfig(stackTemplateID, wfTemplateID, alias string) string {
+ return fmt.Sprintf(`
+resource "stackguardian_stack_template_revision" "test" {
+ parent_template_id = "%s"
+ alias = "%s"
+ notes = "Revision with workflows config"
+ description = "Test revision with workflows configuration"
+ source_config_kind = "TERRAFORM"
+
+ workflows_config = {
+ workflows = [
+ {
+ id = "d8dfaf15-2ad9-da29-8af0-c6b288b12089"
+ template_id = "%s"
+ resource_name = "wf-1"
+
+ vcs_config = {
+ iac_vcs_config = {
+ use_marketplace_template = true
+ iac_template_id = "%s"
+ }
+ iac_input_data = {
+ schema_type = "RAW_JSON"
+ data = jsonencode({
+ bucket_region = "eu-central-1"
+ })
+ }
+ }
+
+ terraform_config = {
+ managed_terraform_state = true
+ terraform_version = "1.5.7"
+ }
+ }
+ ]
+ }
+}
+`, stackTemplateID, alias, wfTemplateID, wfTemplateID)
+}
+
+// --- Tests ---
+
+func TestAccStackTemplateRevision_Basic(t *testing.T) {
+ stackTemplateID := "provider-test-stack-template-rev1"
+ wfTemplateID := "provider-test-wft-for-stack-rev1"
+ revisionAlias := "v1"
+
+ // Register cleanup before creation so defers run even if a later create fails
+ defer deleteStackTemplateRevisionFixture(fmt.Sprintf("%s:1", stackTemplateID))
+ defer deleteStackTemplateFixture(stackTemplateID)
+ defer deleteWorkflowTemplateFixture(wfTemplateID)
+
+ // Create prerequisite resources via SDK
+ if err := createWorkflowTemplateFixture(wfTemplateID, "TERRAFORM"); err != nil {
+ t.Fatalf("failed to create workflow template fixture: %s", err)
+ }
+ if err := createStackTemplateFixture(stackTemplateID, "TERRAFORM"); err != nil {
+ t.Fatalf("failed to create stack template fixture: %s", err)
+ }
+
+ customHeader := http.Header{}
+ customHeader.Set("x-sg-internal-auth-orgid", "sg-provider-test")
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { acctest.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.SkipBelow(tfversion.Version1_1_0),
+ },
+ ProtoV6ProviderFactories: acctest.ProviderFactories(customHeader),
+ Steps: []resource.TestStep{
+ // Create and Read testing
+ {
+ Config: testAccStackTemplateRevisionConfig(stackTemplateID, wfTemplateID, revisionAlias),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("stackguardian_stack_template_revision.test", "alias", revisionAlias),
+ resource.TestCheckResourceAttr("stackguardian_stack_template_revision.test", "notes", "Test revision notes"),
+ resource.TestCheckResourceAttr("stackguardian_stack_template_revision.test", "description", "Test revision description"),
+ resource.TestCheckResourceAttrSet("stackguardian_stack_template_revision.test", "id"),
+ resource.TestCheckResourceAttr("stackguardian_stack_template_revision.test", "parent_template_id", stackTemplateID),
+ resource.TestCheckResourceAttrSet("stackguardian_stack_template_revision.test", "template_id"),
+ resource.TestCheckResourceAttrSet("stackguardian_stack_template_revision.test", "workflows_config.%"),
+ ),
+ },
+ // Update and Read testing
+ {
+ Config: testAccStackTemplateRevisionConfigUpdated(stackTemplateID, wfTemplateID, revisionAlias),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("stackguardian_stack_template_revision.test", "alias", revisionAlias),
+ resource.TestCheckResourceAttr("stackguardian_stack_template_revision.test", "notes", "Updated revision notes"),
+ resource.TestCheckResourceAttr("stackguardian_stack_template_revision.test", "description", "Updated revision description"),
+ resource.TestCheckResourceAttrSet("stackguardian_stack_template_revision.test", "id"),
+ resource.TestCheckResourceAttr("stackguardian_stack_template_revision.test", "parent_template_id", stackTemplateID),
+ ),
+ },
+ // Delete testing automatically occurs
+ },
+ })
+}
+
+func TestAccStackTemplateRevision_WithWorkflowsConfig(t *testing.T) {
+ stackTemplateID := "provider-test-stack-template-rev2"
+ wfTemplateID := "provider-test-wft-for-stack-rev2"
+ revisionAlias := "v1"
+
+ // Register cleanup before creation so defers run even if a later create fails
+ defer deleteStackTemplateRevisionFixture(fmt.Sprintf("%s:1", stackTemplateID))
+ defer deleteStackTemplateFixture(stackTemplateID)
+ defer deleteWorkflowTemplateFixture(wfTemplateID)
+
+ // Create prerequisite resources via SDK
+ if err := createWorkflowTemplateFixture(wfTemplateID, "TERRAFORM"); err != nil {
+ t.Fatalf("failed to create workflow template fixture: %s", err)
+ }
+ if err := createStackTemplateFixture(stackTemplateID, "TERRAFORM"); err != nil {
+ t.Fatalf("failed to create stack template fixture: %s", err)
+ }
+
+ customHeader := http.Header{}
+ customHeader.Set("x-sg-internal-auth-orgid", "sg-provider-test")
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { acctest.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.SkipBelow(tfversion.Version1_1_0),
+ },
+ ProtoV6ProviderFactories: acctest.ProviderFactories(customHeader),
+ Steps: []resource.TestStep{
+ // Create and Read testing
+ {
+ Config: testAccStackTemplateRevisionWithWorkflowsConfig(stackTemplateID, wfTemplateID, revisionAlias),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("stackguardian_stack_template_revision.test", "alias", revisionAlias),
+ resource.TestCheckResourceAttr("stackguardian_stack_template_revision.test", "notes", "Revision with workflows config"),
+ resource.TestCheckResourceAttr("stackguardian_stack_template_revision.test", "description", "Test revision with workflows configuration"),
+ resource.TestCheckResourceAttrSet("stackguardian_stack_template_revision.test", "id"),
+ resource.TestCheckResourceAttr("stackguardian_stack_template_revision.test", "parent_template_id", stackTemplateID),
+ resource.TestCheckResourceAttrSet("stackguardian_stack_template_revision.test", "template_id"),
+ resource.TestCheckResourceAttrSet("stackguardian_stack_template_revision.test", "workflows_config.%"),
+ ),
+ },
+ },
+ })
+}
diff --git a/internal/resource/stack_template_revision/schema.go b/internal/resource/stack_template_revision/schema.go
new file mode 100644
index 0000000..4290f78
--- /dev/null
+++ b/internal/resource/stack_template_revision/schema.go
@@ -0,0 +1,750 @@
+package stacktemplaterevision
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/StackGuardian/terraform-provider-stackguardian/internal/constants"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+// Shared schema attribute maps reused across workflows_config and actions.
+
+var ministepsNotificationRecipients = schema.ListNestedAttribute{
+ Optional: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "recipients": schema.ListAttribute{
+ MarkdownDescription: constants.MiniStepsNotificationsRecipients,
+ Optional: true,
+ ElementType: types.StringType,
+ },
+ },
+ },
+}
+
+var ministepsWebhooks = schema.ListNestedAttribute{
+ Optional: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "webhook_name": schema.StringAttribute{
+ MarkdownDescription: constants.MiniStepsWebhookName,
+ Required: true,
+ },
+ "webhook_url": schema.StringAttribute{
+ MarkdownDescription: constants.MiniStepsWebhookURL,
+ Required: true,
+ },
+ "webhook_secret": schema.StringAttribute{
+ MarkdownDescription: constants.MiniStepsWebhookSecret,
+ Optional: true,
+ },
+ },
+ },
+}
+
+var ministepsWorkflowChaining = schema.ListNestedAttribute{
+ Optional: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "workflow_group_id": schema.StringAttribute{
+ MarkdownDescription: constants.MiniStepsWfChainingWorkflowGroupId,
+ Required: true,
+ },
+ "stack_id": schema.StringAttribute{
+ MarkdownDescription: constants.MiniStepsWfChainingStackId,
+ Optional: true,
+ },
+ "stack_run_payload": schema.StringAttribute{
+ MarkdownDescription: constants.MiniStepsWfChainingStackPayload,
+ Optional: true,
+ },
+ "workflow_id": schema.StringAttribute{
+ MarkdownDescription: constants.MiniStepsWfChainingWorkflowId,
+ Optional: true,
+ },
+ "workflow_run_payload": schema.StringAttribute{
+ MarkdownDescription: constants.MiniStepsWfChainingWorkflowPayload,
+ Optional: true,
+ },
+ },
+ },
+}
+
+var envVarsAttrs = map[string]schema.Attribute{
+ "config": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.EnvVarConfig,
+ Required: true,
+ Attributes: map[string]schema.Attribute{
+ "var_name": schema.StringAttribute{
+ MarkdownDescription: constants.EnvVarConfigVarName,
+ Required: true,
+ },
+ "secret_id": schema.StringAttribute{
+ MarkdownDescription: constants.EnvVarConfigSecretId,
+ Optional: true,
+ },
+ "text_value": schema.StringAttribute{
+ MarkdownDescription: constants.EnvVarConfigTextValue,
+ Optional: true,
+ },
+ },
+ },
+ "kind": schema.StringAttribute{
+ MarkdownDescription: constants.EnvVarKind,
+ Required: true,
+ },
+}
+
+var mountPointAttrs = map[string]schema.Attribute{
+ "source": schema.StringAttribute{
+ MarkdownDescription: constants.MountPointSource,
+ Optional: true,
+ },
+ "target": schema.StringAttribute{
+ MarkdownDescription: constants.MountPointTarget,
+ Optional: true,
+ },
+ "read_only": schema.BoolAttribute{
+ MarkdownDescription: constants.MountPointReadOnly,
+ Optional: true,
+ },
+}
+
+var wfStepsConfigNestedObj = schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "name": schema.StringAttribute{
+ MarkdownDescription: constants.WfStepName,
+ Required: true,
+ },
+ "environment_variables": schema.ListNestedAttribute{
+ MarkdownDescription: constants.WfStepEnvVars,
+ Optional: true,
+ NestedObject: schema.NestedAttributeObject{Attributes: envVarsAttrs},
+ },
+ "approval": schema.BoolAttribute{
+ MarkdownDescription: constants.WfStepApproval,
+ Optional: true,
+ },
+ "timeout": schema.Int64Attribute{
+ MarkdownDescription: constants.WfStepTimeout,
+ Optional: true,
+ },
+ "cmd_override": schema.StringAttribute{
+ MarkdownDescription: constants.WfStepCmdOverride,
+ Optional: true,
+ },
+ "mount_points": schema.ListNestedAttribute{
+ MarkdownDescription: constants.WfStepMountPoints,
+ Optional: true,
+ NestedObject: schema.NestedAttributeObject{Attributes: mountPointAttrs},
+ },
+ "wf_step_template_id": schema.StringAttribute{
+ MarkdownDescription: constants.WfStepTemplateId,
+ Required: true,
+ },
+ "wf_step_input_data": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.WfStepInputData,
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "schema_type": schema.StringAttribute{
+ MarkdownDescription: constants.WfStepInputDataSchemaType,
+ Optional: true,
+ },
+ "data": schema.StringAttribute{
+ MarkdownDescription: constants.WfStepInputDataData,
+ Optional: true,
+ },
+ },
+ },
+ },
+}
+
+var deploymentPlatformConfigAttrs = map[string]schema.Attribute{
+ "kind": schema.StringAttribute{
+ MarkdownDescription: constants.DeploymentPlatformKind,
+ Required: true,
+ },
+ "config": schema.StringAttribute{
+ MarkdownDescription: constants.DeploymentPlatformConfigDetails + " (JSON string)",
+ Optional: true,
+ },
+}
+
+// workflowInStackAttrs defines the schema attributes for a single workflow inside workflows_config.
+// Fields correspond to the SDK's StackTemplateRevisionWorkflow struct.
+var workflowInStackAttrs = map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ MarkdownDescription: "UUID identifying the workflow within the stack template.",
+ Required: true,
+ },
+ "template_id": schema.StringAttribute{
+ MarkdownDescription: "ID of the workflow template that this workflow is based on.",
+ Required: true,
+ },
+ "resource_name": schema.StringAttribute{
+ MarkdownDescription: "Name of the workflow resource within the stack.",
+ Optional: true,
+ },
+ "wf_steps_config": schema.ListNestedAttribute{
+ MarkdownDescription: "Workflow steps configuration.",
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.List{
+ listplanmodifier.UseStateForUnknown(),
+ },
+ NestedObject: wfStepsConfigNestedObj,
+ },
+ "terraform_config": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.TerraformConfig,
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "terraform_version": schema.StringAttribute{
+ MarkdownDescription: constants.TerraformVersion,
+ Optional: true,
+ },
+ "drift_check": schema.BoolAttribute{
+ MarkdownDescription: constants.TerraformDriftCheck,
+ Optional: true,
+ },
+ "drift_cron": schema.StringAttribute{
+ MarkdownDescription: constants.TerraformDriftCron,
+ Optional: true,
+ },
+ "managed_terraform_state": schema.BoolAttribute{
+ MarkdownDescription: constants.TerraformManagedState,
+ Optional: true,
+ },
+ "approval_pre_apply": schema.BoolAttribute{
+ MarkdownDescription: constants.TerraformApprovalPreApply,
+ Optional: true,
+ },
+ "terraform_plan_options": schema.StringAttribute{
+ MarkdownDescription: constants.TerraformPlanOptions,
+ Optional: true,
+ },
+ "terraform_init_options": schema.StringAttribute{
+ MarkdownDescription: constants.TerraformInitOptions,
+ Optional: true,
+ },
+ "terraform_bin_path": schema.ListNestedAttribute{
+ MarkdownDescription: constants.TerraformBinPath,
+ Optional: true,
+ NestedObject: schema.NestedAttributeObject{Attributes: mountPointAttrs},
+ },
+ "timeout": schema.Int64Attribute{
+ MarkdownDescription: constants.TerraformTimeout,
+ Optional: true,
+ },
+ "post_apply_wf_steps_config": schema.ListNestedAttribute{
+ MarkdownDescription: constants.TerraformPostApplyWfSteps,
+ Optional: true,
+ NestedObject: wfStepsConfigNestedObj,
+ },
+ "pre_apply_wf_steps_config": schema.ListNestedAttribute{
+ MarkdownDescription: constants.TerraformPreApplyWfSteps,
+ Optional: true,
+ NestedObject: wfStepsConfigNestedObj,
+ },
+ "pre_plan_wf_steps_config": schema.ListNestedAttribute{
+ MarkdownDescription: constants.TerraformPrePlanWfSteps,
+ Optional: true,
+ NestedObject: wfStepsConfigNestedObj,
+ },
+ "post_plan_wf_steps_config": schema.ListNestedAttribute{
+ MarkdownDescription: constants.TerraformPostPlanWfSteps,
+ Optional: true,
+ NestedObject: wfStepsConfigNestedObj,
+ },
+ "pre_init_hooks": schema.ListAttribute{
+ MarkdownDescription: constants.TerraformPreInitHooks,
+ Optional: true,
+ ElementType: types.StringType,
+ },
+ "pre_plan_hooks": schema.ListAttribute{
+ MarkdownDescription: constants.TerraformPrePlanHooks,
+ Optional: true,
+ ElementType: types.StringType,
+ },
+ "post_plan_hooks": schema.ListAttribute{
+ MarkdownDescription: constants.TerraformPostPlanHooks,
+ Optional: true,
+ ElementType: types.StringType,
+ },
+ "pre_apply_hooks": schema.ListAttribute{
+ MarkdownDescription: constants.TerraformPreApplyHooks,
+ Optional: true,
+ ElementType: types.StringType,
+ },
+ "post_apply_hooks": schema.ListAttribute{
+ MarkdownDescription: constants.TerraformPostApplyHooks,
+ Optional: true,
+ ElementType: types.StringType,
+ },
+ "run_pre_init_hooks_on_drift": schema.BoolAttribute{
+ MarkdownDescription: constants.TerraformRunPreInitHooksOnDrift,
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.Bool{
+ boolplanmodifier.UseStateForUnknown(),
+ },
+ },
+ },
+ },
+ "environment_variables": schema.ListNestedAttribute{
+ MarkdownDescription: "Environment variables for the workflow.",
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.List{
+ listplanmodifier.UseStateForUnknown(),
+ },
+ NestedObject: schema.NestedAttributeObject{Attributes: envVarsAttrs},
+ },
+ "deployment_platform_config": schema.ListNestedAttribute{
+ MarkdownDescription: "Deployment platform configuration.",
+ Optional: true,
+ NestedObject: schema.NestedAttributeObject{Attributes: deploymentPlatformConfigAttrs},
+ },
+ "vcs_config": schema.SingleNestedAttribute{
+ MarkdownDescription: "VCS (version control) configuration for the workflow.",
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "iac_vcs_config": schema.SingleNestedAttribute{
+ MarkdownDescription: "IaC VCS configuration.",
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "use_marketplace_template": schema.BoolAttribute{
+ MarkdownDescription: "Whether to use a marketplace template.",
+ Optional: true,
+ },
+ "iac_template_id": schema.StringAttribute{
+ MarkdownDescription: "ID of the IaC template from the marketplace.",
+ Optional: true,
+ },
+ "custom_source": schema.SingleNestedAttribute{
+ MarkdownDescription: "Custom source configuration.",
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "source_config_dest_kind": schema.StringAttribute{
+ MarkdownDescription: constants.RuntimeSourceDestKind,
+ Required: true,
+ },
+ "config": schema.SingleNestedAttribute{
+ MarkdownDescription: "Source configuration details.",
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "is_private": schema.BoolAttribute{
+ Optional: true,
+ },
+ "auth": schema.StringAttribute{
+ Optional: true,
+ Sensitive: true,
+ },
+ "working_dir": schema.StringAttribute{
+ Optional: true,
+ },
+ "git_sparse_checkout_config": schema.StringAttribute{
+ Optional: true,
+ },
+ "git_core_auto_crlf": schema.BoolAttribute{
+ Optional: true,
+ },
+ "ref": schema.StringAttribute{
+ Optional: true,
+ },
+ "repo": schema.StringAttribute{
+ Optional: true,
+ },
+ "include_sub_module": schema.BoolAttribute{
+ Optional: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ "iac_input_data": schema.SingleNestedAttribute{
+ MarkdownDescription: "IaC input data for the workflow.",
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "schema_id": schema.StringAttribute{
+ Optional: true,
+ },
+ "schema_type": schema.StringAttribute{
+ Required: true,
+ },
+ "data": schema.StringAttribute{
+ MarkdownDescription: "Input data as a JSON string.",
+ Optional: true,
+ },
+ },
+ },
+ },
+ },
+ // iac_input_data at the workflow level corresponds to TemplatesIacInputData in the SDK.
+ // Used when the workflow is instantiated from a workflow template (template_id).
+ "iac_input_data": schema.SingleNestedAttribute{
+ MarkdownDescription: "Top-level IaC input data for this workflow, used when the workflow is instantiated from a workflow template (`template_id`).",
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "schema_type": schema.StringAttribute{
+ MarkdownDescription: "Schema type for the input data (e.g. RAW_JSON).",
+ Required: true,
+ },
+ "data": schema.StringAttribute{
+ MarkdownDescription: "Input data as a JSON string.",
+ Optional: true,
+ },
+ },
+ },
+ "user_schedules": schema.ListNestedAttribute{
+ MarkdownDescription: "Scheduled run configuration.",
+ Optional: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "name": schema.StringAttribute{
+ Optional: true,
+ },
+ "desc": schema.StringAttribute{
+ Optional: true,
+ },
+ "cron": schema.StringAttribute{
+ MarkdownDescription: constants.UserScheduleCron,
+ Required: true,
+ },
+ "state": schema.StringAttribute{
+ MarkdownDescription: constants.UserScheduleState,
+ Required: true,
+ },
+ },
+ },
+ },
+ "approvers": schema.ListAttribute{
+ MarkdownDescription: "List of approvers.",
+ ElementType: types.StringType,
+ Optional: true,
+ },
+ "number_of_approvals_required": schema.Int64Attribute{
+ MarkdownDescription: "Number of approvals required.",
+ Optional: true,
+ },
+ "runner_constraints": schema.SingleNestedAttribute{
+ MarkdownDescription: "Runner constraints for the workflow.",
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "type": schema.StringAttribute{
+ MarkdownDescription: constants.RunnerConstraintsType,
+ Required: true,
+ },
+ "names": schema.ListAttribute{
+ MarkdownDescription: constants.RunnerConstraintsNames,
+ ElementType: types.StringType,
+ Optional: true,
+ },
+ },
+ },
+ "user_job_cpu": schema.Int64Attribute{
+ MarkdownDescription: "CPU limit for the user job.",
+ Optional: true,
+ },
+ "user_job_memory": schema.Int64Attribute{
+ MarkdownDescription: "Memory limit for the user job.",
+ Optional: true,
+ },
+ "input_schemas": schema.ListNestedAttribute{
+ MarkdownDescription: "Input schema definitions for this workflow.",
+ Optional: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ Computed: true,
+ Optional: true,
+ },
+ "name": schema.StringAttribute{
+ Optional: true,
+ },
+ "description": schema.StringAttribute{
+ Optional: true,
+ },
+ "type": schema.StringAttribute{
+ MarkdownDescription: "Schema type (e.g. FORM_JSONSCHEMA).",
+ Required: true,
+ },
+ "encoded_data": schema.StringAttribute{
+ MarkdownDescription: "Base64-encoded schema data.",
+ Optional: true,
+ },
+ "ui_schema_data": schema.StringAttribute{
+ Optional: true,
+ },
+ "is_committed": schema.BoolAttribute{
+ Optional: true,
+ Computed: true,
+ },
+ },
+ },
+ },
+ "mini_steps": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.WorkflowTemplateRevisionMiniSteps,
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "notifications": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.MiniStepsNotifications,
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "email": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.MiniStepsNotificationsEmail,
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "approval_required": ministepsNotificationRecipients,
+ "cancelled": ministepsNotificationRecipients,
+ "completed": ministepsNotificationRecipients,
+ "drift_detected": ministepsNotificationRecipients,
+ "errored": ministepsNotificationRecipients,
+ },
+ },
+ },
+ },
+ "webhooks": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.MiniStepsWebhooks,
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "approval_required": ministepsWebhooks,
+ "cancelled": ministepsWebhooks,
+ "completed": ministepsWebhooks,
+ "drift_detected": ministepsWebhooks,
+ "errored": ministepsWebhooks,
+ },
+ },
+ "wf_chaining": schema.SingleNestedAttribute{
+ Optional: true,
+ MarkdownDescription: constants.MiniStepsWorkflowChaining,
+ Attributes: map[string]schema.Attribute{
+ "completed": ministepsWorkflowChaining,
+ "errored": ministepsWorkflowChaining,
+ },
+ },
+ },
+ },
+}
+
+// actionOrderAttrs defines the schema for a single ActionOrder (value in the order map).
+var actionOrderAttrs = map[string]schema.Attribute{
+ "parameters": schema.SingleNestedAttribute{
+ MarkdownDescription: "Run configuration parameters for the action step.",
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "terraform_action": schema.SingleNestedAttribute{
+ MarkdownDescription: "Terraform-specific action parameters.",
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "action": schema.StringAttribute{
+ MarkdownDescription: `Terraform action to execute. E.g., "apply", "plan", "destroy".`,
+ Computed: true,
+ },
+ },
+ },
+ "deployment_platform_config": schema.ListNestedAttribute{
+ MarkdownDescription: "Deployment platform configuration.",
+ Optional: true,
+ NestedObject: schema.NestedAttributeObject{Attributes: deploymentPlatformConfigAttrs},
+ },
+ "wf_steps_config": schema.ListNestedAttribute{
+ MarkdownDescription: "Workflow steps configuration.",
+ Optional: true,
+ Computed: true,
+ NestedObject: wfStepsConfigNestedObj,
+ },
+ "environment_variables": schema.ListNestedAttribute{
+ MarkdownDescription: "Environment variables.",
+ Optional: true,
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{Attributes: envVarsAttrs},
+ },
+ },
+ },
+ "dependencies": schema.ListNestedAttribute{
+ MarkdownDescription: "List of workflow dependencies that must complete before this step runs.",
+ Optional: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ MarkdownDescription: "ID of the dependent workflow.",
+ Required: true,
+ },
+ "condition": schema.SingleNestedAttribute{
+ MarkdownDescription: "Condition that must be met by the dependency.",
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "latest_status": schema.StringAttribute{
+ MarkdownDescription: "Required latest status of the dependency (e.g., COMPLETED).",
+ Required: true,
+ },
+ },
+ },
+ },
+ },
+ },
+}
+
+// Schema defines the schema for the resource.
+func (r *stackTemplateRevisionResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateRevisionId,
+ Computed: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "parent_template_id": schema.StringAttribute{
+ MarkdownDescription: "ID of the parent stack template to create the revision under.",
+ Required: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.RequiresReplace(),
+ },
+ },
+ "template_id": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateRevisionTemplateId,
+ Computed: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "alias": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateRevisionAlias,
+ Optional: true,
+ Computed: true,
+ },
+ "notes": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateRevisionNotes,
+ Optional: true,
+ Computed: true,
+ },
+ "description": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateRevisionDescription,
+ Optional: true,
+ Computed: true,
+ },
+ "source_config_kind": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateSourceConfigKindCommon,
+ Required: true,
+ },
+ "is_active": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateIsActiveCommon,
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "is_public": schema.StringAttribute{
+ MarkdownDescription: constants.StackTemplateIsPublicCommon,
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "tags": schema.ListAttribute{
+ MarkdownDescription: fmt.Sprintf(constants.Tags, "stack template revision"),
+ ElementType: types.StringType,
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.List{
+ listplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "context_tags": schema.MapAttribute{
+ MarkdownDescription: fmt.Sprintf(constants.ContextTags, "stack template revision"),
+ ElementType: types.StringType,
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.Map{
+ mapplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "deprecation": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.Deprecation,
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "effective_date": schema.StringAttribute{
+ MarkdownDescription: constants.TemplateRevisionDeprecationEffectiveDate,
+ Optional: true,
+ },
+ "message": schema.StringAttribute{
+ MarkdownDescription: constants.TemplateRevisionDeprecation,
+ Optional: true,
+ },
+ },
+ },
+ "workflows_config": schema.SingleNestedAttribute{
+ MarkdownDescription: constants.StackTemplateRevisionWorkflowsConfig,
+ Optional: true,
+ PlanModifiers: []planmodifier.Object{
+ objectplanmodifier.UseStateForUnknown(),
+ },
+ Attributes: map[string]schema.Attribute{
+ "workflows": schema.ListNestedAttribute{
+ MarkdownDescription: "List of workflows that make up the stack.",
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.List{
+ listplanmodifier.UseStateForUnknown(),
+ },
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: workflowInStackAttrs,
+ },
+ },
+ },
+ },
+ "actions": schema.MapNestedAttribute{
+ MarkdownDescription: constants.StackTemplateRevisionActions,
+ PlanModifiers: []planmodifier.Map{
+ mapplanmodifier.UseStateForUnknown(),
+ },
+ Optional: true,
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "name": schema.StringAttribute{
+ MarkdownDescription: "Name of the action.",
+ Optional: true,
+ Computed: true,
+ },
+ "description": schema.StringAttribute{
+ MarkdownDescription: "Description of the action.",
+ Optional: true,
+ Computed: true,
+ },
+ "default": schema.BoolAttribute{
+ MarkdownDescription: "Whether this is the default action.",
+ Optional: true,
+ Computed: true,
+ },
+ "order": schema.MapNestedAttribute{
+ MarkdownDescription: "Ordered map of workflow IDs to their action configurations.",
+ Optional: true,
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: actionOrderAttrs,
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+}