Hello!!! :slightly_smiling_face: Is there no way ...
# automation-api
l
Hello!!! 🙂 Is there no way to extract the value of an Output as a type that isn't an Output? I have a use-case where I need to do something before my pulumi program completes, and I need the value of a resource to do so.. In AWS, what I'm doing is creating a hosted zone (which is a subdomain of a domain in another account) and certificate and then attempting to validate it it with the
acm.Certificate
and
acm.CertificateValidation
APIs. The issue is that before I attempt to validate the certificate, I need to add the name servers to a Hosted Zone in another account. I'm doing this by invoking a lambda function in my pulumi program, which I think will work. But I can't do that because I can't access a non-Output type of the name servers. I keep getting an error from my lambda saying that NoneType is nonsubscriptable, which makes sense, because I can't get pulumi to pass the actual value of the name servers -- just an Output. I'm trying to get creative so any and all ideas are appreciated!
b
Have you tried making your lambda invocation from within an output apply delegate?
l
yes (I think). This is how I am attempting to pass in the name servers from my records.
invoke_stack_function(body=route_53_resources['hosted_zone'].name_servers.apply(lambda name_servers: name_servers), function="addnameservers", message="Name servers were added successfully!")
l
Do you need to use the lambda? Can you instead allow your Pulumi program to access the HZ in the other account, and add the necessary records there in the normal way?
To answer the question, can you use Output's
get()
method? https://www.pulumi.com/docs/reference/pkg/nodejs/pulumi/pulumi/#OutputInstance-get
b
That usage of apply doesn't really do anything. The apply delegate is used to do something with an output once it is known, so if you are acting with the result of the apply rather than within the delegate itself you are still just handing an output. What you want is: var delayedNamedServers = name_servers.apply(lambda name_servers: return invocation(name_servers)); Now you have an output that will do your invocation once the value is known, and if the invocation function also returns an Output<T> that doesn't resolve until your invocation finishes than any downstream consumers of that new output will wait until the invocation completes before deploying.
🤦‍♀️ 1
Or what @little-cartoon-10569 is suggesting would work too. But I think you should checkout the inputs/outputs documentation
l
THank you both! @bored-oyster-3147 - I tried the below, and am getting an error saying that list object has no attribute apply... I also tried with just apply (and not Output.all) and it didn't work either. I haven't tried the .get() yet. I'll do that next.
Copy code
add_name_servers = Output.all(route_53_resources['hosted_zone'].name_servers).apply(lambda name_servers: invoke_stack_function(body={"org_id": org_id, "hosted_zone_name_servers": name_servers}, function="addnameservers", message="Name servers were added successfully!", aws_access_key_id=os.environ['BUILDBOT_ACCESS_KEY'], aws_secret_access_key=os.environ['BUILDBOT_SECRET_KEY']))
b
Hard to say what the issue is, idk what type the “route_53_resources” variable is or what the key “hosted_zone” in that dictionary returns or what type the “name_servers” property is.
l
that's fair. I'll keep digging on my end 🙂 Thanks!
It's been a while! I'm back to solving this issue, but it's a little different and more straightforward. Basically, I have a function that accepts a hosted_zone_id for a hosted zone that already exists, and then attempts to create and validate a certificate. I copy and pasted the example directly from the docs and all I did was remove the "example_zone" resource and pass in my own id. It doesn't work. I get the error
AttributeError: 'Certificate' object has no attribute 'domainValidationOptions
. ANy ideas? Happy to share my code also 🙂 \
Copy code
def create_and_validate_certificate(org_id, hosted_zone_id):
    example_certificate = aws.acm.Certificate(f'shared-certificate-{org_id}',
                                              domain_name=f'{org_id}.<http://getbuildbot.com|getbuildbot.com>',
                                              subject_alternative_names=[f'*.{org_id}.<http://getbuildbot.com|getbuildbot.com>'],
                                              validation_method="DNS")
    example_record = []
    for range in [{"key": k, "value": v} for [k, v] in enumerate({dvo.domainName: {
        name: dvo.resourceRecordName,
        record: dvo.resourceRecordValue,
        type: dvo.resourceRecordType,
    } for dvo in example_certificate.domainValidationOptions})]:
        example_record.append(aws.route53.Record(f"exampleRecord-{range['key']}",
                                                 allow_overwrite=True,
                                                 name=range["value"]["name"],
                                                 records=[range["value"]["record"]],
                                                 ttl=60,
                                                 type=range["value"]["type"],
                                                 zone_id=hosted_zone_id))
    example_certificate_validation = aws.acm.CertificateValidation("exampleCertificateValidation",
                                                                   certificate_arn=example_certificate.arn,
                                                                   validation_record_fqdns=example_record.apply(
                                                                       lambda example_record: [record.fqdn for record in
                                                                                        example_record]))
    return example_certificate_validation.certificate_arn
Just sending this so my thread pops back up in the channel also. In regards to certificate validation with aws acm 🙂
l
Check that you're definitely using the correct class / type for Certificate. aws.acm.Certificate does have a domainValidationOptions property, so if your code is saying that it doesn't, then the problem must be that your object isn't an aws.acm.Certificate.
Ah, no wait, it's domainValidationOptions in typescript. In Python, it's domain_validation_options.
Looks like a copypasta in the docs.
l
@little-cartoon-10569 do you think that is true for
resourceRecordName
also? and the others?
Now I am getting
error: python inline source runtime error: 'Output' object is not iterable, consider iterating the underlying value inside an 'apply'
. I don't understand exactly how apply works, and was hoping the example provided in the docs would just work. Any idea how I would modify the for loop with an apply?
l
Yes, the python convention is to separate words with underscores.
You need to move the for loop inside he apply.