Skip to content

Commit 871d3ce

Browse files
support sensitive values in action configuration
1 parent c4f129f commit 871d3ce

File tree

11 files changed

+180
-50
lines changed

11 files changed

+180
-50
lines changed

internal/command/jsonformat/structured/change.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,11 @@ func FromJsonViewsOutput(output viewsjson.Output) Change {
187187

188188
func FromJsonActionInvocation(actionInvocation jsonplan.ActionInvocation) Change {
189189
return Change{
190-
Before: unwrapAttributeValues(actionInvocation.ConfigValues),
191-
After: unwrapAttributeValues(actionInvocation.ConfigValues),
192-
Unknown: false,
190+
Before: unwrapAttributeValues(actionInvocation.ConfigValues),
191+
After: unwrapAttributeValues(actionInvocation.ConfigValues),
192+
Unknown: false,
193+
BeforeSensitive: unmarshalGeneric(actionInvocation.ConfigSensitive),
194+
AfterSensitive: unmarshalGeneric(actionInvocation.ConfigSensitive),
193195

194196
ReplacePaths: attribute_path.Empty(false),
195197
RelevantAttributes: attribute_path.AlwaysMatcher(),

internal/command/jsonplan/action_invocations.go

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"sort"
1010

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

2728
// ConfigValues is the JSON representation of the values in the config block of the action
28-
ConfigValues map[string]json.RawMessage `json:"config_values,omitempty"`
29+
ConfigValues map[string]json.RawMessage `json:"config_values,omitempty"`
30+
ConfigSensitive json.RawMessage `json:"config_sensitive,omitempty"`
2931

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

8789
ret := make(map[string]json.RawMessage)
88-
it := value.ElementIterator()
90+
it := v.ElementIterator()
8991
for it.Next() {
9092
k, v := it.Element()
9193
vJSON, _ := ctyjson.Marshal(v, v.Type())
@@ -99,7 +101,7 @@ func MarshalActionInvocations(actions []*plans.ActionInvocationInstanceSrc, sche
99101
for _, action := range actions {
100102
ai, err := MarshalActionInvocation(action, schemas)
101103
if err != nil {
102-
return nil, err
104+
return ret, fmt.Errorf("failed to decode action %s: %w", action.Addr, err)
103105
}
104106
ret = append(ret, ai)
105107
}
@@ -113,7 +115,6 @@ func MarshalActionInvocation(action *plans.ActionInvocationInstanceSrc, schemas
113115
Name: action.Addr.Action.Action.Name,
114116
ProviderName: action.ProviderAddr.Provider.String(),
115117
}
116-
117118
schema := schemas.ActionTypeConfig(
118119
action.ProviderAddr.Provider,
119120
action.Addr.Action.Action.Type,
@@ -140,12 +141,8 @@ func MarshalActionInvocation(action *plans.ActionInvocationInstanceSrc, schemas
140141
}
141142

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

157-
if actionDec.ConfigValue.IsWhollyKnown() {
158-
ai.ConfigValues = marshalConfigValues(actionDec.ConfigValue)
159-
} else {
160-
knowns := omitUnknowns(actionDec.ConfigValue)
161-
ai.ConfigValues = marshalConfigValues(knowns)
154+
configValue := actionDec.ConfigValue
155+
if !configValue.IsWhollyKnown() {
156+
configValue = omitUnknowns(actionDec.ConfigValue)
162157
}
158+
cs := jsonstate.SensitiveAsBool(marks.MarkPaths(configValue, marks.Sensitive, sensitivePaths))
159+
configSensitive, err := ctyjson.Marshal(cs, cs.Type())
160+
if err != nil {
161+
return ai, err
162+
}
163+
164+
ai.ConfigValues = marshalConfigValues(configValue)
165+
ai.ConfigSensitive = configSensitive
163166
}
164167
return ai, nil
165168
}

internal/plans/action_invocation.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44
package plans
55

66
import (
7+
"fmt"
8+
79
"github.com/hashicorp/terraform/internal/addrs"
810
"github.com/hashicorp/terraform/internal/configs"
11+
"github.com/hashicorp/terraform/internal/lang/marks"
912
"github.com/hashicorp/terraform/internal/providers"
13+
"github.com/hashicorp/terraform/internal/tfdiags"
1014
"github.com/zclconf/go-cty/cty"
1115
)
1216

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

116+
unmarkedConfigValue, pvms := ai.ConfigValue.UnmarkDeepWithPaths()
117+
sensitivePaths, otherMarks := marks.PathsWithMark(pvms, marks.Sensitive)
118+
if len(otherMarks) > 0 {
119+
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)
120+
}
121+
112122
var err error
113-
ret.ConfigValue, err = NewDynamicValue(ai.ConfigValue, ty)
123+
ret.ConfigValue, err = NewDynamicValue(unmarkedConfigValue, ty)
124+
ret.SensitiveConfigPaths = sensitivePaths
114125
if err != nil {
115126
return nil, err
116127
}

internal/plans/changes_src.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,8 @@ type ActionInvocationInstanceSrc struct {
568568
Addr addrs.AbsActionInstance
569569
ActionTrigger ActionTrigger
570570

571-
ConfigValue DynamicValue
571+
ConfigValue DynamicValue
572+
SensitiveConfigPaths []cty.Path
572573

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

588590
ai := &ActionInvocationInstance{
589591
Addr: acs.Addr,
590592
ActionTrigger: acs.ActionTrigger,
591593
ProviderAddr: acs.ProviderAddr,
592-
ConfigValue: config,
594+
ConfigValue: markedConfigValue,
593595
}
594596
return ai, nil
595597
}

internal/plans/planfile/tfplan.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,6 +1365,11 @@ func actionInvocationFromTfplan(rawAction *planproto.ActionInvocationInstance) (
13651365
return nil, fmt.Errorf("invalid config value: %s", err)
13661366
}
13671367
ret.ConfigValue = configVal
1368+
sensitiveConfigPaths, err := pathsFromTfplan(rawAction.SensitiveConfigPaths)
1369+
if err != nil {
1370+
return nil, err
1371+
}
1372+
ret.SensitiveConfigPaths = sensitiveConfigPaths
13681373
}
13691374

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

14131418
if action.ConfigValue != nil {
14141419
ret.ConfigValue = valueToTfplan(action.ConfigValue)
1420+
sensitiveConfigPaths, err := pathsToTfplan(action.SensitiveConfigPaths)
1421+
if err != nil {
1422+
return nil, err
1423+
}
1424+
ret.SensitiveConfigPaths = sensitiveConfigPaths
14151425
}
14161426

14171427
return ret, nil

internal/plans/planfile/tfplan_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,24 @@ func examplePlanForTest(t *testing.T) *plans.Plan {
335335
"id": cty.StringVal("testing"),
336336
}), objTy),
337337
},
338+
{
339+
Addr: addrs.Action{Type: "example", Name: "baz"}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
340+
ProviderAddr: provider,
341+
ActionTrigger: plans.LifecycleActionTrigger{
342+
ActionTriggerEvent: configs.BeforeCreate,
343+
ActionTriggerBlockIndex: 2,
344+
ActionsListIndex: 1,
345+
TriggeringResourceAddr: addrs.Resource{
346+
Mode: addrs.ManagedResourceMode,
347+
Type: "test_thing",
348+
Name: "woot",
349+
}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
350+
},
351+
ConfigValue: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
352+
"id": cty.StringVal("secret"),
353+
}), objTy),
354+
SensitiveConfigPaths: []cty.Path{cty.GetAttrPath("id")},
355+
},
338356
},
339357
},
340358
DriftedResources: []*plans.ResourceInstanceChangeSrc{

internal/plans/planproto/planfile.pb.go

Lines changed: 29 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/plans/planproto/planfile.proto

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,9 +468,13 @@ message ActionInvocationInstance {
468468

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

472476
oneof action_trigger {
473-
LifecycleActionTrigger lifecycle_action_trigger = 5;
477+
LifecycleActionTrigger lifecycle_action_trigger = 6;
474478
}
475479
}
476480

internal/terraform/context_apply_action_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,6 @@ resource "test_object" "b" {
556556
},
557557

558558
"action with secrets in configuration": {
559-
toBeImplemented: true, // We currently don't suppport sensitive values in the plan
560559
module: map[string]string{
561560
"main.tf": `
562561
variable "secret_value" {

0 commit comments

Comments
 (0)