Is there a way to set global CustomResourceOptions...
# general
i
Is there a way to set global CustomResourceOptions? Pulumi fails so much when trying to create something before deleting the original because of duplicate *something*, it's really annoying me. Why doesn't the deleteBeforeReplace have a sensible default for each resources? If you know the replace is going to fail anyway if you create a new one of a given resource before deleting the original, why isn't this behaviour preset based on the resource type? And if you set deleteBeforeReplace to true, it doesn't work on the next run because I guess it first needs to do a successful run to apply that option, and then it takes effect on the next change? Pfff.... In general it would avoid a lot of trouble if Pulumi would execute *delete*'s before *create*'s always. If I change a resource name, it will result in a delete and a create operation. But Pulumi first tries to create the duplicate, which in most cases will conflict with the existing resources anyway. Cue Pulumi hand-holding (ie. modify-the-code-and-apply iterations) to fix this. Disclaimer: pretty new to Pulumi and mostly really liking it, but some default behaviour doesn't seem to make sense to me at the moment.
d
The common practice is to not specify the name, and let pulumi assign a random suffix. When you specify the name of a resource explicitly, it should also set
deleteBeforeCreate
implicitly for you. What resources are you having trouble with?
i
In this case is was an AWS Route. I don't know how to NOT specify the name? The API is always like this: Resource(String name, ResourceArgs args) ?
d
The name being the name under
args
i
I don't think I ever set that one ..
Take for example the situation where I just change the Route like this: Route("name-a", ...) to Route("name-b", ...) Since default behaviour is create-before-delete, this will not work.
So then I have to remove the resource and do a stack up (hoping the resource does not have dependents and you don't have to go through that rabbit hole), and then re-add it and do another stack up. A bit silly honestly. I feel like I'm doing something wrong.
d
It looks like there isn't a name for Route. I know some of the vpc resources can be tough to handle for creation/deletion, often times from upstream bugs in the terraform provider code.
i
I feel like I want to a a default deleteBeforeReplace on everything. I'd rather have it take a bit longer and work, then having to jump through hoops to get it set up ... 😞
d
I think in your case, you'll just need to specify deleteBeforeCreate for any Route resources. Other resources won't need it
i
But even that doesn't work in the case I described, because the new Pulumi resource name implies a new resource. So I would need some kind of switch to ask Pulumi to delete resources before creating new ones? Kind of surprised this doesn't exist actually, especially given the amount of times I've run into this.
d
If you're renaming the resource, it's worth checking out the
alias
option, so that pulumi handles the state migration. This will prevent the need for recreating the resource in aws
i
Ohh! I don't know that one yet. That seems like the missing link πŸ˜„
Yep that seems exactly what I need! Thank you ☺️
d
In general, renaming a resource should go fine like this too, as cloud providers usually just do duplicate checks based on the resource name (such as ec2 instance id). It's just the Route resource in aws is a little more complex, likely doing duplicate checks based on a combination of attributes to prevent duplicate entries in the route table. Happy the
alias
option will do the job for you here
i
Just ran into this situation again btw, where the alias doesn't help: Trying to change a ClientVPN network assocation from a private to a public subnet. Pulumi tries to create the new association first, which apparently isn't allowed because you can have only one association per AZ. So yet again, I have to fallback to a 2-step process of: 1. Deleting the resource in the code -> pulumi up 2. Re-adding the modified resource -> pulumi up A
--delete-before-create
flag or some kind of global setting would fix this.
d
It looks like the route is also still recreating, so I'm not sure the alias took effect. Do you have a code example?
i
That's a different route, it's not really relevant for this situation I think. Code example is tricky, it's a decently complex setup...
Well actually, this illustrates it I think:
Copy code
private val networkAssociations = attachedSubnets
        .map { subnet ->
            subnet.id().applyValue { it ->
                NetworkAssociation(
                    "clientvpn-attachment-${it}",
                    NetworkAssociationArgs.builder()
                        .clientVpnEndpointId(this.id())
                        .subnetId(subnet.id())
                        .build()
                )
            }
        }
I changed the
attachedSubnets
from a list with one private subnet to a list with one public subnet (in the same AZ)
Kotlin btw! ❀️
d
It's generally not a good idea to create resources within the Apply block. Also not that familiar with kotlin, what is
${it}
?
i
It's equal to this in java:
Copy code
subnet.id().applyValue(it -> ...
)
(trying to remember java πŸ™ˆ ) It's basically just an implied variable
d
Thought so, just wanted to check
i
I edited the code to make it clearer
Copy code
It's generally not a good idea to create resources within the Apply block.
Yes I read that, but I think I still make that mistake too much. Good point, will fix that!
Actually that's another issue I have: if I want to use an
Output<String>
as a resource name, it gets tricky fast ... I guess maybe that's just a bad idea πŸ˜• ? But for example when creating one resource per subnet it is very tempting to put the subnet name in the resource name. And that is quite hard actually. How do people do this usually?
d
Depends on the case. Sometimes Maps to keep track of resources associated to a name, or a ComponentResource to group creation together
Others just use a list index, though maps are safer as they're order independent (less side effects when modifying the list)
i
Yep I think I used indexes before, but I am indeed a bit scared of ordering... I updated the code to this now:
Copy code
private val networkAssociations = attachedSubnets
        .mapIndexed { i, subnet ->
            NetworkAssociation(
                "clientvpn-attachment-$i",
                NetworkAssociationArgs.builder()
                    .clientVpnEndpointId(this.id())
                    .subnetId(subnet.id())
                    .build(),
                CustomResourceOptions.builder()
                    .aliases(Alias.builder().name("clientvpn-attachment-subnet-0b1ccce02a2395452").build())
                    .build()
            )
        }
Using the alias πŸ˜‰
I'm a bit sad I can't see which AZ or subnet the attachment is for in the resource name though.
d
You could use tuples to store the subnet name, or key a Map with the AZ name. I could see using a ComponentResource per AZ to be useful, though that's a more involved change
i
Hmm I need to look into ComponentResource also, haven't used it yet
I've been using inheritance now to logically group resources, that pattern seems to work nicely
The Kotlin way of inheritance lends itself very well to this
Eg.:
Copy code
class MyVpc(...) : Vpc(...) {
  val myCustomVpcResource = ...

  private val myHiddenCustomVpcResource = ...
}