Adding Delays to your Pulumi IaC with Pulumi Time.Sleep

When creating Infrastructure as Code, most IaC languages create dependencies between resources so that resources are created in the correct order, and we wait for a resource to complete creation before we start creating a resource that depends on it. A lot of the time this is handled automatically, but you can also add explicit dependencies as well. However, sometimes these dependencies don’t do the job.

Sometimes a resource will be complete, at least as far as the IaC is concerned, but it’s not actually ready in the cloud provider. If a subsequent resource tries to use it, it errors, because it’s not ready. Or sometimes you need to wait for a resource like DNS or Entra ID users to replicate across multiple resources before you can use them.

In an ideal world, we’d be able to get a call-back when the resource is ready, but that’s not always possible. If we were writing an imperative script, this is where we would do something like:

sleep(30)

Which would have our script sit and wait for 30 seconds before moving on and trying to connect to the resource. It’s not elegant, and some might say it’s bad practice, but sometimes it’s the only option.

When we’re writing Pulumi code, we are writing it using a fully-fledged programming language, so we could just drop out to the native programming language and use that to sleep, but we lose some of the benefits of the declarative parts of Pulumi, we wouldn’t see it in a preview for example, nor would it be used as part of a destroy process. Instead, we can use the Pulumi Time.Sleep resource.

Time. Sleep

The Pulumi sleep resource is part of the time library and allows us to create a native Pulumi resource that will create a delay.

var wait30Seconds = new Time.Sleep("wait30Seconds", new()
{
    CreateDuration = "30s",
},

This will cause the deployment to wait for 30 seconds. However, if we just did the above then the delay would happen as soon as we start deployment, and it would happen at the same time as all your other resources are deployed. Instead, we need to set up dependencies so that the delay occurs after some resources, and before others.

To set up these dependencies we would add a dependency to the sleep resources for any resource that needs to occur before the sleep, and we add a dependency on the sleep resources for any resources that need to deploy after the sleep. In the example below the following occurs:

  1. We create the “previous” resource
  2. We wait 30 seconds
  3. We create the “next” resource
var previous = new Null.Resource("previous");

var wait30Seconds = new Time.Sleep("wait30Seconds", new()
{
    CreateDuration = "30s",
}, new CustomResourceOptions
{
    DependsOn = new[]
    {
        previous,
    },
});

var next = new Null.Resource("next", new()
{
}, new CustomResourceOptions
{
    DependsOn = new[]
    {
        wait30Seconds,
    },
});

The code above applies the delay during an “up” operation. We can also do the same with a destroy operation, adding a delay before moving on to destroy any dependent resources. This uses the DestroyDuration properties.

var wait30Seconds = new Time.Sleep("wait30Seconds", new()
{
    DestroyDuration = "30s",
}, new CustomResourceOptions
{
    DependsOn = new[]
    {
        previous,
    },
});

Summary

Adding a delay to your deployment isn’t a great solution, ideally, you’d be able to achieve this result with dependencies or callbacks when a resource is ready, however, sometimes it’s the only option. By using the time.sleep resource you are at least doing this in a Pulumi native, declarative manner.