/
githubmirror
/
terraform
Обзор
Документация
Войти
/
githubmirror
/
terraform
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
internal/command/meta_policy.go
179 строк
7 KB
Samsondeen
Prevent stacks policy evaluation for pre-destroy refresh (#38826)
08 июл 2026, 10:38
Не верифицирован
08 июл 2026, 10:38
71aa477
Код
Авторство
О чём код?
// Copyright IBM Corp. 2014, 2026 // SPDX-License-Identifier: BUSL-1.1 package command import ( "context" "fmt" "log" "os" "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/backend" "github.com/hashicorp/terraform/internal/command/views" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/initwd" "github.com/hashicorp/terraform/internal/policy" "github.com/hashicorp/terraform/internal/policy/proto" "github.com/hashicorp/terraform/internal/providercache" "github.com/hashicorp/terraform/internal/tfdiags" ) func (c *Meta) PolicyClient(ctx context.Context, policyPaths []string, ent *policy.Entitlement) (policy.Client, policy.Diagnostics, func()) { var client policy.Client closer := func() { if client != nil { client.Stop() } } if !c.AllowExperimentalFeatures { log.Printf("[DEBUG] Policies are not supported without experiments enabled, skipping policy client setup") return client, nil, closer } if len(policyPaths) == 0 { log.Printf("[DEBUG] No policy paths configured, skipping policy client setup") return client, nil, closer } // Use a pre-initialized client for tests if one is available if c.testingOverrides != nil { client = c.testingOverrides.PolicyClient if client != nil { return client, nil, closer } } var diags policy.Diagnostics client, diags = policy.NewPolicyClient(ctx, os.Getenv(policy.TerraformPolicyPluginEnvVar), policyPaths, ent) if diags.HasErrors() { return nil, diags, closer } log.Printf("[DEBUG] backend/operation/policy: Policy engine initialized") return client, diags, closer } var _ initwd.ModuleInstallHook = &policyModuleInstallHook{} // backendPolicyEntitlement returns the entitlement from the backend if it // implements policy.EntitlementProvider, or nil otherwise (e.g. a local // backend). func backendPolicyEntitlement(be backend.Backend) *policy.Entitlement { provider, ok := be.(policy.EntitlementProvider) if !ok { return nil } return provider.PolicyEntitlement() } // policyModuleInstallHook enables policy evaluation during module installation. type policyModuleInstallHook struct { initwd.ModuleInstallHookImpl client policy.Client rootModule *configs.Module view views.Init } // ModuleSourceResolved implements [initwd.ModuleInstallHook] and is called after a module source is resolved, and enables policy evaluation for the module before // it is installed. func (h *policyModuleInstallHook) ModuleSourceResolved(ctx context.Context, req *configs.ModuleRequest, version string) tfdiags.Diagnostics { // If the client is nil, then policy evaluation is disabled, so we can skip. if h.client == nil { return nil } log.Println("[DEBUG] init: evaluating policy for module", req.Path.String(), version) moduleAddr := req.Path.String() result := h.client.EvaluateModule(ctx, policy.EvaluationRequest[*proto.PolicyEvaluateModuleRequest_ModuleMetadata]{ // Configuration attributes may not be available during init, so we will send an unknown // dynamic value to the policy client. Attrs: policy.CtyToPolicyValue(cty.DynamicVal), Target: req.SourceAddr.String(), Meta: &proto.PolicyEvaluateModuleRequest_ModuleMetadata{ Address: moduleAddr, Version: version, }, }) var moduleCall *configs.ModuleCall if req.Parent == nil || req.Parent.Module == nil { log.Printf("[DEBUG] backend/operation/policy: No parent config for module %q. Diagnostics may not contain enough source context", moduleAddr) } else { moduleCall = req.Parent.Module.ModuleCalls[req.Name] } if moduleCall != nil { result = result.WithLocalRange(moduleCall.DeclRange.Ptr()) } h.view.PolicyResult(req.Path.String(), result) // Return a generic error here that the init command returns to the CLI. // The detailed policy diagnostics are included in the policy results // and will be formatted in the CLI output. Init uses diagnostics as the // blocking signal because advisory policies may return deny without any // error diagnostics. if result.Diagnostics.HasErrors() { return tfdiags.Diagnostics{ policy.NewErrorDiagnostic( "Policy evaluation failed", "Module download blocked due to policy violations. Please review other diagnostics for details.", policy.SetupErrorResult, ), } } return nil } var _ providercache.InstallerHook = &providerPolicyHook{} // providerPolicyHook enables policy evaluation during provider installation. type providerPolicyHook struct { client policy.Client rootModule *configs.Module view views.Init } // ProviderVersionSelected satisfies the [providers.InstallerHook] interface. // When a provider version is selected, this method performs policy evaluation for the provider, // and aborts the installation if the policy evaluation fails. func (p *providerPolicyHook) ProviderVersionSelected(ctx context.Context, provider addrs.Provider, version string) error { // If the client is nil, then policy evaluation is disabled, so we can skip. if p.client == nil { return nil } log.Println("[DEBUG] init: evaluating policy for provider", provider.String(), version) result := p.client.EvaluateProvider(ctx, policy.EvaluationRequest[*proto.PolicyEvaluateProviderRequest_ProviderMetadata]{ Target: provider.Type, // Configuration attributes may not be available during init, so we will send an unknown // dynamic value to the policy client. Attrs: policy.CtyToPolicyValue(cty.DynamicVal), Meta: &proto.PolicyEvaluateProviderRequest_ProviderMetadata{ Name: provider.Type, Namespace: provider.Namespace, Source: provider.String(), Version: version, }, }) // We use the root module as the module for provider configs since the version resolution // is ambiguous, and we do not know which module the provider config belongs to. addr := addrs.AbsProviderConfig{Provider: provider, Module: addrs.RootModule} providerConfig := p.rootModule.ProviderConfigs[provider.Type] if providerConfig != nil { // Annotate the result diagnostics with the local range so that diagnostics can be rendered with both the // policy source and the object being enforced. result = result.WithLocalRange(providerConfig.DeclRange.Ptr()) } p.view.PolicyResult(addr.String(), result) log.Println("[DEBUG] init: policy result for provider", provider.String(), version, "overall", result.Overall) // Init uses diagnostics as the blocking signal because advisory policies // may return deny without any error diagnostics. if result.Diagnostics.HasErrors() { return fmt.Errorf("Provider download blocked due to policy violations. Please review other diagnostics for details.") } return nil }