55 "errors"
66 "fmt"
77 "strings"
8+ "time"
89
910 "github.com/hashicorp/terraform-plugin-framework/path"
1011 "github.com/hashicorp/terraform-plugin-framework/resource"
@@ -154,6 +155,21 @@ func (r *roleAssignmentResource) Create(ctx context.Context, req resource.Create
154155 ctx = tflog .SetField (ctx , "role" , model .Role .ValueString ())
155156 ctx = tflog .SetField (ctx , "resource_type" , r .apiName )
156157
158+ lockKey := fmt .Sprintf ("%s,%s,%s" ,
159+ model .ResourceId .ValueString (),
160+ model .Role .ValueString (),
161+ model .Subject .ValueString (),
162+ )
163+
164+ // Terraform executes resources in parallel. If two resources define the same
165+ // role assignment, they create a "Check-Then-Act" race condition:
166+ // 1. Thread A creates -> Success
167+ // 2. Thread B creates -> Duplicate Error
168+ // This lock forces Thread B to wait until Thread A completes, allowing
169+ // the subsequent duplicate check to correctly find the existing assignment.
170+ unlock := authorizationUtils .LockAssignment (lockKey )
171+ defer unlock ()
172+
157173 listMemberResp , err := r .authorizationClient .ListMembers (ctx , r .apiName , model .ResourceId .ValueString ()).Subject (model .Subject .ValueString ()).Execute ()
158174 if err != nil {
159175 core .LogAndAddError (ctx , & resp .Diagnostics , "Error listing current resource members" , fmt .Sprintf ("Calling API: %v" , err ))
@@ -188,6 +204,10 @@ func (r *roleAssignmentResource) Create(ctx context.Context, req resource.Create
188204
189205 err = mapListMembersResponse (listMembersResponse , & model )
190206 if err != nil {
207+ if errors .Is (err , errRoleAssignmentNotFound ) {
208+ resp .State .RemoveResource (ctx )
209+ return
210+ }
191211 core .LogAndAddError (ctx , & resp .Diagnostics , fmt .Sprintf ("Error creating %s role assignment" , r .apiName ), fmt .Sprintf ("Processing API payload: %v" , err ))
192212 return
193213 }
@@ -197,6 +217,10 @@ func (r *roleAssignmentResource) Create(ctx context.Context, req resource.Create
197217 if resp .Diagnostics .HasError () {
198218 return
199219 }
220+
221+ // safety sleep due to api cache
222+ time .Sleep (1 * time .Second )
223+
200224 tflog .Info (ctx , fmt .Sprintf ("%s role assignment created" , r .apiName ))
201225}
202226
0 commit comments