UpCloud Pulumi Provider ManagedObjectStoragePolicy Recreated When Description Is Omitted

by StackCamp Team 89 views

Hey guys! Today, we're diving into a quirky issue with the UpCloud Pulumi provider that can cause some unexpected resource recreations. Specifically, we're talking about the ManagedObjectStoragePolicy and how omitting the Description field can lead to it being replaced on every Pulumi run. Let's break down what's happening and how to fix it.

The Problem: Recreated ManagedObjectStoragePolicy

So, here's the deal: if you're using the ManagedObjectStoragePolicy resource in your Pulumi code and you happen to leave out the Description field, you might notice that Pulumi wants to recreate this policy every time you run pulumi up. This can be a bit of a head-scratcher, especially since the diff might not immediately scream out the reason for the replacement.

Digging into the Details

To give you a clearer picture, here’s what you might see in your Pulumi output when you run pulumi pre --diff:

10:37:33 󰈺  ❯ pulumi pre --diff
Previewing update (dev)

Compiling the program ...
Finished compiling
  pulumi:pulumi:Stack: (same)
    [urn=urn:pulumi:dev::bug-repro::pulumi:pulumi:Stack::bug-repro-dev]
    +-upcloud:index/managedObjectStoragePolicy:ManagedObjectStoragePolicy: (replace)
        [id=129f13d4-0b25-4ed7-a894-6139eb9b0884/example]
        [urn=urn:pulumi:dev::bug-repro::upcloud:index/managedObjectStoragePolicy:ManagedObjectStoragePolicy::this]
        [provider=urn:pulumi:dev::bug-repro::pulumi:providers:upcloud::default_0_5_3_github_/api.github.com/UpCloudLtd/pulumi-upcloud::fc11e34a-0fc1-4ad7-b8a2-4595f1f42286]
      ~ arn             : "urn:ecs:iam::129f13d40b254ed7a8946139eb9b0884:policy/example" => [unknown]
      ~ attachmentCount : 0 => [unknown]
      ~ createdAt       : "2025-09-30 07:37:31 +0000 UTC" => [unknown]
      ~ defaultVersionId: "v1" => [unknown]
      - description     : ""
        document        : "%7B%22Version%22%3A%20%222012-10-17%22%2C%20%20%22Statement%22%3A%20%5B%7B%22Action%22%3A%20%5B%22iam%3AGetUser%22%5D%2C%20%22Resource%22%3A%20%22%2A%..."
      ~ id              : "129f13d4-0b25-4ed7-a894-6139eb9b0884/example" => [unknown]
        name            : "example"
        serviceUuid     : "129f13d4-0b25-4ed7-a894-6139eb9b0884"
      ~ system          : false => [unknown]
      ~ updatedAt       : "2025-09-30 07:37:31 +0000 UTC" => [unknown]
Resources:
    +-1 to replace
    2 unchanged

And when you run pulumi up, you'll see something like this:

10:41:26 󰈺  ❯ pulumi up
Previewing update (dev)  

     Type                                         Name           Plan
     pulumi:pulumi:Stack                          bug-repro-dev          
 +-  └─ upcloud:index:ManagedObjectStoragePolicy  this           replace     

Resources:
    +-1 to replace
    2 unchanged

Notice that the ManagedObjectStoragePolicy is marked for replacement. The key here is the missing description. Pulumi detects a change because the Description field, which the UpCloud API likely defaults to an empty string, is not explicitly set in your Pulumi code. This discrepancy triggers the replacement.

This ManagedObjectStoragePolicy recreation issue is a significant concern because it leads to unnecessary resource churn and can disrupt your infrastructure management workflows. Imagine you have numerous policies defined, and each one is being recreated on every run simply because of a missing description. This not only wastes resources but also increases the risk of unintended side effects.

Why This Happens

The root cause of this behavior likely lies within the UpCloud Pulumi provider's handling of the Description field. When the field is omitted in your Pulumi code, the provider doesn't send any value for the description during the resource creation or update. The UpCloud API, in turn, likely defaults this field to an empty string. However, on subsequent Pulumi runs, the provider detects a difference between the desired state (no description) and the actual state (empty string description), triggering a replacement.

This issue highlights the importance of explicitly managing all resource properties, even those that might seem optional. While it's tempting to omit fields that have default values, doing so can lead to unexpected behavior and resource drift. In the case of the ManagedObjectStoragePolicy, explicitly setting the Description field, even to an empty string, ensures that Pulumi correctly tracks the resource's state and avoids unnecessary recreations.

Sample Program: The Culprit

Here's a snippet of Go code that demonstrates the issue. This example is straight from the ManagedObjectStoragePolicy documentation page, but with the Description field unset:

package main

