Best practice on handling `pulumi.Output` when usi...
# typescript
r
Best practice on handling
pulumi.Output
when using helper functions that want unboxed values (and especially reusing the result for a new pulumi resource name)
I want to build a small abstraction to avoid repetition when creating a set of `azure.dns.ARecord`s. My idea was to use a
ComponentResource
that gets the
azure.dns.Zone
and a list of tuples like
(domainName, ipAddress)
. To build this in a stable fashion (such that reordering of resources doesn’t lead to delete/replaces) I want to create the
ARecord
pulumi resource name dynamically based on the domain and the zone’s name (with some sanitizing). This now leads me to the point that the zone’s name is of type
pulumi.Output<string>
and I might do some interpolation but will end up in trouble when I want to use that as pulumi resource name.
Is there any best practice on how to do that? Of course I could hand in the regular string of the zone name instead of the zone itself, but this again could lead to errors as I also need to pass the zone’s resource group for lookups.
b
Hello, I think you should avoid to use pulumi<Output> in pulumi resource name
This could be possible if you create the resource in a
<output>.apply
bloc, but this prevents you to export it in outputs because, because
export
should be at global level
That’s said, could you give a full example of the resources you want to create and I can look at
r
Thanks for your explanation. I’ll check back on that and build a minimal sample showing my intent…
Hey. Sorry for my delay. Please find the sample code attached. I know I can also put some resolved string as zone-name for the record name generation but I wanted to keep it simple enough such that as a user of the component I’m not able to do silly mistakes like calling the objects differently than the domain they represent.
Copy code
import * as azure from "@pulumi/azure";
import * as pulumi from "@pulumi/pulumi";
import { ComponentResource, ComponentResourceOptions } from "@pulumi/pulumi";

export interface ARecordEntry {
    domain: string;
    ip: string;
    ttl?: number;
}

export interface ZonedEntriesArgs {
    zone: azure.dns.Zone;
    defaultTtl: number;
    entries: ARecordEntry[];
}

export class ZonedEntries extends ComponentResource {
    public readonly aRecords: azure.dns.ARecord[];

    constructor(name: string, private args: ZonedEntriesArgs, opts: ComponentResourceOptions = {}) {
        super("ajaegle:dns:ZonedEntries", name, {}, opts);
        this.aRecords = this.args.entries.map(entry => this.createARecord(entry));
    }

    createARecord(entry: ARecordEntry) {
        const sanitizedName = createSanitizedName(entry.domain, this.args.zone.name)

        return new azure.dns.ARecord(sanitizedName, { // <-- this won't work with output value
            name: entry.domain,
            records: [entry.ip],
            ttl: entry.ttl || this.args.defaultTtl,
            zoneName: this.args.zone.name,
            resourceGroupName: this.args.zone.resourceGroupName,
        });
    }  
}

function createSanitizedName(domain: string, zoneName:pulumi.Output<string>) {
    const sanitizedDomain = domain.replace("*", "wildcard").replace(/\./g, "-");
    const sanitizedZone = zoneName.apply(name => name.replace(/\./g, "-"));
    return pulumi.interpolate `${sanitizedDomain}-${sanitizedZone}`;
}
b
Okay I see. You can’t use a
pulumi.Output<>
in the pulumi resource name, but you don’t need it. The pulumi resource name should be unique, so you just replace the zone name by the component name like that:
Copy code
return new azure.dns.ARecord(`${entry.domain}-${name}, {
Or if you sanitize:
Copy code
return new azure.dns.ARecord(createSanitizedName(entry.domain, name), { // <-- this won't work with output value
...

function createSanitizedName(domain: string, name:string): string {
    const sanitizedDomain = domain.replace("*", "wildcard").replace(/\./g, "-");
    return `${sanitizedDomain}-${name}`;
}
r
Thanks. Good idea to use the ResourceComponent’s name for building the entry name. I didn’t really remember that this is another place where someone needs to hand in some zone-specific identifier. Probably also a good idea to have a bit more consistent naming in the encapsulated resources and their parent…