From 1ab680f11c44b3cc29d72b2fcd43555e3f11b319 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Wed, 30 May 2018 23:15:04 -0400 Subject: [PATCH] resource/aws_cognito_resource_server: Address PR #4530 feedback --- aws/resource_aws_cognito_resource_server.go | 89 +++++++++---- ...source_aws_cognito_resource_server_test.go | 125 ++++++++++++++---- aws/structure.go | 3 +- aws/validators.go | 12 -- website/aws.erb | 3 + .../docs/r/cognito_resource_server.markdown | 22 ++- 6 files changed, 181 insertions(+), 73 deletions(-) diff --git a/aws/resource_aws_cognito_resource_server.go b/aws/resource_aws_cognito_resource_server.go index a77736f4506..5870d99847b 100644 --- a/aws/resource_aws_cognito_resource_server.go +++ b/aws/resource_aws_cognito_resource_server.go @@ -3,11 +3,12 @@ package aws import ( "fmt" "log" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" - "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" ) func resourceAwsCognitoResourceServer() *schema.Resource { @@ -42,17 +43,13 @@ func resourceAwsCognitoResourceServer() *schema.Resource { "scope_description": { Type: schema.TypeString, Required: true, - ValidateFunc: validateCognitoResourceServerScopeDescription, + ValidateFunc: validation.StringLenBetween(1, 256), }, "scope_name": { Type: schema.TypeString, Required: true, ValidateFunc: validateCognitoResourceServerScopeName, }, - "scope_identifier": { - Type: schema.TypeString, - Computed: true, - }, }, }, }, @@ -75,10 +72,13 @@ func resourceAwsCognitoResourceServer() *schema.Resource { func resourceAwsCognitoResourceServerCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cognitoidpconn + identifier := d.Get("identifier").(string) + userPoolID := d.Get("user_pool_id").(string) + params := &cognitoidentityprovider.CreateResourceServerInput{ - Identifier: aws.String(d.Get("identifier").(string)), + Identifier: aws.String(identifier), Name: aws.String(d.Get("name").(string)), - UserPoolId: aws.String(d.Get("user_pool_id").(string)), + UserPoolId: aws.String(userPoolID), } if v, ok := d.GetOk("scope"); ok { @@ -88,13 +88,13 @@ func resourceAwsCognitoResourceServerCreate(d *schema.ResourceData, meta interfa log.Printf("[DEBUG] Creating Cognito Resource Server: %s", params) - resp, err := conn.CreateResourceServer(params) + _, err := conn.CreateResourceServer(params) if err != nil { - return errwrap.Wrapf("Error creating Cognito Resource Server: {{err}}", err) + return fmt.Errorf("Error creating Cognito Resource Server: %s", err) } - d.SetId(*resp.ResourceServer.Identifier) + d.SetId(fmt.Sprintf("%s|%s", userPoolID, identifier)) return resourceAwsCognitoResourceServerRead(d, meta) } @@ -102,9 +102,14 @@ func resourceAwsCognitoResourceServerCreate(d *schema.ResourceData, meta interfa func resourceAwsCognitoResourceServerRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cognitoidpconn + userPoolID, identifier, err := decodeCognitoResourceServerID(d.Id()) + if err != nil { + return err + } + params := &cognitoidentityprovider.DescribeResourceServerInput{ - Identifier: aws.String(d.Id()), - UserPoolId: aws.String(d.Get("user_pool_id").(string)), + Identifier: aws.String(identifier), + UserPoolId: aws.String(userPoolID), } log.Printf("[DEBUG] Reading Cognito Resource Server: %s", params) @@ -112,19 +117,25 @@ func resourceAwsCognitoResourceServerRead(d *schema.ResourceData, meta interface resp, err := conn.DescribeResourceServer(params) if err != nil { - if isAWSErr(err, "ResourceNotFoundException", "") { - log.Printf("[WARN] Cognito Resource Server %s is already gone", d.Id()) + if isAWSErr(err, cognitoidentityprovider.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] Cognito Resource Server %q not found, removing from state", d.Id()) d.SetId("") return nil } return err } - d.SetId(*resp.ResourceServer.Identifier) - d.Set("name", *resp.ResourceServer.Name) - d.Set("user_pool_id", *resp.ResourceServer.UserPoolId) + if resp == nil || resp.ResourceServer == nil { + log.Printf("[WARN] Cognito Resource Server %q not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("identifier", resp.ResourceServer.Identifier) + d.Set("name", resp.ResourceServer.Name) + d.Set("user_pool_id", resp.ResourceServer.UserPoolId) - scopes := flattenCognitoResourceServerScope(*resp.ResourceServer.Identifier, resp.ResourceServer.Scopes) + scopes := flattenCognitoResourceServerScope(resp.ResourceServer.Scopes) if err := d.Set("scope", scopes); err != nil { return fmt.Errorf("Failed setting schema: %s", err) } @@ -132,7 +143,7 @@ func resourceAwsCognitoResourceServerRead(d *schema.ResourceData, meta interface var scopeIdentifiers []string for _, elem := range scopes { - scopeIdentifier := elem["scope_identifier"].(string) + scopeIdentifier := fmt.Sprintf("%s/%s", aws.StringValue(resp.ResourceServer.Identifier), elem["scope_name"].(string)) scopeIdentifiers = append(scopeIdentifiers, scopeIdentifier) } d.Set("scope_identifiers", scopeIdentifiers) @@ -142,17 +153,23 @@ func resourceAwsCognitoResourceServerRead(d *schema.ResourceData, meta interface func resourceAwsCognitoResourceServerUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cognitoidpconn + userPoolID, identifier, err := decodeCognitoResourceServerID(d.Id()) + if err != nil { + return err + } + params := &cognitoidentityprovider.UpdateResourceServerInput{ - Identifier: aws.String(d.Id()), + Identifier: aws.String(identifier), Name: aws.String(d.Get("name").(string)), - UserPoolId: aws.String(d.Get("user_pool_id").(string)), + Scopes: expandCognitoResourceServerScope(d.Get("scope").(*schema.Set).List()), + UserPoolId: aws.String(userPoolID), } log.Printf("[DEBUG] Updating Cognito Resource Server: %s", params) - _, err := conn.UpdateResourceServer(params) + _, err = conn.UpdateResourceServer(params) if err != nil { - return errwrap.Wrapf("Error updating Cognito Resource Server: {{err}}", err) + return fmt.Errorf("Error updating Cognito Resource Server: %s", err) } return resourceAwsCognitoResourceServerRead(d, meta) @@ -161,18 +178,34 @@ func resourceAwsCognitoResourceServerUpdate(d *schema.ResourceData, meta interfa func resourceAwsCognitoResourceServerDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cognitoidpconn + userPoolID, identifier, err := decodeCognitoResourceServerID(d.Id()) + if err != nil { + return err + } + params := &cognitoidentityprovider.DeleteResourceServerInput{ - Identifier: aws.String(d.Id()), - UserPoolId: aws.String(d.Get("user_pool_id").(string)), + Identifier: aws.String(identifier), + UserPoolId: aws.String(userPoolID), } log.Printf("[DEBUG] Deleting Resource Server: %s", params) - _, err := conn.DeleteResourceServer(params) + _, err = conn.DeleteResourceServer(params) if err != nil { - return errwrap.Wrapf("Error deleting Resource Server: {{err}}", err) + if isAWSErr(err, cognitoidentityprovider.ErrCodeResourceNotFoundException, "") { + return nil + } + return fmt.Errorf("Error deleting Resource Server: %s", err) } return nil } + +func decodeCognitoResourceServerID(id string) (string, string, error) { + idParts := strings.Split(id, "|") + if len(idParts) != 2 { + return "", "", fmt.Errorf("expected ID in format UserPoolID|Identifier, received: %s", id) + } + return idParts[0], idParts[1], nil +} diff --git a/aws/resource_aws_cognito_resource_server_test.go b/aws/resource_aws_cognito_resource_server_test.go index 3fa283fc5dc..fc70501bb97 100644 --- a/aws/resource_aws_cognito_resource_server_test.go +++ b/aws/resource_aws_cognito_resource_server_test.go @@ -13,9 +13,12 @@ import ( ) func TestAccAWSCognitoResourceServer_basic(t *testing.T) { + var resourceServer cognitoidentityprovider.ResourceServerType identifier := fmt.Sprintf("tf-acc-test-resource-server-id-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) - name := fmt.Sprintf("tf-acc-test-resource-server-name-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + name1 := fmt.Sprintf("tf-acc-test-resource-server-name-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + name2 := fmt.Sprintf("tf-acc-test-resource-server-name-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) poolName := fmt.Sprintf("tf-acc-test-pool-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + resourceName := "aws_cognito_resource_server.main" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -23,22 +26,40 @@ func TestAccAWSCognitoResourceServer_basic(t *testing.T) { CheckDestroy: testAccCheckAWSCognitoResourceServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSCognitoResourceServerConfig_basic(identifier, name, poolName), + Config: testAccAWSCognitoResourceServerConfig_basic(identifier, name1, poolName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoResourceServerExists(resourceName, &resourceServer), + resource.TestCheckResourceAttr(resourceName, "identifier", identifier), + resource.TestCheckResourceAttr(resourceName, "name", name1), + resource.TestCheckResourceAttr(resourceName, "scope.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope_identifiers.#", "0"), + ), + }, + { + Config: testAccAWSCognitoResourceServerConfig_basic(identifier, name2, poolName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoResourceServerExists("aws_cognito_resource_server.main"), - resource.TestCheckResourceAttr("aws_cognito_resource_server.main", "identifier", identifier), - resource.TestCheckResourceAttr("aws_cognito_resource_server.main", "name", name), - resource.TestCheckResourceAttr("aws_cognito_user_pool.main", "name", poolName), + testAccCheckAWSCognitoResourceServerExists(resourceName, &resourceServer), + resource.TestCheckResourceAttr(resourceName, "identifier", identifier), + resource.TestCheckResourceAttr(resourceName, "name", name2), + resource.TestCheckResourceAttr(resourceName, "scope.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope_identifiers.#", "0"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } -func TestAccAWSCognitoResourceServer_full(t *testing.T) { +func TestAccAWSCognitoResourceServer_scope(t *testing.T) { + var resourceServer cognitoidentityprovider.ResourceServerType identifier := fmt.Sprintf("tf-acc-test-resource-server-id-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) name := fmt.Sprintf("tf-acc-test-resource-server-name-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) poolName := fmt.Sprintf("tf-acc-test-pool-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + resourceName := "aws_cognito_resource_server.main" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -46,20 +67,40 @@ func TestAccAWSCognitoResourceServer_full(t *testing.T) { CheckDestroy: testAccCheckAWSCognitoResourceServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSCognitoResourceServerConfig_full(identifier, name, poolName), + Config: testAccAWSCognitoResourceServerConfig_scope(identifier, name, poolName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoResourceServerExists(resourceName, &resourceServer), + resource.TestCheckResourceAttr(resourceName, "scope.#", "2"), + resource.TestCheckResourceAttr(resourceName, "scope_identifiers.#", "2"), + ), + }, + { + Config: testAccAWSCognitoResourceServerConfig_scope_update(identifier, name, poolName), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoResourceServerExists("aws_cognito_resource_server.main"), - resource.TestCheckResourceAttr("aws_cognito_resource_server.main", "identifier", identifier), - resource.TestCheckResourceAttr("aws_cognito_resource_server.main", "name", name), - resource.TestCheckResourceAttrSet("aws_cognito_resource_server.main", "scope_identifiers"), - resource.TestCheckResourceAttr("aws_cognito_user_pool.main", "name", poolName), + testAccCheckAWSCognitoResourceServerExists(resourceName, &resourceServer), + resource.TestCheckResourceAttr(resourceName, "scope.#", "1"), + resource.TestCheckResourceAttr(resourceName, "scope_identifiers.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + // Ensure we can remove scope completely + { + Config: testAccAWSCognitoResourceServerConfig_basic(identifier, name, poolName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoResourceServerExists(resourceName, &resourceServer), + resource.TestCheckResourceAttr(resourceName, "scope.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope_identifiers.#", "0"), ), }, }, }) } -func testAccCheckAWSCognitoResourceServerExists(n string) resource.TestCheckFunc { +func testAccCheckAWSCognitoResourceServerExists(n string, resourceServer *cognitoidentityprovider.ResourceServerType) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -72,15 +113,26 @@ func testAccCheckAWSCognitoResourceServerExists(n string) resource.TestCheckFunc conn := testAccProvider.Meta().(*AWSClient).cognitoidpconn - _, err := conn.DescribeResourceServer(&cognitoidentityprovider.DescribeResourceServerInput{ - Identifier: aws.String(rs.Primary.ID), - UserPoolId: aws.String(rs.Primary.Attributes["user_pool_id"]), + userPoolID, identifier, err := decodeCognitoResourceServerID(rs.Primary.ID) + if err != nil { + return err + } + + output, err := conn.DescribeResourceServer(&cognitoidentityprovider.DescribeResourceServerInput{ + Identifier: aws.String(identifier), + UserPoolId: aws.String(userPoolID), }) if err != nil { return err } + if output == nil || output.ResourceServer == nil { + return fmt.Errorf("Cognito Resource Server %q information not found", rs.Primary.ID) + } + + *resourceServer = *output.ResourceServer + return nil } } @@ -93,13 +145,18 @@ func testAccCheckAWSCognitoResourceServerDestroy(s *terraform.State) error { continue } - _, err := conn.DescribeResourceServer(&cognitoidentityprovider.DescribeResourceServerInput{ - Identifier: aws.String(rs.Primary.ID), - UserPoolId: aws.String(rs.Primary.Attributes["user_pool_id"]), + userPoolID, identifier, err := decodeCognitoResourceServerID(rs.Primary.ID) + if err != nil { + return err + } + + _, err = conn.DescribeResourceServer(&cognitoidentityprovider.DescribeResourceServerInput{ + Identifier: aws.String(identifier), + UserPoolId: aws.String(userPoolID), }) if err != nil { - if isAWSErr(err, "ResourceNotFoundException", "") { + if isAWSErr(err, cognitoidentityprovider.ErrCodeResourceNotFoundException, "") { return nil } return err @@ -123,19 +180,19 @@ resource "aws_cognito_user_pool" "main" { `, identifier, name, poolName) } -func testAccAWSCognitoResourceServerConfig_full(identifier string, name string, poolName string) string { +func testAccAWSCognitoResourceServerConfig_scope(identifier string, name string, poolName string) string { return fmt.Sprintf(` resource "aws_cognito_resource_server" "main" { identifier = "%s" name = "%s" scope = { - scope_name = "scope_1_name" + scope_name = "scope_1_name" scope_description = "scope_1_description" } scope = { - scope_name = "scope_2_name" + scope_name = "scope_2_name" scope_description = "scope_2_description" } @@ -147,3 +204,23 @@ resource "aws_cognito_user_pool" "main" { } `, identifier, name, poolName) } + +func testAccAWSCognitoResourceServerConfig_scope_update(identifier string, name string, poolName string) string { + return fmt.Sprintf(` +resource "aws_cognito_resource_server" "main" { + identifier = "%s" + name = "%s" + + scope = { + scope_name = "scope_1_name_updated" + scope_description = "scope_1_description" + } + + user_pool_id = "${aws_cognito_user_pool.main.id}" +} + +resource "aws_cognito_user_pool" "main" { + name = "%s" +} +`, identifier, name, poolName) +} diff --git a/aws/structure.go b/aws/structure.go index 6b851a17766..e9146613df8 100644 --- a/aws/structure.go +++ b/aws/structure.go @@ -2719,7 +2719,7 @@ func expandCognitoResourceServerScope(inputs []interface{}) []*cognitoidentitypr return configs } -func flattenCognitoResourceServerScope(identifier string, inputs []*cognitoidentityprovider.ResourceServerScopeType) []map[string]interface{} { +func flattenCognitoResourceServerScope(inputs []*cognitoidentityprovider.ResourceServerScopeType) []map[string]interface{} { values := make([]map[string]interface{}, 0) for _, input := range inputs { @@ -2729,7 +2729,6 @@ func flattenCognitoResourceServerScope(identifier string, inputs []*cognitoident var value = map[string]interface{}{ "scope_name": aws.StringValue(input.ScopeName), "scope_description": aws.StringValue(input.ScopeDescription), - "scope_identifier": strings.Join([]string{identifier, "/", aws.StringValue(input.ScopeName)}, ""), } values = append(values, value) } diff --git a/aws/validators.go b/aws/validators.go index 0f2aa0327ac..2696fa2bc52 100644 --- a/aws/validators.go +++ b/aws/validators.go @@ -1386,18 +1386,6 @@ func validateCognitoUserPoolClientURL(v interface{}, k string) (ws []string, es return } -func validateCognitoResourceServerScopeDescription(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - - if len(value) < 1 { - errors = append(errors, fmt.Errorf("%q cannot be less than 1 character", k)) - } - if len(value) > 256 { - errors = append(errors, fmt.Errorf("%q cannot be longer than 256 character", k)) - } - return -} - func validateCognitoResourceServerScopeName(v interface{}, k string) (ws []string, errors []error) { value := v.(string) diff --git a/website/aws.erb b/website/aws.erb index e07a770c946..28da1a1b09e 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -629,6 +629,9 @@ > aws_cognito_identity_provider + > + aws_cognito_resource_server + > aws_cognito_user_group diff --git a/website/docs/r/cognito_resource_server.markdown b/website/docs/r/cognito_resource_server.markdown index bda43990126..57fb4ee52ad 100644 --- a/website/docs/r/cognito_resource_server.markdown +++ b/website/docs/r/cognito_resource_server.markdown @@ -20,8 +20,8 @@ resource "aws_cognito_user_pool" "pool" { } resource "aws_cognito_resource_server" "resource" { - identifier = "res" - name = "resource" + identifier = "https://example.com" + name = "example" user_pool_id = "${aws_cognito_user_pool.pool.id}" } @@ -35,13 +35,13 @@ resource "aws_cognito_user_pool" "pool" { } resource "aws_cognito_resource_server" "resource" { - identifier = "res" - name = "resource" + identifier = "https://example.com" + name = "example" - scope = { + scope = [{ scope_name = "sample-scope" scope_description = "a Sample Scope Description" - } + }] user_pool_id = "${aws_cognito_user_pool.pool.id}" } @@ -53,7 +53,7 @@ The following arguments are supported: * `identifier` - (Required) An identifier for the resource server. * `name` - (Required) A name for the resource server. -* `scope` - (Optional) The configuration for an [Authorization Scope](#authorization_scope). +* `scope` - (Optional) A list of [Authorization Scope](#authorization_scope). ### Authorization Scope @@ -65,3 +65,11 @@ The following arguments are supported: In addition to the arguments, which are exported, the following attributes are exported: * `scope_identifiers` - A list of all scopes configured for this resource server in the format identifier/scope_name. + +## Import + +`aws_cognito_resource_server` can be imported using their User Pool ID and Identifier, e.g. + +``` +$ terraform import aws_cognito_resource_server.example xxx_yyyyy|https://example.com +```