https://pulumi.com logo
Title
g

great-sunset-355

06/23/2021, 9:51 AM
What is the correct way to get nested config value? I set the value like this:
pulumi config set-all --path \
  --secret passwords.pw1=Secret
How do I call this in the code? I cannot see to wrap my head around strange paths in the yaml file
cfg = pulumi.Config()
# Neither of these worked:
cfg.require_secret('passwords.pw1')
cfg.require_secret('["passwords"].["pw1"]') # does not work
b

billowy-army-68599

06/23/2021, 9:55 AM
passwordConfig = pulumi.Config("passwords")

passwordConfig.require_secret("pw1")
I think is what you need (untested)
g

great-sunset-355

06/23/2021, 10:03 AM
thanks I will try that
no it id not work I got an error please set a value using the command `pulumi config set passwords:pw1 <value>`
b

billowy-army-68599

06/23/2021, 10:23 AM
okay give me a few minutes
g

great-sunset-355

06/23/2021, 10:25 AM
I wish Config could just deserialize into an Object it I'm trying to workout how can I use this with Pydantinc object or at least a data class as my config object
b

billowy-army-68599

06/23/2021, 10:29 AM
@great-sunset-355 it can be grabbed as an object:
config = pulumi.Config()
passwords = config.require_object("passwords")
pw = passwords.get("pw1")

<http://pulumi.log.info|pulumi.log.info>(pw)
there's also
require_secret_object
g

great-sunset-355

06/23/2021, 10:29 AM
yeah that's what I'm working with
b

billowy-army-68599

06/23/2021, 10:30 AM
the snippet above works for me
g

great-sunset-355

06/23/2021, 10:31 AM
yeah I'm trying to deserialize it into an object (dataclass or pydantic settings) so I do not have to refer to the sections as strings
Also if a
secret object
contains mix of secret and not secret values, all of them are treated as secrets
b

billowy-army-68599

06/23/2021, 10:35 AM
yes that's expected behaviour
g

great-sunset-355

06/23/2021, 10:41 AM
I think that the problem is that in python Output looks like a dictionary so I'd naturally expect to behave that way This snippet works
from dataclasses import dataclass
import pulumi 

@dataclass
class MyConfig:
    pw1: Any 

    config = pulumi.Config()
    passwords = config.require_secret_object("passwords")
    mycfg = MyConfig(passwords['pw1'])
    print(mycfg)
However this does not:
from dataclasses import dataclass
import pulumi 

@dataclass
class MyConfig:
    pw1: Any 

    config = pulumi.Config()
    passwords = config.require_secret_object("passwords")
    mycfg = MyConfig(**passwords)
    print(mycfg)
with error
TypeError: attribute of type 'Output' is not callable
    error: an unhandled error occurred: Program exited with non-zero exit code: 1
I wonder if there is a way to get the
keys
from the Output without knowing them
b

billowy-army-68599

06/23/2021, 10:42 AM
No, that’s not possible because output is asynchronous. Have a read of this: https://www.leebriggs.co.uk/blog/2021/05/09/pulumi-apply.html Essentially you’ll have to use an apply
g

great-sunset-355

06/23/2021, 10:43 AM
I see then using apply will expose the secrets in the sate anyway
btw I read that article yesterday it was super helpful
@billowy-army-68599 it looks to me that I am never able to get a list from the secret object
b

billowy-army-68599

06/23/2021, 12:49 PM
you need to do it inside an
apply
because it's an output
g

great-sunset-355

06/23/2021, 12:49 PM
config:
  NS:obj:
    ALLOWED_HOSTS:
    - <http://URL1.com|URL1.com>
    - <http://URL2.com|URL2.com>
I'm trying to construct call
','.join(ALLOWED_HOSTS)
as an string input for another resource And since my ALLOWED_HOSTS is part of a secret object it is not possible even with apply I keep getting `Output`s never the value
b

billowy-army-68599

06/23/2021, 12:50 PM
any secret is an output, that's how secrets work because it needs to decrypt the value, so it's async. if you're working with outputs it MUST be inside an apply if you want to use it with another string
g

great-sunset-355

06/23/2021, 12:54 PM
how can I get a list back not string?
b

billowy-army-68599

06/23/2021, 12:56 PM
from config?
g

great-sunset-355

06/23/2021, 12:56 PM
yeah
b

billowy-army-68599

