From 45d29c08d121071bc22235d1341ca20c1bc87040 Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Wed, 10 Jun 2020 13:54:25 -0400 Subject: [PATCH 01/19] Added new custom resource to the root provider code. --- provider.go | 1 + resource_alks_ltk.go | 0 2 files changed, 1 insertion(+) create mode 100644 resource_alks_ltk.go diff --git a/provider.go b/provider.go index fcae2e17..3c2a3c3e 100644 --- a/provider.go +++ b/provider.go @@ -65,6 +65,7 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "alks_iamrole": resourceAlksIamRole(), "alks_iamtrustrole": resourceAlksIamTrustRole(), + "alks_ltk": resourceAlksLtk(), }, ConfigureFunc: providerConfigure, diff --git a/resource_alks_ltk.go b/resource_alks_ltk.go new file mode 100644 index 00000000..e69de29b From 7cf9dd5482e51a01b8b6e66a6d39372f003427a6 Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Wed, 10 Jun 2020 13:54:45 -0400 Subject: [PATCH 02/19] Added self-service LTK endpoints, but this is NOT the final version. --- resource_alks_ltk.go | 143 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/resource_alks_ltk.go b/resource_alks_ltk.go index e69de29b..6d716a5b 100644 --- a/resource_alks_ltk.go +++ b/resource_alks_ltk.go @@ -0,0 +1,143 @@ +package main + +import ( + alks "alks-go" + "github.com/hashicorp/terraform/helper/schema" + "log" +) + +// TODO: Find a way to NOT store 'secret_key' to TF state file. +func resourceAlksLtk() *schema.Resource { + return &schema.Resource{ + Create: resourceAlksLtkCreate, + Read: resourceAlksLtkRead, + Delete: resourceAlksLtkDelete, + + SchemaVersion: 1, + MigrateState: migrateState, + + Schema: map[string]*schema.Schema{ + "iam_username": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "account_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "role_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "account": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "role": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "action": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "added_iam_user_to_group": &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + }, + "partial_error": &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + }, + "iam_user_arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "access_key": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "secret_key": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "ltks": &schema.Schema{ + Type: schema.TypeMap, + Computed: true, + }, + }, + } +} + +func resourceAlksLtkCreate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[INFO] ALKS LTK User Create") + + var iamUsername = d.Get("iam_username").(string) + + client := meta.(*alks.Client) + resp, err := client.CreateLongTermKey(iamUsername) + + if err != nil { + return err + } + + d.SetId(resp.Account) + d.Set("role", resp.Role) + d.Set("action", resp.Action) + d.Set("added_iam_user_to_group", resp.AddedIAMUserToGroup) + d.Set("partial_error", resp.PartialError) + d.Set("iam_user_arn", resp.IAMUserArn) + d.Set("access_key", resp.AccessKey) + d.Set("secret_key", resp.SecretKey) + + log.Printf("[INFO] alks_ltk.id: %v", d.Id()) + + return nil +} + +func resourceAlksLtkRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[INFO] ALKS LTK Users Read") + + var accountID = d.Get("account_id").(string) + var roleName = d.Get("role_name").(string) + + client := meta.(*alks.Client) + resp, err := client.GetLongTermKeys(accountID, roleName) + + if err != nil { + return err + } + + d.SetId(resp.RequestID) + d.Set("ltks", resp.LongTermKeys) + + log.Printf("[INFO] alks_ltk.id: %v", d.Id()) + + return nil +} + +func resourceAlksLtkDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[INFO] ALKS LTK User Delete") + + var iamUsername = d.Get("iam_username").(string) + + client := meta.(*alks.Client) + resp, err := client.DeleteLongTermKey(iamUsername) + + if err != nil { + return err + } + + d.SetId(resp.Account) + d.Set("role", resp.Role) + d.Set("action", resp.Action) + d.Set("added_iam_user_to_group", resp.AddedIAMUserToGroup) + d.Set("partial_error", resp.PartialError) + + log.Printf("[INFO] alks_ltk.id: %v", d.Id()) + + return nil +} From aab288b4b517a8d09faa10ca2881961919dd5a67 Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Wed, 10 Jun 2020 16:38:45 -0400 Subject: [PATCH 03/19] We do not need to require the vars for the GET command, this is already set by our response in the create. --- resource_alks_ltk.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/resource_alks_ltk.go b/resource_alks_ltk.go index 6d716a5b..20b2822b 100644 --- a/resource_alks_ltk.go +++ b/resource_alks_ltk.go @@ -1,7 +1,7 @@ package main import ( - alks "alks-go" + alks "github.com/Cox-Automotive/alks-go" "github.com/hashicorp/terraform/helper/schema" "log" ) @@ -24,13 +24,11 @@ func resourceAlksLtk() *schema.Resource { }, "account_id": &schema.Schema{ Type: schema.TypeString, - Required: true, - ForceNew: true, + Computed: true, }, "role_name": &schema.Schema{ Type: schema.TypeString, - Required: true, - ForceNew: true, + Computed: true, }, "account": &schema.Schema{ Type: schema.TypeString, @@ -84,7 +82,9 @@ func resourceAlksLtkCreate(d *schema.ResourceData, meta interface{}) error { return err } - d.SetId(resp.Account) + d.Set("account_id", resp.AccountDetails.Account) + d.Set("role_name", resp.AccountDetails.Role) + d.Set("role", resp.Role) d.Set("action", resp.Action) d.Set("added_iam_user_to_group", resp.AddedIAMUserToGroup) @@ -131,7 +131,6 @@ func resourceAlksLtkDelete(d *schema.ResourceData, meta interface{}) error { return err } - d.SetId(resp.Account) d.Set("role", resp.Role) d.Set("action", resp.Action) d.Set("added_iam_user_to_group", resp.AddedIAMUserToGroup) From db413d9b0be608403b262818207aa672cec9c55d Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Thu, 11 Jun 2020 11:35:36 -0400 Subject: [PATCH 04/19] Now we reference the ID as the IAM Username as this is a good way to identify the LTK user. --- config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/config.go b/config.go index 371bdb56..07d00d86 100644 --- a/config.go +++ b/config.go @@ -163,6 +163,7 @@ providing credentials for the ALKS Provider`) log.Println("[INFO] ALKS Client configured") + log.Printf("[INFO] Client: %#v", client) return client, nil } From daab5b50cc32396094b980a4e04d918a718a9c15 Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Thu, 11 Jun 2020 11:36:39 -0400 Subject: [PATCH 05/19] undid my change, not needed. --- config.go | 1 - 1 file changed, 1 deletion(-) diff --git a/config.go b/config.go index 07d00d86..371bdb56 100644 --- a/config.go +++ b/config.go @@ -163,7 +163,6 @@ providing credentials for the ALKS Provider`) log.Println("[INFO] ALKS Client configured") - log.Printf("[INFO] Client: %#v", client) return client, nil } From e2f93a358372c49020b98231f89b28f102f6514f Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Tue, 14 Jul 2020 16:04:51 -0400 Subject: [PATCH 06/19] Ignored some files not needed. --- .gitignore | 4 +++- resource_alks_ltk_test.go | 0 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 resource_alks_ltk_test.go diff --git a/.gitignore b/.gitignore index b2122a29..fc529511 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ glide.lock *.iml # VSCode -.vscode \ No newline at end of file +.vscode +.DS_Store +.terraform/ \ No newline at end of file diff --git a/resource_alks_ltk_test.go b/resource_alks_ltk_test.go new file mode 100644 index 00000000..e69de29b From ed5faaecfc9ba64c8f7e5a2272f9182bc52b488b Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Tue, 14 Jul 2020 16:05:08 -0400 Subject: [PATCH 07/19] Implemented self service LTK with example in the example folder. --- examples/alks.tf | 5 ++++ resource_alks_ltk.go | 51 ++++++++++++-------------------- resource_alks_ltk_test.go | 62 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 32 deletions(-) diff --git a/examples/alks.tf b/examples/alks.tf index 5347bc74..93e605a9 100644 --- a/examples/alks.tf +++ b/examples/alks.tf @@ -40,4 +40,9 @@ EOF resource "aws_iam_role_policy_attachment" "sr-attach" { role = "${alks_iamrole.test_role.name}" policy_arn = "arn:aws:iam::aws:policy/service-role/AWSElasticBeanstalkService" +} + +# CREATE LTK USER +resource "alks_ltk" "ltk" { + iam_username = "TEST_LTK_USER" } \ No newline at end of file diff --git a/resource_alks_ltk.go b/resource_alks_ltk.go index 20b2822b..259c5899 100644 --- a/resource_alks_ltk.go +++ b/resource_alks_ltk.go @@ -6,7 +6,6 @@ import ( "log" ) -// TODO: Find a way to NOT store 'secret_key' to TF state file. func resourceAlksLtk() *schema.Resource { return &schema.Resource{ Create: resourceAlksLtkCreate, @@ -14,7 +13,6 @@ func resourceAlksLtk() *schema.Resource { Delete: resourceAlksLtkDelete, SchemaVersion: 1, - MigrateState: migrateState, Schema: map[string]*schema.Schema{ "iam_username": &schema.Schema{ @@ -62,10 +60,6 @@ func resourceAlksLtk() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "ltks": &schema.Schema{ - Type: schema.TypeMap, - Computed: true, - }, }, } } @@ -82,16 +76,16 @@ func resourceAlksLtkCreate(d *schema.ResourceData, meta interface{}) error { return err } - d.Set("account_id", resp.AccountDetails.Account) - d.Set("role_name", resp.AccountDetails.Role) - - d.Set("role", resp.Role) - d.Set("action", resp.Action) - d.Set("added_iam_user_to_group", resp.AddedIAMUserToGroup) - d.Set("partial_error", resp.PartialError) - d.Set("iam_user_arn", resp.IAMUserArn) - d.Set("access_key", resp.AccessKey) - d.Set("secret_key", resp.SecretKey) + d.SetId(iamUsername) + _ = d.Set("account_id", resp.AccountDetails.Account) + _ = d.Set("role_name", resp.AccountDetails.Role) + _ = d.Set("role", resp.Role) + _ = d.Set("action", resp.Action) + _ = d.Set("added_iam_user_to_group", resp.AddedIAMUserToGroup) + _ = d.Set("partial_error", resp.PartialError) + _ = d.Set("iam_user_arn", resp.IAMUserArn) + _ = d.Set("access_key", resp.AccessKey) + _ = d.Set("secret_key", resp.SecretKey) log.Printf("[INFO] alks_ltk.id: %v", d.Id()) @@ -101,42 +95,35 @@ func resourceAlksLtkCreate(d *schema.ResourceData, meta interface{}) error { func resourceAlksLtkRead(d *schema.ResourceData, meta interface{}) error { log.Printf("[INFO] ALKS LTK Users Read") - var accountID = d.Get("account_id").(string) - var roleName = d.Get("role_name").(string) - client := meta.(*alks.Client) - resp, err := client.GetLongTermKeys(accountID, roleName) + resp, err := client.GetLongTermKey(d.Id()) if err != nil { return err } - d.SetId(resp.RequestID) - d.Set("ltks", resp.LongTermKeys) - log.Printf("[INFO] alks_ltk.id: %v", d.Id()) - return nil + return populateResourceDataFromLTK(resp, d) } func resourceAlksLtkDelete(d *schema.ResourceData, meta interface{}) error { log.Printf("[INFO] ALKS LTK User Delete") - var iamUsername = d.Get("iam_username").(string) - client := meta.(*alks.Client) - resp, err := client.DeleteLongTermKey(iamUsername) + _, err := client.DeleteLongTermKey(d.Id()) if err != nil { return err } - d.Set("role", resp.Role) - d.Set("action", resp.Action) - d.Set("added_iam_user_to_group", resp.AddedIAMUserToGroup) - d.Set("partial_error", resp.PartialError) + return nil +} - log.Printf("[INFO] alks_ltk.id: %v", d.Id()) +func populateResourceDataFromLTK(longTermKey *alks.GetLongTermKeyResponse, d *schema.ResourceData) error { + d.SetId(longTermKey.UserName) + _ = d.Set("access_key", longTermKey.AccessKeyID) + _ = d.Set("create_date", longTermKey.CreateDate) return nil } diff --git a/resource_alks_ltk_test.go b/resource_alks_ltk_test.go index e69de29b..4db93d0b 100644 --- a/resource_alks_ltk_test.go +++ b/resource_alks_ltk_test.go @@ -0,0 +1,62 @@ +package main + +import ( + alks "github.com/Cox-Automotive/alks-go" + "fmt" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "testing" +) + +func TestAlksLTKCreate(t *testing.T) { + var resp alks.CreateLongTermKeyResponse + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAlksLtkDestroy(&resp), + Steps: []resource.TestStep{ + // Create the resource + resource.TestStep{ + Config: testAlksLTKCreateConfig, + Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttr("alks_ltk.foo", "iam_username", "TEST_LTK_USER")), + }, + // Update the resource + resource.TestStep{ + Config: testAlksLTKUpdateConfig, + Check: resource.ComposeTestCheckFunc(resource.TestCheckResourceAttr("alks_ltk.foo", "iam_username", "TEST_LTK_USER_2")), + }, + }, + }) +} + +func testAlksLtkDestroy(ltk *alks.CreateLongTermKeyResponse) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*alks.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alks_ltk" { + continue + } + + resp, err := client.GetLongTermKey(rs.Primary.ID) + if resp != nil { + return fmt.Errorf("long term key still exists: %#v (%v)", resp, err) + } + } + + return nil + } +} + +const testAlksLTKCreateConfig = ` + resource "alks_ltk" "foo" { + iam_username = "TEST_LTK_USER" + } +` + +const testAlksLTKUpdateConfig = ` + resource "alks_ltk" "foo" { + iam_username = "TEST_LTK_USER_2" + } +` From e3f55a76200e7899cbc80d8f8277fac028de09d5 Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Wed, 15 Jul 2020 08:57:28 -0400 Subject: [PATCH 08/19] Added correct path for alks-go --- resource_alks_ltk_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resource_alks_ltk_test.go b/resource_alks_ltk_test.go index 4db93d0b..1d789a7d 100644 --- a/resource_alks_ltk_test.go +++ b/resource_alks_ltk_test.go @@ -1,8 +1,8 @@ package main import ( - alks "github.com/Cox-Automotive/alks-go" "fmt" + alks "github.com/Cox-Automotive/alks-go" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "testing" From cb3f5d51326e7b9097568ef2f555a6f5a6cef358 Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Wed, 15 Jul 2020 09:01:16 -0400 Subject: [PATCH 09/19] Updated alks-go to the latest. --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ff4ab0db..d423cd99 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Cox-Automotive/terraform-provider-alks go 1.14 require ( - github.com/Cox-Automotive/alks-go v0.0.0-20200605150811-11bd4c1de348 + github.com/Cox-Automotive/alks-go v0.0.0-20200714135032-e03438e39d50 github.com/aws/aws-sdk-go v1.31.15 github.com/hashicorp/go-cleanhttp v0.5.1 github.com/hashicorp/terraform v0.12.26 diff --git a/go.sum b/go.sum index f102f2be..925663b3 100644 --- a/go.sum +++ b/go.sum @@ -29,6 +29,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4= github.com/Cox-Automotive/alks-go v0.0.0-20200605150811-11bd4c1de348 h1:4yYbNR8TKaRSYgRbQsH4cC4qFf4VZmmaVC8mp8VWZVg= github.com/Cox-Automotive/alks-go v0.0.0-20200605150811-11bd4c1de348/go.mod h1:on+ImEZYjpdcu+CD07RLqMTQmiPkulolQJb387YMpPo= +github.com/Cox-Automotive/alks-go v0.0.0-20200714135032-e03438e39d50 h1:vGPXuT++0/9xJJvn0rmTsPBJ6jgSIdoyFhNqZIK51bQ= +github.com/Cox-Automotive/alks-go v0.0.0-20200714135032-e03438e39d50/go.mod h1:on+ImEZYjpdcu+CD07RLqMTQmiPkulolQJb387YMpPo= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/Unknwon/com v0.0.0-20151008135407-28b053d5a292/go.mod h1:KYCjqMOeHpNuTOiFQU6WEcTG7poCJrUs0YgyHNtn1no= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= From d62384348223fe8c5a5a8c1debb202df3755acea Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Wed, 15 Jul 2020 09:17:19 -0400 Subject: [PATCH 10/19] Updated alks-go to the latest. --- .../github.com/Cox-Automotive/alks-go/api.go | 17 ++++ .../Cox-Automotive/alks-go/iam_ltk.go | 56 ++++++++++++ .../Cox-Automotive/alks-go/login_role.go | 89 +++++++++++++++++++ vendor/modules.txt | 2 +- 4 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 vendor/github.com/Cox-Automotive/alks-go/login_role.go diff --git a/vendor/github.com/Cox-Automotive/alks-go/api.go b/vendor/github.com/Cox-Automotive/alks-go/api.go index ea8373b9..fea535c0 100644 --- a/vendor/github.com/Cox-Automotive/alks-go/api.go +++ b/vendor/github.com/Cox-Automotive/alks-go/api.go @@ -64,6 +64,13 @@ func NewSTSClient(url string, accessKey string, secretKey string, token string) userAgent: "alks-go", } + // Fetch the current login role, and try to populate the account details object. If we fail, just ignore + loginRole, err := client.GetMyLoginRole() + if err == nil { + client.AccountDetails.Account = loginRole.LoginRole.Account + client.AccountDetails.Role = loginRole.LoginRole.Role + } + return &client, nil } @@ -91,6 +98,16 @@ func (c *Client) SetUserAgent(userAgent string) { c.userAgent = userAgent } +// IsUsingSTSCredentials returns a boolean indicating if the client was configured using AWS STS Credentials for authentication +func (c *Client) IsUsingSTSCredentials() bool { + switch c.Credentials.(type) { + case *STS: + return true + default: + return false + } +} + // NewRequest will create a new request object for API requests. func (c *Client) NewRequest(json []byte, method string, endpoint string) (*http.Request, error) { u, err := url.Parse(c.BaseURL + endpoint) diff --git a/vendor/github.com/Cox-Automotive/alks-go/iam_ltk.go b/vendor/github.com/Cox-Automotive/alks-go/iam_ltk.go index 39418437..0739b123 100644 --- a/vendor/github.com/Cox-Automotive/alks-go/iam_ltk.go +++ b/vendor/github.com/Cox-Automotive/alks-go/iam_ltk.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "log" + "net/http" "strings" ) @@ -21,6 +22,12 @@ type GetLongTermKeysResponse struct { LongTermKeys []LongTermKey `json:"longTermKeys"` } +// GetLongTermKeyResponse is used to represent a single long term key. +type GetLongTermKeyResponse struct { + BaseResponse + LongTermKey `json:"longTermKey"` +} + // BaseLongTermKeyResponse encapsulates shared response fields type BaseLongTermKeyResponse struct { Action string `json:"action,omitempty"` @@ -100,6 +107,55 @@ func (c *Client) GetLongTermKeys() (*GetLongTermKeysResponse, error) { return cr, nil } +// GetLongTermKey gets a single LTK for an account +// If no error is returned, then you will receive an LTK for the given account. +func (c *Client) GetLongTermKey(iamUsername string) (*GetLongTermKeyResponse, error) { + log.Printf("[INFO] Getting long term key") + + var req *http.Request + var err error + + if c.IsUsingSTSCredentials() { + req, err = c.NewRequest(nil, "GET", "/ltk/search/"+iamUsername) + } else { + accountID, err := c.AccountDetails.GetAccountNumber() + if err != nil { + return nil, fmt.Errorf("error reading Account value: %s", err) + } + + roleName, err := c.AccountDetails.GetRoleName(false) + if err != nil { + return nil, fmt.Errorf("error reading Role value: %s", err) + } + + req, err = c.NewRequest(nil, "GET", "/ltk/"+accountID+"/"+roleName+"/search/"+iamUsername) + } + + if err != nil { + return nil, err + } + + resp, err := c.http.Do(req) + if err != nil { + return nil, err + } + + cr := new(GetLongTermKeyResponse) + err = decodeBody(resp, &cr) + + if err != nil { + if reqID := GetRequestID(resp); reqID != "" { + return nil, fmt.Errorf("error parsing GetLongTermKeyResponse: [%s] %s", reqID, err) + } + } + + if cr.RequestFailed() { + return nil, fmt.Errorf("error getting long term keys: [%s] %s", cr.BaseResponse.RequestID, strings.Join(cr.GetErrors(), ", ")) + } + + return cr, nil +} + // CreateLongTermKey creates an LTK user for an account. // If no error is returned, then you will receive an appropriate success message. func (c *Client) CreateLongTermKey(iamUsername string) (*CreateLongTermKeyResponse, error) { diff --git a/vendor/github.com/Cox-Automotive/alks-go/login_role.go b/vendor/github.com/Cox-Automotive/alks-go/login_role.go new file mode 100644 index 00000000..408c58a3 --- /dev/null +++ b/vendor/github.com/Cox-Automotive/alks-go/login_role.go @@ -0,0 +1,89 @@ +package alks + +import ( + "fmt" + "log" + "strings" +) + +// GetMyLoginRole returns the LoginRole corresponding to the clients current STS credentials +func (c *Client) GetMyLoginRole() (*LoginRoleResponse, error) { + log.Printf("[INFO] Requesting Login Role information from ALKS") + + if !c.IsUsingSTSCredentials() { + return nil, fmt.Errorf("GetMyLoginRole only supports clients using STS credentials, try using GetLoginRole instead") + } + + req, err := c.NewRequest(nil, "GET", "/loginRoles/id/me") + if err != nil { + return nil, err + } + + resp, err := c.http.Do(req) + if err != nil { + return nil, err + } + + lrr := new(LoginRoleResponse) + err = decodeBody(resp, &lrr) + if err != nil { + if reqID := GetRequestID(resp); reqID != "" { + return nil, fmt.Errorf("Error parsing LoginRole response: [%s] %s", reqID, err) + } + + return nil, fmt.Errorf("Error parsing LoginRole response: %s", err) + } + + if lrr.RequestFailed() { + return nil, fmt.Errorf("Error fetching role information: [%s] %s", lrr.BaseResponse.RequestID, strings.Join(lrr.GetErrors(), ", ")) + } + + return lrr, nil +} + +// GetLoginRole returns the login role corresponding to the current account and role stored in AccountDetails +func (c *Client) GetLoginRole() (*LoginRoleResponse, error) { + // If the client is configured with STS call the correct method + if c.IsUsingSTSCredentials() { + log.Println("[INFO] Client configured with STS credentials, dispatching to GetMyLoginRole instead") + return c.GetMyLoginRole() + } + + account, err := c.AccountDetails.GetAccountNumber() + if err != nil { + return nil, err + } + + roleName, err := c.AccountDetails.GetRoleName(false) + if err != nil { + return nil, err + } + + log.Printf("[INFO] Requesting Login Role information for %v/%v from ALKS", account, roleName) + + req, err := c.NewRequest(nil, "GET", fmt.Sprintf("/loginRoles/id/%v/%v", account, roleName)) + if err != nil { + return nil, err + } + + resp, err := c.http.Do(req) + if err != nil { + return nil, err + } + + lrr := new(LoginRoleResponse) + err = decodeBody(resp, &lrr) + if err != nil { + if reqID := GetRequestID(resp); reqID != "" { + return nil, fmt.Errorf("Error parsing LoginRole response: [%s] %s", reqID, err) + } + + return nil, fmt.Errorf("Error parsing LoginRole response: %s", err) + } + + if lrr.RequestFailed() { + return nil, fmt.Errorf("Error fetching role information: [%s] %s", lrr.BaseResponse.RequestID, strings.Join(lrr.GetErrors(), ", ")) + } + + return lrr, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index e70d814c..460a7af1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -6,7 +6,7 @@ cloud.google.com/go/internal/optional cloud.google.com/go/internal/trace cloud.google.com/go/internal/version cloud.google.com/go/storage -# github.com/Cox-Automotive/alks-go v0.0.0-20200605150811-11bd4c1de348 +# github.com/Cox-Automotive/alks-go v0.0.0-20200714135032-e03438e39d50 ## explicit github.com/Cox-Automotive/alks-go # github.com/agext/levenshtein v1.2.2 From 20101a82dc297e0faad25522048f97c0f077092b Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Wed, 15 Jul 2020 13:25:12 -0400 Subject: [PATCH 11/19] Sensitive fields marked as such. --- resource_alks_ltk.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resource_alks_ltk.go b/resource_alks_ltk.go index 259c5899..3b352702 100644 --- a/resource_alks_ltk.go +++ b/resource_alks_ltk.go @@ -53,10 +53,12 @@ func resourceAlksLtk() *schema.Resource { Computed: true, }, "access_key": &schema.Schema{ + Sensitive: true, Type: schema.TypeString, Computed: true, }, "secret_key": &schema.Schema{ + Sensitive: true, Type: schema.TypeString, Computed: true, }, From 63580a6fd64079d3fafb9c70da7fe927b8f7b211 Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Wed, 15 Jul 2020 13:29:21 -0400 Subject: [PATCH 12/19] Rather than returning nil for a LTK user not found, we return the resource does not exist. Also, removed some fields from state which are not really needed. --- resource_alks_ltk.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/resource_alks_ltk.go b/resource_alks_ltk.go index 3b352702..51493328 100644 --- a/resource_alks_ltk.go +++ b/resource_alks_ltk.go @@ -79,12 +79,6 @@ func resourceAlksLtkCreate(d *schema.ResourceData, meta interface{}) error { } d.SetId(iamUsername) - _ = d.Set("account_id", resp.AccountDetails.Account) - _ = d.Set("role_name", resp.AccountDetails.Role) - _ = d.Set("role", resp.Role) - _ = d.Set("action", resp.Action) - _ = d.Set("added_iam_user_to_group", resp.AddedIAMUserToGroup) - _ = d.Set("partial_error", resp.PartialError) _ = d.Set("iam_user_arn", resp.IAMUserArn) _ = d.Set("access_key", resp.AccessKey) _ = d.Set("secret_key", resp.SecretKey) @@ -101,7 +95,8 @@ func resourceAlksLtkRead(d *schema.ResourceData, meta interface{}) error { resp, err := client.GetLongTermKey(d.Id()) if err != nil { - return err + d.SetId("") + return nil } log.Printf("[INFO] alks_ltk.id: %v", d.Id()) From 82c86fff39146a98770c4e92146334033c4bfb6b Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Thu, 16 Jul 2020 09:04:27 -0400 Subject: [PATCH 13/19] removed some fields from the state. --- resource_alks_ltk.go | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/resource_alks_ltk.go b/resource_alks_ltk.go index 51493328..e2b64209 100644 --- a/resource_alks_ltk.go +++ b/resource_alks_ltk.go @@ -20,34 +20,6 @@ func resourceAlksLtk() *schema.Resource { Required: true, ForceNew: true, }, - "account_id": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - "role_name": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - "account": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - "role": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - "action": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - "added_iam_user_to_group": &schema.Schema{ - Type: schema.TypeBool, - Computed: true, - }, - "partial_error": &schema.Schema{ - Type: schema.TypeBool, - Computed: true, - }, "iam_user_arn": &schema.Schema{ Type: schema.TypeString, Computed: true, From 9ddf8c5b7b883c88f3def4763441f9fd9aa04640 Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Thu, 16 Jul 2020 09:05:42 -0400 Subject: [PATCH 14/19] Formatting. --- resource_alks_ltk.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resource_alks_ltk.go b/resource_alks_ltk.go index e2b64209..931fd710 100644 --- a/resource_alks_ltk.go +++ b/resource_alks_ltk.go @@ -26,13 +26,13 @@ func resourceAlksLtk() *schema.Resource { }, "access_key": &schema.Schema{ Sensitive: true, - Type: schema.TypeString, - Computed: true, + Type: schema.TypeString, + Computed: true, }, "secret_key": &schema.Schema{ Sensitive: true, - Type: schema.TypeString, - Computed: true, + Type: schema.TypeString, + Computed: true, }, }, } From c7131fd7d93a6daf473f48508aeaae6cb4053342 Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Thu, 16 Jul 2020 10:16:15 -0400 Subject: [PATCH 15/19] Added exists and import functionality to the resource provider. --- resource_alks_ltk.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/resource_alks_ltk.go b/resource_alks_ltk.go index 931fd710..8c53565f 100644 --- a/resource_alks_ltk.go +++ b/resource_alks_ltk.go @@ -11,6 +11,11 @@ func resourceAlksLtk() *schema.Resource { Create: resourceAlksLtkCreate, Read: resourceAlksLtkRead, Delete: resourceAlksLtkDelete, + Exists: resourceAlksLtkExists, + Importer: &schema.ResourceImporter{ + // Terraform provided importer + State: schema.ImportStatePassthrough, + }, SchemaVersion: 1, @@ -57,11 +62,11 @@ func resourceAlksLtkCreate(d *schema.ResourceData, meta interface{}) error { log.Printf("[INFO] alks_ltk.id: %v", d.Id()) - return nil + return resourceAlksLtkRead(d, meta) } func resourceAlksLtkRead(d *schema.ResourceData, meta interface{}) error { - log.Printf("[INFO] ALKS LTK Users Read") + log.Printf("[INFO] ALKS LTK User Read") client := meta.(*alks.Client) resp, err := client.GetLongTermKey(d.Id()) @@ -89,6 +94,24 @@ func resourceAlksLtkDelete(d *schema.ResourceData, meta interface{}) error { return nil } +func resourceAlksLtkExists(d *schema.ResourceData, meta interface{}) (bool, error) { + log.Printf("[INFO] ALKS LTK User Exists") + + client := meta.(*alks.Client) + resp, err := client.GetLongTermKey(d.Id()) + + if err != nil { + return false, err + } + + // We can get a 200, but an empty string so this is the condition to check for. + if len(resp.LongTermKey.UserName) == 0 { + return false, nil + } + + return true, nil +} + func populateResourceDataFromLTK(longTermKey *alks.GetLongTermKeyResponse, d *schema.ResourceData) error { d.SetId(longTermKey.UserName) _ = d.Set("access_key", longTermKey.AccessKeyID) From b0407e01db5095237e36db726876736e2c2c0739 Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Tue, 21 Jul 2020 14:30:28 -0400 Subject: [PATCH 16/19] Updated the READ command, now working correctly for imports. --- resource_alks_ltk.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/resource_alks_ltk.go b/resource_alks_ltk.go index 8c53565f..f0c0481f 100644 --- a/resource_alks_ltk.go +++ b/resource_alks_ltk.go @@ -78,7 +78,9 @@ func resourceAlksLtkRead(d *schema.ResourceData, meta interface{}) error { log.Printf("[INFO] alks_ltk.id: %v", d.Id()) - return populateResourceDataFromLTK(resp, d) + _ = d.Set("iam_username", resp.UserName) + + return nil } func resourceAlksLtkDelete(d *schema.ResourceData, meta interface{}) error { @@ -111,11 +113,3 @@ func resourceAlksLtkExists(d *schema.ResourceData, meta interface{}) (bool, erro return true, nil } - -func populateResourceDataFromLTK(longTermKey *alks.GetLongTermKeyResponse, d *schema.ResourceData) error { - d.SetId(longTermKey.UserName) - _ = d.Set("access_key", longTermKey.AccessKeyID) - _ = d.Set("create_date", longTermKey.CreateDate) - - return nil -} From 8b18e165dc4d5ab658a7a83142af6753448063af Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Tue, 21 Jul 2020 15:42:56 -0400 Subject: [PATCH 17/19] Adding the field needed to fulfill the contract for the READ operation. --- resource_alks_ltk.go | 1 + 1 file changed, 1 insertion(+) diff --git a/resource_alks_ltk.go b/resource_alks_ltk.go index f0c0481f..9994ca73 100644 --- a/resource_alks_ltk.go +++ b/resource_alks_ltk.go @@ -79,6 +79,7 @@ func resourceAlksLtkRead(d *schema.ResourceData, meta interface{}) error { log.Printf("[INFO] alks_ltk.id: %v", d.Id()) _ = d.Set("iam_username", resp.UserName) + _ = d.Set("access_key", resp.AccessKeyID) return nil } From 804f382dba765ae63d807ea14072ebdfb662dae8 Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Wed, 22 Jul 2020 09:56:26 -0400 Subject: [PATCH 18/19] Added documentation of self-service LTK resources. --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 1acf7943..d5176291 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,22 @@ Value | Type | Forces New | Value Type | Descrip `ip_arn` | Computed | n/a | string | If `role_added_to_ip` was `true` this will provide the ARN of the instance profile role. `enable_alks_access` | Optional | yes | bool | If `true`, allows ALKS calls to be made by instance profiles or Lambda functions making use of this role. +### `alks_ltk` + +```tf +resource "alks_ltk" "test_ltk_user" { + iam_username = "My_LTK_User_Name" +} +``` + +Value | Type | Forces New | Value Type | Description +--------------------------------- | -------- | ---------- | ---------- | ----------- +`iam_username` | Required | yes | string | The name of the IAM user to create. This parameter allows a string of characters consisting of upper and lowercase alphanumeric characters with no spaces. You can also include any of the following characters: =,.@-. User names are not distinguished by case. +`iam_user_arn` | Computed | n/a | string | The ARN associated with the LTK user. +`access_key` | Computed | n/a | string | Generated access key for the LTK user. Note: This is saved in the state file, so please be aware of this. +`secret_key` | Computed | n/a | string | Generated secret key for the LTK user. Note: This is saved in the state file, so please be aware of this. + + ## Example See [this example](examples/alks.tf) for a basic Terraform script which: From 32680a5325761b98dd0a79d503bc718b8608dc45 Mon Sep 17 00:00:00 2001 From: Andrew Magana Date: Wed, 22 Jul 2020 09:57:23 -0400 Subject: [PATCH 19/19] Updated the versions of ALKS TFP in the docs to stay up to date. --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d5176291..35022cba 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ This module is used for creating IAM Roles via the ALKS API. For example on macOS: ``` -curl -L https://github.com/Cox-Automotive/terraform-provider-alks/releases/download/1.4.2/terraform-provider-alks-darwin-amd64.tar.gz | tar zxv +curl -L https://github.com/Cox-Automotive/terraform-provider-alks/releases/download/1.4.3/terraform-provider-alks-darwin-amd64.tar.gz | tar zxv ``` * Configure Terraform to use this plugin by placing the binary in `.terraform.d/plugins/` on MacOS/Linux or `terraform.d\plugins\` in your user's "Application Data" directory on Windows. @@ -41,7 +41,7 @@ Static credentials can be provided via an `access_key`, `secret_key` and `token` ```tf provider "alks" { url = "https://alks.foo.com/rest" - version = "~> 1.4.0" + version = "~> 1.4.3" access_key = "accesskey" secret_key = "secretkey" token = "sessiontoken" @@ -55,7 +55,7 @@ You can provide your credentials via the `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS ```tf provider "alks" { url = "https://alks.foo.com/rest" - version = "~> 1.4.0" + version = "~> 1.4.3" } ``` @@ -74,7 +74,7 @@ You can use an AWS credentials file to specify your credentials. The default loc ```tf provider "alks" { url = "https://alks.foo.com/rest" - version = "~> 1.4.0" + version = "~> 1.4.3" shared_credentials_file = "/Users/brianantonelli/.aws/credentials" profile = "foo" } @@ -93,7 +93,7 @@ Your ALKS provider block can look just like this: ```tf provider "alks" { url = "https://alks.foo.com/rest" - version = "~> 1.4.0" + version = "~> 1.4.3" } ``` @@ -102,7 +102,7 @@ Since Machine Identities work with Instance Profile Metadata directly, it can be ```tf provider "alks" { url = "https://alks.foo.com/rest" - version = "~> 1.4.0" + version = "~> 1.4.3" assume_role { role_arn = "arn:aws:iam::112233445566:role/acct-managed/JenkinsPRODAccountTrust" }