Skip to content

Commit c7ef162

Browse files
stacks: include plan and state providers when validating external providers (#35877)
Co-authored-by: Daniel Schmidt <[email protected]>
1 parent 2e4a62d commit c7ef162

File tree

4 files changed

+74
-6
lines changed

4 files changed

+74
-6
lines changed

internal/terraform/context_apply.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ func (c *Context) ApplyAndEval(plan *plans.Plan, config *configs.Config, opts *A
162162
return nil, nil, diags
163163
}
164164

165-
moreDiags = checkExternalProviders(config, opts.ExternalProviders)
165+
moreDiags = checkExternalProviders(config, plan, nil, opts.ExternalProviders)
166166
diags = diags.Append(moreDiags)
167167
if moreDiags.HasErrors() {
168168
return nil, nil, diags

internal/terraform/context_plan.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ func (c *Context) PlanAndEval(config *configs.Config, prevRunState *states.State
197197
return nil, nil, diags
198198
}
199199

200-
providerCfgDiags := checkExternalProviders(config, opts.ExternalProviders)
200+
providerCfgDiags := checkExternalProviders(config, nil, prevRunState, opts.ExternalProviders)
201201
diags = diags.Append(providerCfgDiags)
202202
if providerCfgDiags.HasErrors() {
203203
return nil, nil, diags

internal/terraform/context_plan2_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4579,6 +4579,62 @@ resource "test_object" "a" {
45794579
}
45804580
}
45814581

4582+
func TestContext2Plan_externalProvidersWithState(t *testing.T) {
4583+
// In this test we're going to use an external provider for a resource
4584+
// that is already in the state. Terraform should allow this, even though
4585+
// the provider isn't defined in the configuration.
4586+
4587+
m := testModuleInline(t, map[string]string{
4588+
"main.tf": ``, // no resources
4589+
})
4590+
4591+
state := states.BuildState(func(state *states.SyncState) {
4592+
state.SetResourceInstanceCurrent(mustResourceInstanceAddr("foo.a"), &states.ResourceInstanceObjectSrc{
4593+
AttrsJSON: []byte(`{}`),
4594+
Status: states.ObjectReady,
4595+
}, mustProviderConfig(`provider["hashicorp/foo"]`))
4596+
})
4597+
4598+
fooProvider := &testing_provider.MockProvider{
4599+
GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{
4600+
ResourceTypes: map[string]providers.Schema{
4601+
"foo": {
4602+
Block: &configschema.Block{},
4603+
},
4604+
},
4605+
},
4606+
ConfigureProviderFn: func(providers.ConfigureProviderRequest) providers.ConfigureProviderResponse {
4607+
return providers.ConfigureProviderResponse{
4608+
Diagnostics: tfdiags.Diagnostics{
4609+
tfdiags.Sourceless(
4610+
tfdiags.Error,
4611+
"Pre-configured provider was reconfigured by the modules runtime",
4612+
"An externally-configured provider should not have its ConfigureProvider function called during planning.",
4613+
),
4614+
},
4615+
}
4616+
},
4617+
}
4618+
4619+
ctx, diags := NewContext(&ContextOpts{
4620+
PreloadedProviderSchemas: map[addrs.Provider]providers.GetProviderSchemaResponse{
4621+
addrs.MustParseProviderSourceString("hashicorp/foo"): *fooProvider.GetProviderSchemaResponse,
4622+
},
4623+
})
4624+
assertNoDiagnostics(t, diags)
4625+
4626+
fooProvider.ConfigureProviderCalled = true
4627+
_, diags = ctx.Plan(m, state, &PlanOpts{
4628+
Mode: plans.NormalMode,
4629+
ExternalProviders: map[addrs.RootProviderConfig]providers.Interface{
4630+
addrs.RootProviderConfig{
4631+
Provider: addrs.MustParseProviderSourceString("hashicorp/foo"),
4632+
}: fooProvider,
4633+
},
4634+
})
4635+
assertNoDiagnostics(t, diags)
4636+
}
4637+
45824638
func TestContext2Plan_externalProviders(t *testing.T) {
45834639
// This test exercises the option for callers to pass in their own
45844640
// already-configured provider instances, instead of the modules runtime

internal/terraform/providers.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import (
88

99
"github.com/hashicorp/terraform/internal/addrs"
1010
"github.com/hashicorp/terraform/internal/configs"
11+
"github.com/hashicorp/terraform/internal/plans"
1112
"github.com/hashicorp/terraform/internal/providers"
13+
"github.com/hashicorp/terraform/internal/states"
1214
"github.com/hashicorp/terraform/internal/tfdiags"
1315
)
1416

@@ -24,12 +26,22 @@ import (
2426
// here are somewhat vague to accommodate being used both to describe
2527
// an invalid component configuration and the problem of trying to plan and
2628
// apply a module that wasn't intended to be a root module.
27-
func checkExternalProviders(rootCfg *configs.Config, got map[addrs.RootProviderConfig]providers.Interface) tfdiags.Diagnostics {
29+
func checkExternalProviders(rootCfg *configs.Config, plan *plans.Plan, state *states.State, got map[addrs.RootProviderConfig]providers.Interface) tfdiags.Diagnostics {
2830
var diags tfdiags.Diagnostics
2931

30-
allowedProviders := map[addrs.Provider]struct{}{}
32+
allowedProviders := make(map[addrs.Provider]bool)
3133
for _, addr := range rootCfg.ProviderTypes() {
32-
allowedProviders[addr] = struct{}{}
34+
allowedProviders[addr] = true
35+
}
36+
if state != nil {
37+
for _, addr := range state.ProviderAddrs() {
38+
allowedProviders[addr.Provider] = true
39+
}
40+
}
41+
if plan != nil {
42+
for _, addr := range plan.ProviderAddrs() {
43+
allowedProviders[addr.Provider] = true
44+
}
3345
}
3446
requiredConfigs := rootCfg.EffectiveRequiredProviderConfigs().Keys()
3547

@@ -39,7 +51,7 @@ func checkExternalProviders(rootCfg *configs.Config, got map[addrs.RootProviderC
3951
// we can't be precise because Terraform permits implicit default provider
4052
// configurations.)
4153
for cfgAddr := range got {
42-
if _, allowed := allowedProviders[cfgAddr.Provider]; !allowed {
54+
if !allowedProviders[cfgAddr.Provider] {
4355
diags = diags.Append(tfdiags.Sourceless(
4456
tfdiags.Error,
4557
"Unexpected provider configuration",

0 commit comments

Comments
 (0)