diff --git a/client/configuration_variable.go b/client/configuration_variable.go index 67074798..f3f68c03 100644 --- a/client/configuration_variable.go +++ b/client/configuration_variable.go @@ -1,6 +1,7 @@ package client import ( + "encoding/json" "errors" ) @@ -39,22 +40,30 @@ func (c *ConfigurationVariableSchema) ResourceDataSliceStructValueWrite(values m return nil } +type ConfigurationVariableOverwrites struct { + Value string `json:"value"` + Regex string `json:"regex"` + IsRequired bool `json:"isRequired"` + IsSensitive bool `json:"isSensitive"` +} + type ConfigurationVariable struct { - ScopeId string `json:"scopeId,omitempty"` - Value string `json:"value" tfschema:"-"` - OrganizationId string `json:"organizationId,omitempty"` - UserId string `json:"userId,omitempty"` - IsSensitive *bool `json:"isSensitive,omitempty"` - Scope Scope `json:"scope,omitempty"` - Id string `json:"id,omitempty"` - Name string `json:"name"` - Description string `json:"description,omitempty"` - Type *ConfigurationVariableType `json:"type,omitempty" tfschema:",omitempty"` - Schema *ConfigurationVariableSchema `json:"schema,omitempty"` - ToDelete *bool `json:"toDelete,omitempty"` - IsReadOnly *bool `json:"isReadonly,omitempty"` - IsRequired *bool `json:"isRequired,omitempty"` - Regex string `json:"regex,omitempty"` + ScopeId string `json:"scopeId,omitempty"` + Value string `json:"value" tfschema:"-"` + OrganizationId string `json:"organizationId,omitempty"` + UserId string `json:"userId,omitempty"` + IsSensitive *bool `json:"isSensitive,omitempty"` + Scope Scope `json:"scope,omitempty"` + Id string `json:"id,omitempty"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + Type *ConfigurationVariableType `json:"type,omitempty" tfschema:",omitempty"` + Schema *ConfigurationVariableSchema `json:"schema,omitempty"` + ToDelete *bool `json:"toDelete,omitempty"` + IsReadOnly *bool `json:"isReadonly,omitempty"` + IsRequired *bool `json:"isRequired,omitempty"` + Regex string `json:"regex,omitempty"` + Overwrites *ConfigurationVariableOverwrites `json:"overwrites,omitempty"` // Is removed when marhseling to a JSON. } type ConfigurationVariableCreateParams struct { @@ -77,6 +86,17 @@ type ConfigurationVariableUpdateParams struct { Id string } +func (v ConfigurationVariable) MarshalJSON() ([]byte, error) { + v.Overwrites = nil + + // This is done to prevent an infinite loop. + type ConfigurationVariableDummy ConfigurationVariable + + dummy := ConfigurationVariableDummy(v) + + return json.Marshal(&dummy) +} + func (client *ApiClient) ConfigurationVariablesById(id string) (ConfigurationVariable, error) { var result ConfigurationVariable @@ -93,8 +113,11 @@ func (client *ApiClient) ConfigurationVariablesByScope(scope Scope, scopeId stri if err != nil { return nil, err } + var result []ConfigurationVariable + params := map[string]string{"organizationId": organizationId} + switch { case scope == ScopeGlobal: case scope == ScopeTemplate: @@ -111,6 +134,7 @@ func (client *ApiClient) ConfigurationVariablesByScope(scope Scope, scopeId stri params["environmentId"] = scopeId params["workflowEnvironmentId"] = scopeId } + err = client.http.Get("/configuration", params, &result) if err != nil { return []ConfigurationVariable{}, err @@ -118,6 +142,7 @@ func (client *ApiClient) ConfigurationVariablesByScope(scope Scope, scopeId stri // The API returns variables of upper scopes. Filter them out. var filteredVariables []ConfigurationVariable + for _, variable := range result { if scopeId == variable.ScopeId && scope == variable.Scope { filteredVariables = append(filteredVariables, variable) @@ -131,11 +156,14 @@ func (client *ApiClient) ConfigurationVariableCreate(params ConfigurationVariabl if params.Scope == ScopeDeploymentLog || params.Scope == ScopeDeployment { return ConfigurationVariable{}, errors.New("must not create variable on scope deployment / deploymentLog") } + organizationId, err := client.OrganizationId() if err != nil { return ConfigurationVariable{}, err } + var result []ConfigurationVariable + request := map[string]interface{}{ "name": params.Name, "description": params.Description, @@ -148,6 +176,7 @@ func (client *ApiClient) ConfigurationVariableCreate(params ConfigurationVariabl "isReadonly": params.IsReadOnly, "regex": params.Regex, } + if params.Scope != ScopeGlobal { request["scopeId"] = params.ScopeId } @@ -166,9 +195,11 @@ func getSchema(params ConfigurationVariableCreateParams) map[string]interface{} schema := map[string]interface{}{ "type": "string", } + if params.EnumValues != nil { schema["enum"] = params.EnumValues } + if params.Format != Text { schema["format"] = params.Format } @@ -185,11 +216,14 @@ func (client *ApiClient) ConfigurationVariableUpdate(updateParams ConfigurationV if commonParams.Scope == ScopeDeploymentLog || commonParams.Scope == ScopeDeployment { return ConfigurationVariable{}, errors.New("must not create variable on scope deployment / deploymentLog") } + organizationId, err := client.OrganizationId() if err != nil { return ConfigurationVariable{}, err } + var result []ConfigurationVariable + request := map[string]interface{}{ "id": updateParams.Id, "name": commonParams.Name, @@ -203,6 +237,7 @@ func (client *ApiClient) ConfigurationVariableUpdate(updateParams ConfigurationV "isReadonly": commonParams.IsReadOnly, "regex": commonParams.Regex, } + if commonParams.Scope != ScopeGlobal { request["scopeId"] = commonParams.ScopeId } @@ -210,9 +245,10 @@ func (client *ApiClient) ConfigurationVariableUpdate(updateParams ConfigurationV request["schema"] = getSchema(updateParams.CommonParams) requestInArray := []map[string]interface{}{request} - err = client.http.Post("/configuration", requestInArray, &result) - if err != nil { + + if err := client.http.Post("/configuration", requestInArray, &result); err != nil { return ConfigurationVariable{}, err } + return result[0], nil } diff --git a/client/configuration_variable_test.go b/client/configuration_variable_test.go index c722f7bf..b95a2cd7 100644 --- a/client/configuration_variable_test.go +++ b/client/configuration_variable_test.go @@ -2,12 +2,15 @@ package client_test import ( "encoding/json" + "strings" + "testing" . "github.com/env0/terraform-provider-env0/client" "github.com/jinzhu/copier" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) @@ -52,14 +55,14 @@ var _ = Describe("Configuration Variable", func() { Describe("Schema", func() { It("On schema type is free text, enum should be nil", func() { var parsedPayload ConfigurationVariable - json.Unmarshal([]byte(`{"schema": {"type": "string"}}`), &parsedPayload) + _ = json.Unmarshal([]byte(`{"schema": {"type": "string"}}`), &parsedPayload) Expect(parsedPayload.Schema.Type).Should(Equal("string")) Expect(parsedPayload.Schema.Enum).Should(BeNil()) }) It("On schema type is dropdown, enum should be present", func() { var parsedPayload ConfigurationVariable - json.Unmarshal([]byte(`{"schema": {"type": "string", "enum": ["hello"]}}`), &parsedPayload) + _ = json.Unmarshal([]byte(`{"schema": {"type": "string", "enum": ["hello"]}}`), &parsedPayload) Expect(parsedPayload.Schema.Type).Should(Equal("string")) Expect(parsedPayload.Schema.Enum).Should(BeEquivalentTo([]string{"hello"})) }) @@ -68,7 +71,7 @@ var _ = Describe("Configuration Variable", func() { Describe("Enums", func() { It("Should convert enums correctly", func() { var parsedPayload ConfigurationVariable - json.Unmarshal([]byte(`{"scope":"PROJECT", "type": 1}`), &parsedPayload) + _ = json.Unmarshal([]byte(`{"scope":"PROJECT", "type": 1}`), &parsedPayload) Expect(parsedPayload.Scope).Should(Equal(ScopeProject)) Expect(*parsedPayload.Type).Should(Equal(ConfigurationVariableTypeTerraform)) }) @@ -187,9 +190,10 @@ var _ = Describe("Configuration Variable", func() { }) Describe("ConfigurationVariableDelete", func() { + BeforeEach(func() { httpCall = mockHttpClient.EXPECT().Delete("configuration/"+mockConfigurationVariable.Id, nil) - apiClient.ConfigurationVariableDelete(mockConfigurationVariable.Id) + _ = apiClient.ConfigurationVariableDelete(mockConfigurationVariable.Id) }) It("Should send DELETE request with project id", func() { @@ -291,3 +295,37 @@ var _ = Describe("Configuration Variable", func() { }) }) }) + +func TestConfigurationVariableMarshelling(t *testing.T) { + str := "this is a string" + + variable := ConfigurationVariable{ + Value: "a", + Overwrites: &ConfigurationVariableOverwrites{ + Value: str, + }, + } + + b, err := json.Marshal(&variable) + if assert.NoError(t, err) { + assert.False(t, strings.Contains(string(b), str)) + } + + type ConfigurationVariableDummy ConfigurationVariable + + dummy := ConfigurationVariableDummy(variable) + + b, err = json.Marshal(&dummy) + if assert.NoError(t, err) { + assert.True(t, strings.Contains(string(b), str)) + } + + var variable2 ConfigurationVariable + + err = json.Unmarshal(b, &variable2) + + if assert.NoError(t, err) && assert.NotNil(t, variable2.Overwrites) { + assert.Equal(t, str, variable2.Overwrites.Value) + assert.Equal(t, variable, variable2) + } +} diff --git a/client/environment.go b/client/environment.go index 77d0addd..029a61ed 100644 --- a/client/environment.go +++ b/client/environment.go @@ -7,6 +7,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) +const ENVIRONMENT = "environment" + type ConfigurationVariableType int func (c *ConfigurationVariableType) ReadResourceData(fieldName string, d *schema.ResourceData) error { diff --git a/env0/configuration_variable.go b/env0/configuration_variable.go new file mode 100644 index 00000000..49538730 --- /dev/null +++ b/env0/configuration_variable.go @@ -0,0 +1,143 @@ +package env0 + +import ( + "context" + + "github.com/env0/terraform-provider-env0/client" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func getConfigurationVariablesFromSchema(configuration []interface{}) client.ConfigurationChanges { + configurationChanges := client.ConfigurationChanges{} + + for _, variable := range configuration { + configurationVariable := getConfigurationVariableFromSchema(variable.(map[string]interface{})) + configurationChanges = append(configurationChanges, configurationVariable) + } + + return configurationChanges +} + +func getConfigurationVariableFromSchema(variable map[string]interface{}) client.ConfigurationVariable { + varType, _ := client.GetConfigurationVariableType(variable["type"].(string)) + + configurationVariable := client.ConfigurationVariable{ + Name: variable["name"].(string), + Value: variable["value"].(string), + Scope: client.ScopeDeployment, + Type: &varType, + } + + if variable["scope_id"] != nil { + configurationVariable.ScopeId = variable["scope_id"].(string) + } + + if variable["is_sensitive"] != nil { + isSensitive := variable["is_sensitive"].(bool) + configurationVariable.IsSensitive = &isSensitive + } + + if variable["is_read_only"] != nil { + isReadOnly := variable["is_read_only"].(bool) + configurationVariable.IsReadOnly = &isReadOnly + } + + if variable["is_required"] != nil { + isRequired := variable["is_required"].(bool) + configurationVariable.IsRequired = &isRequired + } + + if variable["description"] != nil { + configurationVariable.Description = variable["description"].(string) + } + + if variable["regex"] != nil { + configurationVariable.Regex = variable["regex"].(string) + } + + configurationSchema := client.ConfigurationVariableSchema{ + Format: client.Format(variable["schema_format"].(string)), + Enum: nil, + Type: variable["schema_type"].(string), + } + + if variable["schema_type"] != "" && len(variable["schema_enum"].([]interface{})) > 0 { + enumOfAny := variable["schema_enum"].([]interface{}) + enum := make([]string, len(enumOfAny)) + + for i := range enum { + enum[i] = enumOfAny[i].(string) + } + + configurationSchema.Type = variable["schema_type"].(string) + configurationSchema.Enum = enum + } + + configurationVariable.Schema = &configurationSchema + + return configurationVariable +} + +func setEnvironmentConfigurationSchema(ctx context.Context, d *schema.ResourceData, configurationVariables []client.ConfigurationVariable) { + ivariables, ok := d.GetOk("configuration") + if !ok { + return + } + + if ivariables == nil { + ivariables = make([]interface{}, 0) + } + + variables := ivariables.([]interface{}) + + newVariables := make([]interface{}, 0) + + // The goal is to maintain existing state order as much as possible. (The backend response order may vary from state). + for _, ivariable := range variables { + variable := ivariable.(map[string]interface{}) + variableName := variable["name"].(string) + + for _, configurationVariable := range configurationVariables { + if configurationVariable.Name == variableName { + newVariable := createVariable(&configurationVariable) + + if configurationVariable.IsSensitive != nil && *configurationVariable.IsSensitive { + // To avoid drift for sensitive variables, don't override with the variable value received from API. Use the one in the schema instead. + newVariable.(map[string]interface{})["value"] = variable["value"] + } + + newVariables = append(newVariables, newVariable) + + break + } + } + } + + // Check for drifts: add new configuration variables received from the backend. + for _, configurationVariable := range configurationVariables { + found := false + + for _, ivariable := range variables { + variable := ivariable.(map[string]interface{}) + variableName := variable["name"].(string) + + if configurationVariable.Name == variableName { + found = true + + break + } + } + + if !found { + tflog.Warn(ctx, "Drift Detected: Terraform will remove id from state", map[string]interface{}{"configuration name": configurationVariable.Name}) + newVariables = append(newVariables, createVariable(&configurationVariable)) + } + } + + if len(newVariables) > 0 { + d.Set("configuration", newVariables) + } else { + d.Set("configuration", nil) + } +} diff --git a/env0/data_configuration_variable.go b/env0/data_configuration_variable.go index d1f7d0ad..0ed97154 100644 --- a/env0/data_configuration_variable.go +++ b/env0/data_configuration_variable.go @@ -185,6 +185,7 @@ func getConfigurationVariable(params ConfigurationVariableParams, meta interface if err != nil { return client.ConfigurationVariable{}, diag.Errorf("Could not query variable: %v", err) } + return variable, nil } @@ -197,37 +198,47 @@ func getConfigurationVariable(params ConfigurationVariableParams, meta interface name, nameOk := params.Name, params.Name != "" typeString, ok := params.ConfigurationType, params.ConfigurationType != "" type_ := -1 + if ok { if !nameOk { return client.ConfigurationVariable{}, diag.Errorf("Specify 'type' only when searching configuration variables by 'name' (not by 'id')") } + switch typeString { - case "environment": + case client.ENVIRONMENT: type_ = int(client.ConfigurationVariableTypeEnvironment) - case "terraform": + case client.TERRAFORM: type_ = int(client.ConfigurationVariableTypeTerraform) default: return client.ConfigurationVariable{}, diag.Errorf("Invalid value for 'type': %s. can be either 'environment' or 'terraform'", typeString) } } + var variable client.ConfigurationVariable + for _, candidate := range variables { if idOk && candidate.Id == id { variable = candidate + break } + if nameOk && candidate.Name == name { if type_ != -1 { if int(*candidate.Type) != type_ { continue } } + variable = candidate + break } } + if variable.Id == "" { return client.ConfigurationVariable{}, diag.Errorf("Could not find variable") } + return variable, nil } diff --git a/env0/resource_environment.go b/env0/resource_environment.go index bec2633e..2743f557 100644 --- a/env0/resource_environment.go +++ b/env0/resource_environment.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "regexp" + "strings" "github.com/env0/terraform-provider-env0/client" "github.com/env0/terraform-provider-env0/client/http" @@ -84,7 +85,7 @@ func resourceEnvironment() *schema.Resource { Optional: true, ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) { value := val.(string) - if value != "environment" && value != "terraform" { + if value != client.ENVIRONMENT && value != client.TERRAFORM { errs = append(errs, fmt.Errorf("%q can be either \"environment\" or \"terraform\", got: %q", key, value)) } return @@ -454,6 +455,7 @@ func setEnvironmentSchema(ctx context.Context, d *schema.ResourceData, environme for _, newv := range variableSetsIds { if schemav == newv { sortedVariablesSet = append(sortedVariablesSet, schemav) + break } } @@ -464,6 +466,7 @@ func setEnvironmentSchema(ctx context.Context, d *schema.ResourceData, environme for _, sortedv := range sortedVariablesSet { if newv == sortedv { found = true + break } } @@ -486,9 +489,9 @@ func createVariable(configurationVariable *client.ConfigurationVariable) interfa variable["value"] = configurationVariable.Value if configurationVariable.Type == nil || *configurationVariable.Type == 0 { - variable["type"] = "environment" + variable["type"] = client.ENVIRONMENT } else { - variable["type"] = "terraform" + variable["type"] = client.TERRAFORM } if configurationVariable.Description != "" { @@ -520,67 +523,6 @@ func createVariable(configurationVariable *client.ConfigurationVariable) interfa return variable } -func setEnvironmentConfigurationSchema(ctx context.Context, d *schema.ResourceData, configurationVariables []client.ConfigurationVariable) { - ivariables, ok := d.GetOk("configuration") - if !ok { - return - } - - if ivariables == nil { - ivariables = make([]interface{}, 0) - } - - variables := ivariables.([]interface{}) - - newVariables := make([]interface{}, 0) - - // The goal is to maintain existing state order as much as possible. (The backend response order may vary from state). - for _, ivariable := range variables { - variable := ivariable.(map[string]interface{}) - variableName := variable["name"].(string) - - for _, configurationVariable := range configurationVariables { - if configurationVariable.Name == variableName { - newVariable := createVariable(&configurationVariable) - - if configurationVariable.IsSensitive != nil && *configurationVariable.IsSensitive { - // To avoid drift for sensitive variables, don't override with the variable value received from API. Use the one in the schema instead. - newVariable.(map[string]interface{})["value"] = variable["value"] - } - - newVariables = append(newVariables, newVariable) - - break - } - } - } - - // Check for drifts: add new configuration variables received from the backend. - for _, configurationVariable := range configurationVariables { - found := false - - for _, ivariable := range variables { - variable := ivariable.(map[string]interface{}) - variableName := variable["name"].(string) - if configurationVariable.Name == variableName { - found = true - break - } - } - - if !found { - tflog.Warn(ctx, "Drift Detected: Terraform will remove id from state", map[string]interface{}{"configuration name": configurationVariable.Name}) - newVariables = append(newVariables, createVariable(&configurationVariable)) - } - } - - if len(newVariables) > 0 { - d.Set("configuration", newVariables) - } else { - d.Set("configuration", nil) - } -} - // Validate that the template is assigned to the "project_id". func validateTemplateProjectAssignment(d *schema.ResourceData, apiClient client.ApiClientInterface) error { projectId := d.Get("project_id").(string) @@ -661,14 +603,14 @@ func resourceEnvironmentCreate(ctx context.Context, d *schema.ResourceData, meta } func getEnvironmentVariableSetIdsFromApi(d *schema.ResourceData, apiClient client.ApiClientInterface) ([]string, error) { - environmentVariableSets, err := apiClient.ConfigurationSetsAssignments("ENVIRONMENT", d.Id()) + environmentVariableSets, err := apiClient.ConfigurationSetsAssignments(strings.ToUpper(client.ENVIRONMENT), d.Id()) if err != nil { return nil, err } var environmentVariableSetIds []string for _, variableSet := range environmentVariableSets { - if variableSet.AssignmentScope == "environment" { + if variableSet.AssignmentScope == client.ENVIRONMENT { environmentVariableSetIds = append(environmentVariableSetIds, variableSet.Id) } } @@ -1141,6 +1083,7 @@ func getEnvironmentConfigurationSetChanges(d *schema.ResourceData, apiClient cli for _, av := range variableSetFromApi { if sv == av { found = true + break } } @@ -1156,6 +1099,7 @@ func getEnvironmentConfigurationSetChanges(d *schema.ResourceData, apiClient cli for _, sv := range variableSetsFromSchema { if sv == av { found = true + break } } @@ -1252,16 +1196,6 @@ func getUpdateConfigurationVariables(configurationChanges client.ConfigurationCh return configurationChanges, nil } -func getConfigurationVariablesFromSchema(configuration []interface{}) client.ConfigurationChanges { - configurationChanges := client.ConfigurationChanges{} - for _, variable := range configuration { - configurationVariable := getConfigurationVariableFromSchema(variable.(map[string]interface{})) - configurationChanges = append(configurationChanges, configurationVariable) - } - - return configurationChanges -} - func deleteUnusedConfigurationVariables(configurationChanges client.ConfigurationChanges, existVariables client.ConfigurationChanges) client.ConfigurationChanges { for _, existVariable := range existVariables { if isExist, _ := isVariableExist(configurationChanges, existVariable); !isExist { @@ -1300,63 +1234,6 @@ func typeEqual(variable client.ConfigurationVariable, search client.Configuratio search.Type == nil && *variable.Type == client.ConfigurationVariableTypeEnvironment } -func getConfigurationVariableFromSchema(variable map[string]interface{}) client.ConfigurationVariable { - varType, _ := client.GetConfigurationVariableType(variable["type"].(string)) - - configurationVariable := client.ConfigurationVariable{ - Name: variable["name"].(string), - Value: variable["value"].(string), - Scope: client.ScopeDeployment, - Type: &varType, - } - - if variable["scope_id"] != nil { - configurationVariable.ScopeId = variable["scope_id"].(string) - } - - if variable["is_sensitive"] != nil { - isSensitive := variable["is_sensitive"].(bool) - configurationVariable.IsSensitive = &isSensitive - } - - if variable["is_read_only"] != nil { - isReadOnly := variable["is_read_only"].(bool) - configurationVariable.IsReadOnly = &isReadOnly - } - - if variable["is_required"] != nil { - isRequired := variable["is_required"].(bool) - configurationVariable.IsRequired = &isRequired - } - - if variable["description"] != nil { - configurationVariable.Description = variable["description"].(string) - } - - if variable["regex"] != nil { - configurationVariable.Regex = variable["regex"].(string) - } - - configurationSchema := client.ConfigurationVariableSchema{ - Format: client.Format(variable["schema_format"].(string)), - Enum: nil, - Type: variable["schema_type"].(string), - } - - if variable["schema_type"] != "" && len(variable["schema_enum"].([]interface{})) > 0 { - enumOfAny := variable["schema_enum"].([]interface{}) - enum := make([]string, len(enumOfAny)) - for i := range enum { - enum[i] = enumOfAny[i].(string) - } - configurationSchema.Type = variable["schema_type"].(string) - configurationSchema.Enum = enum - } - - configurationVariable.Schema = &configurationSchema - return configurationVariable -} - func getEnvironmentByName(meta interface{}, name string, projectId string, excludeArchived bool) (client.Environment, diag.Diagnostics) { apiClient := meta.(client.ApiClientInterface) environmentsByName, err := apiClient.EnvironmentsByName(name) diff --git a/env0/resource_environment_output_configuration_variable.go b/env0/resource_environment_output_configuration_variable.go index ae61fca1..6d91c3b1 100644 --- a/env0/resource_environment_output_configuration_variable.go +++ b/env0/resource_environment_output_configuration_variable.go @@ -121,11 +121,11 @@ func deserializeEnvironmentOutputConfigurationVariableValue(valueStr string) (*E } if value.OutputName == "" { - return nil, fmt.Errorf("after unmarshal 'outputName' is empty") + return nil, errors.New("after unmarshal 'outputName' is empty") } if value.EnvironmentId == "" && value.SubEnvironmentAlias == "" { - return nil, fmt.Errorf("after unmarshal both 'environmentId' and 'subEnvironmentAlias' are empty") + return nil, errors.New("after unmarshal both 'environmentId' and 'subEnvironmentAlias' are empty") } return &value, nil @@ -219,7 +219,7 @@ func resourceEnvironmentOutputConfigurationVariableCreate(ctx context.Context, d createParams, err := getEnvironmentOutputCreateParams(d) if err != nil { - return diag.Errorf(err.Error()) + return diag.FromErr(err) } configurationVariable, err := apiClient.ConfigurationVariableCreate(*createParams) @@ -257,7 +257,7 @@ func resourceEnvironmentOutputConfigurationVariableUpdate(ctx context.Context, d createParams, err := getEnvironmentOutputCreateParams(d) if err != nil { - return diag.Errorf(err.Error()) + return diag.FromErr(err) } id := d.Id()