Skip to content

fix: stop team update from deleting externally-managed scoped user roles#235

Open
fv-nhastings wants to merge 1 commit into
OctopusDeploy:mainfrom
fv-nhastings:fix/team-update-deletes-external-scoped-user-roles
Open

fix: stop team update from deleting externally-managed scoped user roles#235
fv-nhastings wants to merge 1 commit into
OctopusDeploy:mainfrom
fv-nhastings:fix/team-update-deletes-external-scoped-user-roles

Conversation

@fv-nhastings

Copy link
Copy Markdown

What

Stops the octopusdeploy_team resource from deleting scoped user roles that are managed outside the team resource (i.e. via standalone octopusdeploy_scoped_user_role resources) whenever the team is created or updated.

Fixes #180. Follow-up to #100 / #105.

Why

updateUserRoles (called on every team Create/Update) reconciled the team's scoped user roles by:

  1. building newUserRoles from the team's own inline user_role blocks,
  2. fetching all scoped user roles attached to the team on the server, and
  3. deleting every server role not present in newUserRoles.

When a team is managed without inline user_role blocks (roles managed by standalone octopusdeploy_scoped_user_role resources), newUserRoles is empty, so step 3 deletes every scoped user role attached to the team. Terraform then detects the standalone resources as missing on the next refresh and recreates them — requiring 2+ applies to converge, or looping indefinitely if the team drifts on every apply.

PR #105 addressed the symptom on the read/refresh path (filterUserRolesByPreviousState in mapTeamResourceToState), but the write path in resource_team.go::updateUserRoles was never given the equivalent ownership check, so the deletion persisted (re-reported in #180).

How

Apply the same ownership filter to removals. Removal candidates are now drawn only from roles this team resource previously managed (derived from prior state via the existing filterUserRolesByPreviousState helper), rather than from every role on the server:

  • Update now reads req.State and passes the prior user_role set into updateUserRoles.
  • Create passes a null set (nothing was previously managed, so nothing is removable — and a freshly created team has no pre-existing roles anyway).
  • findRemovedScopedUserRoles is now fed the filtered "previously managed" set instead of all server roles.

Add/edit behavior is unchanged; only the removal set is narrowed. Roles managed by inline user_role blocks continue to be added/removed/edited exactly as before.

Tests

UnitTestUpdateUserRolesRemovalOwnership covering the removal-candidate selection:

AcceptanceTestAccOctopusDeployTeamUpdateKeepsStandaloneScopedUserRole: creates a team with a standalone octopusdeploy_scoped_user_role, changes the team's description to force a team update, and asserts an empty follow-up plan (the role survives). The pre-existing TestAccOctopusDeployTeamScopedUserRoleNoConflict never updated the team after adding the standalone role, so it did not exercise this path.

Validated red→green against a live Octopus instance: the new acceptance test fails on the unpatched code (post-update refresh plan shows octopusdeploy_scoped_user_role ... will be created, Plan: 1 to add) and passes with the fix. Existing TestFilterUserRolesByPreviousState continues to pass. gofmt clean; go vet clean.

The team resource's create/update reconciliation (updateUserRoles) treated
itself as the sole owner of a team's scoped user roles: it deleted every
scoped user role on the server that was absent from the team's own user_role
block. When a team is managed without inline user_role blocks and its roles
are managed by standalone octopusdeploy_scoped_user_role resources, any team
update therefore deleted all of those roles, forcing 2+ applies to converge
(or an endless add/remove cycle if the team drifts every apply).

PR OctopusDeploy#105 fixed the read/refresh path (filterUserRolesByPreviousState) but the
write path was left untouched. This applies the same ownership filter to
removals: only roles previously managed by this team resource (from prior
state) are eligible for deletion; externally-managed roles are left alone.

Adds unit coverage (TestUpdateUserRolesRemovalOwnership) and an acceptance
test (TestAccOctopusDeployTeamUpdateKeepsStandaloneScopedUserRole) that changes
a team while a standalone scoped user role exists and asserts an empty
follow-up plan. The pre-existing TestAccOctopusDeployTeamScopedUserRoleNoConflict
never updated the team, so it did not exercise this path.

Fixes OctopusDeploy#180. Follow-up to OctopusDeploy#100 / OctopusDeploy#105.
@CLAassistant

CLAassistant commented Jul 22, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

octopusdeploy_scoped_user_roles always getting removed when octopusdeploy_team is modified

2 participants