There is really no way to get the string value of ...
# golang
There is really no way to get the string value of resources created with pulumi? I have created IPv4 and IPv6 addresses on Hcloud attached to a VM, now I want to generate an ansible inventory and an ansible playbook where I need to real IP addresses but even with ApplyT I don't get the strings... How should something like that work? I'm not sure if something like really still works today 😞 The type system of Pulumi seems really pretty hard...
It takes a moment to really "get" the Pulumi outputs. Once you do it, you have at your disposal the full power of (in this case) Go. Taking the example gist, I think the problem lies here
Copy code
ipv4Address := ipv4.IpAddress.ApplyT(func(val string) string {
		return val
This will NOT do what you think it will do. The return type of ApplyT is still a pulumi.Output. Only inside the closure (the func passed to ApplyT) the type is a plain string...
Talking about Go, thinking about promises (what an Output is) goes against the way of thinking in Go, where everything is sync and blocking in a select. In Pulumi, it is the opposite, everything is async. This I think is the difficulty. Once you "see the light", then everything flows smoothly.
So, to go back to your goal (generating the ansible inventory), it must be done from within an ApplyT. Since you need more than one Output, you will need to use pulumi.All(...).ApplyT. Function pulumi.All is documented in the same place of ApplyT,
Required warning: What I am proposing is to do side-effects inside an ApplyT. For this case, it works OK, but it can mess everything up if the side-effect is manipulating pulumi resources (the same page above warns about this).
Thanks for these useful hints
When I was in the process of ditching Go for Py (because of outputs) I was thinking that maybe channels would be a better (?) abstraction for async nature of outputs? Anyhow, this is how All and Apply work for a task of generating the files using the native types (I know this is in Py, but the concept doesn’t change, just gets awfully verbose with Go)
Copy code
yaml_done = kubeone_cluster.render_yaml(

# renderYAML waits for outputs to be ready
# and renders the kubeone_cluster to a YAML file
def render_yaml(
    cp_host, cp_int_ip, cp_hostname, static_workers: List[compute.Instance]
) -> pulumi.Output[bool]:
    def populate_spec(outputs):
        kubeone_cluster["apiEndpoint"]["host"] = outputs["cp_host"]
        kubeone_cluster["controlPlane"]["hosts"][0]["publicAddress"] = outputs[
        kubeone_cluster["controlPlane"]["hosts"][0]["hostname"] = outputs["cp_hostname"]
        kubeone_cluster["controlPlane"]["hosts"][0]["privateAddress"] = outputs[

        for worker_out in outputs["workers_output"]:
                    "publicAddress": worker_out[0],
                    "privateAddress": worker_out[0],
                    "sshUsername": "root",
                    "hostname": worker_out[1],
                    "bastion": outputs["cp_host"],

        # writing kubeone yaml to file
        with open("kubeone.yaml", "w") as f:

        return True
PS. It still boggles me that none of that is linked from the outputs docs to a blog post or smth. This is table stakes to many, it seems.
Haha, initially I have been playing with kubeone as well... Now I "just" want to create some cloud resources and run ansible to manage the vm configuration. I think I will link the repo to get some feedback for following better practice in pulumi... It doesn't feel really right how I got it now.