11/18/2019, 10:14 AM
Best practice on handling
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
that gets the
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
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
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.


11/25/2019, 9:16 PM
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
bloc, but this prevents you to export it in outputs because, because
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


12/02/2019, 10:05 AM
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.
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.createARecord(entry));

    createARecord(entry: ARecordEntry) {
        const sanitizedName = createSanitizedName(entry.domain,

        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,

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}`;


12/19/2019, 8:48 AM
Okay I see. You can’t use a
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:
return new azure.dns.ARecord(`${entry.domain}-${name}, {
Or if you sanitize:
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}`;


12/19/2019, 9:52 AM
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…