diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index 4c9ec48f6..656ac9438 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -9099,3 +9099,117 @@ func TestContext2Apply_destroyWithProviders(t *testing.T) { t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want) } } + +func TestContext2Apply_providersFromState(t *testing.T) { + m := module.NewEmptyTree() + p := testProvider("aws") + p.DiffFn = testDiffFn + + for _, tc := range []struct { + name string + state *State + output string + err bool + }{ + { + name: "add implicit provider", + state: &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: []string{"root"}, + Resources: map[string]*ResourceState{ + "aws_instance.a": &ResourceState{ + Type: "aws_instance", + Primary: &InstanceState{ + ID: "bar", + }, + Provider: "provider.aws", + }, + }, + }, + }, + }, + err: false, + output: "", + }, + + // an aliased provider must be in the config to remove a resource + { + name: "add aliased provider", + state: &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: []string{"root"}, + Resources: map[string]*ResourceState{ + "aws_instance.a": &ResourceState{ + Type: "aws_instance", + Primary: &InstanceState{ + ID: "bar", + }, + Provider: "provider.aws.bar", + }, + }, + }, + }, + }, + err: true, + }, + + // a provider in a module implies some sort of config, so this isn't + // allowed even without an alias + { + name: "add unaliased module provider", + state: &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: []string{"root", "child"}, + Resources: map[string]*ResourceState{ + "aws_instance.a": &ResourceState{ + Type: "aws_instance", + Primary: &InstanceState{ + ID: "bar", + }, + Provider: "module.child.provider.aws", + }, + }, + }, + }, + }, + err: true, + }, + } { + + t.Run(tc.name, func(t *testing.T) { + ctx := testContext2(t, &ContextOpts{ + Module: m, + ProviderResolver: ResourceProviderResolverFixed( + map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + ), + State: tc.state, + }) + + _, err := ctx.Plan() + if tc.err { + if err == nil { + t.Fatal("expected error") + } else { + return + } + } + if !tc.err && err != nil { + t.Fatal(err) + } + + state, err := ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + checkStateString(t, state, "") + + }) + } + +} diff --git a/terraform/transform_provider.go b/terraform/transform_provider.go index 31aeb3ca5..e95864210 100644 --- a/terraform/transform_provider.go +++ b/terraform/transform_provider.go @@ -193,6 +193,11 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error { } p := pv.ProvidedBy() + // this may be the resolved provider from the state, so we need to get + // the base provider name. + parts := strings.SplitAfter(p, "provider.") + p = parts[len(parts)-1] + key := ResolveProviderName(p, nil) provider := m[key] @@ -203,10 +208,12 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error { // we don't implicitly create aliased providers if strings.Contains(p, ".") { - log.Println("[DEBUG] not adding missing provider alias", p) + log.Println("[DEBUG] not adding missing provider alias:", p) continue } + log.Println("[DEBUG] adding missing provider:", p) + // create the misisng top-level provider provider = t.Concrete(&NodeAbstractProvider{ NameValue: p,