hi folks - I’m here because I’m really struggling ...
# golang
b
hi folks - I’m here because I’m really struggling to understand how to do something in Go for Pulumi. That concept is… if I have a resource that takes a 
string
 as an input. How do I convert from 
pulumi.InputString
 to a vanilla 
string
 ? for a concrete example, I am creating a role: https://www.pulumi.com/registry/packages/aws/api-docs/iam/role/#assumerolepolicy_go this role needs an 
AssumeRolePolicy
 (which is just a string/json document). In order to construct this json document, I need an output value from another resource (an OIDC provider), which would not be known until 
apply
 time I simply don’t understand how to pass in a vanilla string, when that string needs to be async/dynamically created
b
hey @busy-island-31180, this is a common question! You can see an example of doing this here: https://github.com/jaxxstorm/pulumi-awsloadbalancercontroller/blob/main/provider/pkg/provider/awsloadbalancercontroller.go#L99-L126 if you want to understand this a bit more, perhaps this blog post will help? https://www.leebriggs.co.uk/blog/2021/05/09/pulumi-apply.html
the summary is, you can't convert a Input/Output to a string, but you can resolve the Input/Output and then use the value once it's resolved
b
so… close
if the resource supports a
pulumi.String
type
then, yes, I suppose this makes sense, and I totally understand this
but, my question is around code that doesn’t natively accept a
pulumi.String
if I have such a thing, I think I need to wrap that thing in a way such that it can accept these sorts of dynamic inputs
(ie. the
pulumi.String
type)
is there an article on creating new resources in pulumi?
maybe I just need to read that to really understand this fully
b
I'm sorry, I'm not quite following. Does the IAM example above not help?
b
ok.. so what if I have my own struct… my own custom little thing that I want to populate with values
Copy code
type FooBar struct {
   Greeting string
}
greeting needs to be populated with
hello ${ec2instance.ID}
ID in this case is impossible know until runtime
FooBar or one or more of it’s properties or somethingis going to be used elsewhere.. in maybe some other native pulumi resource
b
if you know the value is always going to be an input, you can make the type a
pulumi.StringInput
and it'll accept either a string, or a pulumi.input
if you absolutely want to make it a string, you'd need to populate the struct values inside an
ApplyT
b
say that FooBar isn’t a type I control
b
then you'd need to populate the struct inside an
ApplyT
which is how the value gets resolved
ApplyT
basically means "wait for the result to be returned by the API, and then use the value when it is"
b
ok, so.. if I pass
FooBar.Greeting
to something else… wouldn’t there be some sort of race condition for it to be populated?
b
if it's outside the
ApplyT
you wouldn't be able to pass it anywhere, it has to happen inside the
ApplyT
otherwise the value isn't known
b
ok
so, is there an article for creating my own resources? or higher level abstractions?
like a terraform module or terraform provider?
b
b
thank you
ok, so the mental model I have is essentially there’s always going to be, realistically, 2 “models” for any given “thing” in pulumi land
1 model is your very normal thing like what I described:
Copy code
type FooBar Struct {
   Greeting string
}
but then, if you need to accept “async” values (i.e.
pulumi.String
) you essentially wrap your concrete model, in a pulumi “async” model
at the end of the day though, you still need
string
if you intend on doing any real work
like
http.Get
or whatever.. make an API call or something
but the user may never see or be aware of the internal models
b
I think the best way to think about it is this: if you need to use the value from an API call, you need to resolve it. Think of it like a Channel, you need to wait till the value is taken off the channel before you can use it. That's what
ApplyT
does
b
and converting, using
pulumi.String("Foo")
are so that you can take a value is known at compile time (or at
pulumi up
time) like reading a file from the filesystem, into these “async” inputs
right, ya, that definitely makes sense
I guess I was missing the whole, “how do I do this myself” part
which Terraform makes realllllly easy
since every value in terraform, or even
locals
undergoes this operation
Copy code
locals {
   foo = aws_ec2_instance.foo.id

   my_map_of_things = {
     bar = local.foo
   }
}

resource null_resource foo {
  value = local.my_map_of_things["bar"]
}
if you need a map or array like in the locals example, you need to use the pulumi variants (or make your own)
in order to allow that
channel-like
behavior you mentioned
thanks jaxx
b
yeah terraform makes it easier, but also allows you to get into situations where you can't be idempotent, because sometimes you need to wait for output resolution before you want to do something, this is a more expressive model