How many folks place pulumi resources inside a `.a...
# typescript
e
How many folks place pulumi resources inside a
.apply
of another resource to ensure you get the proper
string
type you want, where if you didn’t, you’d most likely get an error of undefined. I do this all the time, but on a net new stack, pulumi won’t show the resources to be created inside that
.apply
which makes sense to me, but also very hard as some of these resources I want to import before my pulumi up and can’t verify they are imported
v
not sure if i understand the problem 100% here, but could you not use
dependsOn
to achieve this?
e
well I’d be using the string as a conditional
_asg_._loadBalancer_._dnsName_.apply((albDnsName) =>
that’s how I’m getting my albDnsName, and then based upon that name (a string) then do “things”
hard to do a depends on with that
v
ok got you, are you hitting a race condition where the resource isnt imported? or is it just a concern? i cant imagine pulumi would execute that code without the resource being there
i personally tend to avoid resource importing, and instead use stack outputs for stuff that needs to be shared between stacks
e
yea it’s tough one where using a 3rd party provider (we created) and earlier work was done via the console, so now we want it behind code
of course it’s in prod 🙂
v
i was trying to use imported resources just for github repos and config and it was a nightmare, luckily i work at a startup so we have built everything. my approach would be to get some downtime in to recreate things 😬
i appreciate thats not helpful
maybe you could create a stack that only imports resources and outputs the information you need, then you could use stackreferences?
e
honestly, we’ll probably just do a 2nd run, where we build everything but what we need, and then add in the imported resources when the promise is known
v
let me see how i handled the imports first in what i was doing, deleted the code now but should be in git somewhere
looks like i wrapped the imports in a function to get them, then used
.then()
then you can guarantee the import is attempted first
l
place pulumi resources inside a .apply of another resource to ensure you get the proper string type you want
This is never be necessary, and (imo) always wrong. You can use the output properties of the first resource in the 2nd resource without a problem.
e
you can provide a working example?
l
Of using a resource's outputs in another resource? All the example code should do that.. have you browsed github.com/pulumi/examples?
e
are you familiar with promises?
l
Yes.
Very.
e
ok, then you know this doesn’t just work on using new resources
l
It does. Pulumi takes care of it for you.
The output of the 1st resource is waiting on by Pulumi for you, so you don't have to (and shouldn't).
e
this isn’t just the output from one pulumi to pulumi input type by the 2nd
l
Ah, sorry. Can you explain in more detail then? Example code, perhaps?
e
it’s getting a promise back from a library and using the promised string type value as a conditional value to do more things
l
Promises are in the Input union type, you can use them like outputs.
e
this is part of code that is NOT a pulumi resource, it’s being used to decide which pulumi to create next
l
Can I ask: ideally, would you be creating all the resources? And you're using the promised value to decide whether or not you have enough information to do it now?
info is earlier in the thread @little-cartoon-10569
l
I don't think I'm gleaning enough from that. You can use get-methods and imports to avoid putting anything inside a .then() or .apply(). It's not always obvious, but in my experience, it's always possible.
For example, if you have a promised loadbalancer, and you need to use its dnsName to create a DNS record, you could use something like:
Copy code
const record = new aws.route53.Record(name, {
  name: pulumi.output(loadbalancer).dnsName,
  ...
And Pulumi will resolve the promise dependency and create the record only when the name is available.
e
here is some code
Copy code
// Call MBX EC2 Library
	const asg = new mbxec2.ASG({
	...does things
	)}

	// Build DNS Entries for Ultra DNS & CloudFlare
	asg.loadBalancer.dnsName.apply((albDnsName) => {
		// AWS ALB Specific Record per Environment
		if (env.appEnv.includes('-example')) {
			config.ultraDnsRecords.map((ultraRecord) => {
				new ultradns.Record(`${env.appEnv}-${ultraRecord.subDomain}-ultraDnsRecord`, {
					ownerName: ultraRecord.subDomain,
					recordType: 'CNAME',
					zoneName: config.bookerTld,
					ttl: 3600,
					recordDatas:
						ultraRecord.data == 'ALB-DNS-PLACEHOLDER' ? [`${albDnsName}.`] : [`${ultraRecord.data}`]
				});
			});
I’m calling a library that creates a bunch of resources related to an Auto-Scaling Group
then using that alb dns name, I create a record with it at ultradns
v
do you return the outputs in the library? by extending the pulumi componentresource class
e
unfortunately the ultraDNS provider wants an array of strings
l
Can you not move the use of loadBalancer.dnsName down to inside recordDates?
e
if you just use
asg.loadBalancer.dnsName
in the recordDates I would get undefined
l
If the output is resolved inside the constructor parameters, then you don't have to call the constructor inside an apply.
e
w/o the apply
mind you all this is being built at the same time
l
So is the problem that ultraRecord is somehow not a promise, but not available until after dnsName's promise resolves?
e
correct
l
Then make it a promise.
If it's essentially a real value that changes during runtime, then it has to be a promise (or an output).
e
yea we only get an alb Dns name once the resource is created
l
And it would be as simple as
const ultraPromised = lb.name.apply((_) => ultraReal))
It is possible to unconditionally construct the records, and give it promised / outputted parameters. Pulumi will wait correctly, based on those parameters. You never have to do the waiting on Pulumi's behalf.
e
I will try what you suggest, but feel like it will leave me right back where I started. Thanks all for the feedback!
l
There is one exception to this rule, and Pulumi have fallen victim to it themselves:
If the number of things created isn't known until after some other thing is created, then you end up in a very awkward situation, and sometimes have to await/apply.
awsx.ec2.Vpc's subnets do this, and they're wrong.
c
@elegant-crayon-4967
unfortunately the ultraDNS provider wants an array of strings
It seems that the
recordDatas
property takes in an
Input
of array of
Input<string>
. So you should be able to use the ALB's
dnsName
property directly as @little-cartoon-10569 suggested.
Input
is a type union:
Copy code
export declare type Input<T> = T | Promise<T> | OutputInstance<T>;
if you just use
asg.loadBalancer.dnsName
in the recordDates I would get undefined
I think this might provide a hint as to what might be wrong here. How is the
loadBalancer
property getting its value in the component resource
mbxec2.ASG
? Also are you calling
super.registerOutputs({})
in your component resource after you've created all the resources you need to?