Search code examples
amazon-web-servicesgopulumi

How do I reference a StringOutput in an AWS IAM policy


I've looked high and low for how to do this. I don't think I have the right terminology. Using Pulumi with Golang how can I reference the ID of some resource in a string.

For example, I create a bucket then I want to reference that bucket's ID in an IAM policy. This seems impossible.

bucket, err := s3.NewBucket(
    ctx,
    photosBucketName,
    &s3.BucketArgs{})

tmpJSON, err := json.Marshal(map[string]interface{}{
    "Version": "2012-10-17",
    "Statement": []map[string]interface{}{
        {
            "Effect":    "Allow",
            "Principal": "*",
            "Action":    []string{"s3:GetObject"},
            "Resource":  []string{fmt.Sprintf("arn:aws:s3:::%s/*", bucket.ID())},
        },
    },
})

Output being:

Sprintf format %s has arg bucket.ID() of wrong type github.com/pulumi/pulumi/sdk/v2/go/pulumi.IDOutput

Using photosBucketName results in a malformed document because of the generated suffix on the bucket name.

Appreciate the time and help.


Solution

  • Pulumi resources return Outputs, which are values that are not known by Pulumi until the upstream cloud provider API (in this case, the AWS S3 API) until the resource is created.

    What that means is that if you want to access the raw output value as a standard Go string, you'll need to somehow tell the Pulumi engine to wait until that resource has been created. You do this using Pulumi's apply

    So in your particular example, we want to build a JSON string for our IAM policy (IAM policies only take strings, they can't take other Pulumi outputs).

    bucket, err := s3.NewBucket(
        ctx,
        photosBucketName,
        &s3.BucketArgs{})
    
    // notice how we're using the apply function to wrap the building of the JSON string
    bucketPolicy := bucket.Arn.ApplyT(func (arn string) (string, error) {
        policyJSON, err := json.Marshal(map[string]interface{}{
            "Version": "2012-10-17",
            "Statement": []map[string]interface{}{
                {
                    "Effect": "Allow",
                    "Principal": "*",
                    "Action": []string{"s3:GetObject"},
                    "Resource": []string{
                        arn, // I can now pass the arn directy
                    },
                },
            },
        })
        if err != nil {
            return "", err
        }
      return string(policyJSON), nil
    })