Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/aws_cognito_user_pool_domain - add update functionality for certificate_arn #25275

Closed
wants to merge 62 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
7b5c388
Allow updating of `certificate_arn` argument in `aws_cognito_user_poo…
austinvalle Jun 10, 2022
196441c
Merge branch 'hashicorp:main' into main
austinvalle Jun 10, 2022
0e04f5e
Create 25275.txt
austinvalle Jun 10, 2022
8a7050b
Merge branch 'hashicorp:main' into main
pgrbot-01 Sep 15, 2022
f4359e3
Merge branch 'hashicorp:main' into main
pgrbot-01 Sep 16, 2022
4b51144
Merge branch 'hashicorp:main' into main
pgrbot-01 Oct 25, 2022
32af3f6
Merge branch 'hashicorp:main' into main
pgrbot-01 Oct 26, 2022
a8a84ba
Merge branch 'hashicorp:main' into main
pgrbot-01 Oct 27, 2022
6f0c8b6
Merge branch 'hashicorp:main' into main
pgrbot-01 Oct 28, 2022
5ed1583
Merge branch 'hashicorp:main' into main
pgrbot-01 Oct 29, 2022
5625bbd
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 1, 2022
c28e298
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 2, 2022
2394626
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 3, 2022
af27629
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 4, 2022
8293d7b
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 5, 2022
0ff8a5e
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 8, 2022
5f88208
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 9, 2022
03c78a8
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 10, 2022
5219655
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 11, 2022
360e5ed
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 12, 2022
644e8d3
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 15, 2022
ac50345
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 16, 2022
d10434b
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 17, 2022
8fa4829
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 18, 2022
434c278
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 19, 2022
fb6e00d
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 22, 2022
6f19d29
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 23, 2022
8d0dce9
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 24, 2022
328ce60
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 25, 2022
89d28ab
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 26, 2022
28fe1a0
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 29, 2022
127b93c
Merge branch 'hashicorp:main' into main
pgrbot-01 Nov 30, 2022
2a5df37
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 1, 2022
56836f8
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 2, 2022
1f265ec
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 3, 2022
63263dc
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 6, 2022
0eabbbd
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 7, 2022
ea7c40c
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 8, 2022
857edf6
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 9, 2022
577eb8c
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 10, 2022
fc78bb3
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 13, 2022
8883e4c
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 14, 2022
926bb24
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 15, 2022
4134909
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 16, 2022
5bdb84e
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 17, 2022
ec91096
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 20, 2022
80b86ac
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 21, 2022
46b9acb
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 22, 2022
992e2dd
Merge branch 'hashicorp:main' into main
pgrbot-01 Dec 24, 2022
ce59b5d
Merge branch 'hashicorp:main' into main
pgrbot-01 Jan 4, 2023
8d1e776
Merge branch 'hashicorp:main' into main
pgrbot-01 Jan 5, 2023
c87ac91
Merge branch 'hashicorp:main' into main
pgrbot-01 Jan 6, 2023
80bdb64
Merge branch 'hashicorp:main' into main
pgrbot-01 Jan 7, 2023
89a7824
Merge branch 'hashicorp:main' into main
pgrbot-01 Jan 9, 2023
1de5307
Merge branch 'hashicorp:main' into main
pgrbot-01 Jan 10, 2023
21a4791
Merge branch 'hashicorp:main' into main
pgrbot-01 Jan 11, 2023
5c5f808
Merge branch 'hashicorp:main' into main
pgrbot-01 Jan 12, 2023
fda1c55
Merge branch 'hashicorp:main' into main
pgrbot-01 Jan 13, 2023
b0c4d8e
Merge branch 'hashicorp:main' into main
pgrbot-01 Jan 14, 2023
8c8ba11
Merge branch 'hashicorp:main' into main
pgrbot-01 Jan 18, 2023
e38de04
Merge branch 'hashicorp:main' into main
pgrbot-01 Jan 19, 2023
5a1c6e8
Merge branch 'hashicorp:main' into main
pgrbot-01 Jan 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/25275.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
aws_cognito_user_pool_domain: Allow update of certificate_arn argument
```
40 changes: 39 additions & 1 deletion internal/service/cognitoidp/user_pool_domain.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cognitoidp

