@@ -3,17 +3,28 @@ package mongodbatlas
3
3
import (
4
4
"context"
5
5
"encoding/base64"
6
+ "encoding/json"
6
7
"fmt"
7
8
"hash/crc32"
8
9
"log"
9
10
"os"
10
11
"reflect"
12
+ "regexp"
11
13
"sort"
12
14
"strconv"
13
15
"strings"
14
-
16
+ "time"
17
+
18
+ "github.com/aws/aws-sdk-go/aws"
19
+ "github.com/aws/aws-sdk-go/aws/awserr"
20
+ "github.com/aws/aws-sdk-go/aws/credentials"
21
+ "github.com/aws/aws-sdk-go/aws/credentials/stscreds"
22
+ "github.com/aws/aws-sdk-go/aws/endpoints"
23
+ "github.com/aws/aws-sdk-go/aws/session"
24
+ "github.com/aws/aws-sdk-go/service/secretsmanager"
15
25
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
16
26
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
27
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
17
28
"github.com/mwielbut/pointy"
18
29
"github.com/spf13/cast"
19
30
matlas "go.mongodb.org/atlas/mongodbatlas"
24
35
baseURL = ""
25
36
)
26
37
38
+ type SecretData struct {
39
+ PublicKey string `json:"public_key"`
40
+ PrivateKey string `json:"private_key"`
41
+ }
42
+
27
43
// Provider returns the provider to be use by the code.
28
44
func Provider () * schema.Provider {
29
45
provider := & schema.Provider {
@@ -67,6 +83,51 @@ func Provider() *schema.Provider {
67
83
Optional : true ,
68
84
Description : "MongoDB Atlas Base URL default to gov" ,
69
85
},
86
+ "assume_role" : assumeRoleSchema (),
87
+ "secret_name" : {
88
+ Type : schema .TypeString ,
89
+ Optional : true ,
90
+ },
91
+ "region" : {
92
+ Type : schema .TypeString ,
93
+ DefaultFunc : schema .MultiEnvDefaultFunc ([]string {
94
+ "AWS_REGION" ,
95
+ "TF_VAR_AWS_REGION" ,
96
+ }, "" ),
97
+ Optional : true ,
98
+ },
99
+ "sts_endpoint" : {
100
+ Type : schema .TypeString ,
101
+ DefaultFunc : schema .MultiEnvDefaultFunc ([]string {
102
+ "STS_ENDPOINT" ,
103
+ "TF_VAR_STS_ENDPOINT" ,
104
+ }, "" ),
105
+ Optional : true ,
106
+ },
107
+ "aws_access_key_id" : {
108
+ Type : schema .TypeString ,
109
+ DefaultFunc : schema .MultiEnvDefaultFunc ([]string {
110
+ "AWS_ACCESS_KEY_ID" ,
111
+ "TF_VAR_AWS_ACCESS_KEY_ID" ,
112
+ }, "" ),
113
+ Optional : true ,
114
+ },
115
+ "aws_secret_access_key" : {
116
+ Type : schema .TypeString ,
117
+ DefaultFunc : schema .MultiEnvDefaultFunc ([]string {
118
+ "AWS_SECRET_ACCESS_KEY" ,
119
+ "TF_VAR_AWS_SECRET_ACCESS_KEY" ,
120
+ }, "" ),
121
+ Optional : true ,
122
+ },
123
+ "aws_session_token" : {
124
+ Type : schema .TypeString ,
125
+ DefaultFunc : schema .MultiEnvDefaultFunc ([]string {
126
+ "AWS_SESSION_TOKEN" ,
127
+ "TF_VAR_AWS_SESSION_TOKEN" ,
128
+ }, "" ),
129
+ Optional : true ,
130
+ },
70
131
},
71
132
DataSourcesMap : getDataSourcesMap (),
72
133
ResourcesMap : getResourcesMap (),
@@ -226,9 +287,79 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}
226
287
RealmBaseURL : d .Get ("realm_base_url" ).(string ),
227
288
}
228
289
290
+ if v , ok := d .GetOk ("assume_role" ); ok && len (v .([]interface {})) > 0 && v .([]interface {})[0 ] != nil {
291
+ config .AssumeRole = expandAssumeRole (v .([]interface {})[0 ].(map [string ]interface {}))
292
+ secret := d .Get ("secret_name" ).(string )
293
+ region := d .Get ("region" ).(string )
294
+ awsAccessKeyID := d .Get ("aws_access_key_id" ).(string )
295
+ awsSecretAccessKey := d .Get ("aws_secret_access_key" ).(string )
296
+ awsSessionToken := d .Get ("aws_session_token" ).(string )
297
+ endpoint := d .Get ("sts_endpoint" ).(string )
298
+ config , _ = configureCredentialsSTS (& config , secret , region , awsAccessKeyID , awsSecretAccessKey , awsSessionToken , endpoint )
299
+ }
300
+
229
301
return config .NewClient (ctx )
230
302
}
231
303
304
+ func configureCredentialsSTS (config * Config , secret , region , awsAccessKeyID , awsSecretAccessKey , awsSessionToken , endpoint string ) (Config , error ) {
305
+ ep , _ := endpoints .GetSTSRegionalEndpoint ("regional" )
306
+ sess := session .Must (session .NewSession (& aws.Config {
307
+ Region : aws .String (region ),
308
+ Credentials : credentials .NewStaticCredentials (awsAccessKeyID , awsSecretAccessKey , awsSessionToken ),
309
+ STSRegionalEndpoint : ep ,
310
+ Endpoint : & endpoint ,
311
+ }))
312
+
313
+ creds := stscreds .NewCredentials (sess , config .AssumeRole .RoleARN )
314
+
315
+ _ , _ = sess .Config .Credentials .Get ()
316
+ _ , _ = creds .Get ()
317
+ secretString := secretsManagerGetSecretValue (sess , & aws.Config {Credentials : creds , Region : aws .String (region )}, secret )
318
+
319
+ var secretData SecretData
320
+ err := json .Unmarshal ([]byte (secretString ), & secretData )
321
+ if err != nil {
322
+ return * config , nil
323
+ }
324
+ config .PublicKey = secretData .PublicKey
325
+ config .PrivateKey = secretData .PrivateKey
326
+ return * config , nil
327
+ }
328
+
329
+ func secretsManagerGetSecretValue (sess * session.Session , creds * aws.Config , secret string ) string {
330
+ svc := secretsmanager .New (sess , creds )
331
+ input := & secretsmanager.GetSecretValueInput {
332
+ SecretId : aws .String (secret ),
333
+ VersionStage : aws .String ("AWSCURRENT" ),
334
+ }
335
+
336
+ result , err := svc .GetSecretValue (input )
337
+ if err != nil {
338
+ if aerr , ok := err .(awserr.Error ); ok {
339
+ switch aerr .Code () {
340
+ case secretsmanager .ErrCodeResourceNotFoundException :
341
+ fmt .Println (secretsmanager .ErrCodeResourceNotFoundException , aerr .Error ())
342
+ case secretsmanager .ErrCodeInvalidParameterException :
343
+ fmt .Println (secretsmanager .ErrCodeInvalidParameterException , aerr .Error ())
344
+ case secretsmanager .ErrCodeInvalidRequestException :
345
+ fmt .Println (secretsmanager .ErrCodeInvalidRequestException , aerr .Error ())
346
+ case secretsmanager .ErrCodeDecryptionFailure :
347
+ fmt .Println (secretsmanager .ErrCodeDecryptionFailure , aerr .Error ())
348
+ case secretsmanager .ErrCodeInternalServiceError :
349
+ fmt .Println (secretsmanager .ErrCodeInternalServiceError , aerr .Error ())
350
+ default :
351
+ fmt .Println (aerr .Error ())
352
+ }
353
+ } else {
354
+ fmt .Println (err .Error ())
355
+ }
356
+ return ""
357
+ }
358
+
359
+ fmt .Println (result )
360
+ return * result .SecretString
361
+ }
362
+
232
363
func encodeStateID (values map [string ]string ) string {
233
364
encode := func (e string ) string { return base64 .StdEncoding .EncodeToString ([]byte (e )) }
234
365
encodedValues := make ([]string , 0 )
@@ -391,3 +522,167 @@ func HashCodeString(s string) int {
391
522
// v == MinInt
392
523
return 0
393
524
}
525
+
526
+ // assumeRoleSchema From aws provider.go
527
+ func assumeRoleSchema () * schema.Schema {
528
+ return & schema.Schema {
529
+ Type : schema .TypeList ,
530
+ Optional : true ,
531
+ MaxItems : 1 ,
532
+ Elem : & schema.Resource {
533
+ Schema : map [string ]* schema.Schema {
534
+ "duration" : {
535
+ Type : schema .TypeString ,
536
+ Optional : true ,
537
+ Description : "The duration, between 15 minutes and 12 hours, of the role session. Valid time units are ns, us (or µs), ms, s, h, or m." ,
538
+ ValidateFunc : validAssumeRoleDuration ,
539
+ ConflictsWith : []string {"assume_role.0.duration_seconds" },
540
+ },
541
+ "duration_seconds" : {
542
+ Type : schema .TypeInt ,
543
+ Optional : true ,
544
+ Deprecated : "Use assume_role.duration instead" ,
545
+ Description : "The duration, in seconds, of the role session." ,
546
+ ValidateFunc : validation .IntBetween (900 , 43200 ),
547
+ ConflictsWith : []string {"assume_role.0.duration" },
548
+ },
549
+ "external_id" : {
550
+ Type : schema .TypeString ,
551
+ Optional : true ,
552
+ Description : "A unique identifier that might be required when you assume a role in another account." ,
553
+ ValidateFunc : validation .All (
554
+ validation .StringLenBetween (2 , 1224 ),
555
+ validation .StringMatch (regexp .MustCompile (`[\w+=,.@:/\-]*` ), "" ),
556
+ ),
557
+ },
558
+ "policy" : {
559
+ Type : schema .TypeString ,
560
+ Optional : true ,
561
+ Description : "IAM Policy JSON describing further restricting permissions for the IAM Role being assumed." ,
562
+ ValidateFunc : validation .StringIsJSON ,
563
+ },
564
+ "policy_arns" : {
565
+ Type : schema .TypeSet ,
566
+ Optional : true ,
567
+ Description : "Amazon Resource Names (ARNs) of IAM Policies describing further restricting permissions for the IAM Role being assumed." ,
568
+ Elem : & schema.Schema {
569
+ Type : schema .TypeString ,
570
+ },
571
+ },
572
+ "role_arn" : {
573
+ Type : schema .TypeString ,
574
+ Optional : true ,
575
+ Description : "Amazon Resource Name (ARN) of an IAM Role to assume prior to making API calls." ,
576
+ },
577
+ "session_name" : {
578
+ Type : schema .TypeString ,
579
+ Optional : true ,
580
+ Description : "An identifier for the assumed role session." ,
581
+ ValidateFunc : validAssumeRoleSessionName ,
582
+ },
583
+ "source_identity" : {
584
+ Type : schema .TypeString ,
585
+ Optional : true ,
586
+ Description : "Source identity specified by the principal assuming the role." ,
587
+ ValidateFunc : validAssumeRoleSourceIdentity ,
588
+ },
589
+ "tags" : {
590
+ Type : schema .TypeMap ,
591
+ Optional : true ,
592
+ Description : "Assume role session tags." ,
593
+ Elem : & schema.Schema {Type : schema .TypeString },
594
+ },
595
+ "transitive_tag_keys" : {
596
+ Type : schema .TypeSet ,
597
+ Optional : true ,
598
+ Description : "Assume role session tag keys to pass to any subsequent sessions." ,
599
+ Elem : & schema.Schema {Type : schema .TypeString },
600
+ },
601
+ },
602
+ },
603
+ }
604
+ }
605
+
606
+ var validAssumeRoleSessionName = validation .All (
607
+ validation .StringLenBetween (2 , 64 ),
608
+ validation .StringMatch (regexp .MustCompile (`[\w+=,.@\-]*` ), "" ),
609
+ )
610
+
611
+ var validAssumeRoleSourceIdentity = validation .All (
612
+ validation .StringLenBetween (2 , 64 ),
613
+ validation .StringMatch (regexp .MustCompile (`[\w+=,.@\-]*` ), "" ),
614
+ )
615
+
616
+ // validAssumeRoleDuration validates a string can be parsed as a valid time.Duration
617
+ // and is within a minimum of 15 minutes and maximum of 12 hours
618
+ func validAssumeRoleDuration (v interface {}, k string ) (ws []string , errors []error ) {
619
+ duration , err := time .ParseDuration (v .(string ))
620
+
621
+ if err != nil {
622
+ errors = append (errors , fmt .Errorf ("%q cannot be parsed as a duration: %w" , k , err ))
623
+ return
624
+ }
625
+
626
+ if duration .Minutes () < 15 || duration .Hours () > 12 {
627
+ errors = append (errors , fmt .Errorf ("duration %q must be between 15 minutes (15m) and 12 hours (12h), inclusive" , k ))
628
+ }
629
+
630
+ return
631
+ }
632
+
633
+ type AssumeRole struct {
634
+ RoleARN string
635
+ Duration time.Duration
636
+ ExternalID string
637
+ Policy string
638
+ PolicyARNs []string
639
+ SessionName string
640
+ SourceIdentity string
641
+ Tags map [string ]string
642
+ TransitiveTagKeys []string
643
+ }
644
+
645
+ func expandAssumeRole (tfMap map [string ]interface {}) * AssumeRole {
646
+ if tfMap == nil {
647
+ return nil
648
+ }
649
+
650
+ assumeRole := AssumeRole {}
651
+
652
+ if v , ok := tfMap ["duration" ].(string ); ok && v != "" {
653
+ duration , _ := time .ParseDuration (v )
654
+ assumeRole .Duration = duration
655
+ } else if v , ok := tfMap ["duration_seconds" ].(int ); ok && v != 0 {
656
+ assumeRole .Duration = time .Duration (v ) * time .Second
657
+ }
658
+
659
+ if v , ok := tfMap ["external_id" ].(string ); ok && v != "" {
660
+ assumeRole .ExternalID = v
661
+ }
662
+
663
+ if v , ok := tfMap ["policy" ].(string ); ok && v != "" {
664
+ assumeRole .Policy = v
665
+ }
666
+
667
+ if v , ok := tfMap ["policy_arns" ].(* schema.Set ); ok && v .Len () > 0 {
668
+ assumeRole .PolicyARNs = expandStringList (v .List ())
669
+ }
670
+
671
+ if v , ok := tfMap ["role_arn" ].(string ); ok && v != "" {
672
+ assumeRole .RoleARN = v
673
+ }
674
+
675
+ if v , ok := tfMap ["session_name" ].(string ); ok && v != "" {
676
+ assumeRole .SessionName = v
677
+ }
678
+
679
+ if v , ok := tfMap ["source_identity" ].(string ); ok && v != "" {
680
+ assumeRole .SourceIdentity = v
681
+ }
682
+
683
+ if v , ok := tfMap ["transitive_tag_keys" ].(* schema.Set ); ok && v .Len () > 0 {
684
+ assumeRole .TransitiveTagKeys = expandStringList (v .List ())
685
+ }
686
+
687
+ return & assumeRole
688
+ }
0 commit comments