From 2b711f77c8962c302ab798803936786afc99fea9 Mon Sep 17 00:00:00 2001 From: Raphael Randschau Date: Wed, 7 Dec 2016 11:40:53 +0100 Subject: [PATCH] feat/aws: add iam_server_certificate data source (#10558) useful if you have an automatic process creating certs for you, e.g. let's lambda --- .../data_source_aws_iam_server_certificate.go | 134 ++++++++++++++++++ ..._source_aws_iam_server_certificate_test.go | 63 ++++++++ builtin/providers/aws/provider.go | 1 + .../d/iam_server_certificate.html.markdown | 45 ++++++ 4 files changed, 243 insertions(+) create mode 100644 builtin/providers/aws/data_source_aws_iam_server_certificate.go create mode 100644 builtin/providers/aws/data_source_aws_iam_server_certificate_test.go create mode 100644 website/source/docs/providers/aws/d/iam_server_certificate.html.markdown diff --git a/builtin/providers/aws/data_source_aws_iam_server_certificate.go b/builtin/providers/aws/data_source_aws_iam_server_certificate.go new file mode 100644 index 0000000000..f4257a0d71 --- /dev/null +++ b/builtin/providers/aws/data_source_aws_iam_server_certificate.go @@ -0,0 +1,134 @@ +package aws + +import ( + "fmt" + "sort" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" +) + +func dataSourceAwsIAMServerCertificate() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsIAMServerCertificateRead, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name_prefix"}, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) > 128 { + errors = append(errors, fmt.Errorf( + "%q cannot be longer than 128 characters", k)) + } + return + }, + }, + + "name_prefix": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) > 30 { + errors = append(errors, fmt.Errorf( + "%q cannot be longer than 30 characters, name is limited to 128", k)) + } + return + }, + }, + + "latest": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: false, + }, + + "arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "path": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "expiration_date": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +type certificateByExpiration []*iam.ServerCertificateMetadata + +func (m certificateByExpiration) Len() int { + return len(m) +} + +func (m certificateByExpiration) Swap(i, j int) { + m[i], m[j] = m[j], m[i] +} + +func (m certificateByExpiration) Less(i, j int) bool { + return m[i].Expiration.After(*m[j].Expiration) +} + +func dataSourceAwsIAMServerCertificateRead(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + var matcher = func(cert *iam.ServerCertificateMetadata) bool { + return strings.HasPrefix(aws.StringValue(cert.ServerCertificateName), d.Get("name_prefix").(string)) + } + if v, ok := d.GetOk("name"); ok { + matcher = func(cert *iam.ServerCertificateMetadata) bool { + return aws.StringValue(cert.ServerCertificateName) == v.(string) + } + } + + var metadatas = []*iam.ServerCertificateMetadata{} + err := iamconn.ListServerCertificatesPages(&iam.ListServerCertificatesInput{}, func(p *iam.ListServerCertificatesOutput, lastPage bool) bool { + for _, cert := range p.ServerCertificateMetadataList { + if matcher(cert) { + metadatas = append(metadatas, cert) + } + } + return true + }) + if err != nil { + return errwrap.Wrapf("Error describing certificates: {{err}}", err) + } + + if len(metadatas) == 0 { + return fmt.Errorf("Search for AWS IAM server certificate returned no results") + } + if len(metadatas) > 1 { + if !d.Get("latest").(bool) { + return fmt.Errorf("Search for AWS IAM server certificate returned too many results") + } + + sort.Sort(certificateByExpiration(metadatas)) + } + + metadata := metadatas[0] + d.SetId(*metadata.ServerCertificateId) + d.Set("arn", *metadata.Arn) + d.Set("path", *metadata.Path) + d.Set("name", *metadata.ServerCertificateName) + if metadata.Expiration != nil { + d.Set("expiration_date", metadata.Expiration.Format("2006-01-02T15:04:05")) + } + + return nil +} diff --git a/builtin/providers/aws/data_source_aws_iam_server_certificate_test.go b/builtin/providers/aws/data_source_aws_iam_server_certificate_test.go new file mode 100644 index 0000000000..af845508df --- /dev/null +++ b/builtin/providers/aws/data_source_aws_iam_server_certificate_test.go @@ -0,0 +1,63 @@ +package aws + +import ( + "fmt" + "sort" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/terraform/helper/resource" +) + +func timePtr(t time.Time) *time.Time { + return &t +} + +func TestResourceSortByExpirationDate(t *testing.T) { + certs := []*iam.ServerCertificateMetadata{ + &iam.ServerCertificateMetadata{ + ServerCertificateName: aws.String("oldest"), + Expiration: timePtr(time.Now()), + }, + &iam.ServerCertificateMetadata{ + ServerCertificateName: aws.String("latest"), + Expiration: timePtr(time.Now().Add(3 * time.Hour)), + }, + &iam.ServerCertificateMetadata{ + ServerCertificateName: aws.String("in between"), + Expiration: timePtr(time.Now().Add(2 * time.Hour)), + }, + } + sort.Sort(certificateByExpiration(certs)) + if *certs[0].ServerCertificateName != "latest" { + t.Fatalf("Expected first item to be %q, but was %q", "latest", *certs[0].ServerCertificateName) + } +} + +func TestAccAWSDataSourceIAMServerCertificate_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckIAMServerCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsDataIAMServerCertConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("aws_iam_server_certificate.test_cert", "arn"), + resource.TestCheckResourceAttrSet("data.aws_iam_server_certificate.test", "arn"), + resource.TestCheckResourceAttrSet("data.aws_iam_server_certificate.test", "name"), + resource.TestCheckResourceAttrSet("data.aws_iam_server_certificate.test", "path"), + ), + }, + }, + }) +} + +var testAccAwsDataIAMServerCertConfig = fmt.Sprintf(`%s +data "aws_iam_server_certificate" "test" { + name = "${aws_iam_server_certificate.test_cert.name}" + latest = true +} +`, testAccIAMServerCertConfig) diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 642e979fd1..7b7aaabd5f 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -157,6 +157,7 @@ func Provider() terraform.ResourceProvider { "aws_ecs_container_definition": dataSourceAwsEcsContainerDefinition(), "aws_elb_service_account": dataSourceAwsElbServiceAccount(), "aws_iam_policy_document": dataSourceAwsIamPolicyDocument(), + "aws_iam_server_certificate": dataSourceAwsIAMServerCertificate(), "aws_ip_ranges": dataSourceAwsIPRanges(), "aws_prefix_list": dataSourceAwsPrefixList(), "aws_redshift_service_account": dataSourceAwsRedshiftServiceAccount(), diff --git a/website/source/docs/providers/aws/d/iam_server_certificate.html.markdown b/website/source/docs/providers/aws/d/iam_server_certificate.html.markdown new file mode 100644 index 0000000000..0a4ce493e1 --- /dev/null +++ b/website/source/docs/providers/aws/d/iam_server_certificate.html.markdown @@ -0,0 +1,45 @@ +--- +layout: "aws" +page_title: "AWS: aws_iam_server_certificate" +sidebar_current: "docs-aws-iam-server-certificate" +description: |- + Get information about a server certificate +--- + +# aws\_iam\_server\_certificate + +Use this data source to lookup information about IAM Server Certificates. + +## Example Usage + +``` +data "aws_iam_server_certificate" "my-domain" { + name_prefix = "my-domain.org" + latest = true +} + +resource "aws_elb" "elb" { + name = "my-domain-elb" + + + listener { + instance_port = 8000 + instance_protocol = "https" + lb_port = 443 + lb_protocol = "https" + ssl_certificate_id = "${data.aws_iam_server_certificate.my-domain.arn}" + } +} +``` + +## Argument Reference + +* `name_prefix` - prefix of cert to filter by +* `name` - exact name of the cert to lookup +* `latest` - sort results by expiration date. returns the certificate with expiration date in furthest in the future. + +## Attributes Reference + +`arn` is set to the ARN of the IAM Server Certificate +`path` is set to the path of the IAM Server Certificate +`expiration_date` is set to the expiration date of the IAM Server Certificate