Open justinvp opened 3 years ago
From the logs, these are the input properties passed to the AWS provider for bucket-with-nested-unknown
:
Provider[aws].Create(bucket-with-nested-unknown) executing (#props=5)
...
Marshaling property for RPC[Provider[aws].Create(bucket-with-nested-unknown).inputs]: __defaults={[{acl} {bucket} {forceDestroy}]}
Marshaling property for RPC[Provider[aws].Create(bucket-with-nested-unknown).inputs]: acl={private}
Marshaling property for RPC[Provider[aws].Create(bucket-with-nested-unknown).inputs]: bucket={bucket-with-nested-unknown-be14d40}
Marshaling property for RPC[Provider[aws].Create(bucket-with-nested-unknown).inputs]: forceDestroy={false}
Marshaling property for RPC[Provider[aws].Create(bucket-with-nested-unknown).inputs]: tags={map[__defaults:{[]} promptValue:{promptValue} unknown:output<string>{}]}
And these are the output properties that come back from the AWS provider (the tags
property is missing):
Unmarshaling property for RPC[Provider[aws].Create(bucket-with-nested-unknown).outputs]: acl={private}
Unmarshaling property for RPC[Provider[aws].Create(bucket-with-nested-unknown).outputs]: bucket={bucket-with-nested-unknown-be14d40}
Unmarshaling property for RPC[Provider[aws].Create(bucket-with-nested-unknown).outputs]: forceDestroy={false}
Unmarshaling property for RPC[Provider[aws].Create(bucket-with-nested-unknown).outputs]: id={}
Provider[aws].Create(bucket-with-nested-unknown) success: id=; #outs=4
This is happening in the TF bridge, and possibly the TF provider. We need to understand if this is the TF behavior, if the behavior has changed at all, and whether we have any control over it.
From a Pulumi program perspective, this likely isn't too impactful. If the output property value doesn't exist, during preview, the Output
instance will be marked as unknown, and during preview you wouldn't be able to interogate the nested values in the apply
, since it won't be run.
But it is more significant for policies.
The policy in the repro above is a stack validation policy, which notably looks at each resource's output properties.
Resource validation policies, on the other hand, look at a single resource's input properties, and tags
is present in the inputs.
Consider an updated ./policy/index.ts
that includes a resource validation policy:
import * as aws from "@pulumi/aws";
import {PolicyPack, ReportViolation, ResourceValidationArgs, StackValidationArgs} from "@pulumi/policy";
new PolicyPack("aws-typescript", {
policies: [
{
name: "bucket-resource-prop",
description: "Print the props of S3 bucket resource.",
enforcementLevel: "mandatory",
validateResource: (args: ResourceValidationArgs, reportViolation: ReportViolation) => {
if (args.isType(aws.s3.Bucket)) {
console.log("Resource: Resource Name:", args.name)
console.log("Resource: Resource Props:", args.props)
}
},
},
{
name: "print-s3-props",
description: "Print the props of S3 bucket policy resources",
enforcementLevel: "mandatory",
validateStack: async (args: StackValidationArgs, reportViolation: ReportViolation): Promise<void> => {
args.resources.forEach((policyResource => {
if (policyResource.isType(aws.s3.Bucket)) {
console.log("Stack: Resource Name:", policyResource.name)
console.log("Stack: Resource Props:", policyResource.props)
}
}))
},
},
],
});
Running pulumi preview --policy-pack ./policy
has the following output:
Resource: Resource Name: bucket-without-unknowns
Resource: Resource Props: {
acl: 'private',
bucket: 'bucket-without-unknowns-4384ea1',
forceDestroy: false,
tags: { promptValue: 'promptValue' }
}
Resource: Resource Name: bucket-with-nested-unknown
Resource: Resource Props: {
acl: 'private',
bucket: 'bucket-with-nested-unknown-5c498fc',
forceDestroy: false,
tags: {
promptValue: 'promptValue',
unknown: '04da6b54-80e4-46f7-96ec-b56ff0331ba9'
}
}
Stack: Resource Name: bucket-without-unknowns
Stack: Resource Props: {
acl: 'private',
bucket: 'bucket-without-unknowns-4384ea1',
forceDestroy: false,
id: '',
tags: { promptValue: 'promptValue' },
tagsAll: { promptValue: 'promptValue' }
}
Stack: Resource Name: bucket-with-nested-unknown
Stack: Resource Props: {
acl: 'private',
bucket: 'bucket-with-nested-unknown-5c498fc',
forceDestroy: false,
id: ''
}
One potential workaround would be to use a resource validation policy that maintains a global map of resource URNs to inputs, and then in the stack validation policy, look at a resource’s inputs rather than outputs from the map.
import * as aws from "@pulumi/aws";
import {PolicyPack, ReportViolation, ResourceValidationArgs, StackValidationArgs} from "@pulumi/policy";
const urnToInputsMap = new Map<string, Record<string, any>>();
new PolicyPack("aws-typescript", {
policies: [
{
name: "urn-to-inputs",
description: "Track resource inputs.",
enforcementLevel: "mandatory",
validateResource: (args: ResourceValidationArgs, reportViolation: ReportViolation) => {
urnToInputsMap.set(args.urn, args.props);
},
},
{
name: "print-s3-props",
description: "Print the props of S3 bucket policy resources",
enforcementLevel: "mandatory",
validateStack: async (args: StackValidationArgs, reportViolation: ReportViolation): Promise<void> => {
args.resources.forEach((policyResource => {
if (policyResource.isType(aws.s3.Bucket)) {
console.log("Resource Name:", policyResource.name);
console.log("Resource Inputs:", urnToInputsMap.get(policyResource.urn));
console.log("Resource Outputs:", policyResource.props);
}
}))
},
},
],
});
Which outputs:
Resource Name: bucket-without-unknowns
Resource Inputs: {
acl: 'private',
bucket: 'bucket-without-unknowns-81f4a30',
forceDestroy: false,
tags: { promptValue: 'promptValue' }
}
Resource Outputs: {
acl: 'private',
bucket: 'bucket-without-unknowns-81f4a30',
forceDestroy: false,
id: '',
tags: { promptValue: 'promptValue' },
tagsAll: { promptValue: 'promptValue' }
}
Resource Name: bucket-with-nested-unknown
Resource Inputs: {
acl: 'private',
bucket: 'bucket-with-nested-unknown-3985369',
forceDestroy: false,
tags: {
promptValue: 'promptValue',
unknown: '04da6b54-80e4-46f7-96ec-b56ff0331ba9'
}
}
Resource Outputs: {
acl: 'private',
bucket: 'bucket-with-nested-unknown-3985369',
forceDestroy: false,
id: ''
}
I was interested in this, would love to dig in sometime or hear what the root cause of the issue was after all.
To reproduce, in a new empty directory run
pulumi new aws-typescript
.Update
index.ts
with the following:Create a
policy
directory inside the project dir,cd
into that directory and runpulumi policy new aws-typescript
.Update
index.ts
with the following:In this example, there are two buckets, each of which has a
tags
argument. Each of the buckets has one tag that is a prompt value. One of the buckets has a tag with an unknown value and one of them does not. There is a policy pack that prints out theprops
of each policy resource. It is expected that thetags
prop would be available on both buckets. However, thetags
object containing an unknown value is not available at preview time.Change back into the project directory and run:
This results in:
In summary, if an object input property contains an unknown value, the property is missing from the outputs, even if some of the keys in the object are provided as prompt values.