https://pulumi.com logo
Title
r

rich-leather-25702

03/03/2021, 12:22 PM
I’m losing my mind trying to pass an S3 resource to an IAM policy using TypeScript, can someone tell me what rookie mistake I’m making in trying to get the Output<String> value of contentBucket.arn in the below code:
const contentBucket = new aws.s3.Bucket('s3bucket',
  {
    bucket: 'bucket-1',
    acl: 'public-read',
  });

const s3Policy = new aws.iam.Policy('s3-test-policy', {
  policy: JSON.stringify({
    Version: '2012-10-17',
    Statement: [{
      Action: [
        's3:ListBucket',
        's3:GetObject ',
      ],
      Effect: 'Allow',
      Resource: contentBucket.arn
    }]
  })
});
Which returns
aws:iam:Policy (s3-test-policy):
    error: 1 error occurred:
        * Error creating IAM policy shu-tower-frontend-dev-s3-policy-4518918: MalformedPolicyDocument: Partition "
        1" is not valid for resource "arn:
        1: o.apply(v => v.toJSON())
        2: o.apply(v => JSON.stringify(v))
I’ve read the Inputs and Outputs page, but nothing I do seems to reveal the ARN of the resource (even though I can see in the Pulumi console that it was successfully created.
f

fierce-television-51712

03/03/2021, 1:08 PM
I am quite new to pulumi too. But I would certainly try something like this:
policy = contentBucket.arn.apply((arn) => {
  return JSON.stringify({
    Version: '2012-10-17',
    ...
    Resource: arn
  }
})

const s3Policy = new aws.iam.Policy('s3-test-policy', {
  policy: policy
});
apologies for any errors. I have not tried the code, but the idea is that you are passing a function to contentBucket.arn.apply and it is returning a new output that contains the policy… again I think…
f

future-nightfall-79300

03/03/2021, 1:37 PM
Here's a similar aws s3 bucketpolicy usage: Note the
pulumi.all([bucket.arn, bucket.arn]).apply(..
Similar to what Brian already mentioned
s

square-dress-80180

03/03/2021, 6:33 PM
Something similar happened to me the other day (also new). Have you tried @fierce-television-51712’s code @rich-leather-25702? I am wondering if you have to do
contentBucket.apply((bucket) => bucket.arn ...)
instead of
contentBucket.arn.apply()
. I would have guessed that the arn attr is not available at runtime yet and throws an error.
l

little-cartoon-10569

03/03/2021, 8:05 PM
It should be
contentBucket.arn.apply()
.
r

rich-leather-25702

03/03/2021, 8:26 PM
@little-cartoon-10569 that code produces:
TS2554: Expected 1 arguments, but got 0.  output.d.ts(139, 14): An argument for 'func' was not provided.
@square-dress-80180 .apply() isn’t a function on : aws.s3.Bucket. I’ve tried
contentBucket.arn.apply((arn) => arn)
to no avail, same error as before.
l

little-cartoon-10569

03/03/2021, 8:29 PM
That would produce a new output with the same value. You need to produce an output that is the entire policy document.
Pulumi can resolve an Output when it is passed as an Input, but it can't resolve a string that includes an Output
See next snippet for working alternative
r

rich-leather-25702

03/03/2021, 8:32 PM
@little-cartoon-10569 not to seem daft, but that’s the exact code I posted at the beginning that was throwing the error - am I missing a change you’ve made?
l

little-cartoon-10569

03/03/2021, 8:34 PM
Yes, you were, I was still writing it 🙂
In this version, the entire policy is an output. In the earlier version, a string was being built by JSON.stringify, which included an output. When outputs are turned to strings, they just show that warning, not the value inside them.
Almost every property that gets passed as an argument to a Pulumi resource can take its raw value (e.g. string) or an output wrapping that raw value. It can't ever take a composed raw value made by merging another output with something that isn't an output.
No wait, I'm explaining the other version of this code. ignore all that!
Oops, I've really confused things now 😞
This should work, but not because of the reasons I've explained (they're good reasons, just not for this snippet of code).
r

rich-leather-25702

03/03/2021, 8:40 PM
@fierce-television-51712 thanks for the suggestion but
const arnVal = contentBucket.arn.apply((arn) => {
  return arn;
});
or
const arnVal = contentBucket.arn.apply((arn) => {
  return arn;
});
both return the same error:
Calling [toString] on an [Output<T>] is not supported.
    To get the value of an Output<T> as an Output<string> consider either:
    1: o.apply(v => `prefix${v}suffix`)
    2: pulumi.interpolate `prefix${v}suffix`
l

little-cartoon-10569

03/03/2021, 8:40 PM
The JSON.stringify() explanation is the only problem in your code. You can't use JSON.stringify() if there's an output anywhere inside the object being stringified.
However, for policy documents, you don't need to stringify. Pulumi handles that nicely for you.
Well, it's probably TypeScript/Javascript that's handling it nicely for you.. not sure.
You can pass the JS policy document object with an output parameter to the Policy. In this case, the
resource
property gets an array, with an Output value. It's not a string that's been created by JSON.stringify containing an output; it's just an output. Pulumi handles this case, but not the JSON.stringify case.
Apologies for the confusion and double-explanation. Happy to start again to make it clearer!
r

rich-leather-25702

03/03/2021, 8:46 PM
@little-cartoon-10569I think we’re making progress - without the Stringify line, I instead get
index.ts(273,3): error TS2322: Type '{ Version: string; Statement: { Action: string[]; Effect: string; Resource: pulumi.Output<string>[]; }[]; }' is not assignable to type 'Input<string | PolicyDocument>'.
      Type '{ Version: string; Statement: { Action: string[]; Effect: string; Resource: pulumi.Output<string>[]; }[]; }' is not assignable to type 'PolicyDocument'.
        Types of property 'Version' are incompatible.
          Type 'string' is not assignable to type 'Input<"2012-10-17" | "2008-10-17">'.
So I’ll try forcing it to cast to something else
l

little-cartoon-10569

03/03/2021, 8:47 PM
My solution was to return the document from a function with return type
aws.iam.PolicyDocument
. TS looked after everything else. It even highlighted my syntax errors in VSCode as I typed...
Or if you use the snippet I posted, change
const policyDoc
to
const policyDoc: aws.iam.PolicyDocuemnt
r

rich-leather-25702

03/03/2021, 8:50 PM
@little-cartoon-10569 we have a winner, casting to
aws.iam.PolicyDocument
yields the result, can’t believe the gotcha was the JSON.stringify bit!
🎉 1
… and thank you to everyone who pitched in with answers!
l

little-cartoon-10569

03/03/2021, 8:51 PM
Yep. I've learned that (almost?) all of the time with Pulumi, JSON.stringify isn't needed. Just build your objects directly as needed, and the stringification happens
s

square-dress-80180

03/03/2021, 9:00 PM
Very cool - @rich-leather-25702 can you share your final solution just for posterity?
r

rich-leather-25702

03/04/2021, 11:59 AM
Of course, this was the winning solution, which eventually needed to be for CloudFront.
const cfPolicyDoc: aws.iam.PolicyDocument = {
  Version: '2012-10-17',
  Statement: [{
    Action: [
      'cloudfront:CreateInvalidation'
    ],
    Effect: 'Allow',
    Resource: [cdn.arn]
  }]
};

const cfPolicy = new aws.iam.Policy('cf-policy', {
  policy: cfPolicyDoc
});
🙌 2
f

fierce-television-51712

03/04/2021, 12:02 PM
Great news 🎉. I’d be interested in knowing what the “cdn” object is?