How to convert Pulumi Output<t> to string?
Asked Answered
S

4

19

I am dealing with creating AWS API Gateway. I am trying to create CloudWatch Log group and name it API-Gateway-Execution-Logs_${restApiId}/${stageName}. I have no problem in Rest API creation.
My issue is in converting restApi.id which is of type pulumi.Outout to string.

I have tried these 2 versions which are proposed in their PR#2496

  1. const restApiId = apiGatewayToSqsQueueRestApi.id.apply((v) => `${v}`);
  2. const restApiId = pulumi.interpolate `${apiGatewayToSqsQueueRestApi.id}`

here is the code where it is used

const cloudWatchLogGroup = new aws.cloudwatch.LogGroup(
  `API-Gateway-Execution-Logs_${restApiId}/${stageName}`,
  {},
);

stageName is just a string.

I have also tried to apply again like
const restApiIdStrign = restApiId.apply((v) => v);

I always got this error from pulumi up
aws:cloudwatch:LogGroup API-Gateway-Execution-Logs_Calling [toString] on an [Output<T>] is not supported.

Please help me convert Output to string

Sclerometer answered 24/6, 2020 at 18:21 Comment(0)
T
4

Short Answer

You can specify the physical name of your LogGroup by specifying the name input and you can construct this from the API Gateway id output using pulumi.interpolate. You must use a static string as the first argument to your resource. I would recommend using the same name you're providing to your API Gateway resource as the name for your Log Group. Here's an example:

const apiGatewayToSqsQueueRestApi = new aws.apigateway.RestApi("API-Gateway-Execution");

const cloudWatchLogGroup = new aws.cloudwatch.LogGroup(
  "API-Gateway-Execution", // this is the logical name and must be a static string
  {
    name: pulumi.interpolate`API-Gateway-Execution-Logs_${apiGatewayToSqsQueueRestApi.id}/${stageName}` // this the physical name and can be constructed from other resource outputs
  },
);

Longer Answer

The first argument to every resource type in Pulumi is the logical name and is used for Pulumi to track the resource internally from one deployment to the next. By default, Pulumi auto-names the physical resources from this logical name. You can override this behavior by specifying your own physical name, typically via a name input to the resource. More information on resource names and auto-naming is here.

The specific issue here is that logical names cannot be constructed from other resource outputs. They must be static strings. Resource inputs (such as name) can be constructed from other resource outputs.

Teriteria answered 24/6, 2020 at 19:23 Comment(0)
S
23

@Cameron answered the naming question, I want to answer your question in the title.

It's not possible to convert an Output<string> to string, or any Output<T> to T.

Output<T> is a container for a future value T which may not be resolved even after the program execution is over. Maybe, your restApiId is generated by AWS at deployment time, so if you run your program in preview, there's no value for restApiId.

Output<T> is like a Promise<T> which will be eventually resolved, potentially after some resources are created in the cloud.

Therefore, the only operations with Output<T> are:

  • Convert it to another Output<U> with apply(f), where f: T -> U
  • Assign it to an Input<T> to pass it to another resource constructor
  • Export it from the stack

Any value manipulation has to happen within an apply call.

Softa answered 24/6, 2020 at 19:37 Comment(1)
I need to convert the Output<string> to a string in C#, is this not possible at all? I need to use this string value in my resource naming and other places in my code. What can I do instead? I can only get the value (Azure subscription Id) by sending output to Output<string>Convoke
C
7

So long as the Output is resolvable while the Pulumi script is still running, you can use an approach like the below:

import {Output} from "@pulumi/pulumi";
import * as fs from "fs";

// create a GCP registry
const registry = new gcp.container.Registry("my-registry");
const registryUrl = registry.id.apply(_=>gcp.container.getRegistryRepository().then(reg=>reg.repositoryUrl));

// create a GCP storage bucket
const bucket = new gcp.storage.Bucket("my-bucket");
const bucketURL = bucket.url;

function GetValue<T>(output: Output<T>) {
    return new Promise<T>((resolve, reject)=>{
        output.apply(value=>{
            resolve(value);
        });
    });
}

(async()=>{
    fs.writeFileSync("./PulumiOutput_Public.json", JSON.stringify({
        registryURL: await GetValue(registryUrl),
        bucketURL: await GetValue(bucketURL),
    }, null, "\t"));
})();

To clarify, this approach only works when you're doing an actual deployment (ie. pulumi up), not merely a preview. (as explained here)

That's good enough for my use-case though, as I just want a way to store the registry-url and such after each deployment, for other scripts in my project to know where to find the latest version.

Contrapuntal answered 28/8, 2021 at 2:21 Comment(0)
T
4

Short Answer

You can specify the physical name of your LogGroup by specifying the name input and you can construct this from the API Gateway id output using pulumi.interpolate. You must use a static string as the first argument to your resource. I would recommend using the same name you're providing to your API Gateway resource as the name for your Log Group. Here's an example:

const apiGatewayToSqsQueueRestApi = new aws.apigateway.RestApi("API-Gateway-Execution");

const cloudWatchLogGroup = new aws.cloudwatch.LogGroup(
  "API-Gateway-Execution", // this is the logical name and must be a static string
  {
    name: pulumi.interpolate`API-Gateway-Execution-Logs_${apiGatewayToSqsQueueRestApi.id}/${stageName}` // this the physical name and can be constructed from other resource outputs
  },
);

Longer Answer

The first argument to every resource type in Pulumi is the logical name and is used for Pulumi to track the resource internally from one deployment to the next. By default, Pulumi auto-names the physical resources from this logical name. You can override this behavior by specifying your own physical name, typically via a name input to the resource. More information on resource names and auto-naming is here.

The specific issue here is that logical names cannot be constructed from other resource outputs. They must be static strings. Resource inputs (such as name) can be constructed from other resource outputs.

Teriteria answered 24/6, 2020 at 19:23 Comment(0)
V
-1

Encountered a similar issue recently. Adding this for anyone that comes looking.

For pulumi python, some policies requires the input to be stringified json. Say you're writing an sqs queue and a dlq for it, you may initially write something like this:

import pulumi_aws

dlq = aws.sqs.Queue()
queue = pulumi_aws.sqs.Queue(
    redrive_policy=json.dumps({
        "deadLetterTargetArn": dlq.arn,
        "maxReceiveCount": "3"
    })
)

The issue we see here is that the json lib errors out stating type Output cannot be parsed. When you print() dlq.arn, you'd see a memory address for it like <pulumi.output.Output object at 0x10e074b80>

In order to work around this, we have to leverage the Outputs lib and write a callback function

import pulumi_aws

def render_redrive_policy(arn):
    return json.dumps({
        "deadLetterTargetArn": arn,
        "maxReceiveCount": "3"
    })

dlq = pulumi_aws.sqs.Queue()
queue = pulumi_aws.sqs.Queue(
    redrive_policy=Output.all(arn=dlq.arn).apply(
        lambda args: render_redrive_policy(args["arn"])
    )
)
Veedis answered 31/8, 2022 at 15:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.