import (
	"github.com/UpCloudLtd/pulumi-upcloud/sdk/go/upcloud"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		this, err := upcloud.NewManagedObjectStorage(ctx, "this", &upcloud.ManagedObjectStorageArgs{
			Name:             pulumi.String("example"),
			Region:           pulumi.String("europe-1"),
			ConfiguredStatus: pulumi.String("started"),
		})
		if err != nil {
			return err
		}
		_, err = upcloud.NewManagedObjectStoragePolicy(ctx, "this", &upcloud.ManagedObjectStoragePolicyArgs{
			Name:        pulumi.String("example"),
			Document:    pulumi.String("%7B%22Version%22%3A%20%222012-10-17%22%2C%20%20%22Statement%22%3A%20%5B%7B%22Action%22%3A%20%5B%22iam%3AGetUser%22%5D%2C%20%22Resource%22%3A%20%22%2A%22%2C%20%22Effect%22%3A%20%22Allow%22%2C%20%22Sid%22%3A%20%22editor%22%7D%5D%7D"),
			ServiceUuid: this.ID(),
		})
		if err != nil {
			return err
		}
		return nil
	})
}

Reproducing the Issue

To see this in action, just run pulumi up twice in a row with this code. You'll notice that the ManagedObjectStoragePolicy gets recreated on the second run.

The Fix

The simplest fix is to add a Description field to your ManagedObjectStoragePolicyArgs. It doesn't even matter if the description is empty – just including the field is enough to prevent the recreation. Here's how you can modify the code:

_, err = upcloud.NewManagedObjectStoragePolicy(ctx, "this", &upcloud.ManagedObjectStoragePolicyArgs{
	Name:        pulumi.String("example"),
	Document:    pulumi.String("%7B%22Version%22%3A%20%222012-10-17%22%2C%20%20%22Statement%22%3A%20%5B%7B%22Action%22%3A%20%5B%22iam%3AGetUser%22%5D%2C%20%22Resource%22%3A%20%22%2A%22%2C%20%22Effect%22%3A%20%22Allow%22%2C%20%22Sid%22%3A%20%22editor%22%7D%5D%7D"),
	ServiceUuid: this.ID(),
	Description: pulumi.String(""), // Add this line
})

Run pulumi up again, and you'll see that the policy no longer gets recreated unnecessarily.

Log Output and Affected Resources

If you're running into this, you might not see any specific errors in the logs, but the diff and update previews will clearly show the ManagedObjectStoragePolicy being replaced. The affected resource is, of course, the ManagedObjectStoragePolicy itself.

Pulumi About

Here's the output of pulumi about from the environment where this issue was observed:

CLI          
Version      3.198.0
Go Version   go1.25.1
Go Compiler  gc

Plugins
KIND      NAME     VERSION
language  go       3.198.0
resource  upcloud  0.5.3

Host     
OS       darwin
Version  15.6.1
Arch     arm64

This project is written in go: executable='/Users/oula/.local/share/mise/installs/go/1.25.1/bin/go' version='go version go1.25.1 darwin/arm64'

Current Stack: bug-repro/dev

TYPE                                                                 URN
pulumi:pulumi:Stack                                                  urn:pulumi:dev::bug-repro::pulumi:pulumi:Stack::bug-repro-dev
pulumi:providers:upcloud                                             urn:pulumi:dev::bug-repro::pulumi:providers:upcloud::default_0_5_3_github_/api.github.com/UpCloudLtd/pulumi-upcloud
upcloud:index/managedObjectStorage:ManagedObjectStorage              urn:pulumi:dev::bug-repro::upcloud:index/managedObjectStorage:ManagedObjectStorage::this
upcloud:index/managedObjectStoragePolicy:ManagedObjectStoragePolicy  urn:pulumi:dev::bug-repro::upcloud:index/managedObjectStoragePolicy:ManagedObjectStoragePolicy::this


Found no pending operations associated with dev

Backend        
Name           pulumi.com
URL            https://app.pulumi.com/oula-kuuva
User           oula-kuuva
Organizations  oula-kuuva
Token type     personal

Dependencies:
NAME                                      VERSION
github.com/pulumi/pulumi/sdk/v3           v3.198.0
github.com/UpCloudLtd/pulumi-upcloud/sdk  v0.5.3

Pulumi locates its logs in /var/folders/1y/httmdjmd02qf57f9zd3py6kw0000gn/T/ by default

Additional Context

It's suspected that this issue might stem from the underlying Terraform provider, but further investigation would be needed to confirm. If you're curious, feel free to dig into the Terraform provider code and see if you can spot the culprit!

Contributing

If you've run into this issue, give it a 👍 reaction to show your support. If you're feeling adventurous and want to contribute a fix, leave a comment and link to your pull request. Let's make the UpCloud Pulumi provider even better together!

In Summary

The key takeaway here is that omitting the Description field in your ManagedObjectStoragePolicy can lead to unnecessary resource recreations. The simple fix is to explicitly set the Description, even if it's just to an empty string. This ensures that Pulumi correctly manages the resource's state and avoids unwanted replacements. Keep this in mind, and you'll save yourself some headaches down the road! Happy Pulumi-ing, guys!