| // Copyright IBM Corp. 2014, 2026 | |
| // SPDX-License-Identifier: BUSL-1.1 | |
| package command | |
| import ( | |
| "context" | |
| "os" | |
| "sync/atomic" | |
| "testing" | |
| "github.com/hashicorp/hcl/v2" | |
| "github.com/hashicorp/terraform/internal/policy" | |
| "github.com/hashicorp/terraform/internal/policy/proto" | |
| "github.com/hashicorp/terraform/internal/providers" | |
| "github.com/zclconf/go-cty/cty" | |
| ) | |
| // This tests that the apply policy diagnostics are reported. | |
| func TestApply_WithPolicyDiagnosticsJSON(t *testing.T) { | |
| td := t.TempDir() | |
| testCopyDir(t, testFixturePath("plan"), td) | |
| t.Chdir(td) | |
| policyCode := ` resource_policy "resource_type" "policy_name" { | |
| enforce_attrs { | |
| key = attr.value == "foo" | |
| } | |
| } | |
| ` | |
| if err := os.WriteFile("policy.hcl", []byte(policyCode), 0644); err != nil { | |
| t.Fatal(err) | |
| } | |
| p := planFixtureProvider() | |
| view, done := testView(t) | |
| overrides := metaOverridesForProvider(p) | |
| policyClient := policy.NewTestMockClient(t) | |
| overrides.PolicyClient = policyClient | |
| c := &ApplyCommand{ | |
| Meta: Meta{ | |
| testingOverrides: overrides, | |
| View: view, | |
| AllowExperimentalFeatures: true, | |
| }, | |
| } | |
| resp := policy.EvaluationFromProtoResponse( | |
| proto.EvaluateResult_DENY_EVALUATE_RESULT, []*proto.PolicyEvaluationDetail{ | |
| { | |
| Address: "resource_policy.foo", | |
| Result: proto.EvaluateResult_DENY_EVALUATE_RESULT, | |
| File: "policy_file.tfpolicy.hcl", | |
| PolicySetEnforcement: "mandatory", | |
| DefRange: &proto.Range{ | |
| Filename: "policy_file.tfpolicy.hcl", | |
| Start: &proto.Position{ | |
| Line: 1, | |
| Column: 1, | |
| }, | |
| End: &proto.Position{ | |
| Line: 2, | |
| Column: 4, | |
| }, | |
| }, | |
| EnforceResults: []*proto.EnforceBlockResult{ | |
| { | |
| Result: proto.EvaluateResult_DENY_EVALUATE_RESULT, | |
| BlockIndex: 1, | |
| Diagnostics: []*proto.Diagnostic{ | |
| { | |
| Severity: proto.Severity_ERROR, | |
| Summary: "policy denied", | |
| Result: &proto.DiagnosticResult{ | |
| Result: proto.EvaluateResult_DENY_EVALUATE_RESULT, | |
| }, | |
| Subject: &proto.Range{ | |
| Filename: "policy_file.tfpolicy.hcl", | |
| Start: &proto.Position{ | |
| Line: 1, | |
| Column: 1, | |
| }, | |
| End: &proto.Position{ | |
| Line: 2, | |
| Column: 4, | |
| }, | |
| }, | |
| Context: &proto.Range{ | |
| Filename: "policy_file.tfpolicy.hcl", | |
| Start: &proto.Position{ | |
| Line: 1, | |
| Column: 1, | |
| }, | |
| End: &proto.Position{ | |
| Line: 4, | |
| Column: 10, | |
| }, | |
| }, | |
| Snippet: &proto.Snippet{ | |
| Code: policyCode, | |
| StartLine: 1, | |
| HighlightStartOffset: 1, | |
| HighlightEndOffset: 100, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }) | |
| policyClient.EvaluateResponse = &resp | |
| // implicit allow, in a case where the evaluated provider matched no policy in the engine | |
| policyClient.EvaluateProviderResponse = &policy.EvaluationResponse{ | |
| Overall: policy.AllowResult, | |
| Policies: []*policy.Policy{{ | |
| Result: policy.AllowResult, | |
| PolicySetName: "some_policy_set", | |
| Address: "policy_name", | |
| Directory: "some/path/to", | |
| Filename: "provider_policy_file.tfpolicy.hcl", | |
| EnforcementLevel: "mandatory", | |
| Range: &hcl.Range{ | |
| Filename: "provider_policy_file.tfpolicy.hcl", | |
| Start: hcl.Pos{ | |
| Line: 1, | |
| Column: 1, | |
| }, | |
| End: hcl.Pos{ | |
| Line: 5, | |
| Column: 12, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| args := []string{"-policies", td} | |
| code := c.Run(append(args, "-no-color", "-json", "-auto-approve")) | |
| output := done(t) | |
| if code != 0 { | |
| t.Fatalf("bad: %d\n\n%s", code, output.Stdout()) | |
| } | |
| expected := `{"@level":"info","@message":"Terraform 1.16.0-dev","@module":"terraform.ui","terraform":"1.16.0-dev","type":"version","ui":"1.3"} | |
| {"@level":"info","@message":"data.test_data_source.a: Refreshing...","@module":"terraform.ui","hook":{"resource":{"addr":"data.test_data_source.a","module":"","resource":"data.test_data_source.a","implied_provider":"test","resource_type":"test_data_source","resource_name":"a","resource_key":null},"action":"read"},"type":"apply_start"} | |
| {"@level":"info","@message":"data.test_data_source.a: Refresh complete after 0s [id=zzzzz]","@module":"terraform.ui","hook":{"resource":{"addr":"data.test_data_source.a","module":"","resource":"data.test_data_source.a","implied_provider":"test","resource_type":"test_data_source","resource_name":"a","resource_key":null},"action":"read","id_key":"id","id_value":"zzzzz","elapsed_seconds":0},"type":"apply_complete"} | |
| {"@level":"info","@message":"test_instance.foo: Plan to create","@module":"terraform.ui","change":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"planned_change"} | |
| {"@level":"info","@message":"Plan: 1 to add, 0 to change, 0 to destroy.","@module":"terraform.ui","changes":{"add":1,"change":0,"import":0,"remove":0,"action_invocation":0,"action_fail":0,"operation":"plan"},"type":"change_summary"} | |
| {"@level":"info","@message":"test_instance.foo: Creating...","@module":"terraform.ui","hook":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"apply_start"} | |
| {"@level":"info","@message":"test_instance.foo: Creation complete after 0s","@module":"terraform.ui","hook":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create","elapsed_seconds":0},"type":"apply_complete"} | |
| {"@level":"error","@message":"Error: policy denied","@module":"terraform.ui","@policy":"true","policy_diagnostic":{"severity":"error","summary":"policy denied","detail":"","range":{"filename":"main.tf","start":{"line":1,"column":1,"byte":0},"end":{"line":1,"column":31,"byte":30}},"snippet":{"context":null,"code":"resource \"test_instance\" \"foo\" {","start_line":1,"highlight_start_offset":0,"highlight_end_offset":30,"values":[]},"policy_range":{"filename":"policy_file.tfpolicy.hcl","start":{"line":1,"column":1,"byte":0},"end":{"line":2,"column":4,"byte":0}},"policy_snippet":{"context":null,"code":"\t\tresource_policy \"resource_type\" \"policy_name\" {\n\t\t enforce_attrs {\n\t\t key = attr.value == \"foo\"\n\t\t }\n\t\t}\n\t","start_line":1,"highlight_start_offset":1,"highlight_end_offset":100,"values":null}},"policy_metadata":{"policy_set_path":"policy_file.tfpolicy.hcl","policy_name":"resource_policy.foo","file_name":"policy_file.tfpolicy.hcl","enforcement_level":"mandatory","enforce_index":1},"result":"DenyResult","target_address":"test_instance.foo","type":"policy_diagnostic"} | |
| {"@level":"info","@message":"Policy Result","@module":"terraform.ui","@policy":"true","policy_address":"resource_policy.foo","policy_metadata":{"policy_set_path":"policy_file.tfpolicy.hcl","policy_name":"resource_policy.foo","file_name":"policy_file.tfpolicy.hcl","enforcement_level":"mandatory"},"result":"DenyResult","target_address":"test_instance.foo","type":"policy_result"} | |
| {"@level":"info","@message":"Apply complete! Resources: 1 added, 0 changed, 0 destroyed.","@module":"terraform.ui","changes":{"add":1,"change":0,"import":0,"remove":0,"action_invocation":0,"action_fail":0,"operation":"apply"},"type":"change_summary"} | |
| {"@level":"info","@message":"Outputs: 0","@module":"terraform.ui","outputs":{},"type":"outputs"}` | |
| checkGoldenReferenceStr(t, output, expected) | |
| } | |
| func TestApply_PolicyResultsJSON_WithSavedPlan(t *testing.T) { | |
| td := t.TempDir() | |
| testCopyDir(t, testFixturePath("plan"), td) | |
| t.Chdir(td) | |
| policyCode := ` resource_policy "resource_type" "policy_name" { | |
| enforce_attrs { | |
| key = attr.value == "foo" | |
| } | |
| } | |
| ` | |
| if err := os.WriteFile("policy.hcl", []byte(policyCode), 0644); err != nil { | |
| t.Fatal(err) | |
| } | |
| p := planFixtureProvider() | |
| planView, planDone := testView(t) | |
| planOverrides := metaOverridesForProvider(p) | |
| planOverrides.PolicyClient = policy.NewTestMockClient(t) | |
| planCmd := &PlanCommand{ | |
| Meta: Meta{ | |
| testingOverrides: planOverrides, | |
| View: planView, | |
| AllowExperimentalFeatures: true, | |
| }, | |
| } | |
| if code := planCmd.Run([]string{"-policies", td, "-no-color", "-out=planfile"}); code != 0 { | |
| output := planDone(t) | |
| t.Fatalf("plan failed: %d\n\n%s", code, output.All()) | |
| } | |
| _ = planDone(t) | |
| applyView, applyDone := testView(t) | |
| applyOverrides := metaOverridesForProvider(p) | |
| applyPolicyClient := policy.NewTestMockClient(t) | |
| applyOverrides.PolicyClient = applyPolicyClient | |
| applyCmd := &ApplyCommand{ | |
| Meta: Meta{ | |
| testingOverrides: applyOverrides, | |
| View: applyView, | |
| AllowExperimentalFeatures: true, | |
| }, | |
| } | |
| resp := policy.EvaluationFromProtoResponse( | |
| proto.EvaluateResult_DENY_EVALUATE_RESULT, []*proto.PolicyEvaluationDetail{ | |
| { | |
| Address: "resource_policy.foo", | |
| Result: proto.EvaluateResult_DENY_EVALUATE_RESULT, | |
| File: "policy_file.tfpolicy.hcl", | |
| PolicySetEnforcement: "mandatory", | |
| DefRange: &proto.Range{ | |
| Filename: "policy_file.tfpolicy.hcl", | |
| Start: &proto.Position{ | |
| Line: 1, | |
| Column: 1, | |
| }, | |
| End: &proto.Position{ | |
| Line: 2, | |
| Column: 4, | |
| }, | |
| }, | |
| EnforceResults: []*proto.EnforceBlockResult{ | |
| { | |
| Result: proto.EvaluateResult_DENY_EVALUATE_RESULT, | |
| BlockIndex: 1, | |
| Diagnostics: []*proto.Diagnostic{ | |
| { | |
| Severity: proto.Severity_ERROR, | |
| Summary: "policy denied", | |
| Result: &proto.DiagnosticResult{ | |
| Result: proto.EvaluateResult_DENY_EVALUATE_RESULT, | |
| }, | |
| Subject: &proto.Range{ | |
| Filename: "policy_file.tfpolicy.hcl", | |
| Start: &proto.Position{ | |
| Line: 1, | |
| Column: 1, | |
| }, | |
| End: &proto.Position{ | |
| Line: 2, | |
| Column: 4, | |
| }, | |
| }, | |
| Context: &proto.Range{ | |
| Filename: "policy_file.tfpolicy.hcl", | |
| Start: &proto.Position{ | |
| Line: 1, | |
| Column: 1, | |
| }, | |
| End: &proto.Position{ | |
| Line: 4, | |
| Column: 10, | |
| }, | |
| }, | |
| Snippet: &proto.Snippet{ | |
| Code: policyCode, | |
| StartLine: 1, | |
| HighlightStartOffset: 1, | |
| HighlightEndOffset: 100, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }) | |
| applyPolicyClient.EvaluateResponse = &resp | |
| code := applyCmd.Run([]string{"-policies", td, "-no-color", "-json", "-auto-approve", "planfile"}) | |
| output := applyDone(t) | |
| if code != 0 { | |
| t.Fatalf("apply failed: %d\n\n%s", code, output.All()) | |
| } | |
| expected := `{"@level":"info","@message":"Terraform 1.16.0-dev","@module":"terraform.ui","terraform":"1.16.0-dev","type":"version","ui":"1.3"} | |
| {"@level":"info","@message":"test_instance.foo: Plan to create","@module":"terraform.ui","change":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"planned_change"} | |
| {"@level":"info","@message":"test_instance.foo: Creating...","@module":"terraform.ui","hook":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"apply_start"} | |
| {"@level":"info","@message":"test_instance.foo: Creation complete after 0s","@module":"terraform.ui","hook":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create","elapsed_seconds":0},"type":"apply_complete"} | |
| {"@level":"error","@message":"Error: policy denied","@module":"terraform.ui","@policy":"true","policy_diagnostic":{"severity":"error","summary":"policy denied","detail":"","range":{"filename":"main.tf","start":{"line":1,"column":1,"byte":0},"end":{"line":1,"column":31,"byte":30}},"snippet":{"context":null,"code":"resource \"test_instance\" \"foo\" {","start_line":1,"highlight_start_offset":0,"highlight_end_offset":30,"values":[]},"policy_range":{"filename":"policy_file.tfpolicy.hcl","start":{"line":1,"column":1,"byte":0},"end":{"line":2,"column":4,"byte":0}},"policy_snippet":{"context":null,"code":"\t\tresource_policy \"resource_type\" \"policy_name\" {\n\t\t enforce_attrs {\n\t\t key = attr.value == \"foo\"\n\t\t }\n\t\t}\n\t","start_line":1,"highlight_start_offset":1,"highlight_end_offset":100,"values":null}},"policy_metadata":{"policy_set_path":"policy_file.tfpolicy.hcl","policy_name":"resource_policy.foo","file_name":"policy_file.tfpolicy.hcl","enforcement_level":"mandatory","enforce_index":1},"result":"DenyResult","target_address":"test_instance.foo","type":"policy_diagnostic"} | |
| {"@level":"info","@message":"Policy Result","@module":"terraform.ui","@policy":"true","policy_address":"resource_policy.foo","policy_metadata":{"policy_set_path":"policy_file.tfpolicy.hcl","policy_name":"resource_policy.foo","file_name":"policy_file.tfpolicy.hcl","enforcement_level":"mandatory"},"result":"DenyResult","target_address":"test_instance.foo","type":"policy_result"} | |
| {"@level":"info","@message":"Apply complete! Resources: 1 added, 0 changed, 0 destroyed.","@module":"terraform.ui","changes":{"add":1,"change":0,"import":0,"remove":0,"action_invocation":0,"action_fail":0,"operation":"apply"},"type":"change_summary"} | |
| {"@level":"info","@message":"Outputs: 0","@module":"terraform.ui","outputs":{},"type":"outputs"}` | |
| checkGoldenReferenceStr(t, output, expected) | |
| } | |
| func TestApply_WithPolicyClientStopAfterApply(t *testing.T) { | |
| td := t.TempDir() | |
| testCopyDir(t, testFixturePath("plan"), td) | |
| t.Chdir(td) | |
| policyCode := ` resource_policy "resource_type" "policy_name" { | |
| enforce_attrs { | |
| key = attr.value == "foo" | |
| } | |
| } | |
| ` | |
| if err := os.WriteFile("policy.hcl", []byte(policyCode), 0644); err != nil { | |
| t.Fatal(err) | |
| } | |
| p := planFixtureProvider() | |
| view, done := testView(t) | |
| overrides := metaOverridesForProvider(p) | |
| policyClient := policy.NewTestMockClient(t) | |
| var stopCalled atomic.Bool | |
| var policyEvaluated atomic.Bool | |
| policyClient.StopFn = func() { | |
| stopCalled.Store(true) | |
| } | |
| policyClient.EvaluateFn = func(ctx context.Context, req policy.EvaluationRequest[*proto.PolicyEvaluateResourceRequest_ResourceMetadata]) policy.EvaluationResponse { | |
| policyEvaluated.Store(true) | |
| if stopCalled.Load() { | |
| t.Fatal("policy client Stop was called before apply finished") | |
| } | |
| return policy.EvaluationResponse{Overall: policy.AllowResult} | |
| } | |
| overrides.PolicyClient = policyClient | |
| c := &ApplyCommand{ | |
| Meta: Meta{ | |
| testingOverrides: overrides, | |
| View: view, | |
| AllowExperimentalFeatures: true, | |
| }, | |
| } | |
| args := []string{"-policies", td} | |
| code := c.Run(append(args, "-no-color", "-auto-approve")) | |
| output := done(t) | |
| if code != 0 { | |
| t.Fatalf("bad: %d\n\n%s", code, output.All()) | |
| } | |
| if !policyEvaluated.Load() { | |
| t.Fatal("expected policy evaluation to be called during apply") | |
| } | |
| if !stopCalled.Load() { | |
| t.Fatal("expected policy client Stop to be called after apply") | |
| } | |
| } | |
| // This tests that the plan policy diagnostic is superceded by the apply policy evaluation. | |
| func TestApply_WithPlanPolicyDiagnosticsJSON(t *testing.T) { | |
| td := t.TempDir() | |
| testCopyDir(t, testFixturePath("plan"), td) | |
| t.Chdir(td) | |
| policyCode := ` resource_policy "resource_type" "policy_name" { | |
| enforce_attrs { | |
| key = attr.value == "foo" | |
| } | |
| } | |
| ` | |
| if err := os.WriteFile("policy.hcl", []byte(policyCode), 0644); err != nil { | |
| t.Fatal(err) | |
| } | |
| p := planFixtureProvider() | |
| view, done := testView(t) | |
| overrides := metaOverridesForProvider(p) | |
| policyClient := policy.NewTestMockClient(t) | |
| overrides.PolicyClient = policyClient | |
| c := &ApplyCommand{ | |
| Meta: Meta{ | |
| testingOverrides: overrides, | |
| View: view, | |
| AllowExperimentalFeatures: true, | |
| }, | |
| } | |
| p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { | |
| s := req.ProposedNewState.AsValueMap() | |
| s["id"] = cty.UnknownVal(cty.String) | |
| resp.PlannedState = cty.ObjectVal(s) | |
| return | |
| } | |
| evalRespFn := func(result proto.EvaluateResult) policy.EvaluationResponse { | |
| detail := &proto.PolicyEvaluationDetail{ | |
| Address: "resource_policy.foo", | |
| Result: result, | |
| File: "policy_file.tfpolicy.hcl", | |
| PolicySetEnforcement: "mandatory", | |
| DefRange: &proto.Range{ | |
| Filename: "policy_file.tfpolicy.hcl", | |
| Start: &proto.Position{ | |
| Line: 1, | |
| Column: 1, | |
| }, | |
| End: &proto.Position{ | |
| Line: 2, | |
| Column: 4, | |
| }, | |
| }, | |
| EnforceResults: []*proto.EnforceBlockResult{{ | |
| Result: result, | |
| }}, | |
| } | |
| if result == proto.EvaluateResult_DENY_EVALUATE_RESULT { | |
| detail.EnforceResults[0].Diagnostics = []*proto.Diagnostic{ | |
| { | |
| Severity: proto.Severity_ERROR, | |
| Summary: "policy denied", | |
| Result: &proto.DiagnosticResult{ | |
| Result: result, | |
| }, | |
| Subject: &proto.Range{ | |
| Filename: "policy_file.tfpolicy.hcl", | |
| Start: &proto.Position{ | |
| Line: 1, | |
| Column: 1, | |
| }, | |
| End: &proto.Position{ | |
| Line: 2, | |
| Column: 4, | |
| }, | |
| }, | |
| Context: &proto.Range{ | |
| Filename: "policy_file.tfpolicy.hcl", | |
| Start: &proto.Position{ | |
| Line: 1, | |
| Column: 1, | |
| }, | |
| End: &proto.Position{ | |
| Line: 4, | |
| Column: 10, | |
| }, | |
| }, | |
| Snippet: &proto.Snippet{ | |
| Code: policyCode, | |
| StartLine: 1, | |
| HighlightStartOffset: 1, | |
| HighlightEndOffset: 100, | |
| }, | |
| }, | |
| } | |
| } | |
| return policy.EvaluationFromProtoResponse(result, []*proto.PolicyEvaluationDetail{detail}) | |
| } | |
| policyClient.EvaluateFn = func(ctx context.Context, er policy.EvaluationRequest[*proto.PolicyEvaluateResourceRequest_ResourceMetadata]) policy.EvaluationResponse { | |
| // This is what is returned during the post-plan policy evaluation | |
| if !er.Attrs.Raw.GetAttr("id").IsWhollyKnown() { | |
| return evalRespFn(proto.EvaluateResult_DENY_EVALUATE_RESULT) | |
| } | |
| // This is for the post-apply policy evaluation | |
| return evalRespFn(proto.EvaluateResult_ALLOW_EVALUATE_RESULT) | |
| } | |
| // implicit allow, in a case where the evaluated provider matched no policy in the engine | |
| policyClient.EvaluateProviderResponse = &policy.EvaluationResponse{ | |
| Overall: policy.AllowResult, | |
| Policies: []*policy.Policy{{ | |
| Result: policy.AllowResult, | |
| PolicySetName: "some_policy_set", | |
| Address: "policy_name", | |
| Directory: "some/path/to", | |
| Filename: "provider_policy_file.tfpolicy.hcl", | |
| EnforcementLevel: "mandatory", | |
| Range: &hcl.Range{ | |
| Filename: "provider_policy_file.tfpolicy.hcl", | |
| Start: hcl.Pos{ | |
| Line: 1, | |
| Column: 1, | |
| }, | |
| End: hcl.Pos{ | |
| Line: 5, | |
| Column: 12, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| args := []string{"-policies", td} | |
| code := c.Run(append(args, "-no-color", "-json", "-auto-approve")) | |
| output := done(t) | |
| if code != 0 { | |
| t.Fatalf("bad: %d\n\n%s", code, output.Stdout()) | |
| } | |
| // The resulting json only contains the policy result, because the object that | |
| // had a failed policy evaluation during the plan succeeded during apply. | |
| // This can occur when more references become known. | |
| expected := `{"@level":"info","@message":"Terraform 1.16.0-dev","@module":"terraform.ui","terraform":"1.16.0-dev","type":"version","ui":"1.3"} | |
| {"@level":"info","@message":"data.test_data_source.a: Refreshing...","@module":"terraform.ui","hook":{"resource":{"addr":"data.test_data_source.a","module":"","resource":"data.test_data_source.a","implied_provider":"test","resource_type":"test_data_source","resource_name":"a","resource_key":null},"action":"read"},"type":"apply_start"} | |
| {"@level":"info","@message":"data.test_data_source.a: Refresh complete after 0s [id=zzzzz]","@module":"terraform.ui","hook":{"resource":{"addr":"data.test_data_source.a","module":"","resource":"data.test_data_source.a","implied_provider":"test","resource_type":"test_data_source","resource_name":"a","resource_key":null},"action":"read","id_key":"id","id_value":"zzzzz","elapsed_seconds":0},"type":"apply_complete"} | |
| {"@level":"info","@message":"test_instance.foo: Plan to create","@module":"terraform.ui","change":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"planned_change"} | |
| {"@level":"info","@message":"Plan: 1 to add, 0 to change, 0 to destroy.","@module":"terraform.ui","changes":{"add":1,"change":0,"import":0,"remove":0,"action_invocation":0,"action_fail":0,"operation":"plan"},"type":"change_summary"} | |
| {"@level":"info","@message":"test_instance.foo: Creating...","@module":"terraform.ui","hook":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"apply_start"} | |
| {"@level":"info","@message":"test_instance.foo: Creation complete after 0s","@module":"terraform.ui","hook":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create","id_key":"id","elapsed_seconds":0},"type":"apply_complete"} | |
| {"@level":"info","@message":"Policy Result","@module":"terraform.ui","@policy":"true","policy_address":"resource_policy.foo","policy_metadata":{"policy_set_path":"policy_file.tfpolicy.hcl","policy_name":"resource_policy.foo","file_name":"policy_file.tfpolicy.hcl","enforcement_level":"mandatory"},"result":"AllowResult","target_address":"test_instance.foo","type":"policy_result"} | |
| {"@level":"info","@message":"Apply complete! Resources: 1 added, 0 changed, 0 destroyed.","@module":"terraform.ui","changes":{"add":1,"change":0,"import":0,"remove":0,"action_invocation":0,"action_fail":0,"operation":"apply"},"type":"change_summary"} | |
| {"@level":"info","@message":"Outputs: 0","@module":"terraform.ui","outputs":{},"type":"outputs"}` | |
| checkGoldenReferenceStr(t, output, expected) | |
| } |