diff --git a/docs/data-sources/step_template.md b/docs/data-sources/step_template.md
new file mode 100644
index 000000000..369f3c886
--- /dev/null
+++ b/docs/data-sources/step_template.md
@@ -0,0 +1,83 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "octopusdeploy_step_template Data Source - terraform-provider-octopusdeploy"
+subcategory: ""
+description: |-
+ Provides information about existing step_template.
+---
+
+# octopusdeploy_step_template (Data Source)
+
+Provides information about existing step_template.
+
+
+
+
+## Schema
+
+### Required
+
+- `id` (String) Unique identifier of the step template
+
+### Optional
+
+- `space_id` (String) SpaceID of the Step Template
+
+### Read-Only
+
+- `step_template` (Object) (see [below for nested schema](#nestedatt--step_template))
+
+
+### Nested Schema for `step_template`
+
+Read-Only:
+
+- `action_type` (String)
+- `community_action_template_id` (String)
+- `description` (String)
+- `id` (String)
+- `name` (String)
+- `packages` (List of Object) (see [below for nested schema](#nestedobjatt--step_template--packages))
+- `parameters` (List of Object) (see [below for nested schema](#nestedobjatt--step_template--parameters))
+- `properties` (Map of String)
+- `space_id` (String)
+- `step_package_id` (String)
+- `version` (Number)
+
+
+### Nested Schema for `step_template.packages`
+
+Read-Only:
+
+- `acquisition_location` (String)
+- `feed_id` (String)
+- `id` (String)
+- `name` (String)
+- `package_id` (String)
+- `properties` (Object) (see [below for nested schema](#nestedobjatt--step_template--packages--properties))
+
+
+### Nested Schema for `step_template.packages.properties`
+
+Read-Only:
+
+- `extract` (String)
+- `package_parameter_name` (String)
+- `purpose` (String)
+- `selection_mode` (String)
+
+
+
+
+### Nested Schema for `step_template.parameters`
+
+Read-Only:
+
+- `default_value` (String)
+- `display_settings` (Map of String)
+- `help_text` (String)
+- `id` (String)
+- `label` (String)
+- `name` (String)
+
+
diff --git a/docs/resources/step_template.md b/docs/resources/step_template.md
new file mode 100644
index 000000000..afec18d12
--- /dev/null
+++ b/docs/resources/step_template.md
@@ -0,0 +1,86 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "octopusdeploy_step_template Resource - terraform-provider-octopusdeploy"
+subcategory: ""
+description: |-
+ This resource manages step_templates in Octopus Deploy.
+---
+
+# octopusdeploy_step_template (Resource)
+
+This resource manages step_templates in Octopus Deploy.
+
+
+
+
+## Schema
+
+### Required
+
+- `action_type` (String) The action type of the step template
+- `name` (String) The name of this resource.
+- `packages` (Attributes List) Package information for the step template (see [below for nested schema](#nestedatt--packages))
+- `parameters` (Attributes List) List of parameters that can be used in Step Template. (see [below for nested schema](#nestedatt--parameters))
+- `properties` (Map of String) Properties for the step template
+- `step_package_id` (String) The ID of the step package
+
+### Optional
+
+- `community_action_template_id` (String) The ID of the community action template
+- `description` (String) The description of this step_template.
+- `space_id` (String) The space ID associated with this step_template.
+
+### Read-Only
+
+- `id` (String) The unique ID for this resource.
+- `version` (Number) The version of the step template
+
+
+### Nested Schema for `packages`
+
+Required:
+
+- `feed_id` (String) ID of the feed.
+- `name` (String) The name of this resource.
+- `properties` (Attributes) Properties for the package. (see [below for nested schema](#nestedatt--packages--properties))
+
+Optional:
+
+- `acquisition_location` (String) Acquisition location for the package.
+- `package_id` (String) The ID of the package to use.
+
+Read-Only:
+
+- `id` (String) The unique ID for this resource.
+
+
+### Nested Schema for `packages.properties`
+
+Required:
+
+- `selection_mode` (String) The selection mode.
+
+Optional:
+
+- `extract` (String) If the package should extract.
+- `package_parameter_name` (String) The name of the package parameter
+- `purpose` (String) The purpose of this property.
+
+
+
+
+### Nested Schema for `parameters`
+
+Required:
+
+- `id` (String) The id for the property.
+- `name` (String) The name of the variable set by the parameter. The name can contain letters, digits, dashes and periods. Example: `ServerName`
+
+Optional:
+
+- `default_value` (String) A default value for the parameter, if applicable. This can be a hard-coded value or a variable reference.
+- `display_settings` (Map of String) The display settings for the parameter.
+- `help_text` (String) The help presented alongside the parameter input.
+- `label` (String) The label shown beside the parameter when presented in the deployment process. Example: `Server name`.
+
+
diff --git a/go.mod b/go.mod
index 1d574617d..1a16e81ba 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/OctopusDeploy/terraform-provider-octopusdeploy
go 1.21
require (
- github.com/OctopusDeploy/go-octopusdeploy/v2 v2.50.0
+ github.com/OctopusDeploy/go-octopusdeploy/v2 v2.52.0
github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240729041805-46db6fb717b4
github.com/google/uuid v1.6.0
github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637
@@ -17,7 +17,6 @@ require (
github.com/hashicorp/terraform-plugin-testing v1.8.0
github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go v0.32.0
- golang.org/x/exp v0.0.0-20240707233637-46b078467d37
golang.org/x/text v0.16.0
k8s.io/utils v0.0.0-20230505201702-9f6742963106
software.sslmate.com/src/go-pkcs12 v0.4.0
@@ -130,6 +129,7 @@ require (
go.opentelemetry.io/otel/sdk v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
+ golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sync v0.7.0 // indirect
diff --git a/go.sum b/go.sum
index 066bca243..bfd0603f8 100644
--- a/go.sum
+++ b/go.sum
@@ -20,8 +20,8 @@ github.com/Microsoft/hcsshim v0.12.4 h1:Ev7YUMHAHoWNm+aDSPzc5W9s6E2jyL1szpVDJeZ/
github.com/Microsoft/hcsshim v0.12.4/go.mod h1:Iyl1WVpZzr+UkzjekHZbV8o5Z9ZkxNGx6CtY2Qg/JVQ=
github.com/OctopusDeploy/go-octodiff v1.0.0 h1:U+ORg6azniwwYo+O44giOw6TiD5USk8S4VDhOQ0Ven0=
github.com/OctopusDeploy/go-octodiff v1.0.0/go.mod h1:Mze0+EkOWTgTmi8++fyUc6r0aLZT7qD9gX+31t8MmIU=
-github.com/OctopusDeploy/go-octopusdeploy/v2 v2.50.0 h1:rQiLEbqt/D3lPQw3pq9sXAW1C0WhVLrfN/h0cqUzaFY=
-github.com/OctopusDeploy/go-octopusdeploy/v2 v2.50.0/go.mod h1:ggvOXzMnq+w0pLg6C9zdjz6YBaHfO3B3tqmmB7JQdaw=
+github.com/OctopusDeploy/go-octopusdeploy/v2 v2.52.0 h1:X3Tdij/cGqmEtmZ0HqJFeHzTJVxFmYEAog4R4w6KFIw=
+github.com/OctopusDeploy/go-octopusdeploy/v2 v2.52.0/go.mod h1:ggvOXzMnq+w0pLg6C9zdjz6YBaHfO3B3tqmmB7JQdaw=
github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240729041805-46db6fb717b4 h1:QfbVf0bOIRMp/WHAWsuVDB7KHoWnRsGbvDuOf2ua7k4=
github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240729041805-46db6fb717b4/go.mod h1:Oq9KbiRNDBB5jFmrwnrgLX0urIqR/1ptY18TzkqXm7M=
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg=
diff --git a/octopusdeploy_framework/datasource_step_template.go b/octopusdeploy_framework/datasource_step_template.go
new file mode 100644
index 000000000..7343a2d3d
--- /dev/null
+++ b/octopusdeploy_framework/datasource_step_template.go
@@ -0,0 +1,116 @@
+package octopusdeploy_framework
+
+import (
+ "context"
+ "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/actiontemplates"
+ "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas"
+ "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/diag"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+type stepTemplateDataSource struct {
+ *Config
+}
+
+func NewStepTemplateDataSource() datasource.DataSource {
+ return &stepTemplateDataSource{}
+}
+func (*stepTemplateDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = util.GetTypeName("step_template")
+}
+
+func (*stepTemplateDataSource) Schema(_ context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schemas.StepTemplateSchema{}.GetDatasourceSchema()
+}
+
+func (d *stepTemplateDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ d.Config = DataSourceConfiguration(req, resp)
+}
+
+func (d *stepTemplateDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ var err error
+ var data schemas.StepTemplateTypeDataSourceModel
+ resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ query := struct {
+ ID string
+ SpaceID string
+ }{data.ID.ValueString(), data.SpaceID.ValueString()}
+
+ util.DatasourceReading(ctx, "step_template", query)
+
+ actionTemplate, err := actiontemplates.GetByID(d.Config.Client, query.SpaceID, query.ID)
+ if err != nil {
+ resp.Diagnostics.AddError("Unable to load step template", err.Error())
+ return
+ }
+
+ resp.Diagnostics.Append(mapStepTemplateToDatasourceModel(&data, actionTemplate)...)
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func mapStepTemplateToDatasourceModel(data *schemas.StepTemplateTypeDataSourceModel, at *actiontemplates.ActionTemplate) diag.Diagnostics {
+ resp := diag.Diagnostics{}
+
+ data.ID = types.StringValue(at.ID)
+ data.SpaceID = types.StringValue(at.SpaceID)
+ stepTemplate, dg := convertStepTemplateAttributes(at)
+ resp.Append(dg...)
+ data.StepTemplate = stepTemplate
+ return resp
+}
+
+func convertStepTemplateAttributes(at *actiontemplates.ActionTemplate) (types.Object, diag.Diagnostics) {
+ diags := diag.Diagnostics{}
+
+ params := make([]attr.Value, len(at.Parameters))
+ for i, param := range at.Parameters {
+ p, dg := convertStepTemplateParameterAttribute(param)
+ diags.Append(dg...)
+ params[i] = p
+ }
+ paramsListValue, dg := types.ListValue(types.ObjectType{AttrTypes: schemas.GetStepTemplateParameterTypeAttributes()}, params)
+ diags.Append(dg...)
+
+ pkgs := make([]attr.Value, len(at.Packages))
+ for i, pkg := range at.Packages {
+ p, dg := convertStepTemplatePackageAttribute(pkg)
+ diags.Append(dg...)
+ pkgs[i] = p
+ }
+ packageListValue, dg := types.ListValue(types.ObjectType{AttrTypes: schemas.GetStepTemplatePackageTypeAttributes()}, pkgs)
+ diags.Append(dg...)
+
+ props := make(map[string]attr.Value, len(at.Properties))
+ for key, val := range at.Properties {
+ props[key] = types.StringValue(val.Value)
+ }
+ propertiesMap, dg := types.MapValue(types.StringType, props)
+ diags.Append(dg...)
+
+ if diags.HasError() {
+ return types.ObjectNull(schemas.GetStepTemplateParameterTypeAttributes()), diags
+ }
+
+ stepTemplate, dg := types.ObjectValue(schemas.GetStepTemplateAttributes(), map[string]attr.Value{
+ "id": types.StringValue(at.ID),
+ "name": types.StringValue(at.Name),
+ "description": types.StringValue(at.Description),
+ "space_id": types.StringValue(at.SpaceID),
+ "version": types.Int32Value(at.Version),
+ "step_package_id": types.StringValue(at.ActionType),
+ "action_type": types.StringValue(at.ActionType),
+ "community_action_template_id": types.StringValue(at.CommunityActionTemplateID),
+ "packages": packageListValue,
+ "parameters": paramsListValue,
+ "properties": propertiesMap,
+ })
+ diags.Append(dg...)
+ return stepTemplate, diags
+}
diff --git a/octopusdeploy_framework/framework_provider.go b/octopusdeploy_framework/framework_provider.go
index a4a6ba9d6..4ec3df754 100644
--- a/octopusdeploy_framework/framework_provider.go
+++ b/octopusdeploy_framework/framework_provider.go
@@ -65,6 +65,7 @@ func (p *octopusDeployFrameworkProvider) DataSources(ctx context.Context) []func
NewSpacesDataSource,
NewLifecyclesDataSource,
NewEnvironmentsDataSource,
+ NewStepTemplateDataSource,
NewGitCredentialsDataSource,
NewFeedsDataSource,
NewLibraryVariableSetDataSource,
@@ -85,6 +86,7 @@ func (p *octopusDeployFrameworkProvider) Resources(ctx context.Context) []func()
NewMavenFeedResource,
NewLifecycleResource,
NewEnvironmentResource,
+ NewStepTemplateResource,
NewGitCredentialResource,
NewHelmFeedResource,
NewArtifactoryGenericFeedResource,
diff --git a/octopusdeploy_framework/resource_step_template.go b/octopusdeploy_framework/resource_step_template.go
new file mode 100644
index 000000000..01bf45428
--- /dev/null
+++ b/octopusdeploy_framework/resource_step_template.go
@@ -0,0 +1,372 @@
+package octopusdeploy_framework
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/actiontemplates"
+ "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core"
+ "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/packages"
+ "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors"
+ "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas"
+ "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/diag"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+type stepTemplateTypeResource struct {
+ *Config
+}
+
+var _ resource.ResourceWithImportState = &stepTemplateTypeResource{}
+
+func NewStepTemplateResource() resource.Resource {
+ return &stepTemplateTypeResource{}
+}
+
+func (r *stepTemplateTypeResource) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = util.GetTypeName("step_template")
+}
+
+func (r *stepTemplateTypeResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = schemas.StepTemplateSchema{}.GetResourceSchema()
+}
+
+func (r *stepTemplateTypeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+ r.Config = ResourceConfiguration(req, resp)
+}
+
+func (*stepTemplateTypeResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
+}
+
+func (r *stepTemplateTypeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ var data schemas.StepTemplateTypeResourceModel
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ newActionTemplate, dg := mapStepTemplateResourceModelToActionTemplate(ctx, data)
+ resp.Diagnostics.Append(dg...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ actionTemplate, err := actiontemplates.Add(r.Config.Client, newActionTemplate)
+ if err != nil {
+ resp.Diagnostics.AddError("unable to create step template", err.Error())
+ return
+ }
+
+ resp.Diagnostics.Append(mapStepTemplateToResourceModel(&data, actionTemplate)...)
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *stepTemplateTypeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ var data schemas.StepTemplateTypeResourceModel
+ resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ actionTemplate, err := actiontemplates.GetByID(r.Config.Client, data.SpaceID.ValueString(), data.ID.ValueString())
+ if err != nil {
+ if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "action template"); err != nil {
+ resp.Diagnostics.AddError("unable to load environment", err.Error())
+ }
+ return
+ }
+
+ resp.Diagnostics.Append(mapStepTemplateToResourceModel(&data, actionTemplate)...)
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *stepTemplateTypeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ var data, state schemas.StepTemplateTypeResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ at, err := actiontemplates.GetByID(r.Config.Client, state.SpaceID.ValueString(), state.ID.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError("unable to load step template", err.Error())
+ return
+ }
+
+ actionTemplateUpdate, dg := mapStepTemplateResourceModelToActionTemplate(ctx, data)
+ resp.Diagnostics.Append(dg...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ actionTemplateUpdate.ID = at.ID
+ actionTemplateUpdate.SpaceID = at.SpaceID
+ actionTemplateUpdate.Version = at.Version
+
+ updatedActionTemplate, err := actiontemplates.Update(r.Config.Client, actionTemplateUpdate)
+ if err != nil {
+ resp.Diagnostics.AddError("unable to update step template", err.Error())
+ return
+ }
+
+ resp.Diagnostics.Append(mapStepTemplateToResourceModel(&data, updatedActionTemplate)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *stepTemplateTypeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ var data schemas.StepTemplateTypeResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ if err := actiontemplates.DeleteByID(r.Config.Client, data.SpaceID.ValueString(), data.ID.ValueString()); err != nil {
+ resp.Diagnostics.AddError("unable to delete step template", err.Error())
+ return
+ }
+}
+
+func mapStepTemplateToResourceModel(data *schemas.StepTemplateTypeResourceModel, at *actiontemplates.ActionTemplate) diag.Diagnostics {
+ resp := diag.Diagnostics{}
+
+ data.ID = types.StringValue(at.ID)
+ data.SpaceID = types.StringValue(at.SpaceID)
+ data.Name = types.StringValue(at.Name)
+ data.Version = types.Int32Value(at.Version)
+ data.Description = types.StringValue(at.Description)
+ data.CommunityActionTemplateId = types.StringValue(at.CommunityActionTemplateID)
+ data.ActionType = types.StringValue(at.ActionType)
+
+ // Parameters
+ sParams, dg := convertStepTemplateToParameterAttributes(at.Parameters)
+ resp.Append(dg...)
+ data.Parameters = sParams
+
+ // Properties
+ stringProps := make(map[string]attr.Value, len(at.Properties))
+ for keys, value := range at.Properties {
+ stringProps[keys] = types.StringValue(value.Value)
+ }
+ props, dg := types.MapValue(types.StringType, stringProps)
+ resp.Append(dg...)
+ data.Properties = props
+
+ // Packages
+ pkgs, dg := convertStepTemplateToPackageAttributes(at.Packages)
+ resp.Append(dg...)
+ data.Packages = pkgs
+
+ return resp
+}
+
+func mapStepTemplateResourceModelToActionTemplate(ctx context.Context, data schemas.StepTemplateTypeResourceModel) (*actiontemplates.ActionTemplate, diag.Diagnostics) {
+ resp := diag.Diagnostics{}
+ at := actiontemplates.NewActionTemplate(data.Name.ValueString(), data.ActionType.ValueString())
+
+ at.SpaceID = data.SpaceID.ValueString()
+ at.Description = data.Description.ValueString()
+ if !data.CommunityActionTemplateId.IsNull() {
+ at.CommunityActionTemplateID = data.CommunityActionTemplateId.ValueString()
+ }
+
+ pkgs := make([]schemas.StepTemplatePackageType, 0, len(data.Packages.Elements()))
+ resp.Append(data.Packages.ElementsAs(ctx, &pkgs, false)...)
+ if resp.HasError() {
+ return at, resp
+ }
+
+ props := make(map[string]types.String, len(data.Properties.Elements()))
+ resp.Append(data.Properties.ElementsAs(ctx, &props, false)...)
+ if resp.HasError() {
+ return at, resp
+ }
+
+ params := make([]schemas.StepTemplateParameterType, 0, len(data.Parameters.Elements()))
+ resp.Append(data.Parameters.ElementsAs(ctx, ¶ms, false)...)
+ if resp.HasError() {
+ return at, resp
+ }
+
+ if len(props) > 0 {
+ templateProps := make(map[string]core.PropertyValue, len(props))
+ for key, val := range props {
+ templateProps[key] = core.NewPropertyValue(val.ValueString(), false)
+ }
+ at.Properties = templateProps
+ } else {
+ at.Properties = make(map[string]core.PropertyValue)
+ }
+
+ at.Packages = make([]packages.PackageReference, len(pkgs))
+ if len(pkgs) > 0 {
+ for i, val := range pkgs {
+ pkgProps := make(map[string]string, len(val.Properties.Attributes()))
+ for key, prop := range val.Properties.Attributes() {
+ if prop.Type(ctx) == types.StringType {
+ pkgProps[key] = prop.(types.String).ValueString()
+ } else {
+ // We should not get this error unless we add a field to package properties in the schema that is not a string
+ resp.AddError("Unexpected value type in package properties.",
+ fmt.Sprintf("Expected [%s] to have value of string but got [%s].", key, prop.String()))
+ }
+ }
+ if resp.HasError() {
+ return at, resp
+ }
+ pkgRef := packages.PackageReference{
+ AcquisitionLocation: val.AcquisitionLocation.ValueString(),
+ FeedID: val.FeedID.ValueString(),
+ Properties: pkgProps,
+ Name: val.Name.ValueString(),
+ PackageID: val.PackageID.ValueString(),
+ }
+ pkgRef.ID = val.ID.ValueString()
+ at.Packages[i] = pkgRef
+ }
+ }
+
+ at.Parameters = make([]actiontemplates.ActionTemplateParameter, len(params))
+ if len(params) > 0 {
+ paramIDMap := make(map[string]bool, len(params))
+ for i, val := range params {
+ defaultValue := core.NewPropertyValue(val.DefaultValue.ValueString(), false)
+ at.Parameters[i] = actiontemplates.ActionTemplateParameter{
+ DefaultValue: &defaultValue,
+ Name: val.Name.ValueString(),
+ Label: val.Label.ValueString(),
+ HelpText: val.HelpText.ValueString(),
+ DisplaySettings: util.ConvertAttrStringMapToStringMap(val.DisplaySettings.Elements()),
+ }
+ id := val.ID.ValueString()
+ if _, ok := paramIDMap[id]; ok {
+ resp.AddError("ID conflict", fmt.Sprintf("conflicting UUID's within parameters list: %s", id))
+ }
+ paramIDMap[val.ID.ValueString()] = true
+ at.Parameters[i].ID = id
+ at.Parameters[i].ID = val.ID.ValueString()
+ }
+ }
+ if resp.HasError() {
+ return at, resp
+ }
+ return at, resp
+}
+
+func convertStepTemplateToPackageAttributes(atPackage []packages.PackageReference) (types.List, diag.Diagnostics) {
+ resp := diag.Diagnostics{}
+ pkgs := make([]attr.Value, len(atPackage))
+ for key, val := range atPackage {
+ mapVal, dg := convertStepTemplatePackageAttribute(val)
+ resp.Append(dg...)
+ if resp.HasError() {
+ return types.ListNull(types.ObjectType{AttrTypes: schemas.GetStepTemplatePackageTypeAttributes()}), resp
+ }
+ pkgs[key] = mapVal
+ }
+ pkgSet, dg := types.ListValue(types.ObjectType{AttrTypes: schemas.GetStepTemplatePackageTypeAttributes()}, pkgs)
+ resp.Append(dg...)
+ if resp.HasError() {
+ return types.ListNull(types.ObjectType{AttrTypes: schemas.GetStepTemplatePackageTypeAttributes()}), resp
+ }
+ return pkgSet, dg
+}
+
+func convertStepTemplateToParameterAttributes(atParams []actiontemplates.ActionTemplateParameter) (types.List, diag.Diagnostics) {
+ resp := diag.Diagnostics{}
+ params := make([]attr.Value, len(atParams))
+ for i, val := range atParams {
+ objVal, dg := convertStepTemplateParameterAttribute(val)
+ resp.Append(dg...)
+ if resp.HasError() {
+ return types.ListNull(types.ObjectType{AttrTypes: schemas.GetStepTemplateParameterTypeAttributes()}), resp
+ }
+ params[i] = objVal
+ }
+ sParams, dg := types.ListValue(types.ObjectType{AttrTypes: schemas.GetStepTemplateParameterTypeAttributes()}, params)
+ resp.Append(dg...)
+ if resp.HasError() {
+ return types.ListNull(types.ObjectType{AttrTypes: schemas.GetStepTemplateParameterTypeAttributes()}), resp
+ }
+ return sParams, resp
+}
+
+func convertStepTemplateParameterAttribute(atp actiontemplates.ActionTemplateParameter) (types.Object, diag.Diagnostics) {
+ displaySettings, dg := types.MapValue(types.StringType, util.ConvertStringMapToAttrStringMap(atp.DisplaySettings))
+ if dg.HasError() {
+ return types.ObjectNull(schemas.GetStepTemplateParameterTypeAttributes()), dg
+ }
+ return types.ObjectValue(schemas.GetStepTemplateParameterTypeAttributes(), map[string]attr.Value{
+ "id": types.StringValue(atp.ID),
+ "name": types.StringValue(atp.Name),
+ "label": types.StringValue(atp.Label),
+ "help_text": types.StringValue(atp.HelpText),
+ "default_value": types.StringValue(atp.DefaultValue.Value),
+ "display_settings": displaySettings,
+ })
+}
+
+func convertStepTemplatePackageAttribute(atp packages.PackageReference) (types.Object, diag.Diagnostics) {
+ props, dg := convertStepTemplatePackagePropertyAttribute(atp.Properties)
+ if dg.HasError() {
+ return types.ObjectNull(schemas.GetStepTemplatePackageTypeAttributes()), dg
+ }
+ return types.ObjectValue(schemas.GetStepTemplatePackageTypeAttributes(), map[string]attr.Value{
+ "id": types.StringValue(atp.ID),
+ "acquisition_location": types.StringValue(atp.AcquisitionLocation),
+ "name": types.StringValue(atp.Name),
+ "feed_id": types.StringValue(atp.FeedID),
+ "package_id": types.StringValue(atp.PackageID),
+ "properties": props,
+ })
+}
+
+func convertStepTemplatePackagePropertyAttribute(atpp map[string]string) (types.Object, diag.Diagnostics) {
+ prop := make(map[string]attr.Value)
+ diags := diag.Diagnostics{}
+
+ // We need to manually convert the string map to ensure all fields are set.
+ if extract, ok := atpp["extract"]; ok {
+ prop["extract"] = types.StringValue(extract)
+ } else {
+ diags.AddWarning("Package property missing value.", "extract value missing from package property")
+ prop["extract"] = types.StringNull()
+ }
+
+ if purpose, ok := atpp["purpose"]; ok {
+ prop["purpose"] = types.StringValue(purpose)
+ } else {
+ diags.AddWarning("Package property missing value.", "purpose value missing from package property")
+ prop["purpose"] = types.StringNull()
+ }
+
+ if purpose, ok := atpp["package_parameter_name"]; ok {
+ prop["package_parameter_name"] = types.StringValue(purpose)
+ } else {
+ diags.AddWarning("Package property missing value.", "package_parameter_name value missing from package property")
+ prop["package_parameter_name"] = types.StringNull()
+ }
+
+ if selectionMode, ok := atpp["selection_mode"]; ok {
+ prop["selection_mode"] = types.StringValue(selectionMode)
+ } else {
+ diags.AddWarning("Package property missing value.", "selection_mode value missing from package property")
+ prop["selection_mode"] = types.StringNull()
+ }
+
+ propMap, dg := types.ObjectValue(schemas.GetStepTemplatePackagePropertiesTypeAttributes(), prop)
+ if dg.HasError() {
+ diags.Append(dg...)
+ return types.ObjectNull(schemas.GetStepTemplatePackagePropertiesTypeAttributes()), diags
+ }
+ return propMap, diags
+}
diff --git a/octopusdeploy_framework/resource_step_template_test.go b/octopusdeploy_framework/resource_step_template_test.go
new file mode 100644
index 000000000..9b2365da1
--- /dev/null
+++ b/octopusdeploy_framework/resource_step_template_test.go
@@ -0,0 +1,228 @@
+package octopusdeploy_framework
+
+import (
+ "fmt"
+ "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/actiontemplates"
+ "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+ "testing"
+)
+
+type stepTemplatePackagePropsTestData struct {
+ extract string
+ purpose string
+ selectionMode string
+}
+
+type stepTemplatePackageTestData struct {
+ packageID string
+ acquisitonLocation string
+ feedID string
+ name string
+ properties stepTemplatePackagePropsTestData
+}
+
+type stepTemplateParamTestData struct {
+ defaultValue string
+ displaySettings map[string]string
+ helpText string
+ label string
+ name string
+ id string
+}
+
+type stepTemplateTestData struct {
+ localName string
+ prefix string
+ actionType string
+ name string
+ description string
+ stepPackageID string
+ packages []stepTemplatePackageTestData
+ parameters []stepTemplateParamTestData
+ properties map[string]string
+}
+
+func TestAccOctopusStepTemplateBasic(t *testing.T) {
+ localName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)
+ prefix := "octopusdeploy_step_template." + localName
+ data := stepTemplateTestData{
+ localName: localName,
+ prefix: prefix,
+ actionType: "Octopus.Script",
+ name: acctest.RandStringFromCharSet(10, acctest.CharSetAlpha),
+ description: acctest.RandStringFromCharSet(20, acctest.CharSetAlpha),
+ stepPackageID: "Octopus.Script",
+ packages: []stepTemplatePackageTestData{
+ {
+ packageID: "force",
+ acquisitonLocation: "Server",
+ feedID: "feeds-builtin",
+ name: "mypackage",
+ properties: stepTemplatePackagePropsTestData{
+ extract: "True",
+ purpose: "",
+ selectionMode: "immediate",
+ },
+ },
+ },
+ parameters: []stepTemplateParamTestData{
+ {
+ defaultValue: "Hello World",
+ displaySettings: map[string]string{
+ "Octopus.ControlType": "SingleLineText",
+ },
+ helpText: acctest.RandStringFromCharSet(10, acctest.CharSetAlpha),
+ label: acctest.RandStringFromCharSet(10, acctest.CharSetAlpha),
+ name: acctest.RandStringFromCharSet(10, acctest.CharSetAlpha),
+ id: "621e1584-cdf3-4b67-9204-fc82430c908c",
+ },
+ {
+ defaultValue: "Hello Earth",
+ displaySettings: map[string]string{
+ "Octopus.ControlType": "SingleLineText",
+ },
+ helpText: acctest.RandStringFromCharSet(10, acctest.CharSetAlpha),
+ label: acctest.RandStringFromCharSet(10, acctest.CharSetAlpha),
+ name: acctest.RandStringFromCharSet(10, acctest.CharSetAlpha),
+ id: "cd731d21-669a-42e1-81af-048681fd5c69",
+ },
+ },
+ properties: map[string]string{
+ "Octopus.Action.Script.ScriptBody": "echo 'Hello World'",
+ "Octopus.Action.Script.ScriptSource": "Inline",
+ "Octopus.Action.Script.Syntax": "Bash",
+ },
+ }
+
+ resource.Test(t, resource.TestCase{
+ CheckDestroy: func(s *terraform.State) error { return testStepTemplateDestroy(s, localName) },
+ PreCheck: func() { TestAccPreCheck(t) },
+ ProtoV6ProviderFactories: ProtoV6ProviderFactories(),
+ Steps: []resource.TestStep{
+ {
+ Config: testStepTemplateRunScriptBasic(data),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(prefix, "name", data.name),
+ ),
+ },
+ {
+ Config: testStepTemplateRunScriptUpdate(data),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(prefix, "name", data.name+"-updated"),
+ ),
+ },
+ },
+ })
+}
+
+func testStepTemplateRunScriptBasic(data stepTemplateTestData) string {
+ return fmt.Sprintf(`
+ resource "octopusdeploy_step_template" "%s" {
+ action_type = "%s"
+ name = "%s"
+ description = "%s"
+ step_package_id = "%s"
+ packages = [
+ {
+ package_id = "%s"
+ acquisition_location = "%s"
+ feed_id = "%s"
+ name = "%s"
+ properties = {
+ extract = "%s"
+ purpose = "%s"
+ selection_mode = "%s"
+ }
+ }
+ ]
+ parameters = [
+ {
+ default_value = "%s"
+ display_settings = {
+ "Octopus.ControlType" : "%s"
+ }
+ help_text = "%s"
+ label = "%s"
+ name = "%s"
+ id = "%s"
+ },
+ {
+ default_value = "%s"
+ display_settings = {
+ "Octopus.ControlType" : "%s"
+ }
+ help_text = "%s"
+ label = "%s"
+ name = "%s"
+ id = "%s"
+ },
+ ]
+ properties = {
+ "Octopus.Action.Script.ScriptBody" : "%s"
+ "Octopus.Action.Script.ScriptSource" : "%s"
+ "Octopus.Action.Script.Syntax" : "%s"
+ }
+ }
+`,
+ data.localName,
+ data.actionType,
+ data.name,
+ data.description,
+ data.stepPackageID,
+ data.packages[0].packageID,
+ data.packages[0].acquisitonLocation,
+ data.packages[0].feedID,
+ data.packages[0].name,
+ data.packages[0].properties.extract,
+ data.packages[0].properties.purpose,
+ data.packages[0].properties.selectionMode,
+ data.parameters[0].defaultValue,
+ data.parameters[0].displaySettings["Octopus.ControlType"],
+ data.parameters[0].helpText,
+ data.parameters[0].label,
+ data.parameters[0].name,
+ data.parameters[0].id,
+ data.parameters[1].defaultValue,
+ data.parameters[1].displaySettings["Octopus.ControlType"],
+ data.parameters[1].helpText,
+ data.parameters[1].label,
+ data.parameters[1].name,
+ data.parameters[1].id,
+ data.properties["Octopus.Action.Script.ScriptBody"],
+ data.properties["Octopus.Action.Script.ScriptSource"],
+ data.properties["Octopus.Action.Script.Syntax"],
+ )
+}
+
+func testStepTemplateRunScriptUpdate(data stepTemplateTestData) string {
+ data.name = data.name + "-updated"
+
+ return testStepTemplateRunScriptBasic(data)
+}
+
+func testStepTemplateDestroy(s *terraform.State, localName string) error {
+ var actionTemplateID string
+
+ for _, rs := range s.RootModule().Resources {
+ if rs.Type != "octopusdeploy_step_template" {
+ continue
+ }
+
+ actionTemplateID = rs.Primary.ID
+ break
+ }
+ if actionTemplateID == "" {
+ return fmt.Errorf("no octopusdeploy_step_template resource found")
+ }
+
+ actionTemplate, err := actiontemplates.GetByID(octoClient, octoClient.GetSpaceID(), actionTemplateID)
+ if err == nil {
+ if actionTemplate != nil {
+ return fmt.Errorf("step template (%s) still exists", actionTemplate.Name)
+ }
+ }
+
+ return nil
+}
diff --git a/octopusdeploy_framework/schemas/step_template.go b/octopusdeploy_framework/schemas/step_template.go
new file mode 100644
index 000000000..7daa17bb3
--- /dev/null
+++ b/octopusdeploy_framework/schemas/step_template.go
@@ -0,0 +1,288 @@
+package schemas
+
+import (
+ "fmt"
+ "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util"
+ "github.com/google/uuid"
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ ds "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ rs "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/stringdefault"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "regexp"
+)
+
+const (
+ StepTemplateResourceDescription = "step_template"
+ StepTemplateDatasourceDescription = "step_template"
+)
+
+type StepTemplateTypeDataSourceModel struct {
+ ID types.String `tfsdk:"id"`
+ SpaceID types.String `tfsdk:"space_id"`
+ StepTemplate types.Object `tfsdk:"step_template"`
+}
+
+type StepTemplateTypeResourceModel struct {
+ ActionType types.String `tfsdk:"action_type"`
+ SpaceID types.String `tfsdk:"space_id"`
+ CommunityActionTemplateId types.String `tfsdk:"community_action_template_id"`
+ Name types.String `tfsdk:"name"`
+ Description types.String `tfsdk:"description"`
+ Packages types.List `tfsdk:"packages"`
+ Parameters types.List `tfsdk:"parameters"`
+ Properties types.Map `tfsdk:"properties"`
+ StepPackageId types.String `tfsdk:"step_package_id"`
+ Version types.Int32 `tfsdk:"version"`
+
+ ResourceModel
+}
+
+type StepTemplatePackageType struct {
+ ID types.String `tfsdk:"id"`
+ AcquisitionLocation types.String `tfsdk:"acquisition_location"`
+ Name types.String `tfsdk:"name"`
+ FeedID types.String `tfsdk:"feed_id"`
+ PackageID types.String `tfsdk:"package_id"`
+ Properties types.Object `tfsdk:"properties"`
+}
+
+type StepTemplateParameterType struct {
+ ID types.String `tfsdk:"id"`
+ Name types.String `tfsdk:"name"`
+ Label types.String `tfsdk:"label"`
+ HelpText types.String `tfsdk:"help_text"`
+ DisplaySettings types.Map `tfsdk:"display_settings"`
+ DefaultValue types.String `tfsdk:"default_value"`
+}
+
+type StepTemplateSchema struct{}
+
+var _ EntitySchema = StepTemplateSchema{}
+
+func (s StepTemplateSchema) GetDatasourceSchema() ds.Schema {
+ return ds.Schema{
+ Description: util.GetDataSourceDescription(StepTemplateDatasourceDescription),
+ Attributes: map[string]ds.Attribute{
+ "id": ds.StringAttribute{
+ Description: "Unique identifier of the step template",
+ Required: true,
+ },
+ "space_id": ds.StringAttribute{
+ Description: "SpaceID of the Step Template",
+ Optional: true,
+ Computed: true,
+ },
+ "step_template": ds.ObjectAttribute{
+ Computed: true,
+ Optional: false,
+ AttributeTypes: GetStepTemplateAttributes(),
+ },
+ },
+ }
+}
+
+func (s StepTemplateSchema) GetResourceSchema() rs.Schema {
+ return rs.Schema{
+ Description: util.GetResourceSchemaDescription(StepTemplateResourceDescription),
+ Attributes: map[string]rs.Attribute{
+ "id": GetIdResourceSchema(),
+ "name": GetNameResourceSchema(true),
+ "description": GetDescriptionResourceSchema(StepTemplateResourceDescription),
+ "space_id": GetSpaceIdResourceSchema(StepTemplateResourceDescription),
+ "version": rs.Int32Attribute{
+ Description: "The version of the step template",
+ Optional: false,
+ Computed: true,
+ },
+ "step_package_id": rs.StringAttribute{
+ Description: "The ID of the step package",
+ Required: true,
+ },
+ "action_type": rs.StringAttribute{
+ Description: "The action type of the step template",
+ Required: true,
+ },
+ "community_action_template_id": rs.StringAttribute{
+ Description: "The ID of the community action template",
+ Optional: true,
+ Computed: true,
+ Default: stringdefault.StaticString(""),
+ },
+ "packages": GetStepTemplatePackageResourceSchema(),
+ "parameters": GetStepTemplateParameterResourceSchema(),
+ "properties": rs.MapAttribute{
+ Description: "Properties for the step template",
+ Required: true,
+ ElementType: types.StringType,
+ },
+ },
+ }
+}
+
+func GetStepTemplateParameterResourceSchema() rs.ListNestedAttribute {
+ return rs.ListNestedAttribute{
+ Description: "List of parameters that can be used in Step Template.",
+ Required: true,
+ NestedObject: rs.NestedAttributeObject{
+ Attributes: map[string]rs.Attribute{
+ "default_value": rs.StringAttribute{
+ Description: "A default value for the parameter, if applicable. This can be a hard-coded value or a variable reference.",
+ Optional: true,
+ Computed: true,
+ Default: stringdefault.StaticString(""),
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "display_settings": rs.MapAttribute{
+ Description: "The display settings for the parameter.",
+ Optional: true,
+ ElementType: types.StringType,
+ },
+ "help_text": rs.StringAttribute{
+ Description: "The help presented alongside the parameter input.",
+ Optional: true,
+ Computed: true,
+ Default: stringdefault.StaticString(""),
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "id": rs.StringAttribute{
+ Description: "The id for the property.",
+ Required: true,
+ Validators: []validator.String{
+ stringvalidator.RegexMatches(regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"), fmt.Sprintf("must be a valid UUID, unique within this list. Here is one you could use: %s.\nExpect uuid", uuid.New())),
+ },
+ },
+ "label": rs.StringAttribute{
+ Description: "The label shown beside the parameter when presented in the deployment process. Example: `Server name`.",
+ Optional: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "name": rs.StringAttribute{
+ Description: "The name of the variable set by the parameter. The name can contain letters, digits, dashes and periods. Example: `ServerName`",
+ Required: true,
+ Validators: []validator.String{
+ stringvalidator.LengthAtLeast(1),
+ },
+ },
+ },
+ },
+ }
+}
+
+func GetStepTemplatePackageResourceSchema() rs.ListNestedAttribute {
+ return rs.ListNestedAttribute{
+ Description: "Package information for the step template",
+ Required: true,
+ NestedObject: rs.NestedAttributeObject{
+ Attributes: map[string]rs.Attribute{
+ "acquisition_location": rs.StringAttribute{
+ Description: "Acquisition location for the package.",
+ Default: stringdefault.StaticString("Server"),
+ Optional: true,
+ Computed: true,
+ },
+ "feed_id": rs.StringAttribute{
+ Description: "ID of the feed.",
+ Required: true,
+ },
+ "id": GetIdResourceSchema(),
+ "name": GetNameResourceSchema(true),
+ "package_id": rs.StringAttribute{
+ Description: "The ID of the package to use.",
+ Optional: true,
+ Required: false,
+ Computed: true,
+ },
+ "properties": rs.SingleNestedAttribute{
+ Description: "Properties for the package.",
+ Required: true,
+ Attributes: map[string]rs.Attribute{
+ "extract": rs.StringAttribute{
+ Description: "If the package should extract.",
+ Default: stringdefault.StaticString("True"),
+ Optional: true,
+ Computed: true,
+ Validators: []validator.String{
+ stringvalidator.RegexMatches(regexp.MustCompile("^(True|Fasle)$"), "Extract must be True or False"),
+ },
+ },
+ "package_parameter_name": rs.StringAttribute{
+ Description: "The name of the package parameter",
+ Default: stringdefault.StaticString(""),
+ Optional: true,
+ Computed: true,
+ },
+ "purpose": rs.StringAttribute{
+ Description: "The purpose of this property.",
+ Default: stringdefault.StaticString(""),
+ Optional: true,
+ Required: false,
+ Computed: true,
+ },
+ "selection_mode": rs.StringAttribute{
+ Description: "The selection mode.",
+ Required: true,
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func GetStepTemplateAttributes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "id": types.StringType,
+ "name": types.StringType,
+ "description": types.StringType,
+ "space_id": types.StringType,
+ "version": types.Int32Type,
+ "step_package_id": types.StringType,
+ "action_type": types.StringType,
+ "community_action_template_id": types.StringType,
+ "packages": types.ListType{ElemType: types.ObjectType{AttrTypes: GetStepTemplatePackageTypeAttributes()}},
+ "parameters": types.ListType{ElemType: types.ObjectType{AttrTypes: GetStepTemplateParameterTypeAttributes()}},
+ "properties": types.MapType{ElemType: types.StringType},
+ }
+}
+
+func GetStepTemplatePackageTypeAttributes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "id": types.StringType,
+ "acquisition_location": types.StringType,
+ "name": types.StringType,
+ "feed_id": types.StringType,
+ "package_id": types.StringType,
+ "properties": types.ObjectType{AttrTypes: GetStepTemplatePackagePropertiesTypeAttributes()},
+ }
+}
+
+func GetStepTemplatePackagePropertiesTypeAttributes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "extract": types.StringType,
+ "package_parameter_name": types.StringType,
+ "purpose": types.StringType,
+ "selection_mode": types.StringType,
+ }
+}
+
+func GetStepTemplateParameterTypeAttributes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "id": types.StringType,
+ "name": types.StringType,
+ "label": types.StringType,
+ "help_text": types.StringType,
+ "display_settings": types.MapType{ElemType: types.StringType},
+ "default_value": types.StringType,
+ }
+}
diff --git a/octopusdeploy_framework/util/util.go b/octopusdeploy_framework/util/util.go
index 42ae11dd1..b67394f71 100644
--- a/octopusdeploy_framework/util/util.go
+++ b/octopusdeploy_framework/util/util.go
@@ -3,7 +3,6 @@ package util
import (
"context"
"fmt"
-
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
@@ -59,6 +58,26 @@ func SetToStringArray(ctx context.Context, set types.Set) ([]string, diag.Diagno
return convertedSet, diags
}
+func ConvertStringMapToAttrStringMap(strMap map[string]string) map[string]attr.Value {
+ attrMap := make(map[string]attr.Value, len(strMap))
+ for key, val := range strMap {
+ attrMap[key] = types.StringValue(val)
+ }
+ return attrMap
+}
+
+func ConvertAttrStringMapToStringMap(attrMap map[string]attr.Value) map[string]string {
+ nativeMap := make(map[string]string, len(attrMap))
+ for key, val := range attrMap {
+ if val.IsNull() {
+ nativeMap[key] = ""
+ } else {
+ nativeMap[key] = val.(types.String).ValueString()
+ }
+ }
+ return nativeMap
+}
+
func FlattenStringList(list []string) types.List {
if list == nil {
return types.ListValueMust(types.StringType, make([]attr.Value, 0))