import (
"context"
"errors"
"fmt"
"log"
Expand All @@ -9,6 +10,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
Expand All @@ -20,6 +22,7 @@ func ResourceUserPoolDomain() *schema.Resource {
return &schema.Resource{
Create: resourceUserPoolDomainCreate,
Read: resourceUserPoolDomainRead,
Update: resourceUserPoolDomainUpdate,
Delete: resourceUserPoolDomainDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
Expand All @@ -35,7 +38,6 @@ func ResourceUserPoolDomain() *schema.Resource {
"certificate_arn": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: verify.ValidARN,
},
"user_pool_id": {
Expand All @@ -60,6 +62,10 @@ func ResourceUserPoolDomain() *schema.Resource {
Computed: true,
},
},
CustomizeDiff: customdiff.ForceNewIfChange("certificate_arn", func(_ context.Context, old, new, meta interface{}) bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed if you removed ForceNew

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I added this ForceNewIfChange to prevent the update function being ran in these two specific edge cases, which result in an error from the AWS API:

  1. A custom domain was initially set with a certificate arn, then was removed (this triggers an update to set the certificate ARN to blank, which will cause an error)
  2. A custom domain was not initially set with a certificate arn, then one is added (this indicates that there wasn't a custom domain, which means it must be fully destroyed and re-created to add the cert arn)

If there is an easier way to do this outside of the ForceNewIfChange, or if we don't think it's necessary, I can remove it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DrFaust92 Friendly ping 😄 , do you think the above use-cases are valid? Or should I remove the CustomizeDiff function?

// If the cert arn is being changed to a new arn, don't force new
return !(old.(string) != "" && new.(string) != "")
}),
}
}

Expand Down Expand Up @@ -142,6 +148,38 @@ func resourceUserPoolDomainRead(d *schema.ResourceData, meta interface{}) error
return nil
}

func resourceUserPoolDomainUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).CognitoIDPConn

domain := d.Get("domain").(string)
timeout := 60 * time.Minute // Update is only for cert arns on custom domains, which take more time to become active

params := &cognitoidentityprovider.UpdateUserPoolDomainInput{
Domain: aws.String(domain),
UserPoolId: aws.String(d.Get("user_pool_id").(string)),
}

if v, ok := d.GetOk("certificate_arn"); ok {
customDomainConfig := &cognitoidentityprovider.CustomDomainConfigType{
CertificateArn: aws.String(v.(string)),
}
params.CustomDomainConfig = customDomainConfig
}

log.Printf("[DEBUG] Updating Cognito User Pool Domain: %s", params)

_, err := conn.UpdateUserPoolDomain(params)
if err != nil {
return fmt.Errorf("error updating User Pool Domain (%s): %w", d.Id(), err)
}

if _, err := waitUserPoolDomainUpdated(conn, d.Id(), timeout); err != nil {
return fmt.Errorf("error waiting for User Pool Domain (%s) update: %w", d.Id(), err)
}

return resourceUserPoolDomainRead(d, meta)
}

func resourceUserPoolDomainDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).CognitoIDPConn
log.Printf("[DEBUG] Deleting Cognito User Pool Domain: %s", d.Id())
Expand Down
140 changes: 140 additions & 0 deletions internal/service/cognitoidp/user_pool_domain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,44 @@ func TestAccCognitoIDPUserPoolDomain_custom(t *testing.T) {
})
}

func TestAccCognitoIDPUserPoolDomain_customCertUpdate(t *testing.T) {
rootDomain := acctest.ACMCertificateDomainFromEnv(t)
domain := acctest.ACMCertificateRandomSubDomain(rootDomain)
poolName := fmt.Sprintf("tf-acc-test-pool-%s", sdkacctest.RandString(10))

acmInitialValidationResourceName := "aws_acm_certificate_validation.initial_test"
acmUpdatedValidationResourceName := "aws_acm_certificate_validation.updated_test"

acmInitialCertResourceName := "aws_acm_certificate.initial"
acmUpdatedCertResourceName := "aws_acm_certificate.updated"

cognitoPoolResourceName := "aws_cognito_user_pool_domain.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t); testAccPreCheckUserPoolCustomDomain(t) },
ErrorCheck: acctest.ErrorCheck(t, cognitoidentityprovider.EndpointsID),
ProviderFactories: acctest.ProviderFactories,
CheckDestroy: testAccCheckUserPoolDomainDestroy,
Steps: []resource.TestStep{
{
Config: testAccUserPoolDomainConfig_customCertUpdate(rootDomain, domain, poolName, acmInitialValidationResourceName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckUserPoolDomainExists(cognitoPoolResourceName),
testAccCheckUserPoolDomainCertMatches(cognitoPoolResourceName, acmInitialCertResourceName),
resource.TestCheckResourceAttrPair(cognitoPoolResourceName, "certificate_arn", acmInitialCertResourceName, "arn"),
),
},
{
Config: testAccUserPoolDomainConfig_customCertUpdate(rootDomain, domain, poolName, acmUpdatedValidationResourceName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckUserPoolDomainCertMatches(cognitoPoolResourceName, acmUpdatedCertResourceName),
resource.TestCheckResourceAttrPair(cognitoPoolResourceName, "certificate_arn", acmUpdatedCertResourceName, "arn"),
),
},
},
})
}

func TestAccCognitoIDPUserPoolDomain_disappears(t *testing.T) {
domainName := fmt.Sprintf("tf-acc-test-domain-%d", sdkacctest.RandInt())
poolName := fmt.Sprintf("tf-acc-test-pool-%s", sdkacctest.RandString(10))
Expand Down Expand Up @@ -130,6 +168,49 @@ func testAccCheckUserPoolDomainExists(n string) resource.TestCheckFunc {
}
}

