Repository: hashicorp/terraform-provider-vault

Issue:
[Bug]: vault_jwt_auth_backend tune block always updates unless config matches Vault server defaults
### Terraform Core Version

1.7.5

### Terraform Vault Provider Version

4.2.0

### Vault Server Version

1.15.7+ent

### Affected Resource(s)

- vault_jwt_auth_backend

### Expected Behavior

There should be no changes reported by Terraform if the configured `tune` block parameters have not changed.

### Actual Behavior

Terraform always reports that the `vault_jwt_auth_backend` resource will be updated in-place unless all of the tune parameters (`default_lease_ttl`, `max_lease_ttl`, and `token_type` are currently the only ones with default values) match the defaults (I'm assuming as reported by the target Vault server, rather than defaults set within the provider code). This includes when the `tune` block exists but has no attributes configured (as none of the attributes are required).

### Relevant Error/Panic Output Snippet

```shell
$ terraform apply -auto-approve

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # vault_jwt_auth_backend.jwt_backend will be created
  + resource "vault_jwt_auth_backend" "jwt_backend" {
      + accessor           = (known after apply)
      + bound_issuer       = "https://token.actions.githubusercontent.com"
      + disable_remount    = false
      + id                 = (known after apply)
      + local              = false
      + namespace_in_state = true
      + oidc_discovery_url = "https://token.actions.githubusercontent.com"
      + path               = "jwt-test"
      + tune               = [
          + {
              + allowed_response_headers     = []
              + audit_non_hmac_request_keys  = []
              + audit_non_hmac_response_keys = []
              + default_lease_ttl            = ""
              + listing_visibility           = ""
              + max_lease_ttl                = ""
              + passthrough_request_headers  = []
              + token_type                   = "default-service"
            },
        ]
      + type               = "jwt"
    }

Plan: 1 to add, 0 to change, 0 to destroy.
vault_jwt_auth_backend.jwt_backend: Creating...
vault_jwt_auth_backend.jwt_backend: Creation complete after 1s [id=jwt-test]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

$ terraform apply -auto-approve
vault_jwt_auth_backend.jwt_backend: Refreshing state... [id=jwt-test]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # vault_jwt_auth_backend.jwt_backend will be updated in-place
  ~ resource "vault_jwt_auth_backend" "jwt_backend" {
        id                     = "jwt-test"
      ~ tune                   = [
          - {
              - allowed_response_headers     = []
              - audit_non_hmac_request_keys  = []
              - audit_non_hmac_response_keys = []
              - default_lease_ttl            = "768h"
              - listing_visibility           = ""
              - max_lease_ttl                = "768h"
              - passthrough_request_headers  = []
              - token_type                   = "default-service"
            },
          + {
              + allowed_response_headers     = []
              + audit_non_hmac_request_keys  = []
              + audit_non_hmac_response_keys = []
              + default_lease_ttl            = ""
              + listing_visibility           = ""
              + max_lease_ttl                = ""
              + passthrough_request_headers  = []
              + token_type                   = "default-service"
            },
        ]
        # (12 unchanged attributes hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.
vault_jwt_auth_backend.jwt_backend: Modifying... [id=jwt-test]
vault_jwt_auth_backend.jwt_backend: Modifications complete after 0s [id=jwt-test]
```


### Terraform Configuration Files

Sample configuration that always produces an update in-place operation:
```hcl
terraform {
  required_providers {
    vault = {
      source  = "hashicorp/vault"
      version = "= 4.2.0"
    }
  }
  required_version = ">= 1.7.0"
}

resource "vault_jwt_auth_backend" "jwt_backend" {
  path = "jwt-test"
  type = "jwt"

  bound_issuer       = "https://token.actions.githubusercontent.com"
  oidc_discovery_url = "https://token.actions.githubusercontent.com"

  tune {
    token_type = "default-service"  
  }
}
```

Sample configuration that reports "No changes" (I assume the values for `default_lease_ttl`, and `max_lease_ttl` would need to be different if there were different global default values configured for the Vault server):

```hcl
terraform {
  required_providers {
    vault = {
      source  = "hashicorp/vault"
      version = "= 4.2.0"
    }
  }
  required_version = ">= 1.7.0"
}

resource "vault_jwt_auth_backend" "jwt_backend" {
  path = "jwt-test"
  type = "jwt"

  bound_issuer       = "https://token.actions.githubusercontent.com"
  oidc_discovery_url = "https://token.actions.githubusercontent.com"

  tune {
    default_lease_ttl = "768h"
    max_lease_ttl     = "768h"
    token_type        = "default-service"
  }
}
```

### Steps to Reproduce

1. Apply the first Terraform code above to provision the JWT auth backend with `terraform apply`
2. Re-apply the same Terraform code with `terraform apply`
3. Note Terraform reports 1 resource to change when there should be no changes to the infrastructure necessary because the Terraform code has not changed

### Debug Output

_No response_

### Panic Output

_No response_

### Important Factoids

_No response_

### References

Possibly related to https://github.com/hashicorp/terraform-provider-vault/issues/1172 ? Unclear if the behaviour reported in that issue with the `provider_config` block for OIDC is using the same provider machinery for this issue with the `tune` block.

### Would you like to implement a fix?

None

Discussion:

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3e1bd179..6aa0224d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,8 @@ BUILD:
 
 BUGS:
 * Fix panic when reading the `vault_gcp_secret_backend` resource. ([#2549](https://github.com/hashicorp/terraform-provider-vault/pull/2549))
+* Fix the tune block issue where it always updates unless field values match Vault server defaults
+  * `vault_jwt_auth_backend` resource ([#2560](https://github.com/hashicorp/terraform-provider-vault/pull/2560))
 
 ## 5.1.0 (Jul 9, 2025)
 
diff --git a/internal/consts/consts.go b/internal/consts/consts.go
index 0c766766..f57230fa 100644
--- a/internal/consts/consts.go
+++ b/internal/consts/consts.go
@@ -59,9 +59,11 @@ const (
 	FieldDescription                    = "description"
 	FieldTTL                            = "ttl"
 	FieldMaxTTL                         = "max_ttl"
-	FieldDefaultLeaseTTL                = "default_lease_ttl_seconds"
+	FieldDefaultLeaseTTL                = "default_lease_ttl"
+	FieldDefaultLeaseTTLSeconds         = "default_lease_ttl_seconds"
 	FieldDefaultTTL                     = "default_ttl"
-	FieldMaxLeaseTTL                    = "max_lease_ttl_seconds"
+	FieldMaxLeaseTTL                    = "max_lease_ttl"
+	FieldMaxLeaseTTLSeconds             = "max_lease_ttl_seconds"
 	FieldAuditNonHMACRequestKeys        = "audit_non_hmac_request_keys"
 	FieldAuditNonHMACResponseKeys       = "audit_non_hmac_response_keys"
 	FieldLastPassword                   = "last_password"
@@ -487,6 +489,7 @@ const (
 	FieldNoStoreMetadata                = "no_store_metadata"
 	FieldSerialNumberSource             = "serial_number_source"
 	FieldCertMetadata                   = "cert_metadata"
+	FieldTokenType                      = "token_type"
 
 	FieldInput                    = "input"
 	FieldKeyVersion               = "key_version"
diff --git a/internal/provider/meta.go b/internal/provider/meta.go
index f618cf94..c0e69189 100644
--- a/internal/provider/meta.go
+++ b/internal/provider/meta.go
@@ -594,7 +594,7 @@ func createChildToken(d *schema.ResourceData, c *api.Client, namespace string) (
 	// Caution is still required with state files since not all secrets
 	// can explicitly be revoked, and this limited scope won't apply to
 	// any secrets that are *written* by Terraform to Vault.
-	ttl := GetResourceDataInt(d, consts.FieldMaxLeaseTTL, "TERRAFORM_VAULT_MAX_TTL", 1200)
+	ttl := GetResourceDataInt(d, consts.FieldMaxLeaseTTLSeconds, "TERRAFORM_VAULT_MAX_TTL", 1200)
 	childTokenLease, err := clone.Auth().Token().Create(&api.TokenCreateRequest{
 		DisplayName:    tokenName,
 		TTL:            fmt.Sprintf("%ds", ttl),
diff --git a/vault/auth_mount.go b/vault/auth_mount.go
index dfe3fb8a..1e9b7465 100644
--- a/vault/auth_mount.go
+++ b/vault/auth_mount.go
@@ -11,6 +11,7 @@ import (
 	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+	"github.com/hashicorp/terraform-provider-vault/internal/consts"
 	"github.com/hashicorp/terraform-provider-vault/internal/provider"
 	"github.com/hashicorp/vault/api"
 )
@@ -24,49 +25,49 @@ func authMountTuneSchema() *schema.Schema {
 		ConfigMode: schema.SchemaConfigModeAttr,
 		Elem: &schema.Resource{
 			Schema: map[string]*schema.Schema{
-				"default_lease_ttl": {
+				consts.FieldDefaultLeaseTTL: {
 					Type:         schema.TypeString,
 					Optional:     true,
 					Description:  "Specifies the default time-to-live duration. This overrides the global default. A value of 0 is equivalent to the system default TTL",
 					ValidateFunc: provider.ValidateDuration,
 				},
-				"max_lease_ttl": {
+				consts.FieldMaxLeaseTTL: {
 					Type:         schema.TypeString,
 					Optional:     true,
 					Description:  "Specifies the maximum time-to-live duration. This overrides the global default. A value of 0 are equivalent and set to the system max TTL.",
 					ValidateFunc: provider.ValidateDuration,
 				},
-				"audit_non_hmac_request_keys": {
+				consts.FieldAuditNonHMACRequestKeys: {
 					Type:        schema.TypeList,
 					Optional:    true,
 					Description: "Specifies the list of keys that will not be HMAC'd by audit devices in the request data object.",
 					Elem:        &schema.Schema{Type: schema.TypeString},
 				},
-				"audit_non_hmac_response_keys": {
+				consts.FieldAuditNonHMACResponseKeys: {
 					Type:        schema.TypeList,
 					Optional:    true,
 					Description: "Specifies the list of keys that will not be HMAC'd by audit devices in the response data object.",
 					Elem:        &schema.Schema{Type: schema.TypeString},
 				},
-				"listing_visibility": {
+				consts.FieldListingVisibility: {
 					Type:         schema.TypeString,
 					Optional:     true,
 					Description:  "Specifies whether to show this mount in the UI-specific listing endpoint. Valid values are \"unauth\" or \"hidden\". If not set, behaves like \"hidden\".",
 					ValidateFunc: validation.StringInSlice([]string{"unauth", "hidden"}, false),
 				},
-				"passthrough_request_headers": {
+				consts.FieldPassthroughRequestHeaders: {
 					Type:        schema.TypeList,
 					Optional:    true,
 					Description: "List of headers to whitelist and pass from the request to the backend.",
 					Elem:        &schema.Schema{Type: schema.TypeString},
 				},
-				"allowed_response_headers": {
+				consts.FieldAllowedResponseHeaders: {
 					Type:        schema.TypeList,
 					Optional:    true,
 					Description: "List of headers to whitelist and allowing a plugin to include them in the response.",
 					Elem:        &schema.Schema{Type: schema.TypeString},
 				},
-				"token_type": {
+				consts.FieldTokenType: {
 					Type:         schema.TypeString,
 					Optional:     true,
 					Description:  "Specifies the type of tokens that should be returned by the mount.",
diff --git a/vault/resource_aws_secret_backend.go b/vault/resource_aws_secret_backend.go
index c7c559be..8ea71ef6 100644
--- a/vault/resource_aws_secret_backend.go
+++ b/vault/resource_aws_secret_backend.go
@@ -60,13 +60,13 @@ func awsSecretBackendResource() *schema.Resource {
 				Optional:    true,
 				Description: "Human-friendly description of the mount for the backend.",
 			},
-			consts.FieldDefaultLeaseTTL: {
+			consts.FieldDefaultLeaseTTLSeconds: {
 				Type:        schema.TypeInt,
 				Optional:    true,
 				Computed:    true,
 				Description: "Default lease duration for secrets in seconds",
 			},
-			consts.FieldMaxLeaseTTL: {
+			consts.FieldMaxLeaseTTLSeconds: {
 				Type:        schema.TypeInt,
 				Optional:    true,
 				Computed:    true,
@@ -159,8 +159,8 @@ func awsSecretBackendResource() *schema.Resource {
 		consts.FieldPath,
 		consts.FieldType,
 		consts.FieldDescription,
-		consts.FieldDefaultLeaseTTL,
-		consts.FieldMaxLeaseTTL,
+		consts.FieldDefaultLeaseTTLSeconds,
+		consts.FieldMaxLeaseTTLSeconds,
 		consts.FieldIdentityTokenKey,
 		consts.FieldLocal,
 	))
diff --git a/vault/resource_consul_secret_backend.go b/vault/resource_consul_secret_backend.go
index c2a5f4a6..cae06917 100644
--- a/vault/resource_consul_secret_backend.go
+++ b/vault/resource_consul_secret_backend.go
@@ -116,8 +116,8 @@ func consulSecretBackendResource() *schema.Resource {
 		consts.FieldPath,
 		consts.FieldType,
 		consts.FieldDescription,
-		consts.FieldDefaultLeaseTTL,
-		consts.FieldMaxLeaseTTL,
+		consts.FieldDefaultLeaseTTLSeconds,
+		consts.FieldMaxLeaseTTLSeconds,
 		consts.FieldLocal,
 	))
 
diff --git a/vault/resource_gcp_secret_backend.go b/vault/resource_gcp_secret_backend.go
index ad7bf277..3fac311f 100644
--- a/vault/resource_gcp_secret_backend.go
+++ b/vault/resource_gcp_secret_backend.go
@@ -6,11 +6,12 @@ package vault
 import (
 	"context"
 	"fmt"
-	"github.com/hashicorp/go-cty/cty"
-	"github.com/hashicorp/vault/api"
 	"log"
 	"strings"
 
+	"github.com/hashicorp/go-cty/cty"
+	"github.com/hashicorp/vault/api"
+
 	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 	"github.com/hashicorp/terraform-provider-vault/internal/consts"
@@ -74,13 +75,13 @@ func gcpSecretBackendResource(name string) *schema.Resource {
 				Optional:    true,
 				Description: "Human-friendly description of the mount for the backend.",
 			},
-			consts.FieldDefaultLeaseTTL: {
+			consts.FieldDefaultLeaseTTLSeconds: {
 				Type:        schema.TypeInt,
 				Optional:    true,
 				Default:     "",
 				Description: "Default lease duration for secrets in seconds",
 			},
-			consts.FieldMaxLeaseTTL: {
+			consts.FieldMaxLeaseTTLSeconds: {
 				Type:        schema.TypeInt,
 				Optional:    true,
 				Default:     "",
@@ -127,8 +128,8 @@ func gcpSecretBackendResource(name string) *schema.Resource {
 		consts.FieldPath,
 		consts.FieldType,
 		consts.FieldDescription,
-		consts.FieldDefaultLeaseTTL,
-		consts.FieldMaxLeaseTTL,
+		consts.FieldDefaultLeaseTTLSeconds,
+		consts.FieldMaxLeaseTTLSeconds,
 		consts.FieldIdentityTokenKey,
 		consts.FieldAccessor,
 		consts.FieldLocal,
@@ -273,10 +274,10 @@ func gcpSecretBackendUpdate(ctx context.Context, d *schema.ResourceData, meta in
 	useAPIVer117Ent := provider.IsAPISupported(meta, provider.VaultVersion117) && provider.IsEnterpriseSupported(meta)
 	useAPIVer119Ent := provider.IsAPISupported(meta, provider.VaultVersion119) && provider.IsEnterpriseSupported(meta)
 
-	if d.HasChanges(consts.FieldDefaultLeaseTTL, consts.FieldMaxLeaseTTL, consts.FieldIdentityTokenKey) {
+	if d.HasChanges(consts.FieldDefaultLeaseTTLSeconds, consts.FieldMaxLeaseTTLSeconds, consts.FieldIdentityTokenKey) {
 		config := api.MountConfigInput{
-			DefaultLeaseTTL: fmt.Sprintf("%ds", d.Get(consts.FieldDefaultLeaseTTL)),
-			MaxLeaseTTL:     fmt.Sprintf("%ds", d.Get(consts.FieldMaxLeaseTTL)),
+			DefaultLeaseTTL: fmt.Sprintf("%ds", d.Get(consts.FieldDefaultLeaseTTLSeconds)),
+			MaxLeaseTTL:     fmt.Sprintf("%ds", d.Get(consts.FieldMaxLeaseTTLSeconds)),
 		}
 
 		if useAPIVer117Ent {
diff --git a/vault/resource_jwt_auth_backend.go b/vault/resource_jwt_auth_backend.go
index 0cf4af29..bda71118 100644
--- a/vault/resource_jwt_auth_backend.go
+++ b/vault/resource_jwt_auth_backend.go
@@ -339,7 +339,15 @@ func jwtAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta interf
 	if err != nil {
 		return diag.Errorf("error reading tune information from Vault: %s", err)
 	}
-	if err := d.Set("tune", []map[string]interface{}{rawTune}); err != nil {
+
+	input, err := retrieveMountConfigInput(d)
+	if err != nil {
+		return diag.Errorf("error retrieving tune configuration from state: %s", err)
+	}
+
+	mergedTune := mergeAuthMethodTune(rawTune, input)
+
+	if err := d.Set("tune", []map[string]interface{}{mergedTune}); err != nil {
 		log.Printf("[ERROR] Error when setting tune config from path %q to state: %s", path+"/tune", err)
 		return diag.FromErr(err)
 	}
diff --git a/vault/resource_mount.go b/vault/resource_mount.go
index 048c4e48..d3e20fab 100644
--- a/vault/resource_mount.go
+++ b/vault/resource_mount.go
@@ -6,10 +6,11 @@ package vault
 import (
 	"context"
 	"fmt"
-	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
 	"log"
 	"time"
 
+	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 	"github.com/hashicorp/vault/api"
 
@@ -40,7 +41,7 @@ func getMountSchema(excludes ...string) schemaMap {
 			Required:    false,
 			Description: "Human-friendly description of the mount",
 		},
-		consts.FieldDefaultLeaseTTL: {
+		consts.FieldDefaultLeaseTTLSeconds: {
 			Type:        schema.TypeInt,
 			Required:    false,
 			Optional:    true,
@@ -49,7 +50,7 @@ func getMountSchema(excludes ...string) schemaMap {
 			Description: "Default lease duration for tokens and secrets in seconds",
 		},
 
-		consts.FieldMaxLeaseTTL: {
+		consts.FieldMaxLeaseTTLSeconds: {
 			Type:        schema.TypeInt,
 			Required:    false,
 			Optional:    true,
@@ -215,8 +216,8 @@ func createMount(ctx context.Context, d *schema.ResourceData, meta interface{},
 		Type:        mountType,
 		Description: d.Get(consts.FieldDescription).(string),
 		Config: api.MountConfigInput{
-			DefaultLeaseTTL: fmt.Sprintf("%ds", d.Get(consts.FieldDefaultLeaseTTL)),
-			MaxLeaseTTL:     fmt.Sprintf("%ds", d.Get(consts.FieldMaxLeaseTTL)),
+			DefaultLeaseTTL: fmt.Sprintf("%ds", d.Get(consts.FieldDefaultLeaseTTLSeconds)),
+			MaxLeaseTTL:     fmt.Sprintf("%ds", d.Get(consts.FieldMaxLeaseTTLSeconds)),
 			ForceNoCache:    d.Get(consts.FieldForceNoCache).(bool),
 		},
 		Local:                 d.Get(consts.FieldLocal).(bool),
@@ -288,8 +289,8 @@ func updateMount(ctx context.Context, d *schema.ResourceData, meta interface{},
 	}
 
 	config := api.MountConfigInput{
-		DefaultLeaseTTL: fmt.Sprintf("%ds", d.Get(consts.FieldDefaultLeaseTTL)),
-		MaxLeaseTTL:     fmt.Sprintf("%ds", d.Get(consts.FieldMaxLeaseTTL)),
+		DefaultLeaseTTL: fmt.Sprintf("%ds", d.Get(consts.FieldDefaultLeaseTTLSeconds)),
+		MaxLeaseTTL:     fmt.Sprintf("%ds", d.Get(consts.FieldMaxLeaseTTLSeconds)),
 		Options:         mountOptions(d),
 	}
 
@@ -450,13 +451,13 @@ func readMount(ctx context.Context, d *schema.ResourceData, meta interface{}, ex
 	if err := d.Set(consts.FieldDescription, mount.Description); err != nil {
 		return err
 	}
-	if err := d.Set(consts.FieldDefaultLeaseTTL, mount.Config.DefaultLeaseTTL); err != nil {
+	if err := d.Set(consts.FieldDefaultLeaseTTLSeconds, mount.Config.DefaultLeaseTTL); err != nil {
 		return err
 	}
 	if err := d.Set(consts.FieldForceNoCache, mount.Config.ForceNoCache); err != nil {
 		return err
 	}
-	if err := d.Set(consts.FieldMaxLeaseTTL, mount.Config.MaxLeaseTTL); err != nil {
+	if err := d.Set(consts.FieldMaxLeaseTTLSeconds, mount.Config.MaxLeaseTTL); err != nil {
 		return err
 	}
 	if err := d.Set(consts.FieldAuditNonHMACRequestKeys, mount.Config.AuditNonHMACRequestKeys); err != nil {
diff --git a/vault/resource_nomad_secret_backend.go b/vault/resource_nomad_secret_backend.go
index 9d75589e..ab488a77 100644
--- a/vault/resource_nomad_secret_backend.go
+++ b/vault/resource_nomad_secret_backend.go
@@ -6,10 +6,11 @@ package vault
 import (
 	"context"
 	"fmt"
-	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
 	"log"
 	"strings"
 
+	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 	"github.com/hashicorp/terraform-provider-vault/internal/consts"
 	"github.com/hashicorp/terraform-provider-vault/internal/provider"
@@ -114,8 +115,8 @@ func nomadSecretAccessBackendResource() *schema.Resource {
 		consts.FieldPath,
 		consts.FieldType,
 		consts.FieldDescription,
-		consts.FieldDefaultLeaseTTL,
-		consts.FieldMaxLeaseTTL,
+		consts.FieldDefaultLeaseTTLSeconds,
+		consts.FieldMaxLeaseTTLSeconds,
 		consts.FieldLocal,
 	))
 
diff --git a/vault/resource_rabbitmq_secret_backend.go b/vault/resource_rabbitmq_secret_backend.go
index c8793453..708f05ee 100644
--- a/vault/resource_rabbitmq_secret_backend.go
+++ b/vault/resource_rabbitmq_secret_backend.go
@@ -5,10 +5,11 @@ package vault
 
 import (
 	"context"
-	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
 	"log"
 	"strings"
 
+	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 	"github.com/hashicorp/terraform-provider-vault/internal/consts"
 	"github.com/hashicorp/terraform-provider-vault/internal/provider"
@@ -95,8 +96,8 @@ func rabbitMQSecretBackendResource() *schema.Resource {
 		consts.FieldPath,
 		consts.FieldType,
 		consts.FieldDescription,
-		consts.FieldDefaultLeaseTTL,
-		consts.FieldMaxLeaseTTL,
+		consts.FieldDefaultLeaseTTLSeconds,
+		consts.FieldMaxLeaseTTLSeconds,
 	))
 
 	return r
diff --git a/vault/resource_terraform_cloud_secret_backend.go b/vault/resource_terraform_cloud_secret_backend.go
index 3f061557..5b1b123b 100644
--- a/vault/resource_terraform_cloud_secret_backend.go
+++ b/vault/resource_terraform_cloud_secret_backend.go
@@ -5,11 +5,12 @@ package vault
 
 import (
 	"context"
-	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
-	"github.com/hashicorp/terraform-provider-vault/util"
 	"log"
 	"strings"
 
+	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+	"github.com/hashicorp/terraform-provider-vault/util"
+
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 	"github.com/hashicorp/terraform-provider-vault/internal/consts"
 	"github.com/hashicorp/terraform-provider-vault/internal/provider"
@@ -82,8 +83,8 @@ func terraformCloudSecretBackendResource() *schema.Resource {
 		consts.FieldPath,
 		consts.FieldType,
 		consts.FieldDescription,
-		consts.FieldDefaultLeaseTTL,
-		consts.FieldMaxLeaseTTL,
+		consts.FieldDefaultLeaseTTLSeconds,
+		consts.FieldMaxLeaseTTLSeconds,
 	))
 
 	return r
diff --git a/vault/structures.go b/vault/structures.go
index c420545f..708bf2e0 100644
--- a/vault/structures.go
+++ b/vault/structures.go
@@ -5,12 +5,15 @@ package vault
 
 import (
 	"encoding/json"
+	"fmt"
 	"log"
 	"strings"
 	"time"
 
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 	"github.com/hashicorp/vault/api"
 
+	"github.com/hashicorp/terraform-provider-vault/internal/consts"
 	"github.com/hashicorp/terraform-provider-vault/util"
 )
 
@@ -21,28 +24,28 @@ func expandAuthMethodTune(rawL []interface{}) api.MountConfigInput {
 	}
 	raw := rawL[0].(map[string]interface{})
 
-	if v, ok := raw["default_lease_ttl"]; ok {
+	if v, ok := raw[consts.FieldDefaultLeaseTTL]; ok {
 		data.DefaultLeaseTTL = v.(string)
 	}
-	if v, ok := raw["max_lease_ttl"]; ok {
+	if v, ok := raw[consts.FieldMaxLeaseTTL]; ok {
 		data.MaxLeaseTTL = v.(string)
 	}
-	if v, ok := raw["audit_non_hmac_request_keys"]; ok {
+	if v, ok := raw[consts.FieldAuditNonHMACRequestKeys]; ok {
 		data.AuditNonHMACRequestKeys = expandStringSliceWithEmpty(v.([]interface{}), true)
 	}
-	if v, ok := raw["audit_non_hmac_response_keys"]; ok {
+	if v, ok := raw[consts.FieldAuditNonHMACResponseKeys]; ok {
 		data.AuditNonHMACResponseKeys = expandStringSliceWithEmpty(v.([]interface{}), true)
 	}
-	if v, ok := raw["listing_visibility"]; ok {
+	if v, ok := raw[consts.FieldListingVisibility]; ok {
 		data.ListingVisibility = v.(string)
 	}
-	if v, ok := raw["passthrough_request_headers"]; ok {
+	if v, ok := raw[consts.FieldPassthroughRequestHeaders]; ok {
 		data.PassthroughRequestHeaders = expandStringSliceWithEmpty(v.([]interface{}), true)
 	}
-	if v, ok := raw["allowed_response_headers"]; ok {
+	if v, ok := raw[consts.FieldAllowedResponseHeaders]; ok {
 		data.AllowedResponseHeaders = expandStringSliceWithEmpty(v.([]interface{}), true)
 	}
-	if v, ok := raw["token_type"]; ok {
+	if v, ok := raw[consts.FieldTokenType]; ok {
 		data.TokenType = v.(string)
 	}
 	return data
@@ -51,25 +54,72 @@ func expandAuthMethodTune(rawL []interface{}) api.MountConfigInput {
 func flattenAuthMethodTune(dt *api.MountConfigOutput) map[string]interface{} {
 	m := make(map[string]interface{})
 
-	m["default_lease_ttl"] = flattenVaultDuration(dt.DefaultLeaseTTL)
-	m["max_lease_ttl"] = flattenVaultDuration(dt.MaxLeaseTTL)
+	m[consts.FieldDefaultLeaseTTL] = flattenVaultDuration(dt.DefaultLeaseTTL)
+	m[consts.FieldMaxLeaseTTL] = flattenVaultDuration(dt.MaxLeaseTTL)
 	if len(dt.AuditNonHMACRequestKeys) > 0 && dt.AuditNonHMACRequestKeys[0] != "" {
-		m["audit_non_hmac_request_keys"] = flattenStringSlice(dt.AuditNonHMACRequestKeys)
+		m[consts.FieldAuditNonHMACRequestKeys] = flattenStringSlice(dt.AuditNonHMACRequestKeys)
 	}
 	if len(dt.AuditNonHMACResponseKeys) > 0 && dt.AuditNonHMACResponseKeys[0] != "" {
-		m["audit_non_hmac_response_keys"] = flattenStringSlice(dt.AuditNonHMACResponseKeys)
+		m[consts.FieldAuditNonHMACResponseKeys] = flattenStringSlice(dt.AuditNonHMACResponseKeys)
 	}
-	m["listing_visibility"] = dt.ListingVisibility
+	m[consts.FieldListingVisibility] = dt.ListingVisibility
 	if len(dt.PassthroughRequestHeaders) > 0 && dt.PassthroughRequestHeaders[0] != "" {
-		m["passthrough_request_headers"] = flattenStringSlice(dt.PassthroughRequestHeaders)
+		m[consts.FieldPassthroughRequestHeaders] = flattenStringSlice(dt.PassthroughRequestHeaders)
 	}
 	if len(dt.AllowedResponseHeaders) > 0 && dt.AllowedResponseHeaders[0] != "" {
-		m["allowed_response_headers"] = flattenStringSlice(dt.AllowedResponseHeaders)
+		m[consts.FieldAllowedResponseHeaders] = flattenStringSlice(dt.AllowedResponseHeaders)
 	}
-	m["token_type"] = dt.TokenType
+	m[consts.FieldTokenType] = dt.TokenType
 	return m
 }
 
+// retrieveMountConfigInput retrieves the tune block from the resource data
+// and converts it into a reference to api.MountConfigInput
+func retrieveMountConfigInput(d *schema.ResourceData) (*api.MountConfigInput, error) {
+	// If the tune block is not set, it means the user did not
+	// provide any values or the block is imported
+	tune, ok := d.GetOk("tune")
+	if !ok {
+		return nil, nil
+	}
+
+	tuneSchemaSet, ok := tune.(*schema.Set)
+	if !ok {
+		return nil, fmt.Errorf("error type asserting tune block: expected schema.Set, got %T", d.Get("tune"))
+	}
+
+	input := expandAuthMethodTune(tuneSchemaSet.List())
+	return &input, nil
+}
+
+// mergeAuthMethodTune merges the raw tune GET API response with the non-nil
+// *api.MountConfigInput parsed from the resource data.
+// Any field with the Vault APIs's global default effect will be set to empty
+// when the user did not provide a value even if the Vault API response returns non-empty.
+// This is to ensure the tune block reflects the user provided values.
+// See more details in the https://github.com/hashicorp/terraform-provider-vault/issues/2234
+func mergeAuthMethodTune(rawTune map[string]interface{}, input *api.MountConfigInput) map[string]interface{} {
+	// Merge the fields that have the global default effect
+	// github.com/hashicorp/terraform-provider-vault/vault/auth_mount.go
+	// If the input is nil
+	if input != nil {
+		if input.TokenType == "" {
+			rawTune[consts.FieldTokenType] = ""
+		}
+		if input.DefaultLeaseTTL == "" {
+			rawTune[consts.FieldDefaultLeaseTTL] = ""
+		}
+		if input.MaxLeaseTTL == "" {
+			rawTune[consts.FieldMaxLeaseTTL] = ""
+		}
+		if input.ListingVisibility == "" {
+			rawTune[consts.FieldListingVisibility] = ""
+		}
+	}
+
+	return rawTune
+}
+
 func expandStringSlice(configured []interface{}) []string {
 	vs := make([]string, 0, len(configured))
 	for _, v := range configured {



Test patch:
diff --git a/vault/resource_aws_secret_backend_test.go b/vault/resource_aws_secret_backend_test.go
index 247d76dc..3b8fc852 100644
--- a/vault/resource_aws_secret_backend_test.go
+++ b/vault/resource_aws_secret_backend_test.go
@@ -6,10 +6,11 @@ package vault
 import (
 	"context"
 	"fmt"
-	"github.com/hashicorp/terraform-provider-vault/internal/provider"
 	"regexp"
 	"testing"
 
+	"github.com/hashicorp/terraform-provider-vault/internal/provider"
+
 	"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
 	"github.com/hashicorp/terraform-plugin-testing/helper/resource"
 
@@ -32,8 +33,8 @@ func TestAccAWSSecretBackend_basic(t *testing.T) {
 				Check: resource.ComposeTestCheckFunc(
 					resource.TestCheckResourceAttr(resourceName, consts.FieldPath, path),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, "test description"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTL, "3600"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTL, "86400"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTLSeconds, "3600"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTLSeconds, "86400"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldAccessKey, accessKey),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldSecretKey, secretKey),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldRegion, "us-east-1"),
@@ -49,8 +50,8 @@ func TestAccAWSSecretBackend_basic(t *testing.T) {
 				Check: resource.ComposeTestCheckFunc(
 					resource.TestCheckResourceAttr(resourceName, consts.FieldPath, path),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, "test description updated"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTL, "1800"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTL, "43200"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTLSeconds, "1800"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTLSeconds, "43200"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldAccessKey, accessKey),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldSecretKey, secretKey),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldRegion, "us-west-1"),
@@ -64,8 +65,8 @@ func TestAccAWSSecretBackend_basic(t *testing.T) {
 				Check: resource.ComposeTestCheckFunc(
 					resource.TestCheckResourceAttr(resourceName, consts.FieldPath, path),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, "test description"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTL, "1800"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTL, "43200"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTLSeconds, "1800"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTLSeconds, "43200"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldAccessKey, ""),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldSecretKey, ""),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldRegion, "us-west-1"),
@@ -251,8 +252,8 @@ func TestAccAWSSecretBackend_remount(t *testing.T) {
 				Check: resource.ComposeTestCheckFunc(
 					resource.TestCheckResourceAttr(resourceName, consts.FieldPath, path),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, "test description"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTL, "3600"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTL, "86400"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTLSeconds, "3600"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTLSeconds, "86400"),
 				),
 			},
 			{
@@ -260,8 +261,8 @@ func TestAccAWSSecretBackend_remount(t *testing.T) {
 				Check: resource.ComposeTestCheckFunc(
 					resource.TestCheckResourceAttr(resourceName, consts.FieldPath, updatedPath),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, "test description"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTL, "3600"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTL, "86400"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTLSeconds, "3600"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTLSeconds, "86400"),
 				),
 			},
 			testutil.GetImportTestStep(resourceName, false, nil, consts.FieldSecretKey, consts.FieldDisableRemount),
diff --git a/vault/resource_jwt_auth_backend_test.go b/vault/resource_jwt_auth_backend_test.go
index 21ac08f1..8d875cef 100644
--- a/vault/resource_jwt_auth_backend_test.go
+++ b/vault/resource_jwt_auth_backend_test.go
@@ -12,8 +12,10 @@ import (
 
 	"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
 	"github.com/hashicorp/terraform-plugin-testing/helper/resource"
+	"github.com/hashicorp/vault/api"
 
 	"github.com/hashicorp/terraform-provider-vault/internal/consts"
+	"github.com/hashicorp/terraform-provider-vault/internal/provider"
 	"github.com/hashicorp/terraform-provider-vault/testutil"
 )
 
@@ -68,6 +70,14 @@ func TestAccJWTAuthBackend(t *testing.T) {
 						resource.TestCheckResourceAttr(resourceName, "bound_issuer", "api://default"),
 						resource.TestCheckResourceAttr(resourceName, "jwt_supported_algs.#", "1"),
 						resource.TestCheckResourceAttr(resourceName, "type", "jwt"),
+						resource.TestCheckResourceAttr(resourceName, "tune.0.passthrough_request_headers.#", "2"),
+						resource.TestCheckResourceAttr(resourceName, "tune.0.passthrough_request_headers.0", "X-Custom-Header"),
+						// ensure the global default effect from Vault tune API is ignored,
+						// these fields should stay empty
+						resource.TestCheckResourceAttr(resourceName, "tune.0.token_type", ""),
+						resource.TestCheckResourceAttr(resourceName, "tune.0.listing_visibility", ""),
+						resource.TestCheckResourceAttr(resourceName, "tune.0.default_lease_ttl", ""),
+						resource.TestCheckResourceAttr(resourceName, "tune.0.max_lease_ttl", ""),
 					)...,
 				),
 			},
@@ -79,6 +89,14 @@ func TestAccJWTAuthBackend(t *testing.T) {
 						resource.TestCheckResourceAttr(resourceName, "bound_issuer", "api://default"),
 						resource.TestCheckResourceAttr(resourceName, "jwt_supported_algs.#", "2"),
 						resource.TestCheckResourceAttr(resourceName, "type", "jwt"),
+						resource.TestCheckResourceAttr(resourceName, "tune.0.passthrough_request_headers.#", "2"),
+						resource.TestCheckResourceAttr(resourceName, "tune.0.passthrough_request_headers.0", "X-Custom-Header"),
+						// ensure the global default effect from Vault tune API is ignored,
+						// these fields should stay empty
+						resource.TestCheckResourceAttr(resourceName, "tune.0.token_type", ""),
+						resource.TestCheckResourceAttr(resourceName, "tune.0.listing_visibility", ""),
+						resource.TestCheckResourceAttr(resourceName, "tune.0.default_lease_ttl", ""),
+						resource.TestCheckResourceAttr(resourceName, "tune.0.max_lease_ttl", ""),
 					)...,
 				),
 			},
@@ -367,6 +385,9 @@ resource "vault_jwt_auth_backend" "jwt" {
   bound_issuer       = "%s"
   jwt_supported_algs = [%s]
   path               = "%s"
+  tune {
+	passthrough_request_headers = ["X-Custom-Header", "X-Forwarded-To"]
+  }
 `, oidcDiscoveryUrl, boundIssuer, supportedAlgs, path)
 
 	var fragments []string
@@ -707,3 +728,46 @@ EOT`
 		},
 	})
 }
+
+func TestAccJWTAuthBackend_importTuning(t *testing.T) {
+	testutil.SkipTestAcc(t)
+
+	path := acctest.RandomWithPrefix("jwt")
+	resourceType := "vault_jwt_auth_backend"
+	resourceName := resourceType + ".test"
+	var resAuth api.AuthMount
+	resource.Test(t, resource.TestCase{
+		PreCheck:                 func() { testutil.TestAccPreCheck(t) },
+		ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(context.Background(), t),
+		CheckDestroy:             testCheckMountDestroyed(resourceType, consts.MountTypeJWT, consts.FieldPath),
+		Steps: []resource.TestStep{
+			{
+				Config: testAccJWTAuthBackendConfig_tuning(path),
+				Check: testutil.TestAccCheckAuthMountExists(resourceName,
+					&resAuth,
+					testProvider.Meta().(*provider.ProviderMeta).MustGetClient()),
+			},
+			testutil.GetImportTestStep(resourceName, false, nil, "description", "disable_remount"),
+		},
+	})
+}
+
+func testAccJWTAuthBackendConfig_tuning(path string) string {
+	return fmt.Sprintf(`
+resource "vault_jwt_auth_backend" "test" {
+  description        = "JWT backend"
+  oidc_discovery_url = "https://myco.auth0.com/"
+  path			     = "%s"
+  tune {
+    default_lease_ttl = "10m"
+    max_lease_ttl = "20m"
+    listing_visibility = "hidden"
+    audit_non_hmac_request_keys = ["key1", "key2"]
+    audit_non_hmac_response_keys = ["key3", "key4"]
+    passthrough_request_headers = ["X-Custom-Header", "X-Forwarded-To"]
+    allowed_response_headers = ["X-Custom-Response-Header", "X-Forwarded-Response-To"]
+    token_type = "batch"
+  }
+}
+`, path)
+}
diff --git a/vault/resource_kubernetes_secret_backend_test.go b/vault/resource_kubernetes_secret_backend_test.go
index e0afc856..3befd65f 100644
--- a/vault/resource_kubernetes_secret_backend_test.go
+++ b/vault/resource_kubernetes_secret_backend_test.go
@@ -31,8 +31,8 @@ func TestAccKubernetesSecretBackend(t *testing.T) {
 				Check: resource.ComposeTestCheckFunc(
 					resource.TestCheckResourceAttr(resourceName, consts.FieldPath, path),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, ""),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTL, "0"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTL, "0"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTLSeconds, "0"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTLSeconds, "0"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldLocal, "false"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldAuditNonHMACRequestKeys+".#", "0"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldAuditNonHMACResponseKeys+".#", "0"),
@@ -48,8 +48,8 @@ func TestAccKubernetesSecretBackend(t *testing.T) {
 				Check: resource.ComposeTestCheckFunc(
 					resource.TestCheckResourceAttr(resourceName, consts.FieldPath, path),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, "kubernetes secrets engine"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTL, "3600"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTL, "7200"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTLSeconds, "3600"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTLSeconds, "7200"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldLocal, "true"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldAuditNonHMACRequestKeys+".0", "test_req"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldAuditNonHMACResponseKeys+".0", "test_res"),
@@ -65,8 +65,8 @@ func TestAccKubernetesSecretBackend(t *testing.T) {
 				Check: resource.ComposeTestCheckFunc(
 					resource.TestCheckResourceAttr(resourceName, consts.FieldPath, path),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, "kubernetes secrets description updated"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTL, "0"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTL, "0"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTLSeconds, "0"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTLSeconds, "0"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldLocal, "false"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldAuditNonHMACRequestKeys+".#", "0"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldAuditNonHMACResponseKeys+".#", "0"),
diff --git a/vault/resource_ldap_secret_backend_test.go b/vault/resource_ldap_secret_backend_test.go
index 55105ae3..dd291b5a 100644
--- a/vault/resource_ldap_secret_backend_test.go
+++ b/vault/resource_ldap_secret_backend_test.go
@@ -52,8 +52,8 @@ func TestLDAPSecretBackend(t *testing.T) {
 					resource.TestCheckResourceAttr(resourceName, consts.FieldSchema, "openldap"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldPath, path),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, description),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTL, "0"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTL, "0"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTLSeconds, "0"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTLSeconds, "0"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldBindDN, bindDN),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldBindPass, bindPass),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldURL, "ldap://127.0.0.1"),
@@ -75,8 +75,8 @@ func TestLDAPSecretBackend(t *testing.T) {
 					resource.TestCheckResourceAttr(resourceName, consts.FieldPath, path),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldSchema, "openldap"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, updatedDescription),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTL, "3600"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTL, "7200"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTLSeconds, "3600"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTLSeconds, "7200"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldBindDN, bindDN),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldBindPass, bindPass),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldURL, url),
@@ -135,8 +135,8 @@ func TestLDAPSecretBackend_SchemaAD(t *testing.T) {
 					resource.TestCheckResourceAttr(resourceName, consts.FieldSchema, "ad"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldPath, path),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, ""),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTL, "0"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTL, "0"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTLSeconds, "0"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTLSeconds, "0"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldBindDN, bindDN),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldBindPass, bindPass),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldURL, url),
@@ -158,8 +158,8 @@ func TestLDAPSecretBackend_SchemaAD(t *testing.T) {
 					resource.TestCheckResourceAttr(resourceName, consts.FieldSchema, "ad"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldPath, path),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, "new description"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTL, "0"),
-					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTL, "0"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldDefaultLeaseTTLSeconds, "0"),
+					resource.TestCheckResourceAttr(resourceName, consts.FieldMaxLeaseTTLSeconds, "0"),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldBindDN, bindDN),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldURL, url),
 					resource.TestCheckResourceAttr(resourceName, consts.FieldUserDN, userDN),
diff --git a/vault/structures_test.go b/vault/structures_test.go
index bf1eec5c..995031db 100644
--- a/vault/structures_test.go
+++ b/vault/structures_test.go
@@ -7,6 +7,7 @@ import (
 	"reflect"
 	"testing"
 
+	"github.com/hashicorp/terraform-provider-vault/internal/consts"
 	"github.com/hashicorp/vault/api"
 )
 
@@ -72,3 +73,201 @@ func TestFlattenAuthMethodTune(t *testing.T) {
 			expected)
 	}
 }
+
+func TestMergeAuthMethodTune(t *testing.T) {
+	type args struct {
+		rawTune map[string]interface{}
+		input   *api.MountConfigInput
+	}
+	tests := []struct {
+		name     string
+		args     args
+		expected map[string]interface{}
+	}{
+		{
+			name: "Nil input makes merged equal to rawTune. Equivalent to the state import",
+			args: args{
+				rawTune: map[string]interface{}{
+					// Vault's tune API returns the global defaults for these fields
+					consts.FieldDefaultLeaseTTL:   "768h",
+					consts.FieldMaxLeaseTTL:       "768h",
+					consts.FieldListingVisibility: "hidden",
+					consts.FieldTokenType:         "default-service",
+				},
+				input: nil,
+			},
+			expected: map[string]interface{}{
+				consts.FieldDefaultLeaseTTL:   "768h",
+				consts.FieldMaxLeaseTTL:       "768h",
+				consts.FieldListingVisibility: "hidden",
+				consts.FieldTokenType:         "default-service",
+			},
+		},
+		{
+			name: "All empty fields in input zero out corresponding fields in rawTune",
+			args: args{
+				rawTune: map[string]interface{}{
+					// Vault's tune API returns the global defaults for these fields
+					consts.FieldDefaultLeaseTTL:   "768h",
+					consts.FieldMaxLeaseTTL:       "768h",
+					consts.FieldListingVisibility: "hidden",
+					consts.FieldTokenType:         "default-service",
+				},
+				input: &api.MountConfigInput{},
+			},
+			expected: map[string]interface{}{
+				consts.FieldDefaultLeaseTTL:   "",
+				consts.FieldMaxLeaseTTL:       "",
+				consts.FieldListingVisibility: "",
+				consts.FieldTokenType:         "",
+			},
+		},
+		{
+			name: "Mixed: some empty, some non-empty",
+			args: args{
+				rawTune: map[string]interface{}{
+					consts.FieldDefaultLeaseTTL:   "768h",
+					consts.FieldMaxLeaseTTL:       "20h",
+					consts.FieldListingVisibility: "hidden",
+					consts.FieldTokenType:         "default-service",
+				},
+				input: &api.MountConfigInput{
+					MaxLeaseTTL: "20m",
+					TokenType:   "default-service",
+				},
+			},
+			expected: map[string]interface{}{
+				consts.FieldDefaultLeaseTTL:   "",
+				consts.FieldMaxLeaseTTL:       "20h",
+				consts.FieldListingVisibility: "",
+				consts.FieldTokenType:         "default-service",
+			},
+		},
+		{
+			name: "Partial: only DefaultLeaseTTL is specified",
+			args: args{
+				rawTune: map[string]interface{}{
+					consts.FieldDefaultLeaseTTL:   "10m",
+					consts.FieldMaxLeaseTTL:       "768h",
+					consts.FieldListingVisibility: "hidden",
+					consts.FieldTokenType:         "default-service",
+				},
+				input: &api.MountConfigInput{
+					DefaultLeaseTTL: "10m",
+				},
+			},
+			expected: map[string]interface{}{
+				consts.FieldDefaultLeaseTTL:   "10m",
+				consts.FieldMaxLeaseTTL:       "",
+				consts.FieldListingVisibility: "",
+				consts.FieldTokenType:         "",
+			},
+		},
+		{
+			name: "Partial: only MaxLeaseTTL is specified",
+			args: args{
+				rawTune: map[string]interface{}{
+					consts.FieldDefaultLeaseTTL:   "768h",
+					consts.FieldMaxLeaseTTL:       "10m",
+					consts.FieldListingVisibility: "hidden",
+					consts.FieldTokenType:         "default-service",
+				},
+				input: &api.MountConfigInput{
+					MaxLeaseTTL: "10m",
+				},
+			},
+			expected: map[string]interface{}{
+				consts.FieldDefaultLeaseTTL:   "",
+				consts.FieldMaxLeaseTTL:       "10m",
+				consts.FieldListingVisibility: "",
+				consts.FieldTokenType:         "",
+			},
+		},
+		{
+			name: "Partial: only ListingVisibility is specified",
+			args: args{
+				rawTune: map[string]interface{}{
+					consts.FieldDefaultLeaseTTL:   "768h",
+					consts.FieldMaxLeaseTTL:       "768h",
+					consts.FieldListingVisibility: "unauth",
+					consts.FieldTokenType:         "default-service",
+				},
+				input: &api.MountConfigInput{
+					ListingVisibility: "unauth",
+				},
+			},
+			expected: map[string]interface{}{
+				consts.FieldDefaultLeaseTTL:   "",
+				consts.FieldMaxLeaseTTL:       "",
+				consts.FieldListingVisibility: "unauth",
+				consts.FieldTokenType:         "",
+			},
+		},
+		{
+			name: "Partial: only TokenType is specified",
+			args: args{
+				rawTune: map[string]interface{}{
+					consts.FieldDefaultLeaseTTL:   "768h",
+					consts.FieldMaxLeaseTTL:       "768h",
+					consts.FieldListingVisibility: "hidden",
+					// use the non-default value
+					consts.FieldTokenType: "default-batch",
+				},
+				input: &api.MountConfigInput{
+					TokenType: "default-batch",
+				},
+			},
+			expected: map[string]interface{}{
+				consts.FieldDefaultLeaseTTL:   "",
+				consts.FieldMaxLeaseTTL:       "",
+				consts.FieldListingVisibility: "",
+				consts.FieldTokenType:         "default-batch",
+			},
+		},
+		{
+			name: "Full: all fields specified",
+			args: args{
+				rawTune: map[string]interface{}{
+					consts.FieldDefaultLeaseTTL:           "768h", // global default
+					consts.FieldMaxLeaseTTL:               "768h", // global default
+					consts.FieldAuditNonHMACRequestKeys:   []interface{}{"foo", "bar"},
+					consts.FieldAuditNonHMACResponseKeys:  []interface{}{"baz"},
+					consts.FieldListingVisibility:         "unauth",
+					consts.FieldPassthroughRequestHeaders: []interface{}{"X-Custom", "X-Mas"},
+					consts.FieldAllowedResponseHeaders:    []interface{}{"X-Response-Custom", "X-Response-Mas"},
+					consts.FieldTokenType:                 "default-batch",
+				},
+
+				input: &api.MountConfigInput{
+					DefaultLeaseTTL:           "",
+					MaxLeaseTTL:               "",
+					AuditNonHMACRequestKeys:   []string{"foo", "bar"},
+					AuditNonHMACResponseKeys:  []string{"baz"},
+					ListingVisibility:         "unauth",
+					PassthroughRequestHeaders: []string{"X-Custom", "X-Mas"},
+					AllowedResponseHeaders:    []string{"X-Response-Custom", "X-Response-Mas"},
+					TokenType:                 "default-batch",
+				},
+			},
+			expected: map[string]interface{}{
+				consts.FieldDefaultLeaseTTL:           "",
+				consts.FieldMaxLeaseTTL:               "",
+				consts.FieldAuditNonHMACRequestKeys:   []interface{}{"foo", "bar"},
+				consts.FieldAuditNonHMACResponseKeys:  []interface{}{"baz"},
+				consts.FieldListingVisibility:         "unauth",
+				consts.FieldPassthroughRequestHeaders: []interface{}{"X-Custom", "X-Mas"},
+				consts.FieldAllowedResponseHeaders:    []interface{}{"X-Response-Custom", "X-Response-Mas"},
+				consts.FieldTokenType:                 "default-batch",
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			actual := mergeAuthMethodTune(tt.args.rawTune, tt.args.input)
+			if !reflect.DeepEqual(actual, tt.expected) {
+				t.Fatalf("Test %q failed:\nGot:\n%#v\nExpected:\n%#v\n", tt.name, actual, tt.expected)
+			}
+		})
+	}
+}