06/23/2021, 1:06 PM
this returns the correct types:
config = pulumi.Config()
foo = config.require_object("foo")

allowed_hosts = foo.get("allowed_hosts")

print(type(allowed_hosts))
<http://pulumi.log.info|pulumi.log.info>(allowed_hosts[0])
<http://pulumi.log.info|pulumi.log.info>(allowed_hosts[1])
If you want it as a secret object:
config = pulumi.Config()
foo = config.require_object("foo")
secret_foo = config.require_secret_object("foo")

allowed_hosts = foo.get("allowed_hosts")
secret_allowed_hosts = foo.get("allowed_hosts")

print(type(allowed_hosts))
<http://pulumi.log.info|pulumi.log.info>(allowed_hosts[0])
<http://pulumi.log.info|pulumi.log.info>(allowed_hosts[1])

<http://pulumi.log.info|pulumi.log.info>(secret_allowed_hosts[2])

secret_foo.apply(lambda host: print(host))
Type                 Name                      Plan       Info
 +   pulumi:pulumi:Stack  py-structured-config-dev  create     5 messages

Diagnostics:
  pulumi:pulumi:Stack (py-structured-config-dev):
    <class 'list'>
    {'allowed_hosts': ['<http://example.com|example.com>', '<http://example.net|example.net>', '[secret]']}

    <http://example.com|example.com>
    <http://example.net|example.net>
    [secret]


Do you want to perform this update?  [Use arrows to move, enter to select, type to filter]
  yes
> no
  details
g

great-sunset-355

06/23/2021, 1:15 PM
this line is wrong I believe
secret_allowed_hosts = foo.get("allowed_hosts")
b

billowy-army-68599

06/23/2021, 1:19 PM
ah yes, fixing
here we go:
import pulumi

config = pulumi.Config()
foo = config.require_object("foo")
secret_foo = config.require_secret_object("foo")

allowed_hosts = foo.get("allowed_hosts")
secret_allowed_hosts = secret_foo.apply(lambda h: print(h.get("allowed_hosts")))

print(type(allowed_hosts))
<http://pulumi.log.info|pulumi.log.info>(allowed_hosts[0])
<http://pulumi.log.info|pulumi.log.info>(allowed_hosts[1])

<http://pulumi.log.info|pulumi.log.info>(secret_allowed_hosts[2])

secret_foo.apply(lambda host: print(host))
g

great-sunset-355

06/23/2021, 1:24 PM
I wonder if using async and await would fix it
b

billowy-army-68599

06/23/2021, 1:28 PM
why use async/await when you can use apply? it does the same thing
g

great-sunset-355

06/23/2021, 1:29 PM
I find apply way too complicated
lemme try to replicate your example in my environment
b

billowy-army-68599

06/23/2021, 1:35 PM
i'm sorry to hear you're finding it too complicated, unfortunately you're going to come across it quite a lot 🙂
g

great-sunset-355

06/23/2021, 1:47 PM
am I expected to get an error when running your code?
I just created fresh project
b

billowy-army-68599

06/23/2021, 1:54 PM
You’ll need to populate the config the same as I did obviously
g

great-sunset-355

06/23/2021, 1:54 PM
I did this:
pulumi config set-all --path \
    --plaintext foo.allowed_hosts[0]=host1 \
    --plaintext foo.allowed_hosts[1]=host2
I really appreciate we spend the on this, if I am to use pulumi I have to understand this problem.
b

billowy-army-68599

06/23/2021, 2:35 PM
Away from keyboard at the moment be back soon
@great-sunset-355 looking at your comment, you cannot log an output that way, you have to do it inside an apply
your
need_list
method won't work either, because you're trying to do a join on an async value
g

great-sunset-355

06/23/2021, 3:53 PM
can I make something wait for output though?
b

billowy-army-68599

06/23/2021, 3:54 PM
yes, using
apply
g

great-sunset-355

06/23/2021, 4:11 PM
If I need to wait for a Cloudformation stack to finish do I just write a function that waits until it's done and then unblocks the rest of the code?
b

billowy-army-68599

06/23/2021, 4:14 PM
g

great-sunset-355

06/23/2021, 8:27 PM
I tried that and still got an exception + the example did not use the function
wait_for_loadbalancer
it does not wait for the function
@billowy-army-68599 I believe I hit this problem https://github.com/pulumi/pulumi/issues/2484