From 55f64c8bcc0b37ad4b6d98c02e6d70d361c07860 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Thu, 22 Oct 2020 11:23:21 -0400 Subject: [PATCH] New Data Source: aws_rds_certificate Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/15221 Output from acceptance testing in AWS Commercial: ``` --- PASS: TestAccAWSRDSCertificateDataSource_LatestValidTill (10.41s) --- PASS: TestAccAWSRDSCertificateDataSource_Id (11.03s) ``` Output from acceptance testing in AWS GovCloud (US): ``` --- PASS: TestAccAWSRDSCertificateDataSource_LatestValidTill (15.02s) --- PASS: TestAccAWSRDSCertificateDataSource_Id (16.86s) ``` --- aws/data_source_aws_rds_certificate.go | 149 +++++++++++++++++++ aws/data_source_aws_rds_certificate_test.go | 88 +++++++++++ aws/provider.go | 1 + website/docs/d/rds_certificate.html.markdown | 38 +++++ 4 files changed, 276 insertions(+) create mode 100644 aws/data_source_aws_rds_certificate.go create mode 100644 aws/data_source_aws_rds_certificate_test.go create mode 100644 website/docs/d/rds_certificate.html.markdown diff --git a/aws/data_source_aws_rds_certificate.go b/aws/data_source_aws_rds_certificate.go new file mode 100644 index 00000000000..f2cdb0b6ce6 --- /dev/null +++ b/aws/data_source_aws_rds_certificate.go @@ -0,0 +1,149 @@ +package aws + +import ( + "fmt" + "sort" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/rds" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceAwsRdsCertificate() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsRdsCertificateRead, + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "certificate_type": { + Type: schema.TypeString, + Computed: true, + }, + "customer_override": { + Type: schema.TypeBool, + Computed: true, + }, + "customer_override_valid_till": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "latest_valid_till": { + Type: schema.TypeBool, + Optional: true, + }, + "thumbprint": { + Type: schema.TypeString, + Computed: true, + }, + "valid_from": { + Type: schema.TypeString, + Computed: true, + }, + "valid_till": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAwsRdsCertificateRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).rdsconn + + input := &rds.DescribeCertificatesInput{} + + if v, ok := d.GetOk("id"); ok { + input.CertificateIdentifier = aws.String(v.(string)) + } + + var certificates []*rds.Certificate + + err := conn.DescribeCertificatesPages(input, func(page *rds.DescribeCertificatesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, certificate := range page.Certificates { + if certificate == nil { + continue + } + + certificates = append(certificates, certificate) + } + return !lastPage + }) + + if err != nil { + return fmt.Errorf("error reading RDS Certificates: %w", err) + } + + if len(certificates) == 0 { + return fmt.Errorf("no RDS Certificates found") + } + + // client side filtering + var certificate *rds.Certificate + + if d.Get("latest_valid_till").(bool) { + sort.Sort(rdsCertificateValidTillSort(certificates)) + certificate = certificates[len(certificates)-1] + } + + if len(certificates) > 1 { + return fmt.Errorf("multiple RDS Certificates match the criteria; try changing search query") + } + + if certificate == nil && len(certificates) == 1 { + certificate = certificates[0] + } + + if certificate == nil { + return fmt.Errorf("no RDS Certificates match the criteria") + } + + d.SetId(aws.StringValue(certificate.CertificateIdentifier)) + + d.Set("arn", certificate.CertificateArn) + d.Set("certificate_type", certificate.CertificateType) + d.Set("customer_override", certificate.CustomerOverride) + + if certificate.CustomerOverrideValidTill != nil { + d.Set("customer_override_valid_till", aws.TimeValue(certificate.CustomerOverrideValidTill).Format(time.RFC3339)) + } + + d.Set("thumbprint", certificate.Thumbprint) + + if certificate.ValidFrom != nil { + d.Set("valid_from", aws.TimeValue(certificate.ValidFrom).Format(time.RFC3339)) + } + + if certificate.ValidTill != nil { + d.Set("valid_till", aws.TimeValue(certificate.ValidTill).Format(time.RFC3339)) + } + + return nil +} + +type rdsCertificateValidTillSort []*rds.Certificate + +func (s rdsCertificateValidTillSort) Len() int { return len(s) } +func (s rdsCertificateValidTillSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s rdsCertificateValidTillSort) Less(i, j int) bool { + if s[i] == nil || s[i].ValidTill == nil { + return true + } + + if s[j] == nil || s[j].ValidTill == nil { + return false + } + + return (*s[i].ValidTill).Before(*s[j].ValidTill) +} diff --git a/aws/data_source_aws_rds_certificate_test.go b/aws/data_source_aws_rds_certificate_test.go new file mode 100644 index 00000000000..0d3a630217d --- /dev/null +++ b/aws/data_source_aws_rds_certificate_test.go @@ -0,0 +1,88 @@ +package aws + +import ( + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/service/rds" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccAWSRDSCertificateDataSource_Id(t *testing.T) { + dataSourceName := "data.aws_rds_certificate.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAWSRDSCertificatePreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccAWSRDSCertificateDataSourceConfigId(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, "id", "data.aws_rds_certificate.latest", "id"), + ), + }, + }, + }) +} + +func TestAccAWSRDSCertificateDataSource_LatestValidTill(t *testing.T) { + dataSourceName := "data.aws_rds_certificate.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccAWSRDSCertificatePreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccAWSRDSCertificateDataSourceConfigLatestValidTill(), + Check: resource.ComposeTestCheckFunc( + testAccMatchResourceAttrRegionalARNNoAccount(dataSourceName, "arn", "rds", regexp.MustCompile(`cert:rds-ca-[0-9]{4}`)), + resource.TestCheckResourceAttr(dataSourceName, "certificate_type", "CA"), + resource.TestCheckResourceAttr(dataSourceName, "customer_override", "false"), + resource.TestCheckNoResourceAttr(dataSourceName, "customer_override_valid_till"), + resource.TestMatchResourceAttr(dataSourceName, "id", regexp.MustCompile(`rds-ca-[0-9]{4}`)), + resource.TestMatchResourceAttr(dataSourceName, "thumbprint", regexp.MustCompile(`[0-9a-f]+`)), + resource.TestMatchResourceAttr(dataSourceName, "valid_from", regexp.MustCompile(rfc3339RegexPattern)), + resource.TestMatchResourceAttr(dataSourceName, "valid_till", regexp.MustCompile(rfc3339RegexPattern)), + ), + }, + }, + }) +} + +func testAccAWSRDSCertificatePreCheck(t *testing.T) { + conn := testAccProvider.Meta().(*AWSClient).rdsconn + + input := &rds.DescribeCertificatesInput{} + + _, err := conn.DescribeCertificates(input) + + if testAccPreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + +func testAccAWSRDSCertificateDataSourceConfigId() string { + return ` +data "aws_rds_certificate" "latest" { + latest_valid_till = true +} + +data "aws_rds_certificate" "test" { + id = data.aws_rds_certificate.latest.id +} +` +} + +func testAccAWSRDSCertificateDataSourceConfigLatestValidTill() string { + return ` +data "aws_rds_certificate" "test" { + latest_valid_till = true +} +` +} diff --git a/aws/provider.go b/aws/provider.go index a80e58ccf24..588ebacaaaa 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -311,6 +311,7 @@ func Provider() *schema.Provider { "aws_pricing_product": dataSourceAwsPricingProduct(), "aws_qldb_ledger": dataSourceAwsQLDBLedger(), "aws_ram_resource_share": dataSourceAwsRamResourceShare(), + "aws_rds_certificate": dataSourceAwsRdsCertificate(), "aws_rds_cluster": dataSourceAwsRdsCluster(), "aws_rds_engine_version": dataSourceAwsRdsEngineVersion(), "aws_rds_orderable_db_instance": dataSourceAwsRdsOrderableDbInstance(), diff --git a/website/docs/d/rds_certificate.html.markdown b/website/docs/d/rds_certificate.html.markdown new file mode 100644 index 00000000000..e4a11beb31b --- /dev/null +++ b/website/docs/d/rds_certificate.html.markdown @@ -0,0 +1,38 @@ +--- +subcategory: "RDS" +layout: "aws" +page_title: "AWS: aws_rds_certificate" +description: |- + Information about an RDS Certificate. +--- + +# Data Source: aws_rds_certificate + +Information about an RDS Certificate. + +## Example Usage + +```hcl +data "aws_rds_certificate" "example" { + latest_valid_till = true +} +``` + +## Argument Reference + +The following arguments are supported: + +* `id` - (Optional) Certificate identifier. For example, `rds-ca-2019`. +* `latest_valid_till` - (Optional) When enabled, returns the certificate with the latest `ValidTill`. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - Amazon Resource Name (ARN) of the certificate. +* `certificate_type` - Type of certificate. For example, `CA`. +* `customer_override` - Boolean whether there is an override for the default certificate identifier. +* `customer_override_valid_till` - If there is an override for the default certificate identifier, when the override expires. +* `thumbprint` - Thumbprint of the certificate. +* `valid_from` - [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) of certificate starting validity date. +* `valid_till` - [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) of certificate ending validity date.