drakkan / terraform-provider-sftpgo

Terraform provider for SFTPGo
Apache License 2.0
24 stars 7 forks source link

User creation with SFTP backend comes back with 'Error: Provider produced inconsistent result after apply' #3

Closed mcowart123 closed 9 months ago

mcowart123 commented 10 months ago

Example terraform:

resource "sftpgo_user" "user" {
  username = "test"
  password = "test"
  status = 1
  home_dir = "/local/test"

  filesystem = {
    provider = 5

    sftpconfig = {
      endpoint = "${azurerm_storage_account.storage.name}.blob.core.windows.net"
      username = "${azurerm_storage_account.storage.name}.test"
      password = "test"

      buffer_size = 5
      prefix = "/"      
    }
  }

  permissions = {
    "/" = "*"
  }
}

Output of terraform apply:

sftpgo_user.user: Creating...
╷
│ Error: Provider produced inconsistent result after apply
│ 
│ When applying changes to sftpgo_user.user, provider "provider[\"registry.terraform.io/drakkan/sftpgo\"]" produced an unexpected new value: .filesystem.sftpconfig.endpoint: was
│ cty.StringVal("engeastus2cf.blob.core.windows.net"), but now cty.StringVal("engeastus2cf.blob.core.windows.net:22").
│ 
│ This is a bug in the provider, which should be reported in the provider's own issue tracker.
╵
Releasing state lock. This may take a few moments...

The user is successfully created in sftp-go.

Output afterwards of terraform-plan:

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # sftpgo_user.user is tainted, so must be replaced
-/+ resource "sftpgo_user" "user" {
      ~ created_at                  = 1699573354163 -> (known after apply)
      ~ filesystem                  = {
          ~ sftpconfig = {
              ~ endpoint    = "engeastus2cf.blob.core.windows.net:22" -> "engeastus2cf.blob.core.windows.net"
                # (4 unchanged attributes hidden)
            }
            # (1 unchanged attribute hidden)
        }
      ~ filters                     = {
          + allow_api_key_auth        = (known after apply)
          + allowed_ip                = (known after apply)
          + bandwidth_limits          = (known after apply)
          + check_password_disabled   = (known after apply)
          + default_shares_expiration = (known after apply)
          + denied_ip                 = (known after apply)
          + denied_login_methods      = (known after apply)
          + denied_protocols          = (known after apply)
          + disable_fs_checks         = (known after apply)
          + external_auth_cache_time  = (known after apply)
          + external_auth_disabled    = (known after apply)
          + file_patterns             = (known after apply)
          + ftp_security              = (known after apply)
          + is_anonymous              = (known after apply)
          + max_upload_file_size      = (known after apply)
          + password_expiration       = (known after apply)
          + password_strength         = (known after apply)
          + pre_login_disabled        = (known after apply)
          + require_password_change   = (known after apply)
          + start_directory           = (known after apply)
          + tls_username              = (known after apply)
          + two_factor_protocols      = (known after apply)
          + user_type                 = (known after apply)
          + web_client                = (known after apply)
        } -> (known after apply)
      + first_download              = (known after apply)
      + first_upload                = (known after apply)
      ~ id                          = "test" -> (known after apply)
      + last_login                  = (known after apply)
      ~ last_password_change        = 1699573354162 -> (known after apply)
      + last_quota_update           = (known after apply)
      ~ updated_at                  = 1699573354163 -> (known after apply)
      + used_download_data_transfer = (known after apply)
      + used_quota_files            = (known after apply)
      + used_quota_size             = (known after apply)
      + used_upload_data_transfer   = (known after apply)
        # (5 unchanged attributes hidden)
    }

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

To clean up requires a terraform state rm.

mcowart123 commented 10 months ago

Version is 0.0.8 (current latest)

Edit: It looks like adding the port to the end of the endpoint fixes the issue:

endpoint = "${azurerm_storage_account.storage.name}.blob.core.windows.net:22"
drakkan commented 10 months ago

Version is 0.0.8 (current latest)

Edit: It looks like adding the port to the end of the endpoint fixes the issue:

endpoint = "${azurerm_storage_account.storage.name}.blob.core.windows.net:22"

This is the right way, SFTPGo automatically adds :22 on save if the port is missing. Terraform expects to get the same value set, so you need to add :22 yourself and not rely on server-side input sanitization when using Terraform

mcowart123 commented 10 months ago

If port is required it should fail on the plan step and not allow the state to become tainted.

drakkan commented 10 months ago

If port is required it should fail on the plan step and not allow the state to become tainted.

yes, we can improve the validation. Feel free to send a PR, thank you!