@@ -4,10 +4,12 @@ import (
4
4
"context"
5
5
"fmt"
6
6
"regexp"
7
+ "time"
7
8
8
9
"go.mongodb.org/atlas-sdk/v20250312006/admin"
9
10
10
11
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
12
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
11
13
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
12
14
"github.com/mongodb/terraform-provider-mongodbatlas/internal/common/constant"
13
15
"github.com/mongodb/terraform-provider-mongodbatlas/internal/common/conversion"
@@ -22,11 +24,12 @@ import (
22
24
*/
23
25
24
26
const (
25
- errorCloudProviderAccessCreate = "error creating cloud provider access %s"
26
- errorCloudProviderAccessUpdate = "error updating cloud provider access %s"
27
- errorCloudProviderAccessDelete = "error deleting cloud provider access %s"
28
- errorCloudProviderAccessImporter = "error importing cloud provider access %s"
29
- ErrorCloudProviderGetRead = "error reading cloud provider access %s"
27
+ errorCreate = "error creating cloud provider access %s"
28
+ errorUpdate = "error updating cloud provider access %s"
29
+ errorDelete = "error deleting cloud provider access %s"
30
+ errorImporter = "error importing cloud provider access %s"
31
+ ErrorGetRead = "error reading cloud provider access %s"
32
+ defaultTimeout time.Duration = 20 * time .Minute
30
33
)
31
34
32
35
func ResourceSetup () * schema.Resource {
@@ -130,16 +133,16 @@ func resourceCloudProviderAccessSetupRead(ctx context.Context, d *schema.Resourc
130
133
return nil
131
134
}
132
135
133
- return diag .FromErr (fmt .Errorf (ErrorCloudProviderGetRead , err ))
136
+ return diag .FromErr (fmt .Errorf (ErrorGetRead , err ))
134
137
}
135
138
136
139
roleSchema , err := roleToSchemaSetup (role )
137
140
if err != nil {
138
- return diag .Errorf (errorCloudProviderAccessCreate , err )
141
+ return diag .Errorf (errorCreate , err )
139
142
}
140
143
for key , val := range roleSchema {
141
144
if err := d .Set (key , val ); err != nil {
142
- return diag .FromErr (fmt .Errorf (ErrorCloudProviderGetRead , err ))
145
+ return diag .FromErr (fmt .Errorf (ErrorGetRead , err ))
143
146
}
144
147
}
145
148
@@ -169,18 +172,26 @@ func resourceCloudProviderAccessSetupCreate(ctx context.Context, d *schema.Resou
169
172
170
173
role , _ , err := conn .CloudProviderAccessApi .CreateCloudProviderAccessRole (ctx , projectID , requestParameters ).Execute ()
171
174
if err != nil {
172
- return diag .FromErr (fmt .Errorf (errorCloudProviderAccessCreate , err ))
175
+ return diag .FromErr (fmt .Errorf (errorCreate , err ))
176
+ }
177
+
178
+ resourceID := role .GetRoleId ()
179
+ if role .ProviderName == constant .AZURE {
180
+ resourceID = role .GetId () // For Azure, the unique identifier is in the "id" field, not "roleId".
181
+ }
182
+
183
+ if role .ProviderName == constant .GCP {
184
+ r , err := waitForGCPProviderAccessCompletion (ctx , projectID , resourceID , conn )
185
+ if err != nil {
186
+ return diag .FromErr (err )
187
+ }
188
+ role = r
173
189
}
174
190
175
191
// once multiple providers enable here do a switch, select for provider type
176
192
roleSchema , err := roleToSchemaSetup (role )
177
193
if err != nil {
178
- return diag .Errorf (errorCloudProviderAccessCreate , err )
179
- }
180
-
181
- resourceID := role .GetRoleId ()
182
- if role .ProviderName == constant .AZURE {
183
- resourceID = role .GetId ()
194
+ return diag .Errorf (errorCreate , err )
184
195
}
185
196
186
197
d .SetId (conversion .EncodeStateID (map [string ]string {
@@ -191,13 +202,60 @@ func resourceCloudProviderAccessSetupCreate(ctx context.Context, d *schema.Resou
191
202
192
203
for key , val := range roleSchema {
193
204
if err := d .Set (key , val ); err != nil {
194
- return diag .FromErr (fmt .Errorf (errorCloudProviderAccessCreate , err ))
205
+ return diag .FromErr (fmt .Errorf (errorCreate , err ))
195
206
}
196
207
}
197
208
198
209
return nil
199
210
}
200
211
212
+ func waitForGCPProviderAccessCompletion (ctx context.Context , projectID , resourceID string , conn * admin.APIClient ) (* admin.CloudProviderAccessRole , error ) {
213
+ requestParams := & admin.GetCloudProviderAccessRoleApiParams {
214
+ RoleId : resourceID ,
215
+ GroupId : projectID ,
216
+ }
217
+
218
+ stateConf := retry.StateChangeConf {
219
+ Pending : []string {"IN_PROGRESS" , "NOT_INITIATED" },
220
+ Target : []string {"COMPLETE" , "FAILED" },
221
+ Refresh : resourceRefreshFunc (ctx , requestParams , conn ),
222
+ Timeout : defaultTimeout ,
223
+ MinTimeout : 60 * time .Second ,
224
+ Delay : 30 * time .Second ,
225
+ }
226
+
227
+ finalResponse , err := stateConf .WaitForStateContext (ctx )
228
+ if err != nil {
229
+ return nil , err
230
+ }
231
+
232
+ r , ok := finalResponse .(* admin.CloudProviderAccessRole )
233
+ if ! ok {
234
+ return nil , fmt .Errorf ("unexpected type for result: %T" , finalResponse )
235
+ }
236
+ return r , nil
237
+ }
238
+
239
+ func resourceRefreshFunc (ctx context.Context , requestParams * admin.GetCloudProviderAccessRoleApiParams , conn * admin.APIClient ) retry.StateRefreshFunc {
240
+ return func () (any , string , error ) {
241
+ role , resp , err := conn .CloudProviderAccessApi .GetCloudProviderAccessRoleWithParams (ctx , requestParams ).Execute ()
242
+ if err != nil {
243
+ if validate .StatusNotFound (resp ) {
244
+ return nil , "" , fmt .Errorf ("cloud provider access role %q not found in project %q" , requestParams .RoleId , requestParams .GroupId )
245
+ }
246
+ return nil , "" , err
247
+ }
248
+
249
+ status := role .GetStatus ()
250
+ switch status {
251
+ case "IN_PROGRESS" , "NOT_INITIATED" , "COMPLETE" :
252
+ return role , status , nil
253
+ default :
254
+ return nil , status , fmt .Errorf ("cloud provider access setup failed %q for role %q" , status , requestParams .RoleId )
255
+ }
256
+ }
257
+ }
258
+
201
259
func resourceCloudProviderAccessSetupDelete (ctx context.Context , d * schema.ResourceData , meta any ) diag.Diagnostics {
202
260
conn := meta .(* config.MongoDBClient ).AtlasV2
203
261
ids := conversion .DecodeStateID (d .Id ())
@@ -214,7 +272,7 @@ func resourceCloudProviderAccessSetupDelete(ctx context.Context, d *schema.Resou
214
272
215
273
_ , err := conn .CloudProviderAccessApi .DeauthorizeCloudProviderAccessRoleWithParams (ctx , req ).Execute ()
216
274
if err != nil {
217
- return diag .FromErr (fmt .Errorf (errorCloudProviderAccessDelete , err ))
275
+ return diag .FromErr (fmt .Errorf (errorDelete , err ))
218
276
}
219
277
220
278
d .SetId ("" )
@@ -232,7 +290,7 @@ func roleToSchemaSetup(role *admin.CloudProviderAccessRole) (map[string]any, err
232
290
}},
233
291
"gcp_config" : []any {map [string ]any {}},
234
292
"created_date" : conversion .TimeToString (role .GetCreatedDate ()),
235
- "role_id" : role .GetRoleId (),
293
+ "role_id" : role .GetRoleId (), // For AWS, the unique identifier is in the "roleId" field
236
294
}, nil
237
295
case constant .AZURE :
238
296
return map [string ]any {
@@ -246,7 +304,7 @@ func roleToSchemaSetup(role *admin.CloudProviderAccessRole) (map[string]any, err
246
304
"gcp_config" : []any {map [string ]any {}},
247
305
"created_date" : conversion .TimeToString (role .GetCreatedDate ()),
248
306
"last_updated_date" : conversion .TimeToString (role .GetLastUpdatedDate ()),
249
- "role_id" : role .GetId (),
307
+ "role_id" : role .GetId (), // For Azure, the unique identifier is in the "id" field, not "roleId".
250
308
}, nil
251
309
case constant .GCP :
252
310
return map [string ]any {
@@ -256,7 +314,7 @@ func roleToSchemaSetup(role *admin.CloudProviderAccessRole) (map[string]any, err
256
314
"service_account_for_atlas" : role .GetGcpServiceAccountForAtlas (),
257
315
}},
258
316
"aws_config" : []any {map [string ]any {}},
259
- "role_id" : role .GetId (),
317
+ "role_id" : role .GetRoleId (), // For GCP, the unique identifier is in the "roleId"
260
318
"created_date" : conversion .TimeToString (role .GetCreatedDate ()),
261
319
}, nil
262
320
default :
@@ -268,7 +326,7 @@ func resourceCloudProviderAccessSetupImportState(ctx context.Context, d *schema.
268
326
projectID , providerName , roleID , err := splitCloudProviderAccessID (d .Id ())
269
327
270
328
if err != nil {
271
- return nil , fmt .Errorf (errorCloudProviderAccessImporter , err )
329
+ return nil , fmt .Errorf (errorImporter , err )
272
330
}
273
331
274
332
// searching id in internal format
@@ -281,17 +339,17 @@ func resourceCloudProviderAccessSetupImportState(ctx context.Context, d *schema.
281
339
err2 := resourceCloudProviderAccessSetupRead (ctx , d , meta )
282
340
283
341
if err2 != nil {
284
- return nil , fmt .Errorf (errorCloudProviderAccessImporter , err )
342
+ return nil , fmt .Errorf (errorImporter , err )
285
343
}
286
344
287
345
// case of not found
288
346
if d .Id () == "" {
289
- return nil , fmt .Errorf (errorCloudProviderAccessImporter , " Resource not found at the cloud please check your id" )
347
+ return nil , fmt .Errorf (errorImporter , " Resource not found at the cloud please check your id" )
290
348
}
291
349
292
350
// params syncing
293
351
if err = d .Set ("project_id" , projectID ); err != nil {
294
- return nil , fmt .Errorf (errorCloudProviderAccessImporter , err )
352
+ return nil , fmt .Errorf (errorImporter , err )
295
353
}
296
354
297
355
return []* schema.ResourceData {d }, nil
@@ -303,7 +361,7 @@ func splitCloudProviderAccessID(id string) (projectID, providerName, roleID stri
303
361
parts := re .FindStringSubmatch (id )
304
362
305
363
if len (parts ) != 4 {
306
- err = fmt .Errorf (errorCloudProviderAccessImporter , "format please use {project_id}-{provider-name}-{role-id}" )
364
+ err = fmt .Errorf (errorImporter , "format please use {project_id}-{provider-name}-{role-id}" )
307
365
return
308
366
}
309
367
0 commit comments