Thread
#getting-started
    f

    full-king-49894

    5 months ago
    Ok, trying to grok Pulumi outputs. My use-case is creating an AWS CloudFront distribution with a custom OriginAccessIdentity, in NodeJS. I have
    const originAccessIdentity = new aws.cloudfront.OriginAccessIdentity("oai", {
      comment: "Some comment",
    });
    But now I need to dereference the newly created ETag/Id to use in
    ...
      origins: [
        {
          domainName: bucketV2.bucketRegionalDomainName,
          originId: s3OriginId,
          s3OriginConfig: {
            originAccessIdentity: `origin-access-identity/cloudfront/${originAccessIdentity.id}` <=== Does not work
          },
        },
      ],
    ...
    It does not seem possible to simply use
    originAccessIdentity.id
    as the returned value is an object wrapped in pulumi sugar. I have tried all sorts of ways to try and get properties from the returned object including: originAccessIdentity.id originAccessIdentity.get() originAccessIdentity.get("id") originAccessIdentity.id.apply(id=>id) I am following along on https://www.pulumi.com/docs/intro/concepts/inputs-outputs/ as well as using step-through debugging via VSCode to inspect the returned object.
    b

    billowy-army-68599

    5 months ago
    I think you want to use pulumi.interpolate there. I'm on a plane right now so can't write an example
    f

    full-king-49894

    5 months ago
    Oh gawd! I'm writing JavaScript so I just glossed over
    .apply()
    assuming it was JavaScript apply when in actual fact it is a Pulumi SDK method of the same name?
    After reading the linked post it sounds like Pulumi is handling asynchronous logic internally rather than leaving it to the outer language to do that. I did see references to Promises when inspecting the returned results and even tried various permutations of
    await
    but to no avail. I found this about interpolation in the docs but it is in the context of converting inputs to outputs and my use case is very much about simply getting a property from an output object.
    I have reduced my code to
    const originAccessIdentity = new aws.cloudfront.OriginAccessIdentity("test");
    const cmt = pulumi.interpolate(originAccessIdentity.Name);
    console.log("Name", cmt);
    Which still returns the same non-string result
    Name Proxy {__pulumiOutput: true, resources: ƒ, allResources: ƒ, isKnown: Promise, isSecret: Promise, …}
    arg1:Proxy {__pulumiOutput: true, resources: ƒ, allResources: ƒ, isKnown: Promise, isSecret: Promise, …}
    [[Handler]]:Object
    get:(obj, prop) => {…}
    __proto__:Object
    [[IsRevoked]]:false
    [[Target]]:OutputImpl
    __pulumiOutput:true
    allResources:() => lifted.then(l => l.allResources)
    isKnown:Promise {<pending>}
    isSecret:Promise {<pending>}
    promise:(withUnknowns) => OutputImpl.getPromisedValue(lifted.then(l => l.value), withUnknowns)
    resources:() => resourcesCopy
    toJSON:() => {\n            const message = `Calling [toJSON] on an [Output<T>] is not supported.\n\nTo get the value of an Output as a JSON value or JSON string consider either:\n    1: o.apply(v => v.toJSON())\n    2: o.apply(v => JSON.stringify(v))\n\nSee <https://pulumi.io/help/outputs> for more details.\nThis function may throw in a future version of @pulumi/pulumi.`;\n            return message;\n        }
    toString:() => {\n            const message = `Calling [toString] on an [Output<T>] is not supported.\n\nTo get the value of an Output<T> as an Output<string> consider either:\n1: o.apply(v => \\`prefix\\${v}suffix\\`)\n2: pulumi.interpolate \\`prefix\\${v}suffix\\`\n\nSee <https://pulumi.io/help/outputs> for more details.\nThis function may throw in a future version of @pulumi/pulumi.`;\n            return message;\n        }
    __proto__:Object
    b

    billowy-army-68599

    5 months ago
    Yeah you can't log a string like that, you have to do it inside the apply
    f

    full-king-49894

    5 months ago
    I appreciate that you're travelling at the moment but any help you can provide later would be awesome. I am about to give up on Pulumi, even though I recognise it is really powerful. Just getting frustrated that something that should be easy seems really, really difficult. I'm no stranger to programming. I've been doing it for 30 years in more languages than I can remember. I just don't know what I am missing here.
    Going through the GitHub examples repo trying to see if I can find an example there.
    b

    billowy-army-68599

    5 months ago
    I'll be back home in the next hour and should be able to put an example together for you.
    Would you mind sharing the full example of what you're trying to achieve with cloud front?
    f

    full-king-49894

    5 months ago
    Sure, it's this example https://www.pulumi.com/registry/packages/aws/api-docs/cloudfront/distribution/. But I don't have an existing
    originAccessIdentity
    (about line 17). Rather than create one in the AWS console I thought I would dogfood it and use Pulumi. So, between the bucket ACL and creating the distribution I have something like
    const originAccessIdentity = new aws.cloudfront.OriginAccessIdentity("test");
    const cmt = pulumi.interpolate`${originAccessIdentity.id}`;
    console.log("Name", cmt.toString());
    
    //console.log(originAccessIdentity.apply(cloudfrontAccessIdentityPath));
    I say "something like" because I have been editing those same four lines for a few hours now. Once I can understand how to reference the returned property I can clean it all up
    b

    billowy-army-68599

    5 months ago
    @full-king-49894 luckily I already had an example of this 🙂 https://github.com/jaxxstorm/pulumi-examples/blob/main/typescript/aws/s3-cloudfront/index.ts the reason your
    console.log
    isn't working is because it's the wrong way round, it should be:
    originAccessIdentity.cloudfrontAccessIdentityPath.apply(value => {
      console.log(value)
    })
    let me know if that helps
    f

    full-king-49894

    5 months ago
    That did. Thanks. So .apply() is acting like a Promise? Or is it an actual promise? Can I use it like a promise and await it?
    b

    billowy-army-68599

    5 months ago
    it is very similar in nature to a promise, but it isn't handled by the js runtime so it's not actually a promise. it's handled in the pulumi engine. it can't be awaited like a usual promise no, that's basically what
    apply
    is handling for you
    f

    full-king-49894

    5 months ago
    As I have been going through this exercise today I have butchered my environment a bit. Which made me realise that Pulumi is storing state somewhere. Now I am getting errors on
    pulumi up
    because I have deleted Origin access identities from the AWS console. Up is now failing with 404 errors saying it cannot access the identities I have deleted. I have tried
    pulumi destroy
    but that does not seem to clear things up. Do I need to recreate all those test identities in the console THEN delete them via Pulumi?
    Hmm the custom promises thing might be an issue for me. I was hoping Pulumi would be easier to write than the myriad of shell scripts I currently have. But the syntax is a bit confusing. Do you know if anyone has created any sort of JS wrapper to abstract that? I am thinking I could probably write a simply promisify() function that could take
    originAccessIdentity.cloudfrontAccessIdentityPath.apply(value => {
      console.log(value)
    })
    as an example, and promisify the object that gets returned.
    b

    billowy-army-68599

    5 months ago
    Yes Pulumi does store state. If you run a Pulumi refresh it should sync the state with your environment
    f

    full-king-49894

    5 months ago
    ah, that's damn useful 🙂
    Going back to your example
    originAccessIdentity.cloudfrontAccessIdentityPath.apply(value => {
      console.log(value)
    })
    Is it possible to get the object? E.g. one level up? Let's say that in a different case the call results in half a dozen properties that are useful. Do I need to do something like
    someMethod.property1.apply(value => {
      console.log(value)
    })
    someMethod.property2.apply(value => {
      console.log(value)
    })
    someMethod.property3.apply(value => {
      console.log(value)
    })
    ... etc
    I tried .apply on originAccessIdentity and that resulted in an error
    b

    billowy-army-68599

    5 months ago
    what are you trying to do with said object? return it so you can use values with another resource? or view them from the pulumi console so you know what they are?
    f

    full-king-49894

    5 months ago
    use them elsewhere
    mostly - I can see it being useful to be able to view them later in some cases. But mostly I am thinking about the architecture of the code that I am going to be creating
    b

    billowy-army-68599

    5 months ago
    I almost never need to log values like you're doing except for debugging. an output value can be passed to another pulumi resource without the need to get its async value. there are some rare occurrences of this, like the originaccessidentity one where the AWS API only allows a string value rather than an inputty value if you want to just view the value, you can export it:
    export const path = originAccessIdentity.cloudfrontAccessIdentityPath
    to further expand on this:
    an output value can be passed to another pulumi resource without the need to get its async value
    you almost never want to use the plain value because passing an output from one resource to another is how Pulumi builds its dependency graph
    f

    full-king-49894

    5 months ago
    Ah
    I need to change the way I architect and code in order to support the dependency graph
    b

    billowy-army-68599

    5 months ago
    I think (but apologies if I'm wrong) you're coming from an imperative mindset, where you have to check if a resource creation succeeded, Pulumi handles all of that for you because it's declarative
    f

    full-king-49894

    5 months ago
    the export above works when going across modules. I wasn't even thinking that far yet
    Yes, you're right
    Especially with AWS which is, pardon my language, a bit of a pig
    b

    billowy-army-68599

    5 months ago
    no arguments from me 😃
    f

    full-king-49894

    5 months ago
    LOL
    I've been working with it for years, and cursing it for years. If it was still 2012 I would be more forgiving but it hasn't really polished up like Google and Azure
    And I've written bat, powershell, bash and Node wrappers over the years
    Ok, I have taken more than enough of your time today, thank you !!!
    I'm going to read up some of the other example code and shift my approach to a more "Pulumi" centric one
    b

    billowy-army-68599

    5 months ago
    one last thing. if we go back to the cloudfront distribution example you posted earlier... https://www.pulumi.com/registry/packages/aws/api-docs/cloudfront/distribution/ You'll notice here we create an S3 bucket, give it a name etc
    # here we create an S3 bucket
    const bucketV2 = new aws.s3.BucketV2("bucketV2", {tags: {
        Name: "My bucket",
    }});
    then here, we create another resource, a bucket acl. you'll notice we pass the
    bucketV2.id
    from the first resource into here. This is telling Pulumi that the bAcl depends on a value from bucketV2 and builds a dependency graph. This is going to be really hard to do in your imperative code, Pulumi handles it all for you
    const bAcl = new aws.s3.BucketAclV2("bAcl", {
        bucket: bucketV2.id,
        acl: "private",
    });
    f

    full-king-49894

    5 months ago
    Funny, that was the code that derailed me in the first place. My mind immediately went to async and because I did not see any error handling or obvious async/await/promises I thought "That's a failure waiting to happen" and started down the path of writing it declaratively.
    A nice four hour detour on a Friday afternoon. But, I have a much stronger understanding of Pulumi is a result