Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions internal/command/jsonformat/structured/change.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,11 @@ func FromJsonViewsOutput(output viewsjson.Output) Change {

func FromJsonActionInvocation(actionInvocation jsonplan.ActionInvocation) Change {
return Change{
Before: unwrapAttributeValues(actionInvocation.ConfigValues),
After: unwrapAttributeValues(actionInvocation.ConfigValues),
Unknown: false,
Before: unwrapAttributeValues(actionInvocation.ConfigValues),
After: unwrapAttributeValues(actionInvocation.ConfigValues),
Unknown: false,
BeforeSensitive: unmarshalGeneric(actionInvocation.ConfigSensitive),
AfterSensitive: unmarshalGeneric(actionInvocation.ConfigSensitive),

ReplacePaths: attribute_path.Empty(false),
RelevantAttributes: attribute_path.AlwaysMatcher(),
Expand Down
29 changes: 16 additions & 13 deletions internal/command/jsonplan/action_invocations.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"sort"

"github.com/hashicorp/terraform/internal/command/jsonstate"
"github.com/hashicorp/terraform/internal/lang/marks"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/providers"
Expand All @@ -25,7 +26,8 @@ type ActionInvocation struct {
Name string `json:"name,omitempty"`

// ConfigValues is the JSON representation of the values in the config block of the action
ConfigValues map[string]json.RawMessage `json:"config_values,omitempty"`
ConfigValues map[string]json.RawMessage `json:"config_values,omitempty"`
ConfigSensitive json.RawMessage `json:"config_sensitive,omitempty"`

// ProviderName allows the property "type" to be interpreted unambiguously
// in the unusual situation where a provider offers a type whose
Expand Down Expand Up @@ -85,7 +87,7 @@ func marshalConfigValues(value cty.Value) map[string]json.RawMessage {
}

ret := make(map[string]json.RawMessage)
it := value.ElementIterator()
it := v.ElementIterator()
for it.Next() {
k, v := it.Element()
vJSON, _ := ctyjson.Marshal(v, v.Type())
Expand All @@ -99,7 +101,7 @@ func MarshalActionInvocations(actions []*plans.ActionInvocationInstanceSrc, sche
for _, action := range actions {
ai, err := MarshalActionInvocation(action, schemas)
if err != nil {
return nil, err
return ret, fmt.Errorf("failed to decode action %s: %w", action.Addr, err)
}
ret = append(ret, ai)
}
Expand All @@ -113,7 +115,6 @@ func MarshalActionInvocation(action *plans.ActionInvocationInstanceSrc, schemas
Name: action.Addr.Action.Action.Name,
ProviderName: action.ProviderAddr.Provider.String(),
}

schema := schemas.ActionTypeConfig(
action.ProviderAddr.Provider,
action.Addr.Action.Action.Type,
Expand All @@ -140,12 +141,8 @@ func MarshalActionInvocation(action *plans.ActionInvocationInstanceSrc, schemas
}

if actionDec.ConfigValue != cty.NilVal {
// TODO: Support sensitive and ephemeral values in action invocations.
_, pvms := actionDec.ConfigValue.UnmarkDeepWithPaths()
sensitivePaths, otherMarks := marks.PathsWithMark(pvms, marks.Sensitive)
if len(sensitivePaths) > 0 {
return ai, fmt.Errorf("action %s has sensitive config values, which are not supported in action invocations", action.Addr)
}
ephemeralPaths, otherMarks := marks.PathsWithMark(otherMarks, marks.Ephemeral)
if len(ephemeralPaths) > 0 {
return ai, fmt.Errorf("action %s has ephemeral config values, which are not supported in action invocations", action.Addr)
Expand All @@ -154,12 +151,18 @@ func MarshalActionInvocation(action *plans.ActionInvocationInstanceSrc, schemas
return ai, fmt.Errorf("action %s has config values with unsupported marks: %v", action.Addr, otherMarks)
}

if actionDec.ConfigValue.IsWhollyKnown() {
ai.ConfigValues = marshalConfigValues(actionDec.ConfigValue)
} else {
knowns := omitUnknowns(actionDec.ConfigValue)
ai.ConfigValues = marshalConfigValues(knowns)
configValue := actionDec.ConfigValue
if !configValue.IsWhollyKnown() {
configValue = omitUnknowns(actionDec.ConfigValue)
}
cs := jsonstate.SensitiveAsBool(marks.MarkPaths(configValue, marks.Sensitive, sensitivePaths))
configSensitive, err := ctyjson.Marshal(cs, cs.Type())
if err != nil {
return ai, err
}

ai.ConfigValues = marshalConfigValues(configValue)
ai.ConfigSensitive = configSensitive
}
return ai, nil
}
Expand Down
13 changes: 12 additions & 1 deletion internal/plans/action_invocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
package plans

import (
"fmt"

"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/lang/marks"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/tfdiags"
"github.com/zclconf/go-cty/cty"
)

Expand Down Expand Up @@ -109,8 +113,15 @@ func (ai *ActionInvocationInstance) Encode(schema *providers.ActionSchema) (*Act
ty = schema.ConfigSchema.ImpliedType()
}

unmarkedConfigValue, pvms := ai.ConfigValue.UnmarkDeepWithPaths()
sensitivePaths, otherMarks := marks.PathsWithMark(pvms, marks.Sensitive)
if len(otherMarks) > 0 {
return nil, fmt.Errorf("%s: error serializing action invocation with unexpected marks on config value: %#v. This is a bug in Terraform.", tfdiags.FormatCtyPath(otherMarks[0].Path), otherMarks[0].Marks)
}

var err error
ret.ConfigValue, err = NewDynamicValue(ai.ConfigValue, ty)
ret.ConfigValue, err = NewDynamicValue(unmarkedConfigValue, ty)
ret.SensitiveConfigPaths = sensitivePaths
if err != nil {
return nil, err
}
Expand Down
6 changes: 4 additions & 2 deletions internal/plans/changes_src.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,8 @@ type ActionInvocationInstanceSrc struct {
Addr addrs.AbsActionInstance
ActionTrigger ActionTrigger

ConfigValue DynamicValue
ConfigValue DynamicValue
SensitiveConfigPaths []cty.Path

ProviderAddr addrs.AbsProviderConfig
}
Expand All @@ -584,12 +585,13 @@ func (acs *ActionInvocationInstanceSrc) Decode(schema *providers.ActionSchema) (
if err != nil {
return nil, fmt.Errorf("error decoding 'config' value: %s", err)
}
markedConfigValue := marks.MarkPaths(config, marks.Sensitive, acs.SensitiveConfigPaths)

ai := &ActionInvocationInstance{
Addr: acs.Addr,
ActionTrigger: acs.ActionTrigger,
ProviderAddr: acs.ProviderAddr,
ConfigValue: config,
ConfigValue: markedConfigValue,
}
return ai, nil
}
Expand Down
10 changes: 10 additions & 0 deletions internal/plans/planfile/tfplan.go
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,11 @@ func actionInvocationFromTfplan(rawAction *planproto.ActionInvocationInstance) (
return nil, fmt.Errorf("invalid config value: %s", err)
}
ret.ConfigValue = configVal
sensitiveConfigPaths, err := pathsFromTfplan(rawAction.SensitiveConfigPaths)
if err != nil {
return nil, err
}
ret.SensitiveConfigPaths = sensitiveConfigPaths
}

return ret, nil
Expand Down Expand Up @@ -1412,6 +1417,11 @@ func actionInvocationToTfPlan(action *plans.ActionInvocationInstanceSrc) (*planp

if action.ConfigValue != nil {
ret.ConfigValue = valueToTfplan(action.ConfigValue)
sensitiveConfigPaths, err := pathsToTfplan(action.SensitiveConfigPaths)
if err != nil {
return nil, err
}
ret.SensitiveConfigPaths = sensitiveConfigPaths
}

return ret, nil
Expand Down
18 changes: 18 additions & 0 deletions internal/plans/planfile/tfplan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,24 @@ func examplePlanForTest(t *testing.T) *plans.Plan {
"id": cty.StringVal("testing"),
}), objTy),
},
{
Addr: addrs.Action{Type: "example", Name: "baz"}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
ProviderAddr: provider,
ActionTrigger: plans.LifecycleActionTrigger{
ActionTriggerEvent: configs.BeforeCreate,
ActionTriggerBlockIndex: 2,
ActionsListIndex: 1,
TriggeringResourceAddr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_thing",
Name: "woot",
}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
},
ConfigValue: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("secret"),
}), objTy),
SensitiveConfigPaths: []cty.Path{cty.GetAttrPath("id")},
},
},
},
DriftedResources: []*plans.ResourceInstanceChangeSrc{
Expand Down
45 changes: 29 additions & 16 deletions internal/plans/planproto/planfile.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion internal/plans/planproto/planfile.proto
Original file line number Diff line number Diff line change
Expand Up @@ -468,9 +468,13 @@ message ActionInvocationInstance {

repeated ResourceInstanceActionChange linked_resources = 3;
DynamicValue config_value = 4;
// An unordered set of paths into config_value which are marked as
// sensitive. Values at these paths should be obscured in human-readable
// output.
repeated Path sensitive_config_paths = 5;

oneof action_trigger {
LifecycleActionTrigger lifecycle_action_trigger = 5;
LifecycleActionTrigger lifecycle_action_trigger = 6;
}
}

Expand Down
1 change: 0 additions & 1 deletion internal/terraform/context_apply_action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,6 @@ resource "test_object" "b" {
},

"action with secrets in configuration": {
toBeImplemented: true, // We currently don't suppport sensitive values in the plan
module: map[string]string{
"main.tf": `
variable "secret_value" {
Expand Down
Loading