terraform-plugin-sdk icon indicating copy to clipboard operation
terraform-plugin-sdk copied to clipboard

Terraform apply always shows a change for unset optional boolean with default of false

Open techBeck03 opened this issue 4 years ago • 1 comments

SDK version

github.com/hashicorp/terraform-plugin-sdk/v2 v2.4.0

Relevant provider source code

func guacamoleUser() *schema.Resource {
	return &schema.Resource{
		CreateContext: resourceUserCreate,
		ReadContext:   resourceUserRead,
		UpdateContext: resourceUserUpdate,
		DeleteContext: resourceUserDelete,
		Schema: map[string]*schema.Schema{
			"username": {
				Type:        schema.TypeString,
				Description: "Username of guacamole user",
				Required:    true,
				ForceNew:    true,
			},
			"last_active": {
				Type:        schema.TypeString,
				Description: "Epoch time string of last user activity",
				Computed:    true,
			},
			"attributes": {
				Type:        schema.TypeList,
				Description: "Attributes of guacamole user",
				Optional:    true,
				MaxItems:    1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"organizational_role": {
							Type:        schema.TypeString,
							Description: "Organizational role of user",
							Optional:    true,
							Default:     "",
						},
						"full_name": {
							Type:        schema.TypeString,
							Description: "Full name of user",
							Optional:    true,
							Default:     "",
						},
						"email": {
							Type:        schema.TypeString,
							Description: "Email of user",
							Optional:    true,
							Default:     "",
						},
						"expired": {
							Type:        schema.TypeBool,
							Description: "Whether the user is expired",
							Optional:    true,
							Default:     false,
						},
						"timezone": {
							Type:        schema.TypeString,
							Description: "Timezone of user",
							Optional:    true,
							Default:     "",
						},
						"access_window_start": {
							Type:        schema.TypeString,
							Description: "Access window start time for user",
							Optional:    true,
							Default:     "",
						},
						"access_window_end": {
							Type:        schema.TypeString,
							Description: "Access window end time for user",
							Optional:    true,
							Default:     "",
						},
						"disabled": {
							Type:        schema.TypeBool,
							Description: "Whether account is disabled",
							Optional:    true,
							Default:     false,
						},
						"valid_from": {
							Type:        schema.TypeString,
							Description: "Start date for when user is valid",
							Optional:    true,
							Default:     "",
						},
						"valid_until": {
							Type:        schema.TypeString,
							Description: "End date for when user is valid",
							Optional:    true,
							Default:     "",
						},
					},
				},
			},
			"group_membership": {
				Type:        schema.TypeSet,
				Description: "Groups this user is a member of",
				Optional:    true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},
			"system_permissions": {
				Type:        schema.TypeSet,
				Description: "System permissions assigned to user",
				Optional:    true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},
		},
	}
}

Terraform Configuration Files

...

Debug Output

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

2021/01/06 08:53:23 [DEBUG] command: asking for input: "Do you want to perform these actions?"
  # guacamole_user.test_user will be updated in-place
  ~ resource "guacamole_user" "test_user" {
        id                 = "testUser"
        # (4 unchanged attributes hidden)

      - attributes {
          - disabled = false -> null
          - expired  = false -> null
        }
    }

Plan: 0 to add, 1 to change, 0 to destroy.

Config snippets

main.tf

resource "guacamole_user" "test_user" {
    username = "testUser"
}

terraform.tfstate

{
  "version": 4,
  "terraform_version": "0.14.3",
  "serial": 234,
  "lineage": "06621170-99a5-4601-6744-02b6e5831bb1",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "guacamole_user",
      "name": "test_user",
      "provider": "provider[\"techbeck03.com/techbeck03/guacamole\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "attributes": [
              {
                "access_window_end": "",
                "access_window_start": "",
                "disabled": false,
                "email": "",
                "expired": false,
                "full_name": "",
                "organizational_role": "",
                "timezone": "",
                "valid_from": "",
                "valid_until": ""
              }
            ],
            "group_membership": [],
            "id": "testUser",
            "last_active": "0",
            "system_permissions": [],
            "username": "testUser"
          },
          "sensitive_attributes": [],
          "private": "bnVsbA=="
        }
      ]
    }
  ]
}

Expected Behavior

On the first terraform apply the resource should be created and the state file should reflect the one shown above. Subsequent calls to terraform apply without any changes to resource definition should show no pending changes.

Actual Behavior

On the first terraform apply everything works as expected and the above state file is created (and looks as expected). Subsequent calls to terraform apply always show the following pending changes:

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

2021/01/06 08:53:23 [DEBUG] command: asking for input: "Do you want to perform these actions?"
  # guacamole_user.test_user will be updated in-place
  ~ resource "guacamole_user" "test_user" {
        id                 = "testUser"
        # (4 unchanged attributes hidden)

      - attributes {
          - disabled = false -> null
          - expired  = false -> null
        }
    }

Plan: 0 to add, 1 to change, 0 to destroy.

This happens every time i do a terraform apply even though nothing has changed within the resource definition.

Steps to Reproduce

  1. terraform init
  2. terraform apply

References

techBeck03 avatar Jan 06 '21 15:01 techBeck03

Working with a custom provider and this is happening to me too. I only have issues when I use _ = d.Set("project", project). As long as I don't actually set anything other than the id, it works. Sort of. If I don't set anything then I can't actually update the state from the API I'm calling.

EDIT: This fixed me up https://github.com/hashicorp/terraform-provider-aws/issues/17161#issuecomment-762942937

rdeusser avatar Aug 02 '22 19:08 rdeusser