From 39fcdef2812e8408cc5620e7924d8397a7a249f8 Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Thu, 28 Apr 2022 16:22:38 -0500 Subject: [PATCH 01/19] Initial Commit - Added defaultTagsSchema to provider block --- provider.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/provider.go b/provider.go index 1dd2d0f3..67632d5d 100644 --- a/provider.go +++ b/provider.go @@ -73,6 +73,7 @@ func Provider() *schema.Provider { DefaultFunc: schema.EnvDefaultFunc("Role", nil), }, "assume_role": assumeRoleSchema(), + "default_tags": defaultTagsSchema(), }, ResourcesMap: map[string]*schema.Resource{ @@ -121,6 +122,31 @@ func assumeRoleSchema() *schema.Schema { } } +func defaultTagsSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "tags": { + Type: schema.TypeMap, + Required: true, + Elem: schema.TypeString, + }, + }, + }, + } +} + +// func tagsSchema() *schema.Schema { +// return &schema.Schema{ +// Type: schema.TypeMap, +// Required: true, +// Elem: schema.TypeString, +// } +// } + func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { var diags diag.Diagnostics From f62e9765bb65ee0ef8f2d71c6e14b102d3047140 Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Fri, 29 Apr 2022 11:16:31 -0500 Subject: [PATCH 02/19] Added default tags block to match aws provider implementation --- provider.go | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/provider.go b/provider.go index 67632d5d..576c26e3 100644 --- a/provider.go +++ b/provider.go @@ -2,9 +2,10 @@ package main import ( "context" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "log" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/mitchellh/go-homedir" ) @@ -72,7 +73,7 @@ func Provider() *schema.Provider { Description: "The role which you'd like to retrieve credentials for.", DefaultFunc: schema.EnvDefaultFunc("Role", nil), }, - "assume_role": assumeRoleSchema(), + "assume_role": assumeRoleSchema(), "default_tags": defaultTagsSchema(), }, @@ -124,29 +125,23 @@ func assumeRoleSchema() *schema.Schema { func defaultTagsSchema() *schema.Schema { return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - MaxItems: 1, + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "Configuration block with settings to default resource tags across all resources.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "tags": { - Type: schema.TypeMap, - Required: true, - Elem: schema.TypeString, + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Resource tags to default across all resources", }, }, }, } } -// func tagsSchema() *schema.Schema { -// return &schema.Schema{ -// Type: schema.TypeMap, -// Required: true, -// Elem: schema.TypeString, -// } -// } - func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { var diags diag.Diagnostics From 6fabef884d1e34a3584aa32996215084949c0cba Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Fri, 29 Apr 2022 17:33:48 -0500 Subject: [PATCH 03/19] Added default tags block, and passed those tags through provider configure to AlksClient struct started modifying resource_alks_iamrole --- go.mod | 3 +- go.sum | 17 +- provider.go | 38 +++- resource_alks_iamrole.go | 26 ++- .../Cox-Automotive/alks-go/iam_role.go | 203 +++++++++++++----- vendor/modules.txt | 2 +- 6 files changed, 208 insertions(+), 81 deletions(-) diff --git a/go.mod b/go.mod index d19b4f92..78ccf0ff 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,8 @@ module github.com/Cox-Automotive/terraform-provider-alks go 1.14 require ( - github.com/Cox-Automotive/alks-go v0.0.0-20220218164749-e4de33286d92 + github.com/Cox-Automotive/alks-go v0.0.0-20220429202951-085c873654ea + // github.com/Cox-Automotive/alks-go v0.0.0-20220218164749-e4de33286d92 github.com/aws/aws-sdk-go v1.31.15 github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/terraform-json v0.13.0 // indirect diff --git a/go.sum b/go.sum index 087a58c2..a47bbefe 100644 --- a/go.sum +++ b/go.sum @@ -34,14 +34,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Cox-Automotive/alks-go v0.0.0-20210507174057-2080b54cd46c h1:fAoVhk7jvz2Dyv68E5GIgIugkFGrPSADxef+gM83Evc= -github.com/Cox-Automotive/alks-go v0.0.0-20210507174057-2080b54cd46c/go.mod h1:jJNgXthl59Vt2tJHSC3WZ0vlopV9xqdclfQuLgwHjOw= -github.com/Cox-Automotive/alks-go v0.0.0-20220204185347-3eb3a57f4a8e h1:7NtH5BFwe34RO3N21O7jm1kT6Rcg0KvbTX0PYCyhgTY= -github.com/Cox-Automotive/alks-go v0.0.0-20220204185347-3eb3a57f4a8e/go.mod h1:jJNgXthl59Vt2tJHSC3WZ0vlopV9xqdclfQuLgwHjOw= -github.com/Cox-Automotive/alks-go v0.0.0-20220204233541-ba8ac1a4fbb6 h1:uL+oMMRaAvQxw/ETTuiD8vzftU8hA6zvFqUDCVnPmTA= -github.com/Cox-Automotive/alks-go v0.0.0-20220204233541-ba8ac1a4fbb6/go.mod h1:jJNgXthl59Vt2tJHSC3WZ0vlopV9xqdclfQuLgwHjOw= -github.com/Cox-Automotive/alks-go v0.0.0-20220218164749-e4de33286d92 h1:skXr8jFqk0BXpcCOPXGk+aQsgHRLkhKDZZVyAv3cFPc= -github.com/Cox-Automotive/alks-go v0.0.0-20220218164749-e4de33286d92/go.mod h1:jJNgXthl59Vt2tJHSC3WZ0vlopV9xqdclfQuLgwHjOw= +github.com/Cox-Automotive/alks-go v0.0.0-20220429202951-085c873654ea h1:BJWnEf+8/OjU40RBygIkyLtzHFF7DrypqyUVX79Dyn8= +github.com/Cox-Automotive/alks-go v0.0.0-20220429202951-085c873654ea/go.mod h1:jJNgXthl59Vt2tJHSC3WZ0vlopV9xqdclfQuLgwHjOw= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= @@ -142,7 +136,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -189,7 +182,6 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= @@ -201,7 +193,6 @@ github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/terraform-exec v0.13.0 h1:1Pth+pdWJAufJuWWjaVOVNEkoRTOjGn3hQpAqj4aPdg= github.com/hashicorp/terraform-exec v0.13.0/go.mod h1:SGhto91bVRlgXQWcJ5znSz+29UZIa8kpBbkGwQ+g9E8= -github.com/hashicorp/terraform-json v0.8.0 h1:XObQ3PgqU52YLQKEaJ08QtUshAfN3yu4u8ebSW0vztc= github.com/hashicorp/terraform-json v0.8.0/go.mod h1:3defM4kkMfttwiE7VakJDwCd4R+umhSQnvJwORXbprE= github.com/hashicorp/terraform-json v0.13.0 h1:Li9L+lKD1FO5RVFRM1mMMIBDoUHslOniyEi5CM+FWGY= github.com/hashicorp/terraform-json v0.13.0/go.mod h1:y5OdLBCT+rxbwnpxZs9kGL7R9ExU76+cpdY8zHwoazk= @@ -250,7 +241,6 @@ github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW1 github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mitchellh/cli v1.1.1/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= @@ -267,7 +257,6 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -314,7 +303,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= -github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8= github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zclconf/go-cty v1.9.1 h1:viqrgQwFl5UpSxc046qblj78wZXVDFnSOufaOTER+cc= github.com/zclconf/go-cty v1.9.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= @@ -442,7 +430,6 @@ golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fq golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/provider.go b/provider.go index 576c26e3..22ade8ef 100644 --- a/provider.go +++ b/provider.go @@ -4,6 +4,7 @@ import ( "context" "log" + "github.com/Cox-Automotive/alks-go" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -154,6 +155,7 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{} Profile: d.Get("profile").(string), Account: d.Get("account").(string), Role: d.Get("role").(string), + // DefaultTags: expandProviderDefaultTags(d.Get("default_tags").([]interface{})), } assumeRoleList := d.Get("assume_role").(*schema.Set).List() @@ -171,12 +173,46 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{} return nil, diag.FromErr(err) } config.CredsFilename = credsPath + defaultTags := expandProviderDefaultTags(d.Get("default_tags").([]interface{})) c, err := config.Client() if err != nil { return nil, diag.FromErr(err) } + alksClient := &AlksClient{} + alksClient.client = c + if defaultTags != nil { + alksClient.defaultTags = defaultTags + } + log.Println("[INFO] Initializing ALKS client") - return c, diags + return alksClient, diags + // return c, diags +} + +func expandProviderDefaultTags(l []interface{}) *[]alks.Tag { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + tagSlice := []alks.Tag{} + if tagMap, ok := m["tags"].(map[string]interface{}); ok { + for key, value := range tagMap { + tag := alks.Tag{} + tag.Key = key + str, ok := value.(string) + if ok { + tag.Value = str + } + tagSlice = append(tagSlice, tag) + } + } + return &tagSlice +} + +type AlksClient struct { + client *alks.Client + defaultTags *[]alks.Tag } diff --git a/resource_alks_iamrole.go b/resource_alks_iamrole.go index 6e251b6a..670fedda 100644 --- a/resource_alks_iamrole.go +++ b/resource_alks_iamrole.go @@ -94,22 +94,32 @@ func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta templateFields[k] = v.(string) } - var include int + var include bool if incDefPol { - include = 1 + include = true } - client := meta.(*alks.Client) + providerStruct := meta.(*AlksClient) + client := providerStruct.client + + defaultTags := providerStruct.defaultTags + // client := meta.(*alks.Client) + if err := validateIAMEnabled(client); err != nil { return diag.FromErr(err) } - options := alks.CreateIamRoleOptions{IncDefPols: include, - AlksAccess: enableAlksAccess, - TemplateFields: templateFields, - MaxSessionDurationInSeconds: maxSessionDurationInSeconds} + options := &alks.CreateIamRoleOptions{ + RoleName: &roleName, + RoleType: &roleType, + IncludeDefaultPolicies: &include, + AlksAccess: &enableAlksAccess, + TemplateFields: &templateFields, + MaxSessionDurationInSeconds: &maxSessionDurationInSeconds, + Tags: defaultTags, + } - resp, err := client.CreateIamRoleWithOptions(roleName, roleType, options) + resp, err := client.CreateIamRole(options) if err != nil { return diag.FromErr(err) } diff --git a/vendor/github.com/Cox-Automotive/alks-go/iam_role.go b/vendor/github.com/Cox-Automotive/alks-go/iam_role.go index e61c7cb8..7a12ed2d 100644 --- a/vendor/github.com/Cox-Automotive/alks-go/iam_role.go +++ b/vendor/github.com/Cox-Automotive/alks-go/iam_role.go @@ -8,29 +8,32 @@ import ( "strings" ) +type Tag struct { + Key string `json:"key"` + Value string `json:"value"` +} + type CreateIamRoleOptions struct { - IncDefPols int - AlksAccess bool - TemplateFields map[string]string - MaxSessionDurationInSeconds int + RoleName *string + RoleType *string + IncludeDefaultPolicies *bool + AlksAccess *bool + TrustArn *string + TemplateFields *map[string]string + MaxSessionDurationInSeconds *int + Tags *[]Tag } // IamRoleRequest is used to represent a new IAM Role request. type IamRoleRequest struct { RoleName string `json:"roleName"` - RoleType string `json:"roleType"` - IncDefPols int `json:"includeDefaultPolicy"` - AlksAccess bool `json:"enableAlksAccess"` + RoleType string `json:"roleType,omitempty"` + IncDefPols int `json:"includeDefaultPolicy,omitempty"` + AlksAccess bool `json:"enableAlksAccess,omitempty"` + TrustArn string `json:"trustArn,omitempty"` TemplateFields map[string]string `json:"templateFields,omitempty"` - MaxSessionDurationInSeconds int `json:"maxSessionDurationInSeconds"` -} - -// IamTrustRoleRequest is used to represent a new IAM Trust Role request. -type IamTrustRoleRequest struct { - RoleName string `json:"roleName"` - RoleType string `json:"roleType"` - TrustArn string `json:"trustArn"` - AlksAccess bool `json:"enableAlksAccess"` + MaxSessionDurationInSeconds int `json:"maxSessionDurationInSeconds,omitempty"` + Tags []Tag `json:"tags,omitempty"` } // IamRoleResponse is used to represent a a IAM Role. @@ -103,24 +106,74 @@ type MachineIdentityResponse struct { MachineIdentityArn string `json:"machineIdentityArn"` } -// CreateIamRoleWithOptions will create a new IAM role on AWS. If no error is returned +// Creates a new IamRoleRequest object from options +func NewIamRoleRequest(options *CreateIamRoleOptions) (*IamRoleRequest, error) { + if options.RoleName == nil { + return nil, fmt.Errorf("RoleName option must not be nil") + } + + if options.RoleType == nil { + return nil, fmt.Errorf("RoleType option must not be nil") + } + + iam := &IamRoleRequest{ + RoleName: *options.RoleName, + RoleType: *options.RoleType, + } + if options.IncludeDefaultPolicies != nil && *options.IncludeDefaultPolicies { + iam.IncDefPols = 1 + } else { + iam.IncDefPols = 0 + } + + if options.AlksAccess != nil { + iam.AlksAccess = *options.AlksAccess + } else { + iam.AlksAccess = false + } + + if options.TemplateFields != nil { + iam.TemplateFields = *options.TemplateFields + } else { + iam.TemplateFields = nil + } + + if options.TrustArn != nil { + iam.TrustArn = *options.TrustArn + } else { + iam.TrustArn = "" + } + + if options.MaxSessionDurationInSeconds != nil { + iam.MaxSessionDurationInSeconds = *options.MaxSessionDurationInSeconds + } else { + iam.MaxSessionDurationInSeconds = 3600 + } + + if options.Tags != nil { + iam.Tags = *options.Tags + } else { + iam.Tags = nil + } + + return iam, nil +} + +// CreateIamRole will create a new IAM role in AWS. If no error is returned // then you will receive a IamRoleResponse object representing the new role. -func (c *Client) CreateIamRoleWithOptions(roleName, roleType string, options CreateIamRoleOptions) (*IamRoleResponse, error) { - log.Printf("[INFO] Creating IAM role: %s", roleName) +func (c *Client) CreateIamRole(options *CreateIamRoleOptions) (*IamRoleResponse, error) { + request, err := NewIamRoleRequest(options) - iam := IamRoleRequest{ - roleName, - roleType, - options.IncDefPols, - options.AlksAccess, - options.TemplateFields, - options.MaxSessionDurationInSeconds, + if err != nil { + return nil, err } + log.Printf("[INFO] Creating IAM role: %s", request.RoleName) + b, err := json.Marshal(struct { IamRoleRequest AccountDetails - }{iam, c.AccountDetails}) + }{*request, c.AccountDetails}) if err != nil { return nil, fmt.Errorf("Error encoding IAM create role JSON: %s", err) @@ -154,39 +207,15 @@ func (c *Client) CreateIamRoleWithOptions(roleName, roleType string, options Cre return cr, nil } -func (c *Client) CreateIamRole(roleName, roleType string, templateFields map[string]string, includeDefaultPolicies, enableAlksAccess bool) (*IamRoleResponse, error) { - - var include int - if includeDefaultPolicies { - include = 1 - } - - options := CreateIamRoleOptions{ - IncDefPols: include, - AlksAccess: enableAlksAccess, - TemplateFields: templateFields, - MaxSessionDurationInSeconds: 3600, - } - - return c.CreateIamRoleWithOptions(roleName, roleType, options) -} - // CreateIamTrustRole will create a new IAM trust role on AWS. If no error is returned // then you will receive a IamRoleResponse object representing the new role. -func (c *Client) CreateIamTrustRole(roleName string, roleType string, trustArn string, enableAlksAccess bool) (*IamRoleResponse, error) { - log.Printf("[INFO] Creating IAM trust role: %s", roleName) - - iam := IamTrustRoleRequest{ - roleName, - roleType, - trustArn, - enableAlksAccess, - } +func (c *Client) CreateIamTrustRole(options *CreateIamRoleOptions) (*IamRoleResponse, error) { + request, err := NewIamRoleRequest(options) b, err := json.Marshal(struct { - IamTrustRoleRequest + IamRoleRequest AccountDetails - }{iam, c.AccountDetails}) + }{*request, c.AccountDetails}) if err != nil { return nil, fmt.Errorf("Error encoding IAM create trust role JSON: %s", err) @@ -220,6 +249,70 @@ func (c *Client) CreateIamTrustRole(roleName string, roleType string, trustArn s return cr, nil } +type UpdateIamRoleRequest struct { + RoleName *string `json:"roleName"` + Tags *[]Tag `json:"tags"` +} + +type UpdateIamRoleResponse struct { + BaseResponse + RoleArn *string `json:"roleArn"` + RoleName *string `json:"roleName"` + BasicAuth *bool `json:"basicAuthUsed"` + Exists *bool `json:"roleExists"` + RoleIPArn *string `json:"instanceProfileArn"` + MachineIdentity *bool `json:"isMachineIdentity"` + Tags *[]Tag `json:"tags"` +} + +/* UpdateIamRole adds resource tags to an existing IAM role. + */ +func (c *Client) UpdateIamRole(options *UpdateIamRoleRequest) (*UpdateIamRoleResponse, error) { + if e := options.updateIamRoleValidate(); e != nil { + return nil, e + } + log.Printf("[INFO] update IAM role %s with Tags: %v", *options.RoleName, *options.Tags) + + b, e := json.Marshal(struct { + UpdateIamRoleRequest + AccountDetails + }{*options, c.AccountDetails}) + if e != nil { + return nil, e + } + req, e := c.NewRequest(b, "PATCH", "/role/") + if e != nil { + return nil, e + } + resp, e := c.http.Do(req) + if e != nil { + return nil, e + } + + respObj := &UpdateIamRoleResponse{} + if e = decodeBody(resp, respObj); e != nil { + if reqID := GetRequestID(resp); reqID != "" { + return nil, fmt.Errorf("error parsing update role response: [%s] %s", reqID, e) + } + return nil, fmt.Errorf("error parsing update role response: %s", e) + } + if respObj.RequestFailed() { + return nil, fmt.Errorf("error from update IAM role request: [%s] %s", respObj.RequestID, strings.Join(respObj.GetErrors(), ", ")) + } + + return respObj, nil +} + +func (req *UpdateIamRoleRequest) updateIamRoleValidate() error { + if req.RoleName == nil { + return fmt.Errorf("roleName option must not be nil") + } + if req.Tags == nil { + return fmt.Errorf("tags option must not be nil") + } + return nil +} + // DeleteIamRole will delete an existing IAM role from AWS. If no error is returned // then the deletion was successful. func (c *Client) DeleteIamRole(id string) error { diff --git a/vendor/modules.txt b/vendor/modules.txt index 65521ece..e0328924 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -8,7 +8,7 @@ cloud.google.com/go/internal/trace cloud.google.com/go/internal/version # cloud.google.com/go/storage v1.10.0 cloud.google.com/go/storage -# github.com/Cox-Automotive/alks-go v0.0.0-20220218164749-e4de33286d92 +# github.com/Cox-Automotive/alks-go v0.0.0-20220429202951-085c873654ea ## explicit github.com/Cox-Automotive/alks-go # github.com/agext/levenshtein v1.2.2 From e2c17ce1430b85c3eaf45475d0714caa1f148643 Mon Sep 17 00:00:00 2001 From: Spencer Finkel Date: Sat, 30 Apr 2022 16:50:50 -0500 Subject: [PATCH 04/19] Adds tag fields to IamRoleCreate and retrofits function calls with newest alks-go signatures --- resource_alks_iamrole.go | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/resource_alks_iamrole.go b/resource_alks_iamrole.go index 6e251b6a..4a1ecc92 100644 --- a/resource_alks_iamrole.go +++ b/resource_alks_iamrole.go @@ -75,6 +75,12 @@ func resourceAlksIamRole() *schema.Resource { Default: 3600, Optional: true, }, + "tags": { + Type: schema.TypeMap, + Elem: schema.TypeString, + ForceNew: false, + Optional: true, + }, }, } } @@ -88,12 +94,20 @@ func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta var enableAlksAccess = d.Get("enable_alks_access").(bool) var rawTemplateFields = d.Get("template_fields").(map[string]interface{}) var maxSessionDurationInSeconds = d.Get("max_session_duration_in_seconds").(int) + var rawTags = d.Get("tags").(map[string]interface{}) templateFields := make(map[string]string) for k, v := range rawTemplateFields { templateFields[k] = v.(string) } + tags := []*alks.Tag{} + for k, v := range rawTags { + kp := &k + vp := &v + tags = append(tags, &alks.Tag{Key: kp, Value: vp}) + } + var include int if incDefPol { include = 1 @@ -104,12 +118,17 @@ func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta return diag.FromErr(err) } - options := alks.CreateIamRoleOptions{IncDefPols: include, - AlksAccess: enableAlksAccess, - TemplateFields: templateFields, - MaxSessionDurationInSeconds: maxSessionDurationInSeconds} + options := &alks.CreateIamRoleOptions{ + RoleName: &roleName, + RoleType: &roleType, + IncludeDefaultPolicies: &include, + AlksAccess: &enableAlksAccess, + TemplateFields: &templateFields, + MaxSessionDurationInSeconds: &maxSessionDurationInSeconds, + Tags: &tags, + } - resp, err := client.CreateIamRoleWithOptions(roleName, roleType, options) + resp, err := client.CreateIamRole(options) if err != nil { return diag.FromErr(err) } From 0b49e4892eda7e05f6e23d24f4cd548619be7183 Mon Sep 17 00:00:00 2001 From: Spencer Finkel Date: Sat, 30 Apr 2022 17:15:58 -0500 Subject: [PATCH 05/19] Adds tags field to update role along with functionality to make update request --- resource_alks_iamrole.go | 46 +++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/resource_alks_iamrole.go b/resource_alks_iamrole.go index 4a1ecc92..e8adabb2 100644 --- a/resource_alks_iamrole.go +++ b/resource_alks_iamrole.go @@ -94,20 +94,13 @@ func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta var enableAlksAccess = d.Get("enable_alks_access").(bool) var rawTemplateFields = d.Get("template_fields").(map[string]interface{}) var maxSessionDurationInSeconds = d.Get("max_session_duration_in_seconds").(int) - var rawTags = d.Get("tags").(map[string]interface{}) + var tags = getTags(d) templateFields := make(map[string]string) for k, v := range rawTemplateFields { templateFields[k] = v.(string) } - tags := []*alks.Tag{} - for k, v := range rawTags { - kp := &k - vp := &v - tags = append(tags, &alks.Tag{Key: kp, Value: vp}) - } - var include int if incDefPol { include = 1 @@ -202,6 +195,13 @@ func resourceAlksIamRoleUpdate(ctx context.Context, d *schema.ResourceData, meta } } + if d.HasChange("tags") { + // try updating enable_alks_access + if err := updateIamTags(d, meta); err != nil { + return diag.FromErr(err) + } + } + d.Partial(false) return resourceAlksIamRoleRead(ctx, d, meta) @@ -230,6 +230,36 @@ func updateAlksAccess(d *schema.ResourceData, meta interface{}) error { return nil } +func updateIamTags(d *schema.ResourceData, meta interface{}) error { + client := meta.(*alks.Client) + if err := validateIAMEnabled(client); err != nil { + return err + } + + tags := getTags(d) + roleName := NameWithPrefix(d.Get("name").(string), d.Get("name_prefix").(string)) + options := alks.UpdateIamRoleRequest{ + RoleName: &roleName, + Tags: &tags, + } + + if _, err := alks.UpdateIamRole(&options); err != nil { + return err + } + return nil +} + +func getTags(d *schema.ResourceData) []Tag { + rawTags := d.Get("tags").(map[string]interface{}) + tags := []alks.Tag{} + for k, v := range rawTags { + kp := &k + vp := &v + tags = append(tags, alks.Tag{Key: kp, Value: vp}) + } + return tags +} + func migrateState(version int, state *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { switch version { case 0: From 95cf4a5e5486ee86acef0e5e6dc2726f5c5c46e1 Mon Sep 17 00:00:00 2001 From: Spencer Finkel Date: Mon, 2 May 2022 10:23:41 -0500 Subject: [PATCH 06/19] Translates include default policy value from int to bool --- resource_alks_iamrole.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resource_alks_iamrole.go b/resource_alks_iamrole.go index e8adabb2..c0d0237a 100644 --- a/resource_alks_iamrole.go +++ b/resource_alks_iamrole.go @@ -101,9 +101,9 @@ func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta templateFields[k] = v.(string) } - var include int + include := false if incDefPol { - include = 1 + include = true } client := meta.(*alks.Client) From 4d81fddf02e209b822bb5c9b3acf746fb1411338 Mon Sep 17 00:00:00 2001 From: Spencer Finkel Date: Mon, 2 May 2022 10:47:10 -0500 Subject: [PATCH 07/19] changes environement variables to match what's displayed in alks web --- provider_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/provider_test.go b/provider_test.go index e6bbac50..1d9cdf9a 100644 --- a/provider_test.go +++ b/provider_test.go @@ -31,13 +31,13 @@ func testAccPreCheck(t *testing.T) { if v := os.Getenv("ALKS_URL"); v == "" { t.Fatal("ALKS_URL must be set for acceptance tests") } - if v := os.Getenv("ALKS_ACCESS_KEY_ID"); v == "" { + if v := os.Getenv("AWS_ACCESS_KEY_ID"); v == "" { t.Fatal("ALKS_ACCESS_KEY_ID must be set for acceptance tests") } - if v := os.Getenv("ALKS_SECRET_ACCESS_KEY"); v == "" { + if v := os.Getenv("AWS_SECRET_ACCESS_KEY"); v == "" { t.Fatal("ALKS_SECRET_ACCESS_KEY must be set for acceptance tests") } - if v := os.Getenv("ALKS_SESSION_TOKEN"); v == "" { + if v := os.Getenv("AWS_SESSION_TOKEN"); v == "" { t.Fatal("ALKS_SESSION_TOKEN must be set for acceptance tests") } } From 7deb1cf18227e228557a2d7a9d0e2175675bbf1a Mon Sep 17 00:00:00 2001 From: Spencer Finkel Date: Mon, 2 May 2022 12:59:45 -0500 Subject: [PATCH 08/19] Creates tests for roles with tags and adds tags field set state --- resource_alks_iamrole.go | 10 ++++----- resource_alks_iamrole_test.go | 42 ++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/resource_alks_iamrole.go b/resource_alks_iamrole.go index c0d0237a..e4e62d73 100644 --- a/resource_alks_iamrole.go +++ b/resource_alks_iamrole.go @@ -173,6 +173,7 @@ func resourceAlksIamRoleRead(ctx context.Context, d *schema.ResourceData, meta i _ = d.Set("arn", foundRole.RoleArn) _ = d.Set("ip_arn", foundRole.RoleIPArn) _ = d.Set("enable_alks_access", foundRole.AlksAccess) + _ = d.Set("tags", foundRole.Tags) // TODO: In the future, our API or tags need to dynamically grab these values. // Till then, all imports require a destroy + create. @@ -243,19 +244,18 @@ func updateIamTags(d *schema.ResourceData, meta interface{}) error { Tags: &tags, } - if _, err := alks.UpdateIamRole(&options); err != nil { + if _, err := client.UpdateIamRole(&options); err != nil { return err } return nil } -func getTags(d *schema.ResourceData) []Tag { +func getTags(d *schema.ResourceData) []alks.Tag { rawTags := d.Get("tags").(map[string]interface{}) tags := []alks.Tag{} for k, v := range rawTags { - kp := &k - vp := &v - tags = append(tags, alks.Tag{Key: kp, Value: vp}) + tag := alks.Tag{Key: k, Value: v.(string)} + tags = append(tags, tag) } return tags } diff --git a/resource_alks_iamrole_test.go b/resource_alks_iamrole_test.go index 83fafb1f..4e7a914c 100644 --- a/resource_alks_iamrole_test.go +++ b/resource_alks_iamrole_test.go @@ -23,19 +23,20 @@ func TestAccAlksIamRole_Basic(t *testing.T) { Config: testAccCheckAlksIamRoleConfigBasic, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( - "alks_iamrole.foo", "name", "bar420"), + "alks_iamrole.foo", "name", "bar430"), resource.TestCheckResourceAttr( "alks_iamrole.foo", "type", "Amazon EC2"), resource.TestCheckResourceAttr( "alks_iamrole.foo", "include_default_policies", "false"), ), }, + { // update the resource Config: testAccCheckAlksIamRoleConfigUpdateBasic, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( - "alks_iamrole.foo", "name", "bar420"), + "alks_iamrole.foo", "name", "bar430"), resource.TestCheckResourceAttr( "alks_iamrole.foo", "type", "Amazon EC2"), resource.TestCheckResourceAttr( @@ -49,6 +50,41 @@ func TestAccAlksIamRole_Basic(t *testing.T) { }, }) } +func TestAccAlksIamRole_Tags(t *testing.T) { + var resp alks.IamRoleResponse + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAlksIamRoleDestroy(&resp), + Steps: []resource.TestStep{ + { + // create resource with tags + Config: testAccCheckAlksIamRoleCreateWithTags, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "name", "Car420"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "type", "Amazon EC2"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "include_default_policies", "false"), + ), + }, + { + // update resource with tags + Config: testAccCheckAlksIamRoleUpdateWithTags, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "name", "Car420"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "type", "Amazon EC2"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "include_default_policies", "false"), + ), + }, + }, + }) +} func TestAccAlksIamRole_NoMaxDuration(t *testing.T) { var resp alks.IamRoleResponse @@ -165,7 +201,7 @@ func testAccCheckAlksIamRoleAttributes(role *alks.IamRoleResponse) resource.Test const testAccCheckAlksIamRoleConfigBasic = ` resource "alks_iamrole" "foo" { - name = "bar420" + name = "bar430" type = "Amazon EC2" include_default_policies = false } From 0a3c2398a35787019a79033f983ec647f8939b02 Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Mon, 2 May 2022 13:09:28 -0500 Subject: [PATCH 09/19] Commit prior to merging in add-tags branch --- provider.go | 16 +- provider_test.go | 12 +- resource_alks_iamrole.go | 25 +++- resource_alks_iamrole_test.go | 16 +- resource_alks_iamtrustrole.go | 119 --------------- resource_alks_iamtrustrole_test.go | 139 ------------------ tags.go | 11 ++ .../Cox-Automotive/alks-go/.gitattributes | 1 + 8 files changed, 59 insertions(+), 280 deletions(-) delete mode 100644 resource_alks_iamtrustrole.go delete mode 100644 resource_alks_iamtrustrole_test.go create mode 100644 tags.go create mode 100644 vendor/github.com/Cox-Automotive/alks-go/.gitattributes diff --git a/provider.go b/provider.go index 22ade8ef..856f641d 100644 --- a/provider.go +++ b/provider.go @@ -26,8 +26,8 @@ func Provider() *schema.Provider { Optional: true, Description: "This is the AWS access key. It must be provided, but it can also be sourced from the ALKS_ACCESS_KEY_ID or AWS_ACCESS_KEY_ID environment variable.", DefaultFunc: schema.MultiEnvDefaultFunc([]string{ - "ALKS_ACCESS_KEY_ID", "AWS_ACCESS_KEY_ID", + "ALKS_ACCESS_KEY_ID", }, nil), }, "secret_key": { @@ -35,8 +35,8 @@ func Provider() *schema.Provider { Optional: true, Description: "This is the AWS secret key. It must be provided, but it can also be sourced from the ALKS_SECRET_ACCESS_KEY or AWS_SECRET_ACCESS_KEY environment variable", DefaultFunc: schema.MultiEnvDefaultFunc([]string{ - "ALKS_SECRET_ACCESS_KEY", "AWS_SECRET_ACCESS_KEY", + "ALKS_SECRET_ACCESS_KEY", }, nil), }, "token": { @@ -44,8 +44,8 @@ func Provider() *schema.Provider { Optional: true, Description: "This is the AWS session token. It must be provided, but it can also be sourced from the ALKS_SESSION_TOKEN or AWS_SESSION_TOKEN environment variable", DefaultFunc: schema.MultiEnvDefaultFunc([]string{ - "ALKS_SESSION_TOKEN", "AWS_SESSION_TOKEN", + "ALKS_SESSION_TOKEN", }, nil), }, "profile": { @@ -79,9 +79,9 @@ func Provider() *schema.Provider { }, ResourcesMap: map[string]*schema.Resource{ - "alks_iamrole": resourceAlksIamRole(), - "alks_iamtrustrole": resourceAlksIamTrustRole(), - "alks_ltk": resourceAlksLtk(), + "alks_iamrole": resourceAlksIamRole(), + // "alks_iamtrustrole": resourceAlksIamTrustRole(), + "alks_ltk": resourceAlksLtk(), }, DataSourcesMap: map[string]*schema.Resource{ @@ -212,6 +212,10 @@ func expandProviderDefaultTags(l []interface{}) *[]alks.Tag { return &tagSlice } +// func (tags *[]alks.Tag) removeDefaultTagsConfigFromAllTags(defaultTags *[]alks.Tag) *[]alks.Tag { + +// } + type AlksClient struct { client *alks.Client defaultTags *[]alks.Tag diff --git a/provider_test.go b/provider_test.go index e6bbac50..b09c2514 100644 --- a/provider_test.go +++ b/provider_test.go @@ -31,13 +31,13 @@ func testAccPreCheck(t *testing.T) { if v := os.Getenv("ALKS_URL"); v == "" { t.Fatal("ALKS_URL must be set for acceptance tests") } - if v := os.Getenv("ALKS_ACCESS_KEY_ID"); v == "" { - t.Fatal("ALKS_ACCESS_KEY_ID must be set for acceptance tests") + if v := os.Getenv("AWS_ACCESS_KEY_ID"); v == "" { + t.Fatal("AWS_ACCESS_KEY_ID must be set for acceptance tests") } - if v := os.Getenv("ALKS_SECRET_ACCESS_KEY"); v == "" { - t.Fatal("ALKS_SECRET_ACCESS_KEY must be set for acceptance tests") + if v := os.Getenv("AWS_SECRET_ACCESS_KEY"); v == "" { + t.Fatal("AWS_SECRET_ACCESS_KEY must be set for acceptance tests") } - if v := os.Getenv("ALKS_SESSION_TOKEN"); v == "" { - t.Fatal("ALKS_SESSION_TOKEN must be set for acceptance tests") + if v := os.Getenv("AWS_SESSION_TOKEN"); v == "" { + t.Fatal("AWS_SESSION_TOKEN must be set for acceptance tests") } } diff --git a/resource_alks_iamrole.go b/resource_alks_iamrole.go index 670fedda..024e00a9 100644 --- a/resource_alks_iamrole.go +++ b/resource_alks_iamrole.go @@ -75,10 +75,29 @@ func resourceAlksIamRole() *schema.Resource { Default: 3600, Optional: true, }, + "tags": TagsSchema(), + "tags_all": TagsSchemaComputed(), }, } } +func TagsSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + } +} + +func TagsSchemaComputed() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + } +} + func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { log.Printf("[INFO] ALKS IAM Role Create") @@ -103,7 +122,7 @@ func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta client := providerStruct.client defaultTags := providerStruct.defaultTags - // client := meta.(*alks.Client) + // client := meta.(*alks.Client) if err := validateIAMEnabled(client); err != nil { return diag.FromErr(err) @@ -150,7 +169,8 @@ func resourceAlksIamRoleDelete(ctx context.Context, d *schema.ResourceData, meta func resourceAlksIamRoleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { log.Printf("[INFO] ALKS IAM Role Read") - client := meta.(*alks.Client) + providerStruct := meta.(*AlksClient) + client := providerStruct.client // Check if role exists. if d.Id() == "" || d.Id() == "none" { @@ -171,6 +191,7 @@ func resourceAlksIamRoleRead(ctx context.Context, d *schema.ResourceData, meta i _ = d.Set("arn", foundRole.RoleArn) _ = d.Set("ip_arn", foundRole.RoleIPArn) _ = d.Set("enable_alks_access", foundRole.AlksAccess) + _ = d.Set("tags_all", foundRole.tags) // TODO: In the future, our API or tags need to dynamically grab these values. // Till then, all imports require a destroy + create. diff --git a/resource_alks_iamrole_test.go b/resource_alks_iamrole_test.go index 83fafb1f..ef6ff0d1 100644 --- a/resource_alks_iamrole_test.go +++ b/resource_alks_iamrole_test.go @@ -23,7 +23,7 @@ func TestAccAlksIamRole_Basic(t *testing.T) { Config: testAccCheckAlksIamRoleConfigBasic, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( - "alks_iamrole.foo", "name", "bar420"), + "alks_iamrole.foo", "name", "bar430"), resource.TestCheckResourceAttr( "alks_iamrole.foo", "type", "Amazon EC2"), resource.TestCheckResourceAttr( @@ -35,7 +35,7 @@ func TestAccAlksIamRole_Basic(t *testing.T) { Config: testAccCheckAlksIamRoleConfigUpdateBasic, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( - "alks_iamrole.foo", "name", "bar420"), + "alks_iamrole.foo", "name", "bar430"), resource.TestCheckResourceAttr( "alks_iamrole.foo", "type", "Amazon EC2"), resource.TestCheckResourceAttr( @@ -62,7 +62,7 @@ func TestAccAlksIamRole_NoMaxDuration(t *testing.T) { Config: testAccCheckAlksIamRoleConfigBasic, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( - "alks_iamrole.foo", "name", "bar420"), + "alks_iamrole.foo", "name", "bar430"), resource.TestCheckResourceAttr( "alks_iamrole.foo", "type", "Amazon EC2"), resource.TestCheckResourceAttr( @@ -74,7 +74,7 @@ func TestAccAlksIamRole_NoMaxDuration(t *testing.T) { Config: testAccCheckAlksIamRoleConfigUpdateNoMaxDuration, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( - "alks_iamrole.foo", "name", "bar420"), + "alks_iamrole.foo", "name", "bar430"), resource.TestCheckResourceAttr( "alks_iamrole.foo", "type", "Amazon EC2"), resource.TestCheckResourceAttr( @@ -152,7 +152,7 @@ func testAccCheckAlksIamRoleDestroy(role *alks.IamRoleResponse) resource.TestChe func testAccCheckAlksIamRoleAttributes(role *alks.IamRoleResponse) resource.TestCheckFunc { return func(s *terraform.State) error { log.Printf("[INFO] its this %v", role) - if role.RoleName != "bar420" { + if role.RoleName != "bar430" { return fmt.Errorf("Bad name: %s", role.RoleName) } if role.RoleType != "Amazon EC2" { @@ -165,7 +165,7 @@ func testAccCheckAlksIamRoleAttributes(role *alks.IamRoleResponse) resource.Test const testAccCheckAlksIamRoleConfigBasic = ` resource "alks_iamrole" "foo" { - name = "bar420" + name = "bar430" type = "Amazon EC2" include_default_policies = false } @@ -173,7 +173,7 @@ const testAccCheckAlksIamRoleConfigBasic = ` const testAccCheckAlksIamRoleConfigUpdateBasic = ` resource "alks_iamrole" "foo" { - name = "bar420" + name = "bar430" type = "Amazon EC2" include_default_policies = false enable_alks_access = true @@ -183,7 +183,7 @@ const testAccCheckAlksIamRoleConfigUpdateBasic = ` const testAccCheckAlksIamRoleConfigUpdateNoMaxDuration = ` resource "alks_iamrole" "foo" { - name = "bar420" + name = "bar430" type = "Amazon EC2" include_default_policies = false enable_alks_access = true diff --git a/resource_alks_iamtrustrole.go b/resource_alks_iamtrustrole.go deleted file mode 100644 index 800f939e..00000000 --- a/resource_alks_iamtrustrole.go +++ /dev/null @@ -1,119 +0,0 @@ -package main - -import ( - "context" - "log" - "strings" - "time" - - "github.com/Cox-Automotive/alks-go" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func resourceAlksIamTrustRole() *schema.Resource { - return &schema.Resource{ - CreateContext: resourceAlksIamTrustRoleCreate, - ReadContext: resourceAlksIamRoleRead, - UpdateContext: resourceAlksIamRoleUpdate, - DeleteContext: resourceAlksIamRoleDelete, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - SchemaVersion: 1, - MigrateState: migrateState, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"name_prefix"}, - }, - "name_prefix": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"name"}, - }, - "type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "trust_arn": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "role_added_to_ip": { - Type: schema.TypeBool, - Computed: true, - }, - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "ip_arn": { - Type: schema.TypeString, - Computed: true, - }, - "enable_alks_access": { - Type: schema.TypeBool, - Default: false, - Optional: true, - }, - }, - } -} - -func resourceAlksIamTrustRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - log.Printf("[INFO] ALKS IAM Trust Role Create") - - var roleName = NameWithPrefix(d.Get("name").(string), d.Get("name_prefix").(string)) - var roleType = d.Get("type").(string) - var trustArn = d.Get("trust_arn").(string) - var enableAlksAccess = d.Get("enable_alks_access").(bool) - - client := meta.(*alks.Client) - if err := validateIAMEnabled(client); err != nil { - return diag.FromErr(err) - } - - var resp *alks.IamRoleResponse - err := resource.RetryContext(ctx, 2*time.Minute, func() *resource.RetryError { - var err error - resp, err = client.CreateIamTrustRole(roleName, roleType, trustArn, enableAlksAccess) - if err != nil { - if strings.Contains(err.Error(), "Role already exists") || strings.Contains(err.Error(), "Instance profile exists") { - return resource.NonRetryableError(err) - } - - // Amazon IAM utilizes an eventual consistency model: - // https://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_general.html#troubleshoot_general_eventual-consistency - // - // The newly created IAM role may not exist immediately and could result in dependent - // resources failing non-deterministically. Loop for 15 second increments up to 2 - // minutes checking to ensure the resouce was successfully created and is visible. - - time.Sleep(15 * time.Second) - return resource.RetryableError(err) - } - return nil - }) - - if err != nil { - return diag.FromErr(err) - } - - response := *resp - - d.SetId(response.RoleName) - _ = d.Set("role_added_to_ip", resp.RoleAddedToIP) - - log.Printf("[INFO] alks_iamtrustrole.id: %v", d.Id()) - - return resourceAlksIamRoleRead(ctx, d, meta) -} diff --git a/resource_alks_iamtrustrole_test.go b/resource_alks_iamtrustrole_test.go deleted file mode 100644 index 85b33fb2..00000000 --- a/resource_alks_iamtrustrole_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "regexp" - "testing" - - "github.com/Cox-Automotive/alks-go" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -func TestAccAlksIamTrustRole_Basic(t *testing.T) { - var resp alks.IamRoleResponse - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAlksIamRoleDestroy(&resp), - Steps: []resource.TestStep{ - { - Config: testAccCheckAlksIamTrustRoleConfigBasic, - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "alks_iamtrustrole.bar", "name", "bar"), - resource.TestCheckResourceAttr( - "alks_iamtrustrole.bar", "type", "Inner Account"), - ), - }, - { - // update the resource - Config: testAccCheckAlksIamTrustRoleConfigUpdateBasic, - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "alks_iamtrustrole.bar", "name", "bar"), - resource.TestCheckResourceAttr( - "alks_iamtrustrole.bar", "type", "Inner Account"), - resource.TestCheckResourceAttr( - "alks_iamtrustrole.bar", "enable_alks_access", "true"), - ), - }, - }, - }) -} - -func TestAccAlksIamTrustRole_NamePrefix(t *testing.T) { - var resp alks.IamRoleResponse - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAlksIamRoleDestroy(&resp), - Steps: []resource.TestStep{ - { - Config: testAccCheckAlksIamTrustRoleConfigNamePrefix, - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "alks_iamtrustrole.nameprefix_trustrole", "name_prefix", "alks_test_acc_"), - resource.TestMatchResourceAttr( - "alks_iamtrustrole.nameprefix_trustrole", "name", regexp.MustCompile("alks_test_acc_[0-9]{26}")), - resource.TestCheckResourceAttr( - "alks_iamtrustrole.nameprefix_trustrole", "type", "Inner Account"), - ), - }, - }, - }) -} - -func TestAccAlksIamTrustRole_NameAndNamePrefixConflict(t *testing.T) { - var resp alks.IamRoleResponse - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAlksIamRoleDestroy(&resp), - Steps: []resource.TestStep{ - { - Config: testAccCheckAlksIamTrustRoleConfigNameAndNamePrefixConflict, - ExpectError: regexp.MustCompile(".*\"name\": conflicts with name_prefix.*"), - }, - }, - }) -} - -const testAccCheckAlksIamTrustRoleConfigBasic = ` - resource "alks_iamrole" "foo" { - name = "foo" - type = "Amazon EC2" - include_default_policies = false - } - - resource "alks_iamtrustrole" "bar" { - name = "bar" - type = "Inner Account" - trust_arn = "${alks_iamrole.foo.arn}" - } -` - -const testAccCheckAlksIamTrustRoleConfigUpdateBasic = ` - resource "alks_iamrole" "foo" { - name = "foo" - type = "Amazon EC2" - include_default_policies = false - } - - resource "alks_iamtrustrole" "bar" { - name = "bar" - type = "Inner Account" - trust_arn = "${alks_iamrole.foo.arn}" - enable_alks_access = true - } -` - -const testAccCheckAlksIamTrustRoleConfigNamePrefix = ` - resource "alks_iamrole" "nameprefix_role" { - name_prefix = "alks_test_acc_" - type = "Amazon EC2" - include_default_policies = false - } - - resource "alks_iamtrustrole" "nameprefix_trustrole" { - name_prefix = "alks_test_acc_" - type = "Inner Account" - trust_arn = "${alks_iamrole.nameprefix_role.arn}" - } -` - -const testAccCheckAlksIamTrustRoleConfigNameAndNamePrefixConflict = ` - resource "alks_iamrole" "nameprefixconflict_role" { - name_prefix = "alks_test_acc_" - type = "Amazon EC2" - include_default_policies = false - } - - resource "alks_iamtrustrole" "nameprefixconflict_trustrole" { - name = "alks_test_acc" - name_prefix = "alks_test_acc_" - type = "Inner Account" - trust_arn = "${alks_iamrole.nameprefixconflict_role.arn}" - } -` diff --git a/tags.go b/tags.go new file mode 100644 index 00000000..c0549576 --- /dev/null +++ b/tags.go @@ -0,0 +1,11 @@ +package main + +import ( + "github.com/Cox-Automotive/alks-go" +) + +// DefaultConfig contains tags to default across all resources. + +type DefaultTagsConfig struct { + tags *[]alks.Tag +} diff --git a/vendor/github.com/Cox-Automotive/alks-go/.gitattributes b/vendor/github.com/Cox-Automotive/alks-go/.gitattributes new file mode 100644 index 00000000..833dca70 --- /dev/null +++ b/vendor/github.com/Cox-Automotive/alks-go/.gitattributes @@ -0,0 +1 @@ +vendor/ linguist-generated=true From eccca856e014a7d19073ecf8a54c24964560a526 Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Mon, 2 May 2022 15:09:44 -0500 Subject: [PATCH 10/19] Commit before updating to latest go client --- provider.go | 18 +-------- resource_alks_iamrole.go | 76 +++++++++++++++++++++++++++++------ resource_alks_iamrole_test.go | 31 ++++++++++++++ 3 files changed, 96 insertions(+), 29 deletions(-) diff --git a/provider.go b/provider.go index 856f641d..1971cca5 100644 --- a/provider.go +++ b/provider.go @@ -197,25 +197,11 @@ func expandProviderDefaultTags(l []interface{}) *[]alks.Tag { } m := l[0].(map[string]interface{}) - tagSlice := []alks.Tag{} - if tagMap, ok := m["tags"].(map[string]interface{}); ok { - for key, value := range tagMap { - tag := alks.Tag{} - tag.Key = key - str, ok := value.(string) - if ok { - tag.Value = str - } - tagSlice = append(tagSlice, tag) - } - } + tagSlice := tagMapToSlice(m["tags"].(map[string]interface{})) + return &tagSlice } -// func (tags *[]alks.Tag) removeDefaultTagsConfigFromAllTags(defaultTags *[]alks.Tag) *[]alks.Tag { - -// } - type AlksClient struct { client *alks.Client defaultTags *[]alks.Tag diff --git a/resource_alks_iamrole.go b/resource_alks_iamrole.go index bc69a9a1..7665cd7b 100644 --- a/resource_alks_iamrole.go +++ b/resource_alks_iamrole.go @@ -107,14 +107,14 @@ func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta var enableAlksAccess = d.Get("enable_alks_access").(bool) var rawTemplateFields = d.Get("template_fields").(map[string]interface{}) var maxSessionDurationInSeconds = d.Get("max_session_duration_in_seconds").(int) - var tags = getTags(d) + var tags = tagMapToSlice(d.Get("tags").(map[string]interface{})) templateFields := make(map[string]string) for k, v := range rawTemplateFields { templateFields[k] = v.(string) } - var include int + include := false if incDefPol { include = true } @@ -122,17 +122,23 @@ func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta providerStruct := meta.(*AlksClient) client := providerStruct.client - defaultTags := providerStruct.defaultTags + defaultTags := *providerStruct.defaultTags + allTags := combineTagsWithDefault(tags, defaultTags) // client := meta.(*alks.Client) if err := validateIAMEnabled(client); err != nil { return diag.FromErr(err) } - options := alks.CreateIamRoleOptions{IncDefPols: include, - AlksAccess: enableAlksAccess, - TemplateFields: templateFields, - MaxSessionDurationInSeconds: maxSessionDurationInSeconds} + options := &alks.CreateIamRoleOptions{ + RoleName: &roleName, + RoleType: &roleType, + IncludeDefaultPolicies: &include, + AlksAccess: &enableAlksAccess, + TemplateFields: &templateFields, + MaxSessionDurationInSeconds: &maxSessionDurationInSeconds, + Tags: &allTags, + } resp, err := client.CreateIamRole(options) if err != nil { @@ -168,6 +174,8 @@ func resourceAlksIamRoleRead(ctx context.Context, d *schema.ResourceData, meta i providerStruct := meta.(*AlksClient) client := providerStruct.client + defaultTags := *providerStruct.defaultTags + // Check if role exists. if d.Id() == "" || d.Id() == "none" { return nil @@ -188,6 +196,17 @@ func resourceAlksIamRoleRead(ctx context.Context, d *schema.ResourceData, meta i _ = d.Set("ip_arn", foundRole.RoleIPArn) _ = d.Set("enable_alks_access", foundRole.AlksAccess) + allTags := foundRole.Tags + roleSpecificTags := removeDefaultTags(tagSliceToMap(allTags), defaultTags) + + if err := d.Set("tags", tagSliceToMap(roleSpecificTags)); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("tags_all", tagSliceToMap(allTags)); err != nil { + return diag.FromErr(err) + } + // TODO: In the future, our API or tags need to dynamically grab these values. // Till then, all imports require a destroy + create. //_ = d.Set("type", foundrole.RoleType) @@ -209,7 +228,7 @@ func resourceAlksIamRoleUpdate(ctx context.Context, d *schema.ResourceData, meta } } - if d.HasChange("tags") { + if d.HasChange("tags_all") { // try updating enable_alks_access if err := updateIamTags(d, meta); err != nil { return diag.FromErr(err) @@ -245,12 +264,14 @@ func updateAlksAccess(d *schema.ResourceData, meta interface{}) error { } func updateIamTags(d *schema.ResourceData, meta interface{}) error { - client := meta.(*alks.Client) + providerStruct := meta.(*AlksClient) + client := providerStruct.client + if err := validateIAMEnabled(client); err != nil { return err } - tags := getTags(d) + tags := tagMapToSlice(d.Get("tags_all").(map[string]interface{})) roleName := NameWithPrefix(d.Get("name").(string), d.Get("name_prefix").(string)) options := alks.UpdateIamRoleRequest{ RoleName: &roleName, @@ -263,16 +284,45 @@ func updateIamTags(d *schema.ResourceData, meta interface{}) error { return nil } -func getTags(d *schema.ResourceData) []alks.Tag { - rawTags := d.Get("tags").(map[string]interface{}) +//Combines tags defined on an individual resource with the default tags listed on the provider block +//Resource specific tags will overwrite default tags +func combineTagsWithDefault(tags []alks.Tag, defaultTags []alks.Tag) []alks.Tag { + defaultTagsMap := tagSliceToMap(defaultTags) + + for _,t := range tags { + defaultTagsMap[t.Key] = t.Value + } + allTags := tagMapToSlice(defaultTagsMap) + + return allTags +} + +//Removes default tags from a map of role specific + default tags +func removeDefaultTags(allTags map[string]interface{}, defalutTags []alks.Tag) []alks.Tag { + for _,t := range defalutTags { + delete(allTags, t.Key) + } + + return tagMapToSlice(allTags) +} + +func tagMapToSlice(tagMap map[string]interface{}) []alks.Tag { tags := []alks.Tag{} - for k, v := range rawTags { + for k, v := range tagMap { tag := alks.Tag{Key: k, Value: v.(string)} tags = append(tags, tag) } return tags } +func tagSliceToMap(tagSlice []alks.Tag) map[string]interface{} { + tagMap := make(map[string]interface{}) + for _, t := range tagSlice { + tagMap[t.Key] = t.Value + } + return tagMap +} + func migrateState(version int, state *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { switch version { case 0: diff --git a/resource_alks_iamrole_test.go b/resource_alks_iamrole_test.go index 53768455..17d017d7 100644 --- a/resource_alks_iamrole_test.go +++ b/resource_alks_iamrole_test.go @@ -68,6 +68,10 @@ func TestAccAlksIamRole_Tags(t *testing.T) { "alks_iamrole.foo", "type", "Amazon EC2"), resource.TestCheckResourceAttr( "alks_iamrole.foo", "include_default_policies", "false"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "tags.testKey1", "testValue1"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "tags.testKey2", "testValue2"), ), }, { @@ -80,6 +84,10 @@ func TestAccAlksIamRole_Tags(t *testing.T) { "alks_iamrole.foo", "type", "Amazon EC2"), resource.TestCheckResourceAttr( "alks_iamrole.foo", "include_default_policies", "false"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "tags.testKey3", "testValue3"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "tags.testKey2", "testValue2"), ), }, }, @@ -216,6 +224,29 @@ const testAccCheckAlksIamRoleConfigUpdateBasic = ` max_session_duration_in_seconds = 3600 } ` +const testAccCheckAlksIamRoleCreateWithTags = ` +resource "alks_iamrole" "foo" { + name = "bar430" + type = "Amazon EC2" + include_default_policies = false + } + tags = { + testKey1 = "testValue1" + testKey2 = "testValue2" + } +` + +const testAccCheckAlksIamRoleUpdateWithTags = ` +resource "alks_iamrole" "foo" { + name = "bar430" + type = "Amazon EC2" + include_default_policies = false + } + tags = { + testKey3 = "testValue3" + testKey2 = "testValue2" + } +` const testAccCheckAlksIamRoleConfigUpdateNoMaxDuration = ` resource "alks_iamrole" "foo" { From a9f5c9ef63585bdb02017c668985e9d9690c3b4b Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Mon, 2 May 2022 15:16:28 -0500 Subject: [PATCH 11/19] Updated to latest go client --- go.mod | 3 +-- go.sum | 4 ++-- vendor/github.com/Cox-Automotive/alks-go/iam_role.go | 2 ++ vendor/modules.txt | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 78ccf0ff..f414371a 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,7 @@ module github.com/Cox-Automotive/terraform-provider-alks go 1.14 require ( - github.com/Cox-Automotive/alks-go v0.0.0-20220429202951-085c873654ea - // github.com/Cox-Automotive/alks-go v0.0.0-20220218164749-e4de33286d92 + github.com/Cox-Automotive/alks-go v0.0.0-20220502192728-623c28f3b92b github.com/aws/aws-sdk-go v1.31.15 github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/terraform-json v0.13.0 // indirect diff --git a/go.sum b/go.sum index a47bbefe..39bd7ed6 100644 --- a/go.sum +++ b/go.sum @@ -34,8 +34,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Cox-Automotive/alks-go v0.0.0-20220429202951-085c873654ea h1:BJWnEf+8/OjU40RBygIkyLtzHFF7DrypqyUVX79Dyn8= -github.com/Cox-Automotive/alks-go v0.0.0-20220429202951-085c873654ea/go.mod h1:jJNgXthl59Vt2tJHSC3WZ0vlopV9xqdclfQuLgwHjOw= +github.com/Cox-Automotive/alks-go v0.0.0-20220502192728-623c28f3b92b h1:lTQ/h4MVJzOmrWk0a16zb9pUapImXFeTlQkO3vlZtUI= +github.com/Cox-Automotive/alks-go v0.0.0-20220502192728-623c28f3b92b/go.mod h1:jJNgXthl59Vt2tJHSC3WZ0vlopV9xqdclfQuLgwHjOw= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= diff --git a/vendor/github.com/Cox-Automotive/alks-go/iam_role.go b/vendor/github.com/Cox-Automotive/alks-go/iam_role.go index 7a12ed2d..568adcbc 100644 --- a/vendor/github.com/Cox-Automotive/alks-go/iam_role.go +++ b/vendor/github.com/Cox-Automotive/alks-go/iam_role.go @@ -8,6 +8,7 @@ import ( "strings" ) +// Tag struct is used to represent a AWS Tag type Tag struct { Key string `json:"key"` Value string `json:"value"` @@ -59,6 +60,7 @@ type GetIamRoleResponse struct { RoleAddedToIP bool `json:"addedRoleToInstanceProfile"` Exists bool `json:"roleExists"` AlksAccess bool `json:"machineIdentity"` + Tags []Tag `json:"tags"` } // GetRoleRequest is used to represent a request for details about diff --git a/vendor/modules.txt b/vendor/modules.txt index e0328924..1fed2ba6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -8,7 +8,7 @@ cloud.google.com/go/internal/trace cloud.google.com/go/internal/version # cloud.google.com/go/storage v1.10.0 cloud.google.com/go/storage -# github.com/Cox-Automotive/alks-go v0.0.0-20220429202951-085c873654ea +# github.com/Cox-Automotive/alks-go v0.0.0-20220502192728-623c28f3b92b ## explicit github.com/Cox-Automotive/alks-go # github.com/agext/levenshtein v1.2.2 From 045aad45206abfe41e949cfc8e3384f8207c0169 Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Tue, 3 May 2022 09:15:12 -0500 Subject: [PATCH 12/19] Commit Before merge with Spencer --- data_source_alks_keys.go | 8 ++- provider.go | 6 +- provider_test.go | 52 ++++++++++++++ resource_alks_iamrole.go | 58 +++++++++++++--- resource_alks_iamrole_test.go | 124 ++++++++++++++++++++++++++++------ resource_alks_ltk.go | 14 ++-- resource_alks_ltk_test.go | 3 +- 7 files changed, 224 insertions(+), 41 deletions(-) diff --git a/data_source_alks_keys.go b/data_source_alks_keys.go index 34c047db..0475b061 100644 --- a/data_source_alks_keys.go +++ b/data_source_alks_keys.go @@ -1,10 +1,11 @@ package main import ( - "github.com/Cox-Automotive/alks-go" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + // "github.com/Cox-Automotive/alks-go" "log" "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func dataSourceAlksKeys() *schema.Resource { @@ -38,7 +39,8 @@ func dataSourceAlksKeys() *schema.Resource { func dataSourceAlksKeysRead(d *schema.ResourceData, meta interface{}) error { log.Printf("[INFO] ALKS Keys Data Source Read") - client := meta.(*alks.Client) + providerStruct := meta.(*AlksClient) + client := providerStruct.client resp, err := client.CreateIamSession() if err != nil { diff --git a/provider.go b/provider.go index 1971cca5..e0dd7f59 100644 --- a/provider.go +++ b/provider.go @@ -191,7 +191,7 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{} // return c, diags } -func expandProviderDefaultTags(l []interface{}) *[]alks.Tag { +func expandProviderDefaultTags(l []interface{}) []alks.Tag { if len(l) == 0 || l[0] == nil { return nil } @@ -199,10 +199,10 @@ func expandProviderDefaultTags(l []interface{}) *[]alks.Tag { m := l[0].(map[string]interface{}) tagSlice := tagMapToSlice(m["tags"].(map[string]interface{})) - return &tagSlice + return tagSlice } type AlksClient struct { client *alks.Client - defaultTags *[]alks.Tag + defaultTags []alks.Tag //Not making this a pointer because I was having to check everywhere if it was nil } diff --git a/provider_test.go b/provider_test.go index b09c2514..ac3db2aa 100644 --- a/provider_test.go +++ b/provider_test.go @@ -4,7 +4,10 @@ import ( "os" "testing" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) var testAccProviders map[string]*schema.Provider @@ -41,3 +44,52 @@ func testAccPreCheck(t *testing.T) { t.Fatal("AWS_SESSION_TOKEN must be set for acceptance tests") } } + +func testAccProvider_DefaultTags(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testProviderWithDefaultTags, + Check: resource.ComposeTestCheckFunc( + testAccCheckIgnoreTagsKeys(testAccProviders["alks"], []string{"defaultTagKey1", "defaultTagKey2"}), + ), + }, + }, + }) +} + +func testAccCheckIgnoreTagsKeys(providers *schema.Provider, expectedKeys []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + providerStruct := meta.(*AlksClient) + client := providerStruct.client + + defaultTags := providerStruct.defaultTags + found := false + for _, k := range expectedKeys { + for _, i := range defaultTags { + if k == i.Key { + found = true + break + } + } + if !found { + return diag.FromErr("Expected Key Not found") + } + } + return nil + } +} + +const testProviderWithDefaultTags = ` + provider "alks" { + default_tags { + tags = { + defaultTagKey1 = "defaultTagValue1" + defaultTagKey2 = "defaultTagValue2" + } + } + } +` diff --git a/resource_alks_iamrole.go b/resource_alks_iamrole.go index 7665cd7b..e292d971 100644 --- a/resource_alks_iamrole.go +++ b/resource_alks_iamrole.go @@ -75,9 +75,10 @@ func resourceAlksIamRole() *schema.Resource { Default: 3600, Optional: true, }, - "tags": TagsSchema(), + "tags": TagsSchema(), "tags_all": TagsSchemaComputed(), }, + CustomizeDiff: SetTagsDiff, } } @@ -100,7 +101,7 @@ func TagsSchemaComputed() *schema.Schema { func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { log.Printf("[INFO] ALKS IAM Role Create") - + fmt.Printf("In Role Create\n") var roleName = NameWithPrefix(d.Get("name").(string), d.Get("name_prefix").(string)) var roleType = d.Get("type").(string) var incDefPol = d.Get("include_default_policies").(bool) @@ -122,7 +123,10 @@ func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta providerStruct := meta.(*AlksClient) client := providerStruct.client - defaultTags := *providerStruct.defaultTags + defaultTags := []alks.Tag{} + if (*providerStruct).defaultTags != nil { + defaultTags = (*providerStruct).defaultTags + } allTags := combineTagsWithDefault(tags, defaultTags) // client := meta.(*alks.Client) @@ -156,7 +160,8 @@ func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta func resourceAlksIamRoleDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { log.Printf("[INFO] ALKS IAM Role Delete") - client := meta.(*alks.Client) + providerStruct := meta.(*AlksClient) + client := providerStruct.client if err := validateIAMEnabled(client); err != nil { return diag.FromErr(err) } @@ -170,11 +175,11 @@ func resourceAlksIamRoleDelete(ctx context.Context, d *schema.ResourceData, meta func resourceAlksIamRoleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { log.Printf("[INFO] ALKS IAM Role Read") - + fmt.Printf("In Role Read\n") providerStruct := meta.(*AlksClient) client := providerStruct.client - defaultTags := *providerStruct.defaultTags + defaultTags := providerStruct.defaultTags // Check if role exists. if d.Id() == "" || d.Id() == "none" { @@ -197,6 +202,9 @@ func resourceAlksIamRoleRead(ctx context.Context, d *schema.ResourceData, meta i _ = d.Set("enable_alks_access", foundRole.AlksAccess) allTags := foundRole.Tags + for _, t := range allTags { + fmt.Printf("Read Tag: %s\n", t.Key) + } roleSpecificTags := removeDefaultTags(tagSliceToMap(allTags), defaultTags) if err := d.Set("tags", tagSliceToMap(roleSpecificTags)); err != nil { @@ -217,6 +225,7 @@ func resourceAlksIamRoleRead(ctx context.Context, d *schema.ResourceData, meta i func resourceAlksIamRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { log.Printf("[INFO] ALKS IAM Role Update") + fmt.Printf("In Role Update\n") // enable partial state mode d.Partial(true) @@ -243,7 +252,8 @@ func resourceAlksIamRoleUpdate(ctx context.Context, d *schema.ResourceData, meta func updateAlksAccess(d *schema.ResourceData, meta interface{}) error { var alksAccess = d.Get("enable_alks_access").(bool) var roleArn = d.Get("arn").(string) - client := meta.(*alks.Client) + providerStruct := meta.(*AlksClient) + client := providerStruct.client if err := validateIAMEnabled(client); err != nil { return err } @@ -264,6 +274,7 @@ func updateAlksAccess(d *schema.ResourceData, meta interface{}) error { } func updateIamTags(d *schema.ResourceData, meta interface{}) error { + fmt.Printf("In updateIamTags\n") providerStruct := meta.(*AlksClient) client := providerStruct.client @@ -289,7 +300,7 @@ func updateIamTags(d *schema.ResourceData, meta interface{}) error { func combineTagsWithDefault(tags []alks.Tag, defaultTags []alks.Tag) []alks.Tag { defaultTagsMap := tagSliceToMap(defaultTags) - for _,t := range tags { + for _, t := range tags { defaultTagsMap[t.Key] = t.Value } allTags := tagMapToSlice(defaultTagsMap) @@ -299,7 +310,7 @@ func combineTagsWithDefault(tags []alks.Tag, defaultTags []alks.Tag) []alks.Tag //Removes default tags from a map of role specific + default tags func removeDefaultTags(allTags map[string]interface{}, defalutTags []alks.Tag) []alks.Tag { - for _,t := range defalutTags { + for _, t := range defalutTags { delete(allTags, t.Key) } @@ -323,6 +334,35 @@ func tagSliceToMap(tagSlice []alks.Tag) map[string]interface{} { return tagMap } +func SetTagsDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { + defaultTags := meta.(*AlksClient).defaultTags + + resourceTags := tagMapToSlice(diff.Get("tags").(map[string]interface{})) + + allTags := combineTagsWithDefault(resourceTags, defaultTags) + + // To ensure "tags_all" is correctly computed, we explicitly set the attribute diff + // when the merger of resource-level tags onto provider-level tags results in n > 0 tags, + // otherwise we mark the attribute as "Computed" only when their is a known diff (excluding an empty map) + // or a change for "tags_all". + + if len(allTags) > 0 { + if err := diff.SetNew("tags_all", tagSliceToMap(allTags)); err != nil { + return fmt.Errorf("error setting new tags_all diff: %w", err) + } + } else if len(diff.Get("tags_all").(map[string]interface{})) > 0 { + if err := diff.SetNewComputed("tags_all"); err != nil { + return fmt.Errorf("error setting tags_all to computed: %w", err) + } + } else if diff.HasChange("tags_all") { + if err := diff.SetNewComputed("tags_all"); err != nil { + return fmt.Errorf("error setting tags_all to computed: %w", err) + } + } + + return nil +} + func migrateState(version int, state *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { switch version { case 0: diff --git a/resource_alks_iamrole_test.go b/resource_alks_iamrole_test.go index 17d017d7..6b34194a 100644 --- a/resource_alks_iamrole_test.go +++ b/resource_alks_iamrole_test.go @@ -63,7 +63,7 @@ func TestAccAlksIamRole_Tags(t *testing.T) { Config: testAccCheckAlksIamRoleCreateWithTags, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( - "alks_iamrole.foo", "name", "Car420"), + "alks_iamrole.foo", "name", "bar430"), resource.TestCheckResourceAttr( "alks_iamrole.foo", "type", "Amazon EC2"), resource.TestCheckResourceAttr( @@ -79,7 +79,7 @@ func TestAccAlksIamRole_Tags(t *testing.T) { Config: testAccCheckAlksIamRoleUpdateWithTags, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( - "alks_iamrole.foo", "name", "Car420"), + "alks_iamrole.foo", "name", "bar430"), resource.TestCheckResourceAttr( "alks_iamrole.foo", "type", "Amazon EC2"), resource.TestCheckResourceAttr( @@ -94,6 +94,52 @@ func TestAccAlksIamRole_Tags(t *testing.T) { }) } +func TestAccAlksIamRole_DefaultTags(t *testing.T) { + var resp alks.IamRoleResponse + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAlksIamRoleDestroy(&resp), + Steps: []resource.TestStep{ + { + // create resource with tags + Config: testAccCheckAlksIamRoleCreateWithTagsWithDefault, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "name", "bar430"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "type", "Amazon EC2"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "include_default_policies", "false"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "tags.testKey1", "testValue1"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "tags.testKey2", "testValue2"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "tags_all.defaultTagKey1", "defaultTagValue1"), + ), + }, + { + // update resource with tags + Config: testAccCheckAlksIamRoleUpdateWithTagsWithDefault, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "name", "bar430"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "type", "Amazon EC2"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "include_default_policies", "false"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "tags.testKey3", "testValue3"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "tags_all.defaultTagKey2", "defaultTagValue2"), + ), + }, + }, + }) +} + func TestAccAlksIamRole_NoMaxDuration(t *testing.T) { var resp alks.IamRoleResponse @@ -176,7 +222,8 @@ func TestAccIAMRole_NameAndNamePrefixConflict(t *testing.T) { func testAccCheckAlksIamRoleDestroy(role *alks.IamRoleResponse) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*alks.Client) + providerStruct := testAccProvider.Meta().(*AlksClient) + client := providerStruct.client for _, rs := range s.RootModule().Resources { if rs.Type != "alks_iamrole" && rs.Type != "alks_iamtrustrole" { @@ -225,27 +272,64 @@ const testAccCheckAlksIamRoleConfigUpdateBasic = ` } ` const testAccCheckAlksIamRoleCreateWithTags = ` -resource "alks_iamrole" "foo" { - name = "bar430" - type = "Amazon EC2" - include_default_policies = false - } - tags = { - testKey1 = "testValue1" - testKey2 = "testValue2" - } + resource "alks_iamrole" "foo" { + name = "bar430" + type = "Amazon EC2" + include_default_policies = false + tags = { + testKey1 = "testValue1" + testKey2 = "testValue2" + } + } +` + +const testAccCheckAlksIamRoleCreateWithTagsWithDefault = ` + provider "alks" { + default_tags { + tags = { + defaultTagKey1 = "defaultTagValue1" + } + } + } + resource "alks_iamrole" "foo" { + name = "bar430" + type = "Amazon EC2" + include_default_policies = false + tags = { + testKey1 = "testValue1" + testKey2 = "testValue2" + } + } +` +const testAccCheckAlksIamRoleUpdateWithTagsWithDefault = ` + provider "alks" { + default_tags { + tags = { + defaultTagKey2 = "defaultTagValue2" + } + } + } + resource "alks_iamrole" "foo" { + name = "bar430" + type = "Amazon EC2" + include_default_policies = false + tags = { + testKey1 = "testValue1" + testKey3 = "testValue3" + } + } ` const testAccCheckAlksIamRoleUpdateWithTags = ` resource "alks_iamrole" "foo" { - name = "bar430" - type = "Amazon EC2" - include_default_policies = false - } - tags = { - testKey3 = "testValue3" - testKey2 = "testValue2" - } + name = "bar430" + type = "Amazon EC2" + include_default_policies = false + tags = { + testKey3 = "testValue3" + testKey2 = "testValue2" + } +} ` const testAccCheckAlksIamRoleConfigUpdateNoMaxDuration = ` diff --git a/resource_alks_ltk.go b/resource_alks_ltk.go index a294df56..e4767530 100644 --- a/resource_alks_ltk.go +++ b/resource_alks_ltk.go @@ -2,10 +2,11 @@ package main import ( "context" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "log" - "github.com/Cox-Automotive/alks-go" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + + // "github.com/Cox-Automotive/alks-go" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -47,7 +48,8 @@ func resourceAlksLtkCreate(ctx context.Context, d *schema.ResourceData, meta int var iamUsername = d.Get("iam_username").(string) - client := meta.(*alks.Client) + providerStruct := meta.(*AlksClient) + client := providerStruct.client if err := validateIAMEnabled(client); err != nil { return diag.FromErr(err) } @@ -70,7 +72,8 @@ func resourceAlksLtkCreate(ctx context.Context, d *schema.ResourceData, meta int func resourceAlksLtkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { log.Printf("[INFO] ALKS LTK User Read") - client := meta.(*alks.Client) + providerStruct := meta.(*AlksClient) + client := providerStruct.client // Check if role exists. if d.Id() == "" || d.Id() == "none" { @@ -95,7 +98,8 @@ func resourceAlksLtkRead(ctx context.Context, d *schema.ResourceData, meta inter func resourceAlksLtkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { log.Printf("[INFO] ALKS LTK User Delete") - client := meta.(*alks.Client) + providerStruct := meta.(*AlksClient) + client := providerStruct.client if err := validateIAMEnabled(client); err != nil { return diag.FromErr(err) } diff --git a/resource_alks_ltk_test.go b/resource_alks_ltk_test.go index e5ea18d6..f0694be9 100644 --- a/resource_alks_ltk_test.go +++ b/resource_alks_ltk_test.go @@ -32,7 +32,8 @@ func TestAlksLTKCreate(t *testing.T) { func testAlksLtkDestroy(ltk *alks.CreateLongTermKeyResponse) resource.TestCheckFunc { return func(s *terraform.State) error { - client := testAccProvider.Meta().(*alks.Client) + providerStruct := testAccProvider.Meta().(*AlksClient) + client := providerStruct.client for _, rs := range s.RootModule().Resources { if rs.Type != "alks_ltk" { From 7eadb0a5415d70486ee784914e7fad75f7300752 Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Tue, 3 May 2022 10:11:54 -0500 Subject: [PATCH 13/19] All tests passing in role and trust role --- README.md | 23 +++ iam_tags.go | 94 +++++++++ provider.go | 6 +- provider_test.go | 93 +++++---- resource_alks_iamrole.go | 83 -------- resource_alks_iamtrustrole.go | 140 +++++++++++++ resource_alks_iamtrustrole_test.go | 302 +++++++++++++++++++++++++++++ tags.go | 11 -- 8 files changed, 607 insertions(+), 145 deletions(-) create mode 100644 iam_tags.go create mode 100644 resource_alks_iamtrustrole.go create mode 100644 resource_alks_iamtrustrole_test.go delete mode 100644 tags.go diff --git a/README.md b/README.md index 38ec7948..c360ffb4 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,31 @@ cd terraform-provider-alks make build test ``` +As stated above, if the tests run, everything is working. What you won't see are any passing tests. To get to an operational testing state: +1. set the ALKS_URL and TF_ACC environment variables +```bash +export TF_ACC=true +export ALKS_URL=https://dev.alks.coxautoinc.com/rest +``` +2. Copy the environment variables from **CoxAT Labs 95 (ALKS Dev)** into your terminal +```bash +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_SESSION_TOKEN= +export AWS_DEFAULT_REGION=us-east-1 +``` +If an error stating `Role already exists with the same name: ` in encountered during testing (errored out tests do not initiate resource taredown), from ALKSWeb, navigate to the AWS console of the Labs account and manually delete the IAM role listed in the error. + If you need any additional dependencies while developing, add the dependency by running `go get ` and then add it to the vendor folder by running `go mod vendor`. +## Updating The Version of alks-go +If using VSCode, hover over the versioned alks-go import in go.mod and click on the link to go.dev (Go package index). The latest version should have the commit hash at the HEAD of master (you may have to wait for the site to update ~ 20 min). Copy the version number and paste it over the previous version in go.mod. On the command line: +```bash +go mod download github.com/Cox-Automotive/alks-go +go mod vendor +go mod tidy +``` +At this point, the dependancy should reflect the state of alks-go in master ## Documentation Documentation and examples can be found on the [Terraform website](https://registry.terraform.io/providers/Cox-Automotive/alks/latest/docs). \ No newline at end of file diff --git a/iam_tags.go b/iam_tags.go new file mode 100644 index 00000000..2f2cb5d5 --- /dev/null +++ b/iam_tags.go @@ -0,0 +1,94 @@ +package main + +import ( + "context" + "fmt" + + "github.com/Cox-Automotive/alks-go" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func TagsSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + } +} + +func TagsSchemaComputed() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + } +} + +//Removes default tags from a map of role specific + default tags +func removeDefaultTags(allTags map[string]interface{}, defalutTags []alks.Tag) []alks.Tag { + for _, t := range defalutTags { + delete(allTags, t.Key) + } + + return tagMapToSlice(allTags) +} + +func tagMapToSlice(tagMap map[string]interface{}) []alks.Tag { + tags := []alks.Tag{} + for k, v := range tagMap { + tag := alks.Tag{Key: k, Value: v.(string)} + tags = append(tags, tag) + } + return tags +} + +func tagSliceToMap(tagSlice []alks.Tag) map[string]interface{} { + tagMap := make(map[string]interface{}) + for _, t := range tagSlice { + tagMap[t.Key] = t.Value + } + return tagMap +} + +//Combines tags defined on an individual resource with the default tags listed on the provider block +//Resource specific tags will overwrite default tags +func combineTagsWithDefault(tags []alks.Tag, defaultTags []alks.Tag) []alks.Tag { + defaultTagsMap := tagSliceToMap(defaultTags) + + for _, t := range tags { + defaultTagsMap[t.Key] = t.Value + } + allTags := tagMapToSlice(defaultTagsMap) + + return allTags +} + +func SetTagsDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { + defaultTags := meta.(*AlksClient).defaultTags + + resourceTags := tagMapToSlice(diff.Get("tags").(map[string]interface{})) + + allTags := combineTagsWithDefault(resourceTags, defaultTags) + + // To ensure "tags_all" is correctly computed, we explicitly set the attribute diff + // when the merger of resource-level tags onto provider-level tags results in n > 0 tags, + // otherwise we mark the attribute as "Computed" only when their is a known diff (excluding an empty map) + // or a change for "tags_all". + + if len(allTags) > 0 { + if err := diff.SetNew("tags_all", tagSliceToMap(allTags)); err != nil { + return fmt.Errorf("error setting new tags_all diff: %w", err) + } + } else if len(diff.Get("tags_all").(map[string]interface{})) > 0 { + if err := diff.SetNewComputed("tags_all"); err != nil { + return fmt.Errorf("error setting tags_all to computed: %w", err) + } + } else if diff.HasChange("tags_all") { + if err := diff.SetNewComputed("tags_all"); err != nil { + return fmt.Errorf("error setting tags_all to computed: %w", err) + } + } + + return nil +} \ No newline at end of file diff --git a/provider.go b/provider.go index e0dd7f59..6443d96f 100644 --- a/provider.go +++ b/provider.go @@ -79,9 +79,9 @@ func Provider() *schema.Provider { }, ResourcesMap: map[string]*schema.Resource{ - "alks_iamrole": resourceAlksIamRole(), - // "alks_iamtrustrole": resourceAlksIamTrustRole(), - "alks_ltk": resourceAlksLtk(), + "alks_iamrole": resourceAlksIamRole(), + "alks_iamtrustrole": resourceAlksIamTrustRole(), + "alks_ltk": resourceAlksLtk(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/provider_test.go b/provider_test.go index ac3db2aa..3e368ecc 100644 --- a/provider_test.go +++ b/provider_test.go @@ -4,10 +4,7 @@ import ( "os" "testing" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) var testAccProviders map[string]*schema.Provider @@ -45,51 +42,51 @@ func testAccPreCheck(t *testing.T) { } } -func testAccProvider_DefaultTags(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: nil, - Steps: []resource.TestStep{ - { - Config: testProviderWithDefaultTags, - Check: resource.ComposeTestCheckFunc( - testAccCheckIgnoreTagsKeys(testAccProviders["alks"], []string{"defaultTagKey1", "defaultTagKey2"}), - ), - }, - }, - }) -} +// func testAccProvider_DefaultTags(t *testing.T) { +// resource.Test(t, resource.TestCase{ +// PreCheck: func() { testAccPreCheck(t) }, +// Providers: testAccProviders, +// CheckDestroy: nil, +// Steps: []resource.TestStep{ +// { +// Config: testProviderWithDefaultTags, +// Check: resource.ComposeTestCheckFunc( +// testAccCheckDefaultTagsKeys(testAccProviders["alks"], []string{"defaultTagKey1", "defaultTagKey2"}), +// ), +// }, +// }, +// }) +// } -func testAccCheckIgnoreTagsKeys(providers *schema.Provider, expectedKeys []string) resource.TestCheckFunc { - return func(s *terraform.State) error { - providerStruct := meta.(*AlksClient) - client := providerStruct.client +// func testAccCheckDefaultTagsKeys(providers *schema.Provider, expectedKeys []string) resource.TestCheckFunc { +// return func(s *terraform.State) error { +// providerStruct := meta.(*AlksClient) +// client := providerStruct.client - defaultTags := providerStruct.defaultTags - found := false - for _, k := range expectedKeys { - for _, i := range defaultTags { - if k == i.Key { - found = true - break - } - } - if !found { - return diag.FromErr("Expected Key Not found") - } - } - return nil - } -} +// defaultTags := providerStruct.defaultTags +// found := false +// for _, k := range expectedKeys { +// for _, i := range defaultTags { +// if k == i.Key { +// found = true +// break +// } +// } +// if !found { +// return diag.FromErr("Expected Key Not found") +// } +// } +// return nil +// } +// } -const testProviderWithDefaultTags = ` - provider "alks" { - default_tags { - tags = { - defaultTagKey1 = "defaultTagValue1" - defaultTagKey2 = "defaultTagValue2" - } - } - } -` +// const testProviderWithDefaultTags = ` +// provider "alks" { +// default_tags { +// tags = { +// defaultTagKey1 = "defaultTagValue1" +// defaultTagKey2 = "defaultTagValue2" +// } +// } +// } +// ` diff --git a/resource_alks_iamrole.go b/resource_alks_iamrole.go index e292d971..3f0dc341 100644 --- a/resource_alks_iamrole.go +++ b/resource_alks_iamrole.go @@ -82,23 +82,6 @@ func resourceAlksIamRole() *schema.Resource { } } -func TagsSchema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - } -} - -func TagsSchemaComputed() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeMap, - Optional: true, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - } -} - func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { log.Printf("[INFO] ALKS IAM Role Create") fmt.Printf("In Role Create\n") @@ -295,73 +278,7 @@ func updateIamTags(d *schema.ResourceData, meta interface{}) error { return nil } -//Combines tags defined on an individual resource with the default tags listed on the provider block -//Resource specific tags will overwrite default tags -func combineTagsWithDefault(tags []alks.Tag, defaultTags []alks.Tag) []alks.Tag { - defaultTagsMap := tagSliceToMap(defaultTags) - - for _, t := range tags { - defaultTagsMap[t.Key] = t.Value - } - allTags := tagMapToSlice(defaultTagsMap) - - return allTags -} - -//Removes default tags from a map of role specific + default tags -func removeDefaultTags(allTags map[string]interface{}, defalutTags []alks.Tag) []alks.Tag { - for _, t := range defalutTags { - delete(allTags, t.Key) - } - - return tagMapToSlice(allTags) -} -func tagMapToSlice(tagMap map[string]interface{}) []alks.Tag { - tags := []alks.Tag{} - for k, v := range tagMap { - tag := alks.Tag{Key: k, Value: v.(string)} - tags = append(tags, tag) - } - return tags -} - -func tagSliceToMap(tagSlice []alks.Tag) map[string]interface{} { - tagMap := make(map[string]interface{}) - for _, t := range tagSlice { - tagMap[t.Key] = t.Value - } - return tagMap -} - -func SetTagsDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { - defaultTags := meta.(*AlksClient).defaultTags - - resourceTags := tagMapToSlice(diff.Get("tags").(map[string]interface{})) - - allTags := combineTagsWithDefault(resourceTags, defaultTags) - - // To ensure "tags_all" is correctly computed, we explicitly set the attribute diff - // when the merger of resource-level tags onto provider-level tags results in n > 0 tags, - // otherwise we mark the attribute as "Computed" only when their is a known diff (excluding an empty map) - // or a change for "tags_all". - - if len(allTags) > 0 { - if err := diff.SetNew("tags_all", tagSliceToMap(allTags)); err != nil { - return fmt.Errorf("error setting new tags_all diff: %w", err) - } - } else if len(diff.Get("tags_all").(map[string]interface{})) > 0 { - if err := diff.SetNewComputed("tags_all"); err != nil { - return fmt.Errorf("error setting tags_all to computed: %w", err) - } - } else if diff.HasChange("tags_all") { - if err := diff.SetNewComputed("tags_all"); err != nil { - return fmt.Errorf("error setting tags_all to computed: %w", err) - } - } - - return nil -} func migrateState(version int, state *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { switch version { diff --git a/resource_alks_iamtrustrole.go b/resource_alks_iamtrustrole.go new file mode 100644 index 00000000..358cb80e --- /dev/null +++ b/resource_alks_iamtrustrole.go @@ -0,0 +1,140 @@ +package main + +import ( + "context" + "log" + "strings" + "time" + + "github.com/Cox-Automotive/alks-go" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceAlksIamTrustRole() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceAlksIamTrustRoleCreate, + ReadContext: resourceAlksIamRoleRead, + UpdateContext: resourceAlksIamRoleUpdate, + DeleteContext: resourceAlksIamRoleDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + SchemaVersion: 1, + MigrateState: migrateState, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name_prefix"}, + }, + "name_prefix": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name"}, + }, + "type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "trust_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "role_added_to_ip": { + Type: schema.TypeBool, + Computed: true, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "ip_arn": { + Type: schema.TypeString, + Computed: true, + }, + "enable_alks_access": { + Type: schema.TypeBool, + Default: false, + Optional: true, + }, + "tags": TagsSchema(), + "tags_all": TagsSchemaComputed(), + }, + CustomizeDiff: SetTagsDiff, + } +} + +func resourceAlksIamTrustRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + log.Printf("[INFO] ALKS IAM Trust Role Create") + + var roleName = NameWithPrefix(d.Get("name").(string), d.Get("name_prefix").(string)) + var roleType = d.Get("type").(string) + var trustArn = d.Get("trust_arn").(string) + var enableAlksAccess = d.Get("enable_alks_access").(bool) + var tags = tagMapToSlice(d.Get("tags").(map[string]interface{})) + + providerStruct := meta.(*AlksClient) + client := providerStruct.client + + if err := validateIAMEnabled(client); err != nil { + return diag.FromErr(err) + } + + defaultTags := []alks.Tag{} + if (*providerStruct).defaultTags != nil { + defaultTags = (*providerStruct).defaultTags + } + + allTags := combineTagsWithDefault(tags, defaultTags) + + var resp *alks.IamRoleResponse + err := resource.RetryContext(ctx, 2*time.Minute, func() *resource.RetryError { + var err error + options := &alks.CreateIamRoleOptions{ + RoleName: &roleName, + RoleType: &roleType, + TrustArn: &trustArn, + AlksAccess: &enableAlksAccess, + Tags: &allTags, + } + + resp, err = client.CreateIamTrustRole(options) + if err != nil { + if strings.Contains(err.Error(), "Role already exists") || strings.Contains(err.Error(), "Instance profile exists") { + return resource.NonRetryableError(err) + } + + // Amazon IAM utilizes an eventual consistency model: + // https://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_general.html#troubleshoot_general_eventual-consistency + // + // The newly created IAM role may not exist immediately and could result in dependent + // resources failing non-deterministically. Loop for 15 second increments up to 2 + // minutes checking to ensure the resouce was successfully created and is visible. + + time.Sleep(15 * time.Second) + return resource.RetryableError(err) + } + return nil + }) + + if err != nil { + return diag.FromErr(err) + } + + response := *resp + + d.SetId(response.RoleName) + _ = d.Set("role_added_to_ip", resp.RoleAddedToIP) + + log.Printf("[INFO] alks_iamtrustrole.id: %v", d.Id()) + + return resourceAlksIamRoleRead(ctx, d, meta) +} diff --git a/resource_alks_iamtrustrole_test.go b/resource_alks_iamtrustrole_test.go new file mode 100644 index 00000000..486e22a1 --- /dev/null +++ b/resource_alks_iamtrustrole_test.go @@ -0,0 +1,302 @@ +package main + +import ( + "regexp" + "testing" + + "github.com/Cox-Automotive/alks-go" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccAlksIamTrustRole_Basic(t *testing.T) { + var resp alks.IamRoleResponse + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAlksIamRoleDestroy(&resp), + Steps: []resource.TestStep{ + { + Config: testAccCheckAlksIamTrustRoleConfigBasic, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "name", "bar"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "type", "Inner Account"), + ), + }, + { + // update the resource + Config: testAccCheckAlksIamTrustRoleConfigUpdateBasic, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "name", "bar"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "type", "Inner Account"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "enable_alks_access", "true"), + ), + }, + }, + }) +} + +func TestAccAlksIamTrustRole_Tags(t *testing.T) { + var resp alks.IamRoleResponse + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAlksIamRoleDestroy(&resp), + Steps: []resource.TestStep{ + { + Config: testAccCheckAlksIamTrustRoleCreateWithTags, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "name", "bar"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "type", "Inner Account"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "tags.testKey1", "testValue1"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "tags.testKey2", "testValue2"), + ), + }, + { + // update the resource + Config: testAccCheckAlksIamTrustRoleUpdateWithTags, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "name", "bar"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "type", "Inner Account"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "tags.testKey3", "testValue3"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "tags.testKey2", "testValue2"), + ), + }, + }, + }) +} + +func TestAccAlksIamTrustRole_DefaultTags(t *testing.T) { + var resp alks.IamRoleResponse + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAlksIamRoleDestroy(&resp), + Steps: []resource.TestStep{ + { + Config: testAccCheckAlksIamRoleTrustCreateWithTagsWithDefault, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "name", "bar"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "type", "Inner Account"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "tags.testKey1", "testValue1"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "tags.testKey2", "testValue2"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "tags_all.defaultTagKey1", "defaultTagValue1"), + ), + }, + { + // update the resource + Config: testAccCheckAlksIamTrustRoleUpdateWithTagsWithDefault, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "name", "bar"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "type", "Inner Account"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "tags.testKey3", "testValue3"), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.bar", "tags_all.defaultTagKey2", "defaultTagValue2"), + ), + }, + }, + }) +} + +func TestAccAlksIamTrustRole_NamePrefix(t *testing.T) { + var resp alks.IamRoleResponse + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAlksIamRoleDestroy(&resp), + Steps: []resource.TestStep{ + { + Config: testAccCheckAlksIamTrustRoleConfigNamePrefix, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "alks_iamtrustrole.nameprefix_trustrole", "name_prefix", "alks_test_acc_"), + resource.TestMatchResourceAttr( + "alks_iamtrustrole.nameprefix_trustrole", "name", regexp.MustCompile("alks_test_acc_[0-9]{26}")), + resource.TestCheckResourceAttr( + "alks_iamtrustrole.nameprefix_trustrole", "type", "Inner Account"), + ), + }, + }, + }) +} + +func TestAccAlksIamTrustRole_NameAndNamePrefixConflict(t *testing.T) { + var resp alks.IamRoleResponse + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAlksIamRoleDestroy(&resp), + Steps: []resource.TestStep{ + { + Config: testAccCheckAlksIamTrustRoleConfigNameAndNamePrefixConflict, + ExpectError: regexp.MustCompile(".*\"name\": conflicts with name_prefix.*"), + }, + }, + }) +} + +const testAccCheckAlksIamTrustRoleConfigBasic = ` + resource "alks_iamrole" "foo" { + name = "foo" + type = "Amazon EC2" + include_default_policies = false + } + + resource "alks_iamtrustrole" "bar" { + name = "bar" + type = "Inner Account" + trust_arn = "${alks_iamrole.foo.arn}" + } +` + +const testAccCheckAlksIamTrustRoleConfigUpdateBasic = ` + resource "alks_iamrole" "foo" { + name = "foo" + type = "Amazon EC2" + include_default_policies = false + } + + resource "alks_iamtrustrole" "bar" { + name = "bar" + type = "Inner Account" + trust_arn = "${alks_iamrole.foo.arn}" + enable_alks_access = true + } +` +const testAccCheckAlksIamTrustRoleCreateWithTags = ` + resource "alks_iamrole" "foo" { + name = "foo" + type = "Amazon EC2" + include_default_policies = false + } + resource "alks_iamtrustrole" "bar" { + name = "bar" + type = "Inner Account" + trust_arn = "${alks_iamrole.foo.arn}" + tags = { + testKey1 = "testValue1" + testKey2 = "testValue2" + } + } +` + +const testAccCheckAlksIamRoleTrustCreateWithTagsWithDefault = ` + provider "alks" { + default_tags { + tags = { + defaultTagKey1 = "defaultTagValue1" + } + } + } + resource "alks_iamrole" "foo" { + name = "foo" + type = "Amazon EC2" + include_default_policies = false + + } + resource "alks_iamtrustrole" "bar" { + name = "bar" + type = "Inner Account" + trust_arn = "${alks_iamrole.foo.arn}" + tags = { + testKey1 = "testValue1" + testKey2 = "testValue2" + } + } + +` +const testAccCheckAlksIamTrustRoleUpdateWithTagsWithDefault = ` + provider "alks" { + default_tags { + tags = { + defaultTagKey2 = "defaultTagValue2" + } + } + } + resource "alks_iamrole" "foo" { + name = "foo" + type = "Amazon EC2" + include_default_policies = false + + } + resource "alks_iamtrustrole" "bar" { + name = "bar" + type = "Inner Account" + trust_arn = "${alks_iamrole.foo.arn}" + tags = { + testKey1 = "testValue1" + testKey3 = "testValue3" + } + } +` + +const testAccCheckAlksIamTrustRoleUpdateWithTags = ` +resource "alks_iamrole" "foo" { + name = "foo" + type = "Amazon EC2" + include_default_policies = false +} +resource "alks_iamtrustrole" "bar" { + name = "bar" + type = "Inner Account" + trust_arn = "${alks_iamrole.foo.arn}" + tags = { + testKey3 = "testValue3" + testKey2 = "testValue2" + } +} +` + +const testAccCheckAlksIamTrustRoleConfigNamePrefix = ` + resource "alks_iamrole" "nameprefix_role" { + name_prefix = "alks_test_acc_" + type = "Amazon EC2" + include_default_policies = false + } + + resource "alks_iamtrustrole" "nameprefix_trustrole" { + name_prefix = "alks_test_acc_" + type = "Inner Account" + trust_arn = "${alks_iamrole.nameprefix_role.arn}" + } +` + +const testAccCheckAlksIamTrustRoleConfigNameAndNamePrefixConflict = ` + resource "alks_iamrole" "nameprefixconflict_role" { + name_prefix = "alks_test_acc_" + type = "Amazon EC2" + include_default_policies = false + } + + resource "alks_iamtrustrole" "nameprefixconflict_trustrole" { + name = "alks_test_acc" + name_prefix = "alks_test_acc_" + type = "Inner Account" + trust_arn = "${alks_iamrole.nameprefixconflict_role.arn}" + } +` diff --git a/tags.go b/tags.go deleted file mode 100644 index c0549576..00000000 --- a/tags.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import ( - "github.com/Cox-Automotive/alks-go" -) - -// DefaultConfig contains tags to default across all resources. - -type DefaultTagsConfig struct { - tags *[]alks.Tag -} From 16aaa9a4d392c08fd28a661ae6b8e01e8429a263 Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Tue, 3 May 2022 14:40:04 -0500 Subject: [PATCH 14/19] Commiting for pr. Functionality for default tags, tags on alks_iamrole and alks_iamtrustrole --- docs/guides/local_installation.md | 2 +- iam_tags.go | 12 +++-- provider.go | 4 +- provider_test.go | 48 +------------------ resource_alks_iamrole.go | 10 +--- resource_alks_iamrole_test.go | 78 +++++++++++++++++++++++++++++++ 6 files changed, 92 insertions(+), 62 deletions(-) diff --git a/docs/guides/local_installation.md b/docs/guides/local_installation.md index 7c9a5e75..65e38b2a 100644 --- a/docs/guides/local_installation.md +++ b/docs/guides/local_installation.md @@ -48,7 +48,7 @@ mkdir -p ~/.terraform.d/plugins && ```sh mkdir -p ~/.terraform.d/plugins/Cox-Automotive/engineering-enablement/alks/2.0.5/darwin_amd64 && - curl -Ls https://api.github.com/repos/Cox-Automotive/terraform-provider-alks/releases | jq -r --arg release "v2.0.5" --arg arch "$(uname -s | tr A-Z a-z)" '.[] | select(.tag_name | contains($release)) | .assets[]| select(.browser_download_url | contains($arch)) | select(.browser_download_url | contains("amd64")) | .browser_download_url' | + curl -Ls https://api.github.com/repos/Cox-Automotive/terraform-provider-alks/releases | jq -r --arg release "v2.1.1" --arg arch "$(uname -s | tr A-Z a-z)" '.[] | select(.tag_name | contains($release)) | .assets[]| select(.browser_download_url | contains($arch)) | select(.browser_download_url | contains("amd64")) | .browser_download_url' | xargs -n 1 curl -Lo ~/.terraform.d/plugins/Cox-Automotive/engineering-enablement/alks/2.0.5/darwin_amd64/terraform-provider-alks.zip && pushd ~/.terraform.d/plugins/Cox-Automotive/engineering-enablement/alks/2.0.5/darwin_amd64 && unzip ~/.terraform.d/plugins/Cox-Automotive/engineering-enablement/alks/2.0.5/darwin_amd64/terraform-provider-alks.zip -d terraform-provider-alks-tmp && diff --git a/iam_tags.go b/iam_tags.go index 2f2cb5d5..146bad7d 100644 --- a/iam_tags.go +++ b/iam_tags.go @@ -28,9 +28,15 @@ func TagsSchemaComputed() *schema.Schema { //Removes default tags from a map of role specific + default tags func removeDefaultTags(allTags map[string]interface{}, defalutTags []alks.Tag) []alks.Tag { for _, t := range defalutTags { - delete(allTags, t.Key) - } + //If the key and value of a tag returned from the role exists in the defaultTags list + //We will assume it was set as a default tag and remove it from role specific tag list + if val, ok := allTags[t.Key]; ok { + if val == t.Value { + delete(allTags, t.Key) + } + } + } return tagMapToSlice(allTags) } @@ -91,4 +97,4 @@ func SetTagsDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) } return nil -} \ No newline at end of file +} diff --git a/provider.go b/provider.go index 6443d96f..f635d0e3 100644 --- a/provider.go +++ b/provider.go @@ -13,7 +13,7 @@ import ( // Provider returns a terraform.ResourceProvider. func Provider() *schema.Provider { - return &schema.Provider{ + provider := &schema.Provider{ Schema: map[string]*schema.Schema{ "url": { Type: schema.TypeString, @@ -90,6 +90,7 @@ func Provider() *schema.Provider { ConfigureContextFunc: providerConfigure, } + return provider } func assumeRoleSchema() *schema.Schema { @@ -144,7 +145,6 @@ func defaultTagsSchema() *schema.Schema { } func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { - var diags diag.Diagnostics config := Config{ diff --git a/provider_test.go b/provider_test.go index 3e368ecc..4b53fea0 100644 --- a/provider_test.go +++ b/provider_test.go @@ -27,6 +27,7 @@ func TestProvider_impl(t *testing.T) { var _ = Provider() } + func testAccPreCheck(t *testing.T) { if v := os.Getenv("ALKS_URL"); v == "" { t.Fatal("ALKS_URL must be set for acceptance tests") @@ -42,51 +43,4 @@ func testAccPreCheck(t *testing.T) { } } -// func testAccProvider_DefaultTags(t *testing.T) { -// resource.Test(t, resource.TestCase{ -// PreCheck: func() { testAccPreCheck(t) }, -// Providers: testAccProviders, -// CheckDestroy: nil, -// Steps: []resource.TestStep{ -// { -// Config: testProviderWithDefaultTags, -// Check: resource.ComposeTestCheckFunc( -// testAccCheckDefaultTagsKeys(testAccProviders["alks"], []string{"defaultTagKey1", "defaultTagKey2"}), -// ), -// }, -// }, -// }) -// } - -// func testAccCheckDefaultTagsKeys(providers *schema.Provider, expectedKeys []string) resource.TestCheckFunc { -// return func(s *terraform.State) error { -// providerStruct := meta.(*AlksClient) -// client := providerStruct.client - -// defaultTags := providerStruct.defaultTags -// found := false -// for _, k := range expectedKeys { -// for _, i := range defaultTags { -// if k == i.Key { -// found = true -// break -// } -// } -// if !found { -// return diag.FromErr("Expected Key Not found") -// } -// } -// return nil -// } -// } -// const testProviderWithDefaultTags = ` -// provider "alks" { -// default_tags { -// tags = { -// defaultTagKey1 = "defaultTagValue1" -// defaultTagKey2 = "defaultTagValue2" -// } -// } -// } -// ` diff --git a/resource_alks_iamrole.go b/resource_alks_iamrole.go index 3f0dc341..2df448db 100644 --- a/resource_alks_iamrole.go +++ b/resource_alks_iamrole.go @@ -84,7 +84,6 @@ func resourceAlksIamRole() *schema.Resource { func resourceAlksIamRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { log.Printf("[INFO] ALKS IAM Role Create") - fmt.Printf("In Role Create\n") var roleName = NameWithPrefix(d.Get("name").(string), d.Get("name_prefix").(string)) var roleType = d.Get("type").(string) var incDefPol = d.Get("include_default_policies").(bool) @@ -158,7 +157,6 @@ func resourceAlksIamRoleDelete(ctx context.Context, d *schema.ResourceData, meta func resourceAlksIamRoleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { log.Printf("[INFO] ALKS IAM Role Read") - fmt.Printf("In Role Read\n") providerStruct := meta.(*AlksClient) client := providerStruct.client @@ -185,9 +183,7 @@ func resourceAlksIamRoleRead(ctx context.Context, d *schema.ResourceData, meta i _ = d.Set("enable_alks_access", foundRole.AlksAccess) allTags := foundRole.Tags - for _, t := range allTags { - fmt.Printf("Read Tag: %s\n", t.Key) - } + roleSpecificTags := removeDefaultTags(tagSliceToMap(allTags), defaultTags) if err := d.Set("tags", tagSliceToMap(roleSpecificTags)); err != nil { @@ -208,7 +204,6 @@ func resourceAlksIamRoleRead(ctx context.Context, d *schema.ResourceData, meta i func resourceAlksIamRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { log.Printf("[INFO] ALKS IAM Role Update") - fmt.Printf("In Role Update\n") // enable partial state mode d.Partial(true) @@ -257,7 +252,6 @@ func updateAlksAccess(d *schema.ResourceData, meta interface{}) error { } func updateIamTags(d *schema.ResourceData, meta interface{}) error { - fmt.Printf("In updateIamTags\n") providerStruct := meta.(*AlksClient) client := providerStruct.client @@ -278,8 +272,6 @@ func updateIamTags(d *schema.ResourceData, meta interface{}) error { return nil } - - func migrateState(version int, state *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { switch version { case 0: diff --git a/resource_alks_iamrole_test.go b/resource_alks_iamrole_test.go index 6b34194a..1e53f322 100644 --- a/resource_alks_iamrole_test.go +++ b/resource_alks_iamrole_test.go @@ -140,6 +140,50 @@ func TestAccAlksIamRole_DefaultTags(t *testing.T) { }) } +func TestAccAlksIamRole_DefaultTagsEmpty(t *testing.T) { + var resp alks.IamRoleResponse + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAlksIamRoleDestroy(&resp), + Steps: []resource.TestStep{ + { + // create resource with tags + Config: testAccCheckAlksIamRoleCreateWithTagsWithEmptyDefault, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "name", "bar430"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "type", "Amazon EC2"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "include_default_policies", "false"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "tags.testKey1", "testValue1"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "tags.testKey2", "testValue2"), + ), + }, + { + // update resource with tags + Config: testAccCheckAlksIamRoleCreateWithTagsWithDefaultTagsEmpty, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "name", "bar430"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "type", "Amazon EC2"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "include_default_policies", "false"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "tags.testKey1", "testValue1"), + resource.TestCheckResourceAttr( + "alks_iamrole.foo", "tags.testKey2", "testValue2"), + ), + }, + }, + }) +} + func TestAccAlksIamRole_NoMaxDuration(t *testing.T) { var resp alks.IamRoleResponse @@ -301,6 +345,40 @@ const testAccCheckAlksIamRoleCreateWithTagsWithDefault = ` } } ` + +const testAccCheckAlksIamRoleCreateWithTagsWithEmptyDefault = ` + provider "alks" { + default_tags { + } + } + resource "alks_iamrole" "foo" { + name = "bar430" + type = "Amazon EC2" + include_default_policies = false + tags = { + testKey1 = "testValue1" + testKey2 = "testValue2" + } + } +` +const testAccCheckAlksIamRoleCreateWithTagsWithDefaultTagsEmpty = ` + provider "alks" { + default_tags { + tags = { + + } + } + } + resource "alks_iamrole" "foo" { + name = "bar430" + type = "Amazon EC2" + include_default_policies = false + tags = { + testKey1 = "testValue1" + testKey2 = "testValue2" + } + } +` const testAccCheckAlksIamRoleUpdateWithTagsWithDefault = ` provider "alks" { default_tags { From daeee4f5cd28162825b3b6965eeb15b41ffa249b Mon Sep 17 00:00:00 2001 From: Zack Elliott <40495980+elliottzack429@users.noreply.github.com> Date: Tue, 3 May 2022 16:11:42 -0500 Subject: [PATCH 15/19] Update README.md Co-authored-by: Spencer Finkel <56093725+LumaC0@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c360ffb4..b6659a06 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ export AWS_SECRET_ACCESS_KEY= export AWS_SESSION_TOKEN= export AWS_DEFAULT_REGION=us-east-1 ``` -If an error stating `Role already exists with the same name: ` in encountered during testing (errored out tests do not initiate resource taredown), from ALKSWeb, navigate to the AWS console of the Labs account and manually delete the IAM role listed in the error. +If an error stating `Role already exists with the same name: ` is encountered during testing (errored out tests do not initiate resource tare down), navigate to the AWS console of the Labs account from ALKSWeb and manually delete the IAM role listed in the error. If you need any additional dependencies while developing, add the dependency by running `go get ` and then add it to the vendor folder by running `go mod vendor`. From bb779501c9264b10b6000591f65e42ad85b5352b Mon Sep 17 00:00:00 2001 From: Zack Elliott <40495980+elliottzack429@users.noreply.github.com> Date: Tue, 3 May 2022 16:11:49 -0500 Subject: [PATCH 16/19] Update README.md Co-authored-by: Spencer Finkel <56093725+LumaC0@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b6659a06..dfdfb091 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ go mod download github.com/Cox-Automotive/alks-go go mod vendor go mod tidy ``` -At this point, the dependancy should reflect the state of alks-go in master +At this point, the dependency should reflect the state of alks-go's master branch ## Documentation Documentation and examples can be found on the [Terraform website](https://registry.terraform.io/providers/Cox-Automotive/alks/latest/docs). \ No newline at end of file From 0b5512966c599bb7bf55a911885dd2e7eec6cece Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Tue, 3 May 2022 16:13:59 -0500 Subject: [PATCH 17/19] Remove commented out lines --- data_source_alks_keys.go | 1 - provider.go | 2 -- 2 files changed, 3 deletions(-) diff --git a/data_source_alks_keys.go b/data_source_alks_keys.go index 0475b061..8532a217 100644 --- a/data_source_alks_keys.go +++ b/data_source_alks_keys.go @@ -1,7 +1,6 @@ package main import ( - // "github.com/Cox-Automotive/alks-go" "log" "strings" diff --git a/provider.go b/provider.go index f635d0e3..55cc931c 100644 --- a/provider.go +++ b/provider.go @@ -155,7 +155,6 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{} Profile: d.Get("profile").(string), Account: d.Get("account").(string), Role: d.Get("role").(string), - // DefaultTags: expandProviderDefaultTags(d.Get("default_tags").([]interface{})), } assumeRoleList := d.Get("assume_role").(*schema.Set).List() @@ -188,7 +187,6 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{} log.Println("[INFO] Initializing ALKS client") return alksClient, diags - // return c, diags } func expandProviderDefaultTags(l []interface{}) []alks.Tag { From 4b46664146a61612b8cfbef5d4b65edcdb6bb02c Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Tue, 3 May 2022 17:29:41 -0500 Subject: [PATCH 18/19] Added Unit Tests For utility functions in iam_tags.go : iam_tags_test.go --- iam_tags_test.go | 106 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 iam_tags_test.go diff --git a/iam_tags_test.go b/iam_tags_test.go new file mode 100644 index 00000000..5f0ea2c5 --- /dev/null +++ b/iam_tags_test.go @@ -0,0 +1,106 @@ +package main + +import ( + "reflect" + "testing" + + "github.com/Cox-Automotive/alks-go" +) + +func TestRemoveDefaultTags(t *testing.T) { + cases := []struct { + allTagsMap map[string]interface{} + defaultTagsSlice []alks.Tag + expected []alks.Tag + }{ + { + allTagsMap: map[string]interface{}{ + "resourceKey1": "resourceValue1", + "defaultKey1": "defaultValue1", + }, + defaultTagsSlice: []alks.Tag{{Key: "defaultKey1", Value: "defaultValue1"}}, + expected: []alks.Tag{{Key: "resourceKey1", Value: "resourceValue1"}}, + }, + { + allTagsMap: map[string]interface{}{ + "defaultKey2": "defaultValue2", + "defaultKey1": "resourceValue2", + }, + defaultTagsSlice: []alks.Tag{ + {Key: "defaultKey2", Value: "defaultValue2"}, + {Key: "defaultKey1", Value: "defaultValue2"}, + }, + expected: []alks.Tag{ + {Key: "defaultKey1", Value: "resourceValue2"}, //Should not remove this key. We are assuming that if the key matches one in default but not the value, that the default key was overwritten on purpose in the role definition and shouldnt be removed + }, + }, + } + + for _, c := range cases { + resourceTagsSlice := removeDefaultTags(c.allTagsMap, c.defaultTagsSlice) + if !reflect.DeepEqual(resourceTagsSlice, c.expected) { + t.Fatalf("Error matching output and expected: %#v vs %#v", resourceTagsSlice, c.expected) + } + } +} + +func TestTagMapToSlice(t *testing.T) { + cases := []struct { + tagMap map[string]interface{} + expected []alks.Tag + }{ + { + tagMap: map[string]interface{}{ + "key1": "value1", + }, + expected: []alks.Tag{{Key: "key1", Value: "value1"}}, + }, + } + + for _, c := range cases { + tagSlice := tagMapToSlice(c.tagMap) + if !reflect.DeepEqual(tagSlice, c.expected) { + t.Fatalf("Error matching output and expected: %#v vs %#v", tagSlice, c.expected) + } + } +} + +func TestTagSliceToMap(t *testing.T) { + cases := []struct { + tagSlice []alks.Tag + expected map[string]interface{} + }{ + { + tagSlice: []alks.Tag{{Key: "defaultKey1", Value: "defaultValue1"}}, + expected: map[string]interface{}{"defaultKey1": "defaultValue1"}, + }, + } + + for _, c := range cases { + tagMap := tagSliceToMap(c.tagSlice) + if !reflect.DeepEqual(tagMap, c.expected) { + t.Fatalf("Error matching output and expected: %#v vs %#v", tagMap, c.expected) + } + } +} + +func TestCombineTagsWithDefault(t *testing.T) { + cases := []struct { + defaultTagSlice []alks.Tag + resourceTagSlice []alks.Tag + expected []alks.Tag + }{ + { + defaultTagSlice: []alks.Tag{{Key: "defaultKey1", Value: "defaultValue1"}}, + resourceTagSlice: []alks.Tag{{Key: "defaultKey1", Value: "resourceValue1"}}, + expected: []alks.Tag{{Key: "defaultKey1", Value: "resourceValue1"}}, + }, + } + + for _, c := range cases { + tagSlice := combineTagsWithDefault(c.resourceTagSlice, c.defaultTagSlice) + if !reflect.DeepEqual(tagSlice, c.expected) { + t.Fatalf("Error matching output and expected: %#v vs %#v", tagSlice, c.expected) + } + } +} From 5ebaa1a4bb74f9106e9bed881934d75b86ba4960 Mon Sep 17 00:00:00 2001 From: Zack Elliott <40495980+elliottzack429@users.noreply.github.com> Date: Wed, 4 May 2022 12:22:48 -0500 Subject: [PATCH 19/19] Update README.md Co-authored-by: Ben Watson --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dfdfb091..721ffe10 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ export AWS_SECRET_ACCESS_KEY= export AWS_SESSION_TOKEN= export AWS_DEFAULT_REGION=us-east-1 ``` -If an error stating `Role already exists with the same name: ` is encountered during testing (errored out tests do not initiate resource tare down), navigate to the AWS console of the Labs account from ALKSWeb and manually delete the IAM role listed in the error. +If an error stating `Role already exists with the same name: ` is encountered during testing (errored out tests do not initiate resource tear down), navigate to the AWS console of the Labs account from ALKSWeb and manually delete the IAM role listed in the error. If you need any additional dependencies while developing, add the dependency by running `go get ` and then add it to the vendor folder by running `go mod vendor`.