diff --git a/internal/builtin/providers/terraform/provider.go b/internal/builtin/providers/terraform/provider.go index 525fa91629..300bd81d95 100644 --- a/internal/builtin/providers/terraform/provider.go +++ b/internal/builtin/providers/terraform/provider.go @@ -24,6 +24,7 @@ func NewProvider() providers.Interface { // GetSchema returns the complete schema for the provider. func (p *Provider) GetProviderSchema() providers.GetProviderSchemaResponse { resp := providers.GetProviderSchemaResponse{ + Provider: providers.Schema{}, ServerCapabilities: providers.ServerCapabilities{ MoveResourceState: true, }, diff --git a/internal/terraform/context_plan.go b/internal/terraform/context_plan.go index da15f9bc63..6ad958fd40 100644 --- a/internal/terraform/context_plan.go +++ b/internal/terraform/context_plan.go @@ -378,6 +378,7 @@ func (c *Context) checkApplyGraph(plan *plans.Plan, config *configs.Config, opts return nil } log.Println("[DEBUG] building apply graph to check for errors") + _, _, diags := c.applyGraph(plan, config, opts.ApplyOpts(), true) return diags } diff --git a/internal/terraform/graph_builder_plan.go b/internal/terraform/graph_builder_plan.go index a16ad908d3..3b6cfa8470 100644 --- a/internal/terraform/graph_builder_plan.go +++ b/internal/terraform/graph_builder_plan.go @@ -137,7 +137,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { &ConfigTransformer{ Concrete: b.ConcreteResource, Config: b.Config, - skip: b.Operation == walkDestroy || b.Operation == walkPlanDestroy, + destroy: b.Operation == walkDestroy || b.Operation == walkPlanDestroy, importTargets: b.ImportTargets, diff --git a/internal/terraform/transform_config.go b/internal/terraform/transform_config.go index 86678235cb..aa2ee0b74b 100644 --- a/internal/terraform/transform_config.go +++ b/internal/terraform/transform_config.go @@ -35,7 +35,8 @@ type ConfigTransformer struct { ModeFilter bool Mode addrs.ResourceMode - skip bool + // some actions are skipped during the destroy process + destroy bool // importTargets specifies a slice of addresses that will have state // imported for them. @@ -52,11 +53,6 @@ type ConfigTransformer struct { } func (t *ConfigTransformer) Transform(g *Graph) error { - // no import ops happen during destroy - if t.skip { - return nil - } - // If no configuration is available, we don't do anything if t.Config == nil { return nil @@ -97,12 +93,17 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) er log.Printf("[TRACE] ConfigTransformer: Starting for path: %v", path) var allResources []*configs.Resource - for _, r := range module.ManagedResources { - allResources = append(allResources, r) - } - for _, r := range module.DataResources { - allResources = append(allResources, r) + if !t.destroy { + for _, r := range module.ManagedResources { + allResources = append(allResources, r) + } + for _, r := range module.DataResources { + allResources = append(allResources, r) + } } + + // ephemeral resources act like temporary values and must be added to the + // graph even during destroy operations. for _, r := range module.EphemeralResources { allResources = append(allResources, r) } @@ -204,6 +205,9 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) er // validateImportTargets ensures that the import target module exists in the // configuration. Individual resources will be check by the validation node. func (t *ConfigTransformer) validateImportTargets() error { + if t.destroy { + return nil + } var diags tfdiags.Diagnostics for _, i := range t.importTargets { diff --git a/internal/terraform/transform_destroy_edge.go b/internal/terraform/transform_destroy_edge.go index 64c38aa0ff..6c8e201a9b 100644 --- a/internal/terraform/transform_destroy_edge.go +++ b/internal/terraform/transform_destroy_edge.go @@ -346,6 +346,16 @@ func (t *pruneUnusedNodesTransformer) Transform(g *Graph) error { } case graphNodeExpandsInstances: + // FIXME: Ephemeral resources have kind of broken this + // transformer. They work like temporary values in that they + // should not exist when there are no dependencies, but also + // share expansion nodes with all other resource modes. We + // can't use graphNodeTemporaryValue because then all + // resources will be caught by the previous case here. + if n, ok := n.(GraphNodeConfigResource); ok && n.ResourceAddr().Resource.Mode == addrs.EphemeralResourceMode { + return + } + // Any nodes that expand instances are kept when their // instances may need to be evaluated. for _, v := range g.UpEdges(n) {