@@ -135,7 +135,13 @@ func resourceAdminRoleCustomRead(ctx context.Context, d *schema.ResourceData, me
135135 if err != nil {
136136 return diag .Errorf ("failed to list permissions for custom admin role: %v" , err )
137137 }
138- _ = d .Set ("permissions" , flattenPermissions (perms .Permissions ))
138+ // The Okta API can return both the legacy workflow permission labels
139+ // (okta.workflows.invoke / okta.workflows.read) and their newer aliases
140+ // (okta.workflows.flows.invoke / okta.workflows.flows.read). Keep whichever
141+ // form is already recorded in state so we don't introduce perpetual drift.
142+ statePermissions := utils .ConvertInterfaceToStringSetNullable (d .Get ("permissions" ))
143+ apiPermissions := reconcileWorkflowPermissions (statePermissions , perms .Permissions )
144+ _ = d .Set ("permissions" , flattenPermissions (apiPermissions ))
139145 return nil
140146}
141147
@@ -199,40 +205,52 @@ func flattenPermissions(permissions []*sdk.Permission) interface{} {
199205 for i := range permissions {
200206 arr [i ] = permissions [i ].Label
201207 }
202- // The Okta API auto-expands some permissions, returning additional labels
203- // alongside the ones that were configured. Normalize them back to the
204- // user's intended configuration to avoid perpetual state drift.
205- normalized := normalizePermissions (utils .ConvertInterfaceArrToStringArr (arr ))
206- arr = make ([]interface {}, len (normalized ))
207- for i , label := range normalized {
208- arr [i ] = label
209- }
210208 return schema .NewSet (schema .HashString , arr )
211209}
212210
213- func normalizePermissions (apiPermissions []string ) []string {
214- present := make (map [string ]bool , len (apiPermissions ))
215- for _ , perm := range apiPermissions {
216- present [perm ] = true
211+ // reconcileWorkflowPermissions removes a redundant workflow permission alias
212+ // from the API response when state already tracks the other form. The Okta API
213+ // exposes the same workflow permission under a legacy label and a newer alias:
214+ //
215+ // okta.workflows.invoke <-> okta.workflows.flows.invoke
216+ // okta.workflows.read <-> okta.workflows.flows.read
217+ //
218+ // Whichever form the user already has in state wins, so the opposite form is
219+ // discarded from the API response before it is written back to state.
220+ func reconcileWorkflowPermissions (statePermissions []string , apiPermissions []* sdk.Permission ) []* sdk.Permission {
221+ inState := make (map [string ]bool , len (statePermissions ))
222+ for _ , p := range statePermissions {
223+ inState [p ] = true
224+ }
225+
226+ existingToNew := map [string ]string {
227+ "okta.workflows.invoke" : "okta.workflows.flows.invoke" ,
228+ "okta.workflows.read" : "okta.workflows.flows.read" ,
217229 }
218230
219- // Suppress an expanded permission only when its original is also present.
220- suppressed := make (map [string ]bool )
221- if present ["okta.workflows.read" ] {
222- suppressed ["okta.workflows.flows.read" ] = true
231+ discard := make (map [string ]bool )
232+ for legacy , modern := range existingToNew {
233+ switch {
234+ case inState [legacy ]:
235+ // state uses the legacy label, discard the newer alias from the API response
236+ discard [modern ] = true
237+ case inState [modern ]:
238+ // state uses the newer alias, discard the legacy label from the API response
239+ discard [legacy ] = true
240+ }
223241 }
224- if present [ "okta.workflows.invoke" ] {
225- suppressed [ "okta.workflows.flows.invoke" ] = true
242+ if len ( discard ) == 0 {
243+ return apiPermissions
226244 }
227245
228- result := make ([]string , 0 , len (apiPermissions ))
229- for _ , perm := range apiPermissions {
230- if suppressed [ perm ] {
246+ filtered := make ([]* sdk. Permission , 0 , len (apiPermissions ))
247+ for _ , p := range apiPermissions {
248+ if p != nil && discard [ p . Label ] {
231249 continue
232250 }
233- result = append (result , perm )
251+ filtered = append (filtered , p )
234252 }
235- return result
253+ return filtered
236254}
237255
238256func addCustomRolePermissions (ctx context.Context , client * sdk.APISupplement , roleIdOrLabel string , permissions []string ) error {
0 commit comments