I’m losing my mind trying to pass an S3 resource t...
# aws
r
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:
Copy 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
Copy code
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
I am quite new to pulumi too. But I would certainly try something like this:
Copy code
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
Here's a similar aws s3 bucketpolicy usage: Note the
Copy code
pulumi.all([bucket.arn, bucket.arn]).apply(..
Similar to what Brian already mentioned
s
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
It should be
contentBucket.arn.apply()
.
r
@little-cartoon-10569 that code produces:
Copy code
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
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
@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
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
@fierce-television-51712 thanks for the suggestion but
Copy code
const arnVal = contentBucket.arn.apply((arn) => {
  return arn;
});
or
Copy code
const arnVal = contentBucket.arn.apply((arn) => {
  return arn;
});
both return the same error:
Copy code
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
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
@little-cartoon-10569I think we’re making progress - without the Stringify line, I instead get
Copy code
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
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
@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
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
Very cool - @rich-leather-25702 can you share your final solution just for posterity?
r
Of course, this was the winning solution, which eventually needed to be for CloudFront.
Copy code
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
Great news 🎉. I’d be interested in knowing what the “cdn” object is?