[Fix] Permanent permissions drift when user_name casing in databricks_permissions access_control blocks differs from the API response#5757
Merged
Conversation
…rom API response (#5183) Squashed commits for rebase onto main. Co-authored-by: Isaac
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes persistent drift in databricks_permissions when user_name / service_principal_name casing differs between Terraform config/state and the Databricks Permissions API response by treating these identity fields as case-insensitive.
Changes:
- Normalize
user_name/service_principal_nameto lowercase in theaccess_controlTypeSethash function to prevent set element identity drift. - Normalize
user_name/service_principal_namefrom API responses (Read) to lowercase, and switch current-user matching to case-insensitive comparisons. - Add unit tests covering same-casing vs different-casing API responses; add a changelog entry.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| permissions/resource_permissions.go | Updates access_control set hashing to lowercase case-insensitive identity fields before hashing. |
| permissions/permission_definitions.go | Uses case-insensitive comparison for current-user filtering and lowercases usernames/SPNs when flattening API response into state. |
| permissions/entity/permissions_entity.go | Makes ContainsUserOrServicePrincipal case-insensitive via strings.EqualFold. |
| permissions/resource_permissions_test.go | Adds regression tests reproducing and preventing casing-driven drift in Read/refresh behavior. |
| NEXT_CHANGELOG.md | Adds bugfix entry for the drift issue. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+231
to
233
| if strings.EqualFold(me, accessControl.UserName) || strings.EqualFold(me, accessControl.ServicePrincipalName) { | ||
| if !existing.ContainsUserOrServicePrincipal(me) { | ||
| continue |
Contributor
|
If integration tests don't run automatically, an authorized user can run them manually by following the instructions below: Trigger: Inputs:
Checks will be approved automatically on success. |
hectorcast-db
approved these changes
Jun 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
It's just a resubmission of #5391 to workaround the GH actions problems when contributor is outside of the or
Changes
Closes #5183, Fixes #5712
Fixes permanent permissions drift when
user_namecasing indatabricks_permissionsaccess_controlblocks differs from the API response.Problem: The Databricks API normalizes user emails to lowercase (e.g. config has
User@Example.com, API returnsuser@example.com). Because emails are case-insensitive, both refer to the same user, but Terraform treats them as different entries, causing a remove/re-add diff on every plan that never converges.Fix: Normalize
user_nameandservice_principal_nameto lowercase throughout the permissions resource so that casing differences between config and API are ignored. The fix touches three areas:user_nameandservice_principal_namebefore hashing so config and API values produce the same hash.user_nameandservice_principal_namewhen reading the API response into state.strings.EqualFold) when checking if the current user is in the config. Without this, the provider silently dropped the current user's permissions from state when their email casing differed, a second source of drift.Detailed Changes
Hash function case normalization (
permissions/resource_permissions.go, lines 191-217)Before:
After:
Why: The
access_controlblock usesschema.TypeSet, which relies on a hash function to determine element identity. If the user's config hasUser@Example.comand the API returnsuser@example.com, they produced different hashes, so Terraform saw them as two different set elements: one to remove and one to add. Lowercasing before hashing ensures both values produce the same hash.API response lowercasing (
permissions/permission_definitions.go, lines 247-248)Before:
After:
Why: Even with matching hashes, if the stored string value in state differs from the config value, Terraform detects a diff. Lowercasing the API response ensures the state value matches a lowercase config value.
Case-insensitive current-user comparison (
permissions/permission_definitions.go, line 231)Before:
After:
Why:
==is case-sensitive, so"Admin@Example.com" == "admin@example.com"returnsfalse.strings.EqualFoldcompares strings case-insensitively, correctly matching the same user regardless of casing.Case-insensitive lookup in
ContainsUserOrServicePrincipal(permissions/entity/permissions_entity.go, line 17)Before:
After:
Why: Same as above.
==is case-sensitive, so"Admin@Example.com" == "admin@example.com"returnsfalse.strings.EqualFoldcompares strings case-insensitively, correctly matching the same user regardless of casing.Tests
TestPermissionsDrift_UserNameNoDriftWithSameCasing: Simulates an existing resource in state with
user_name="user@example.com", then performs a Read where the API returns the same casing. Constructs a fullInstanceStatewith computed set hashes and verifies that the Read produces exactly 2access_controlentries (no drift).TestPermissionsDrift_UserNameNoDriftWithDifferentCasing: Simulates the exact drift from issue [ISSUE] Issue with
databricks_permissionsresource whenuser_nameis used for access_control. There is a permanent drift with each terraform plan and apply #5183:InstanceStatehasuser_name="user@example.com"but the API returnsUser@Example.com. Verifies that after the fix, the Read normalizes the casing so that:access_controlset still has exactly 2 entries (not 3, which would indicate drift)user_namevalue in state is"user@example.com"(lowercase)Both tests use
MockWorkspaceClientFuncwith mockedCurrentUser.Me()andPermissions.Get()responses, and construct realisticInstanceStatemaps with pre-computed set hashes to simulate the post-apply state that Terraform would produce.make testrun locallydocs/folderinternal/acceptanceNEXT_CHANGELOG.mdfileNotes