func testAccCheckUserPoolDomainCertMatches(cognitoResourceName string, certResourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
cognitoResource, ok := s.RootModule().Resources[cognitoResourceName]
if !ok {
return fmt.Errorf("Not found: %s", cognitoResourceName)
}

if cognitoResource.Primary.ID == "" {
return errors.New("No Cognito User Pool Domain ID is set")
}

certResource, ok := s.RootModule().Resources[certResourceName]
if !ok {
return fmt.Errorf("Not found: %s", cognitoResourceName)
}

if certResource.Primary.ID == "" {
return errors.New("No ACM Certificate ID is set")
}

conn := acctest.Provider.Meta().(*conns.AWSClient).CognitoIDPConn

domain, err := conn.DescribeUserPoolDomain(&cognitoidentityprovider.DescribeUserPoolDomainInput{
Domain: aws.String(cognitoResource.Primary.ID),
})

if err != nil {
return err
}
desc := domain.DomainDescription

if desc.CustomDomainConfig == nil {
return fmt.Errorf("No Custom Domain set on User pool: %s", *desc.UserPoolId)
}

if *desc.CustomDomainConfig.CertificateArn != certResource.Primary.ID {
return fmt.Errorf("Certificate ARN on Custom Domain does not match, expected: %s, got: %s", certResource.Primary.ID, *desc.CustomDomainConfig.CertificateArn)
}

return nil
}
}

func testAccCheckUserPoolDomainDestroy(s *terraform.State) error {
conn := acctest.Provider.Meta().(*conns.AWSClient).CognitoIDPConn

Expand Down Expand Up @@ -226,3 +307,62 @@ resource "aws_cognito_user_pool_domain" "test" {
}
`, rootDomain, domain, poolName))
}

func testAccUserPoolDomainConfig_customCertUpdate(rootDomain string, domain string, poolName string, appliedCertValidation string) string {
return acctest.ConfigCompose(
testAccUserPoolCustomDomainRegionProviderConfig(),
fmt.Sprintf(`
data "aws_route53_zone" "test" {
name = %[1]q
private_zone = false
}

resource "aws_acm_certificate" "initial" {
domain_name = %[2]q
validation_method = "DNS"
}

resource "aws_acm_certificate" "updated" {
domain_name = %[2]q
validation_method = "DNS"
}

resource "aws_route53_record" "initial_test" {
allow_overwrite = true
name = tolist(aws_acm_certificate.initial.domain_validation_options)[0].resource_record_name
records = [tolist(aws_acm_certificate.initial.domain_validation_options)[0].resource_record_value]
ttl = 60
type = tolist(aws_acm_certificate.initial.domain_validation_options)[0].resource_record_type
zone_id = data.aws_route53_zone.test.zone_id
}

resource "aws_route53_record" "updated_test" {
allow_overwrite = true
name = tolist(aws_acm_certificate.updated.domain_validation_options)[0].resource_record_name
records = [tolist(aws_acm_certificate.updated.domain_validation_options)[0].resource_record_value]
ttl = 60
type = tolist(aws_acm_certificate.updated.domain_validation_options)[0].resource_record_type
zone_id = data.aws_route53_zone.test.zone_id
}

resource "aws_acm_certificate_validation" "initial_test" {
certificate_arn = aws_acm_certificate.initial.arn
validation_record_fqdns = [aws_route53_record.initial_test.fqdn]
}

resource "aws_acm_certificate_validation" "updated_test" {
certificate_arn = aws_acm_certificate.updated.arn
validation_record_fqdns = [aws_route53_record.updated_test.fqdn]
}

resource "aws_cognito_user_pool" "test" {
name = %[3]q
}

resource "aws_cognito_user_pool_domain" "test" {
certificate_arn = %[4]s.certificate_arn
domain = aws_acm_certificate.initial.domain_name
user_pool_id = aws_cognito_user_pool.test.id
}
`, rootDomain, domain, poolName, appliedCertValidation))
}
25 changes: 25 additions & 0 deletions internal/service/cognitoidp/wait.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,31 @@ func waitUserPoolDomainCreated(conn *cognitoidentityprovider.CognitoIdentityProv

outputRaw, err := stateConf.WaitForState()

if err != nil {
return nil, err
}

if output, ok := outputRaw.(*cognitoidentityprovider.DescribeUserPoolDomainOutput); ok {
return output, err
}

return nil, err
}

func waitUserPoolDomainUpdated(conn *cognitoidentityprovider.CognitoIdentityProvider, domain string, timeout time.Duration) (*cognitoidentityprovider.DescribeUserPoolDomainOutput, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{
cognitoidentityprovider.DomainStatusTypeUpdating,
},
Target: []string{
cognitoidentityprovider.DomainStatusTypeActive,
},
Refresh: statusUserPoolDomain(conn, domain),
Timeout: timeout,
}

outputRaw, err := stateConf.WaitForState()

if output, ok := outputRaw.(*cognitoidentityprovider.DescribeUserPoolDomainOutput); ok {
return output, err
}
Expand Down