44 "context"
55 "fmt"
66 "slices"
7- "sort"
87 "strings"
98 "time"
109
@@ -34,28 +33,28 @@ func ResourceGroupMembership() *schema.Resource {
3433 },
3534 SchemaVersion : 0 ,
3635 Schema : map [string ]* schema.Schema {
36+ "group_id" : {
37+ Type : schema .TypeString ,
38+ Required : true ,
39+ Description : "The ID of the group to add the users or applications to" ,
40+ ForceNew : true ,
41+ },
3742 "user_ids" : {
3843 Type : schema .TypeList ,
3944 Elem : & schema.Schema {Type : schema .TypeString },
4045 Optional : true ,
41- ExactlyOneOf : [] string { "application_ids" } ,
42- Description : "The IDs of the users" ,
46+ Description : "The IDs of the users to add to the group" ,
47+ AtLeastOneOf : [] string { "application_ids" } ,
4348 ForceNew : true ,
4449 },
4550 "application_ids" : {
4651 Type : schema .TypeList ,
4752 Elem : & schema.Schema {Type : schema .TypeString },
4853 Optional : true ,
49- ExactlyOneOf : [] string { "user_ids" } ,
50- Description : "The IDs of the applications" ,
54+ Description : "The IDs of the applications to add to the group" ,
55+ AtLeastOneOf : [] string { "user_ids" } ,
5156 ForceNew : true ,
5257 },
53- "group_id" : {
54- Type : schema .TypeString ,
55- Required : true ,
56- Description : "The ID of the group to add the user to" ,
57- ForceNew : true ,
58- },
5958 },
6059 }
6160}
@@ -83,7 +82,7 @@ func resourceIamGroupMembershipCreate(ctx context.Context, d *schema.ResourceDat
8382func resourceIamGroupMembershipRead (ctx context.Context , d * schema.ResourceData , m interface {}) diag.Diagnostics {
8483 api := NewAPI (m )
8584
86- groupID , entityKind , entityIDs , err := ExpandGroupMembershipResourceID (d .Id ())
85+ groupID , entityIDs , err := ExpandGroupMembershipResourceID (d .Id ())
8786 if err != nil {
8887 return diag .FromErr (err )
8988 }
@@ -101,46 +100,43 @@ func resourceIamGroupMembershipRead(ctx context.Context, d *schema.ResourceData,
101100 return diag .FromErr (err )
102101 }
103102
104- foundEntityIDs := make ([]bool , len (entityIDs ))
103+ foundUserIDs := make ([]bool , len (entityIDs [EntityKindUser ]))
104+ foundApplicationIDs := make ([]bool , len (entityIDs [EntityKindApplication ]))
105105
106- if entityKind == EntityKindUser {
107- for i , groupUserID := range group .UserIDs {
108- if slices .Contains (entityIDs , groupUserID ) {
109- foundEntityIDs [i ] = true
110- }
111- }
112- } else if entityKind == EntityKindApplication {
113- for i , groupApplicationID := range group .ApplicationIDs {
114- if slices .Contains (entityIDs , groupApplicationID ) {
115- foundEntityIDs [i ] = true
116- }
106+ for i , userID := range entityIDs [EntityKindUser ] {
107+ if slices .Contains (group .UserIDs , userID ) {
108+ foundUserIDs [i ] = true
109+ } else {
110+ return diag .FromErr (fmt .Errorf ("user %s not found in group %s" , userID , groupID ))
117111 }
118112 }
119113
120- if slices .Contains (foundEntityIDs , false ) {
121- d .SetId ("" )
122-
123- return nil
114+ for i , applicationID := range entityIDs [EntityKindApplication ] {
115+ if slices .Contains (group .ApplicationIDs , applicationID ) {
116+ foundApplicationIDs [i ] = true
117+ } else {
118+ return diag .FromErr (fmt .Errorf ("application %s not found in group %s" , applicationID , groupID ))
119+ }
124120 }
125121
126122 _ = d .Set ("group_id" , groupID )
127- _ = d .Set (fmt .Sprintf ("%s_ids" , entityKind ), entityIDs )
123+ _ = d .Set ("user_ids" , entityIDs [EntityKindUser ])
124+ _ = d .Set ("application_ids" , entityIDs [EntityKindApplication ])
128125
129126 return nil
130127}
131128
132129func resourceIamGroupMembershipDelete (ctx context.Context , d * schema.ResourceData , m interface {}) diag.Diagnostics {
133130 api := NewAPI (m )
134131
135- groupID , _ , _ , err := ExpandGroupMembershipResourceID (d .Id ())
132+ groupID , _ , err := ExpandGroupMembershipResourceID (d .Id ())
136133 if err != nil {
137134 return diag .FromErr (err )
138135 }
139136
140137 _ , err = MakeSetGroupMembershipRequest (ctx , api , & iam.SetGroupMembersRequest {
141138 GroupID : groupID ,
142139 })
143-
144140 if err != nil {
145141 if httperrors .Is404 (err ) {
146142 d .SetId ("" )
@@ -154,31 +150,49 @@ func resourceIamGroupMembershipDelete(ctx context.Context, d *schema.ResourceDat
154150 return nil
155151}
156152
153+ // Depending on the kind of entity, it will generate a parsable state like:
154+ // groupID/user:userID,application:applicationID
157155func SetGroupMembershipResourceID (groupID string , userIDs []string , applicationIDs []string ) (resourceID string ) {
158- sort .Strings (userIDs )
159- sort .Strings (applicationIDs )
156+ entityIDs := make ([]string , 0 )
157+
158+ for _ , userID := range userIDs {
159+ entityIDs = append (entityIDs , fmt .Sprintf ("%s:%s" , EntityKindUser , userID ))
160+ }
160161
161- if len (userIDs ) > 0 {
162- resourceID = fmt .Sprintf ("%s/%s/%s" , groupID , EntityKindUser , strings .Join (userIDs , "," ))
163- } else if len (applicationIDs ) > 0 {
164- resourceID = fmt .Sprintf ("%s/%s/%s" , groupID , EntityKindApplication , strings .Join (applicationIDs , "," ))
162+ for _ , applicationID := range applicationIDs {
163+ entityIDs = append (entityIDs , fmt .Sprintf ("%s:%s" , EntityKindApplication , applicationID ))
165164 }
166165
166+ resourceID = fmt .Sprintf ("%s/%s" , groupID , strings .Join (entityIDs , "," ))
167+
167168 return
168169}
169170
170- func ExpandGroupMembershipResourceID (id string ) (groupID string , kind EntityKind , entityIDs []string , err error ) {
171+ func ExpandGroupMembershipResourceID (id string ) (groupID string , entityIDs map [ EntityKind ] []string , err error ) {
171172 elems := strings .Split (id , "/" )
172- if len (elems ) != 3 {
173- return "" , "" , [] string {}, fmt .Errorf ("invalid group membership id format, expected {groupID}/{entityKind}/ {entityIDs}, got: %s" , id )
173+ if len (elems ) != 2 {
174+ return "" , nil , fmt .Errorf ("invalid group membership id format, expected {groupID}/{entityKind}: {entityIDs}, got: %s" , id )
174175 }
175176
176177 groupID = elems [0 ]
177- kind = EntityKind (elems [1 ])
178- if kind != EntityKindUser && kind != EntityKindApplication {
179- return "" , "" , []string {}, fmt .Errorf ("invalid entity kind, expected %s or %s, got: %s" , EntityKindUser , EntityKindApplication , kind )
178+
179+ // entityKind:entityID,entityKind:entityID
180+ entityKindAndIDs := strings .Split (elems [1 ], "," )
181+ entityIDs = make (map [EntityKind ][]string )
182+
183+ for _ , entityKindAndID := range entityKindAndIDs {
184+ splitted := strings .Split (entityKindAndID , ":" )
185+ if len (splitted ) != 2 {
186+ return "" , nil , fmt .Errorf ("invalid entity kind and id format, expected {entityKind}:{entityID}, got: %s" , entityKindAndID )
187+ }
188+
189+ entityKind , entityID := EntityKind (splitted [0 ]), splitted [1 ]
190+ if entityKind != EntityKindUser && entityKind != EntityKindApplication {
191+ return "" , nil , fmt .Errorf ("invalid entity kind, expected %s or %s, got: %s" , EntityKindUser , EntityKindApplication , entityKind )
192+ }
193+
194+ entityIDs [entityKind ] = append (entityIDs [entityKind ], entityID )
180195 }
181- entityIDs = strings .Split (elems [2 ], "," )
182196
183197 return
184198}
@@ -191,15 +205,14 @@ func MakeSetGroupMembershipRequest(ctx context.Context, api *iam.API, request *i
191205 retryInterval = * transport .DefaultWaitRetryInterval
192206 }
193207
194- var response * iam.Group
195- var err error
196-
197208 // exponential backoff
198- for i := 0 ; i < maxRetries ; i ++ {
199- response , err = api .SetGroupMembers (request , scw .WithContext (ctx ))
209+ for i := range maxRetries {
210+ response , err := api .SetGroupMembers (request , scw .WithContext (ctx ))
211+
200212 if err != nil {
201213 if httperrors .Is409 (err ) && strings .Contains (err .Error (), fmt .Sprintf ("resource group with ID %s is in a transient state: updating" , request .GroupID )) {
202214 time .Sleep (retryInterval * time .Duration (i ))
215+
203216 continue
204217 }
205218
0 commit comments