From a32028aeed11b9ae5626f8a20b389c90e5648dd2 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 6 Oct 2020 17:14:53 -0400 Subject: [PATCH 1/2] evaluate vars and outputs during import Outputs were not being evaluated during import, because it was not added to the walk filter. Remove any unnecessary walk filters from all the Execute nodes. --- terraform/context_eval_test.go | 2 +- terraform/node_module_variable.go | 8 +- terraform/node_output.go | 94 +++++---- terraform/node_resource_destroy.go | 217 ++++++++++----------- terraform/node_resource_destroy_deposed.go | 71 ++++--- terraform/transform_reference.go | 2 +- 6 files changed, 191 insertions(+), 203 deletions(-) diff --git a/terraform/context_eval_test.go b/terraform/context_eval_test.go index 145ef1372..8f7035d01 100644 --- a/terraform/context_eval_test.go +++ b/terraform/context_eval_test.go @@ -38,7 +38,7 @@ func TestContextEval(t *testing.T) { }, { `module.child.result`, - cty.UnknownVal(cty.Number), + cty.NumberIntVal(6), false, }, } diff --git a/terraform/node_module_variable.go b/terraform/node_module_variable.go index b635685d1..78ddb6e8f 100644 --- a/terraform/node_module_variable.go +++ b/terraform/node_module_variable.go @@ -153,13 +153,13 @@ func (n *nodeModuleVariable) Execute(ctx EvalContext, op walkOperation) error { var err error switch op { - case walkPlan, walkApply, walkDestroy: - vals, err = n.EvalModuleCallArgument(ctx, false) + case walkValidate: + vals, err = n.EvalModuleCallArgument(ctx, true) if err != nil { return err } - case walkValidate: - vals, err = n.EvalModuleCallArgument(ctx, true) + default: + vals, err = n.EvalModuleCallArgument(ctx, false) if err != nil { return err } diff --git a/terraform/node_output.go b/terraform/node_output.go index ec4f48276..439c2a9cf 100644 --- a/terraform/node_output.go +++ b/terraform/node_output.go @@ -199,61 +199,55 @@ func (n *NodeApplyableOutput) References() []*addrs.Reference { // GraphNodeExecutable func (n *NodeApplyableOutput) Execute(ctx EvalContext, op walkOperation) error { - switch op { - // Everything except walkImport - case walkEval, walkPlan, walkApply, walkValidate, walkDestroy, walkPlanDestroy: - // This has to run before we have a state lock, since evaluation also - // reads the state - val, diags := ctx.EvaluateExpr(n.Config.Expr, cty.DynamicPseudoType, nil) - // We'll handle errors below, after we have loaded the module. + // This has to run before we have a state lock, since evaluation also + // reads the state + val, diags := ctx.EvaluateExpr(n.Config.Expr, cty.DynamicPseudoType, nil) + // We'll handle errors below, after we have loaded the module. - // Outputs don't have a separate mode for validation, so validate - // depends_on expressions here too - diags = diags.Append(validateDependsOn(ctx, n.Config.DependsOn)) + // Outputs don't have a separate mode for validation, so validate + // depends_on expressions here too + diags = diags.Append(validateDependsOn(ctx, n.Config.DependsOn)) - // Ensure that non-sensitive outputs don't include sensitive values - _, marks := val.UnmarkDeep() - _, hasSensitive := marks["sensitive"] - if !n.Config.Sensitive && hasSensitive { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Output refers to sensitive values", - Detail: "Expressions used in outputs can only refer to sensitive values if the sensitive attribute is true.", - Subject: n.Config.DeclRange.Ptr(), - }) - } + // Ensure that non-sensitive outputs don't include sensitive values + _, marks := val.UnmarkDeep() + _, hasSensitive := marks["sensitive"] + if !n.Config.Sensitive && hasSensitive { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Output refers to sensitive values", + Detail: "Expressions used in outputs can only refer to sensitive values if the sensitive attribute is true.", + Subject: n.Config.DeclRange.Ptr(), + }) + } - state := ctx.State() - if state == nil { - return nil - } - - changes := ctx.Changes() // may be nil, if we're not working on a changeset - - // handling the interpolation error - if diags.HasErrors() { - if flagWarnOutputErrors { - log.Printf("[ERROR] Output interpolation %q failed: %s", n.Addr, diags.Err()) - // if we're continuing, make sure the output is included, and - // marked as unknown. If the evaluator was able to find a type - // for the value in spite of the error then we'll use it. - n.setValue(state, changes, cty.UnknownVal(val.Type())) - return EvalEarlyExitError{} - } - return diags.Err() - } - n.setValue(state, changes, val) - - // If we were able to evaluate a new value, we can update that in the - // refreshed state as well. - if state = ctx.RefreshState(); state != nil && val.IsWhollyKnown() { - n.setValue(state, changes, val) - } - - return nil - default: + state := ctx.State() + if state == nil { return nil } + + changes := ctx.Changes() // may be nil, if we're not working on a changeset + + // handling the interpolation error + if diags.HasErrors() { + if flagWarnOutputErrors { + log.Printf("[ERROR] Output interpolation %q failed: %s", n.Addr, diags.Err()) + // if we're continuing, make sure the output is included, and + // marked as unknown. If the evaluator was able to find a type + // for the value in spite of the error then we'll use it. + n.setValue(state, changes, cty.UnknownVal(val.Type())) + return EvalEarlyExitError{} + } + return diags.Err() + } + n.setValue(state, changes, val) + + // If we were able to evaluate a new value, we can update that in the + // refreshed state as well. + if state = ctx.RefreshState(); state != nil && val.IsWhollyKnown() { + n.setValue(state, changes, val) + } + + return nil } // dag.GraphNodeDotter impl. diff --git a/terraform/node_resource_destroy.go b/terraform/node_resource_destroy.go index c54bc0cec..872df3654 100644 --- a/terraform/node_resource_destroy.go +++ b/terraform/node_resource_destroy.go @@ -136,133 +136,130 @@ func (n *NodeDestroyResourceInstance) Execute(ctx EvalContext, op walkOperation) var state *states.ResourceInstanceObject var provisionerErr error - switch op { - case walkApply, walkDestroy: - provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) + provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) + if err != nil { + return err + } + + changeApply, err = n.readDiff(ctx, providerSchema) + if err != nil { + return err + } + + evalReduceDiff := &EvalReduceDiff{ + Addr: addr.Resource, + InChange: &changeApply, + Destroy: true, + OutChange: &changeApply, + } + _, err = evalReduceDiff.Eval(ctx) + if err != nil { + return err + } + + // EvalReduceDiff may have simplified our planned change + // into a NoOp if it does not require destroying. + if changeApply == nil || changeApply.Action == plans.NoOp { + return EvalEarlyExitError{} + } + + state, err = n.ReadResourceInstanceState(ctx, addr) + if err != nil { + return err + } + + // Exit early if the state object is null after reading the state + if state == nil || state.Value.IsNull() { + return EvalEarlyExitError{} + } + + evalApplyPre := &EvalApplyPre{ + Addr: addr.Resource, + State: &state, + Change: &changeApply, + } + _, err = evalApplyPre.Eval(ctx) + if err != nil { + return err + } + + // Run destroy provisioners if not tainted + if state != nil && state.Status != states.ObjectTainted { + evalApplyProvisioners := &EvalApplyProvisioners{ + Addr: addr.Resource, + State: &state, + ResourceConfig: n.Config, + Error: &provisionerErr, + When: configs.ProvisionerWhenDestroy, + } + _, err := evalApplyProvisioners.Eval(ctx) if err != nil { return err } - - changeApply, err = n.readDiff(ctx, providerSchema) - if err != nil { - return err - } - - evalReduceDiff := &EvalReduceDiff{ - Addr: addr.Resource, - InChange: &changeApply, - Destroy: true, - OutChange: &changeApply, - } - _, err = evalReduceDiff.Eval(ctx) - if err != nil { - return err - } - - // EvalReduceDiff may have simplified our planned change - // into a NoOp if it does not require destroying. - if changeApply == nil || changeApply.Action == plans.NoOp { - return EvalEarlyExitError{} - } - - state, err = n.ReadResourceInstanceState(ctx, addr) - if err != nil { - return err - } - - // Exit early if the state object is null after reading the state - if state == nil || state.Value.IsNull() { - return EvalEarlyExitError{} - } - - evalApplyPre := &EvalApplyPre{ - Addr: addr.Resource, - State: &state, - Change: &changeApply, - } - _, err = evalApplyPre.Eval(ctx) - if err != nil { - return err - } - - // Run destroy provisioners if not tainted - if state != nil && state.Status != states.ObjectTainted { - evalApplyProvisioners := &EvalApplyProvisioners{ - Addr: addr.Resource, - State: &state, - ResourceConfig: n.Config, - Error: &provisionerErr, - When: configs.ProvisionerWhenDestroy, + if provisionerErr != nil { + // If we have a provisioning error, then we just call + // the post-apply hook now. + evalApplyPost := &EvalApplyPost{ + Addr: addr.Resource, + State: &state, + Error: &provisionerErr, } - _, err := evalApplyProvisioners.Eval(ctx) + _, err = evalApplyPost.Eval(ctx) if err != nil { return err } - if provisionerErr != nil { - // If we have a provisioning error, then we just call - // the post-apply hook now. - evalApplyPost := &EvalApplyPost{ - Addr: addr.Resource, - State: &state, - Error: &provisionerErr, - } - _, err = evalApplyPost.Eval(ctx) - if err != nil { - return err - } - } } + } - // Managed resources need to be destroyed, while data sources - // are only removed from state. - if addr.Resource.Resource.Mode == addrs.ManagedResourceMode { - evalApply := &EvalApply{ - Addr: addr.Resource, - Config: nil, // No configuration because we are destroying - State: &state, - Change: &changeApply, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderMetas: n.ProviderMetas, - ProviderSchema: &providerSchema, - Output: &state, - Error: &provisionerErr, - } - _, err = evalApply.Eval(ctx) - if err != nil { - return err - } - - evalWriteState := &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - } - _, err = evalWriteState.Eval(ctx) - if err != nil { - return err - } - } else { - log.Printf("[TRACE] NodeDestroyResourceInstance: removing state object for %s", n.Addr) - state := ctx.State() - state.SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider) + // Managed resources need to be destroyed, while data sources + // are only removed from state. + if addr.Resource.Resource.Mode == addrs.ManagedResourceMode { + evalApply := &EvalApply{ + Addr: addr.Resource, + Config: nil, // No configuration because we are destroying + State: &state, + Change: &changeApply, + Provider: &provider, + ProviderAddr: n.ResolvedProvider, + ProviderMetas: n.ProviderMetas, + ProviderSchema: &providerSchema, + Output: &state, + Error: &provisionerErr, } - - evalApplyPost := &EvalApplyPost{ - Addr: addr.Resource, - State: &state, - Error: &provisionerErr, - } - _, err = evalApplyPost.Eval(ctx) + _, err = evalApply.Eval(ctx) if err != nil { return err } - err = UpdateStateHook(ctx) + evalWriteState := &EvalWriteState{ + Addr: addr.Resource, + ProviderAddr: n.ResolvedProvider, + ProviderSchema: &providerSchema, + State: &state, + } + _, err = evalWriteState.Eval(ctx) if err != nil { return err } + } else { + log.Printf("[TRACE] NodeDestroyResourceInstance: removing state object for %s", n.Addr) + state := ctx.State() + state.SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider) + } + + evalApplyPost := &EvalApplyPost{ + Addr: addr.Resource, + State: &state, + Error: &provisionerErr, + } + _, err = evalApplyPost.Eval(ctx) + if err != nil { + return err + } + + err = UpdateStateHook(ctx) + if err != nil { + return err } return nil diff --git a/terraform/node_resource_destroy_deposed.go b/terraform/node_resource_destroy_deposed.go index 6d5af049b..a77cdf476 100644 --- a/terraform/node_resource_destroy_deposed.go +++ b/terraform/node_resource_destroy_deposed.go @@ -76,44 +76,41 @@ func (n *NodePlanDeposedResourceInstanceObject) Execute(ctx EvalContext, op walk var change *plans.ResourceInstanceChange var state *states.ResourceInstanceObject - switch op { - case walkPlan, walkPlanDestroy: - - readStateDeposed := &EvalReadStateDeposed{ - Addr: addr.Resource, - Output: &state, - Key: n.DeposedKey, - Provider: &provider, - ProviderSchema: &providerSchema, - } - _, err = readStateDeposed.Eval(ctx) - if err != nil { - return err - } - - diffDestroy := &EvalDiffDestroy{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - DeposedKey: n.DeposedKey, - State: &state, - Output: &change, - } - _, err = diffDestroy.Eval(ctx) - if err != nil { - return err - } - - writeDiff := &EvalWriteDiff{ - Addr: addr.Resource, - DeposedKey: n.DeposedKey, - ProviderSchema: &providerSchema, - Change: &change, - } - _, err = writeDiff.Eval(ctx) - if err != nil { - return err - } + readStateDeposed := &EvalReadStateDeposed{ + Addr: addr.Resource, + Output: &state, + Key: n.DeposedKey, + Provider: &provider, + ProviderSchema: &providerSchema, } + _, err = readStateDeposed.Eval(ctx) + if err != nil { + return err + } + + diffDestroy := &EvalDiffDestroy{ + Addr: addr.Resource, + ProviderAddr: n.ResolvedProvider, + DeposedKey: n.DeposedKey, + State: &state, + Output: &change, + } + _, err = diffDestroy.Eval(ctx) + if err != nil { + return err + } + + writeDiff := &EvalWriteDiff{ + Addr: addr.Resource, + DeposedKey: n.DeposedKey, + ProviderSchema: &providerSchema, + Change: &change, + } + _, err = writeDiff.Eval(ctx) + if err != nil { + return err + } + return nil } diff --git a/terraform/transform_reference.go b/terraform/transform_reference.go index b813226f5..0b7d0e566 100644 --- a/terraform/transform_reference.go +++ b/terraform/transform_reference.go @@ -301,7 +301,7 @@ func (m ReferenceMap) References(v dag.Vertex) []dag.Vertex { case addrs.ModuleCallInstance: subject = ri.Call default: - log.Printf("[WARN] ReferenceTransformer: reference not found: %q", subject) + log.Printf("[INFO] ReferenceTransformer: reference not found: %q", subject) continue } key = m.referenceMapKey(v, subject) From 35714e61e6e5cf18078cd40c608c846858c8f54c Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 6 Oct 2020 17:39:53 -0400 Subject: [PATCH 2/2] audit graph builder to make them more similar Auditing the graph builder to remove unused transformers (planning does not need to close provisioners for example), and re-order them. While many of the transformations are commutative, using the same order ensures the same behavior between operations when the commutative property is lost or changed. --- terraform/graph_builder_apply.go | 25 ++++++----------- terraform/graph_builder_destroy_plan.go | 5 ++++ terraform/graph_builder_eval.go | 28 ++++++------------- terraform/graph_builder_import.go | 22 +++++---------- terraform/graph_builder_plan.go | 37 ++++++++----------------- terraform/transform_config.go | 3 -- 6 files changed, 41 insertions(+), 79 deletions(-) diff --git a/terraform/graph_builder_apply.go b/terraform/graph_builder_apply.go index b4e573142..0ca7cf543 100644 --- a/terraform/graph_builder_apply.go +++ b/terraform/graph_builder_apply.go @@ -87,6 +87,12 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { Config: b.Config, }, + // Add dynamic values + &RootVariableTransformer{Config: b.Config}, + &ModuleVariableTransformer{Config: b.Config}, + &LocalTransformer{Config: b.Config}, + &OutputTransformer{Config: b.Config}, + // Creates all the resource instances represented in the diff, along // with dependency edges against the whole-resource nodes added by // ConfigTransformer above. @@ -96,31 +102,19 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { Changes: b.Changes, }, + // Attach the state + &AttachStateTransformer{State: b.State}, + // Create orphan output nodes &OrphanOutputTransformer{Config: b.Config, State: b.State}, // Attach the configuration to any resources &AttachResourceConfigTransformer{Config: b.Config}, - // Attach the state - &AttachStateTransformer{State: b.State}, - // Provisioner-related transformations &MissingProvisionerTransformer{Provisioners: b.Components.ResourceProvisioners()}, &ProvisionerTransformer{}, - // Add root variables - &RootVariableTransformer{Config: b.Config}, - - // Add the local values - &LocalTransformer{Config: b.Config}, - - // Add the outputs - &OutputTransformer{Config: b.Config}, - - // Add module variables - &ModuleVariableTransformer{Config: b.Config}, - // add providers TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), @@ -150,7 +144,6 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { State: b.State, Schemas: b.Schemas, }, - &CBDEdgeTransformer{ Config: b.Config, State: b.State, diff --git a/terraform/graph_builder_destroy_plan.go b/terraform/graph_builder_destroy_plan.go index 94cc90d6c..cbd0c12fc 100644 --- a/terraform/graph_builder_destroy_plan.go +++ b/terraform/graph_builder_destroy_plan.go @@ -92,8 +92,13 @@ func (b *DestroyPlanGraphBuilder) Steps() []GraphTransformer { // created proper destroy ordering. &TargetsTransformer{Targets: b.Targets}, + // Close opened plugin connections + &CloseProviderTransformer{}, + // Close the root module &CloseRootModuleTransformer{}, + + &TransitiveReductionTransformer{}, } return steps diff --git a/terraform/graph_builder_eval.go b/terraform/graph_builder_eval.go index 31aaf0cfc..5c01f6fc9 100644 --- a/terraform/graph_builder_eval.go +++ b/terraform/graph_builder_eval.go @@ -60,28 +60,20 @@ func (b *EvalGraphBuilder) Steps() []GraphTransformer { // Creates all the data resources that aren't in the state. This will also // add any orphans from scaling in as destroy nodes. &ConfigTransformer{ - Concrete: nil, // just use the abstract type - Config: b.Config, - Unique: true, + Config: b.Config, }, - // Attach the state - &AttachStateTransformer{State: b.State}, + // Add dynamic values + &RootVariableTransformer{Config: b.Config}, + &ModuleVariableTransformer{Config: b.Config}, + &LocalTransformer{Config: b.Config}, + &OutputTransformer{Config: b.Config}, // Attach the configuration to any resources &AttachResourceConfigTransformer{Config: b.Config}, - // Add root variables - &RootVariableTransformer{Config: b.Config}, - - // Add the local values - &LocalTransformer{Config: b.Config}, - - // Add the outputs - &OutputTransformer{Config: b.Config}, - - // Add module variables - &ModuleVariableTransformer{Config: b.Config}, + // Attach the state + &AttachStateTransformer{State: b.State}, TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), @@ -92,9 +84,7 @@ func (b *EvalGraphBuilder) Steps() []GraphTransformer { // Create expansion nodes for all of the module calls. This must // come after all other transformers that create nodes representing // objects that can belong to modules. - &ModuleExpansionTransformer{ - Config: b.Config, - }, + &ModuleExpansionTransformer{Config: b.Config}, // Connect so that the references are ready for targeting. We'll // have to connect again later for providers and so on. diff --git a/terraform/graph_builder_import.go b/terraform/graph_builder_import.go index 872478119..8754f3ebc 100644 --- a/terraform/graph_builder_import.go +++ b/terraform/graph_builder_import.go @@ -55,26 +55,20 @@ func (b *ImportGraphBuilder) Steps() []GraphTransformer { // Create all our resources from the configuration and state &ConfigTransformer{Config: config}, + // Add dynamic values + &RootVariableTransformer{Config: b.Config}, + &ModuleVariableTransformer{Config: b.Config}, + &LocalTransformer{Config: b.Config}, + &OutputTransformer{Config: b.Config}, + // Attach the configuration to any resources &AttachResourceConfigTransformer{Config: b.Config}, // Add the import steps &ImportStateTransformer{Targets: b.ImportTargets, Config: b.Config}, - // Add root variables - &RootVariableTransformer{Config: b.Config}, - TransformProviders(b.Components.ResourceProviders(), concreteProvider, config), - // Add the local values - &LocalTransformer{Config: b.Config}, - - // Add the outputs - &OutputTransformer{Config: b.Config}, - - // Add module variables - &ModuleVariableTransformer{Config: b.Config}, - // Must attach schemas before ReferenceTransformer so that we can // analyze the configuration to find references. &AttachSchemaTransformer{Schemas: b.Schemas, Config: b.Config}, @@ -82,9 +76,7 @@ func (b *ImportGraphBuilder) Steps() []GraphTransformer { // Create expansion nodes for all of the module calls. This must // come after all other transformers that create nodes representing // objects that can belong to modules. - &ModuleExpansionTransformer{ - Config: b.Config, - }, + &ModuleExpansionTransformer{Config: b.Config}, // Connect so that the references are ready for targeting. We'll // have to connect again later for providers and so on. diff --git a/terraform/graph_builder_plan.go b/terraform/graph_builder_plan.go index d6bab3a6d..7e19e1343 100644 --- a/terraform/graph_builder_plan.go +++ b/terraform/graph_builder_plan.go @@ -84,10 +84,10 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { Config: b.Config, }, - // Add the local values + // Add dynamic values + &RootVariableTransformer{Config: b.Config}, + &ModuleVariableTransformer{Config: b.Config}, &LocalTransformer{Config: b.Config}, - - // Add the outputs &OutputTransformer{Config: b.Config}, // Add orphan resources @@ -106,29 +106,20 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { State: b.State, }, + // Attach the state + &AttachStateTransformer{State: b.State}, + // Create orphan output nodes - &OrphanOutputTransformer{ - Config: b.Config, - State: b.State, - }, + &OrphanOutputTransformer{Config: b.Config, State: b.State}, // Attach the configuration to any resources &AttachResourceConfigTransformer{Config: b.Config}, - // Attach the state - &AttachStateTransformer{State: b.State}, - - // Add root variables - &RootVariableTransformer{Config: b.Config}, - + // Provisioner-related transformations &MissingProvisionerTransformer{Provisioners: b.Components.ResourceProvisioners()}, &ProvisionerTransformer{}, - // Add module variables - &ModuleVariableTransformer{ - Config: b.Config, - }, - + // add providers TransformProviders(b.Components.ResourceProviders(), b.ConcreteProvider, b.Config), // Remove modules no longer present in the config @@ -141,10 +132,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { // Create expansion nodes for all of the module calls. This must // come after all other transformers that create nodes representing // objects that can belong to modules. - &ModuleExpansionTransformer{ - Concrete: b.ConcreteModule, - Config: b.Config, - }, + &ModuleExpansionTransformer{Concrete: b.ConcreteModule, Config: b.Config}, // Connect so that the references are ready for targeting. We'll // have to connect again later for providers and so on. @@ -156,9 +144,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { &attachDataResourceDependenciesTransformer{}, // Target - &TargetsTransformer{ - Targets: b.Targets, - }, + &TargetsTransformer{Targets: b.Targets}, // Detect when create_before_destroy must be forced on for a particular // node due to dependency edges, to avoid graph cycles during apply. @@ -171,7 +157,6 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { // Close opened plugin connections &CloseProviderTransformer{}, - &CloseProvisionerTransformer{}, // Close the root module &CloseRootModuleTransformer{}, diff --git a/terraform/transform_config.go b/terraform/transform_config.go index 95606dd71..35f448490 100644 --- a/terraform/transform_config.go +++ b/terraform/transform_config.go @@ -25,9 +25,6 @@ type ConfigTransformer struct { // Module is the module to add resources from. Config *configs.Config - // Unique will only add resources that aren't already present in the graph. - Unique bool - // Mode will only add resources that match the given mode ModeFilter bool Mode addrs.ResourceMode