From be7ece0ce7f764a1755e6efef7d97c3e0242ccf3 Mon Sep 17 00:00:00 2001 From: Clint Shryock Date: Tue, 26 May 2015 09:52:58 -0500 Subject: [PATCH] provider/aws: Add IAM Server Certificate resource --- builtin/providers/aws/provider.go | 1 + .../resource_aws_iam_server_certificate.go | 147 +++++++++++++ ...esource_aws_iam_server_certificate_test.go | 194 ++++++++++++++++++ .../r/iam_server_certificate.html.markdown | 102 +++++++++ website/source/layouts/aws.erb | 4 + 5 files changed, 448 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_iam_server_certificate.go create mode 100644 builtin/providers/aws/resource_aws_iam_server_certificate_test.go create mode 100644 website/source/docs/providers/aws/r/iam_server_certificate.html.markdown diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index db90549d2..8c90f6898 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -103,6 +103,7 @@ func Provider() terraform.ResourceProvider { "aws_iam_policy": resourceAwsIamPolicy(), "aws_iam_role_policy": resourceAwsIamRolePolicy(), "aws_iam_role": resourceAwsIamRole(), + "aws_iam_server_certificate": resourceAwsIAMServerCertificate(), "aws_iam_user_policy": resourceAwsIamUserPolicy(), "aws_iam_user": resourceAwsIamUser(), "aws_instance": resourceAwsInstance(), diff --git a/builtin/providers/aws/resource_aws_iam_server_certificate.go b/builtin/providers/aws/resource_aws_iam_server_certificate.go new file mode 100644 index 000000000..b1d876337 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_server_certificate.go @@ -0,0 +1,147 @@ +package aws + +import ( + "crypto/sha1" + "encoding/hex" + "fmt" + "strings" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/aws/awserr" + "github.com/awslabs/aws-sdk-go/service/iam" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsIAMServerCertificate() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsIAMServerCertificateCreate, + Read: resourceAwsIAMServerCertificateRead, + Delete: resourceAwsIAMServerCertificateDelete, + + Schema: map[string]*schema.Schema{ + "certificate_body": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + StateFunc: normalizeCert, + }, + + "certificate_chain": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "path": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + + "private_key": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + StateFunc: normalizeCert, + }, + + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "arn": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + } +} + +func resourceAwsIAMServerCertificateCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).iamconn + + createOpts := &iam.UploadServerCertificateInput{ + CertificateBody: aws.String(d.Get("certificate_body").(string)), + PrivateKey: aws.String(d.Get("private_key").(string)), + ServerCertificateName: aws.String(d.Get("name").(string)), + } + + if v, ok := d.GetOk("certificate_chain"); ok { + createOpts.CertificateChain = aws.String(v.(string)) + } + + if v, ok := d.GetOk("Path"); ok { + createOpts.Path = aws.String(v.(string)) + } + + resp, err := conn.UploadServerCertificate(createOpts) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok { + return fmt.Errorf("[WARN] Error uploading server certificate, error: %s: %s", awsErr.Code(), awsErr.Message()) + } + return fmt.Errorf("[WARN] Error uploading server certificate, error: %s", err) + } + + d.SetId(*resp.ServerCertificateMetadata.ServerCertificateID) + + return resourceAwsIAMServerCertificateRead(d, meta) +} + +func resourceAwsIAMServerCertificateRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).iamconn + resp, err := conn.GetServerCertificate(&iam.GetServerCertificateInput{ + ServerCertificateName: aws.String(d.Get("name").(string)), + }) + + if err != nil { + if awsErr, ok := err.(awserr.Error); ok { + return fmt.Errorf("[WARN] Error reading IAM Server Certificate: %s: %s", awsErr.Code(), awsErr.Message()) + } + return fmt.Errorf("[WARN] Error reading IAM Server Certificate: %s", err) + } + + // these values should always be present, and have a default if not set in + // configuration, and so safe to reference with nil checks + d.Set("certificate_body", normalizeCert(resp.ServerCertificate.CertificateBody)) + d.Set("certificate_chain", resp.ServerCertificate.CertificateChain) + d.Set("path", resp.ServerCertificate.ServerCertificateMetadata.Path) + d.Set("arn", resp.ServerCertificate.ServerCertificateMetadata.ARN) + + return nil +} + +func resourceAwsIAMServerCertificateDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).iamconn + _, err := conn.DeleteServerCertificate(&iam.DeleteServerCertificateInput{ + ServerCertificateName: aws.String(d.Get("name").(string)), + }) + + if err != nil { + if awsErr, ok := err.(awserr.Error); ok { + return fmt.Errorf("[WARN] Error deleting server certificate: %s: %s", awsErr.Code(), awsErr.Message()) + } + return err + } + + d.SetId("") + return nil +} + +func normalizeCert(cert interface{}) string { + if cert == nil { + return "" + } + switch cert.(type) { + case string: + hash := sha1.Sum([]byte(strings.TrimSpace(cert.(string)))) + return hex.EncodeToString(hash[:]) + case *string: + hash := sha1.Sum([]byte(strings.TrimSpace(*cert.(*string)))) + return hex.EncodeToString(hash[:]) + default: + return "" + } +} diff --git a/builtin/providers/aws/resource_aws_iam_server_certificate_test.go b/builtin/providers/aws/resource_aws_iam_server_certificate_test.go new file mode 100644 index 000000000..31c9d97f4 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_server_certificate_test.go @@ -0,0 +1,194 @@ +package aws + +import ( + "fmt" + "math/rand" + "strings" + "testing" + "time" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccIAMServerCertificate_basic(t *testing.T) { + var cert iam.ServerCertificate + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckIAMServerCertificateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccIAMServerCertConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert), + testAccCheckAWSServerCertAttributes(&cert), + ), + }, + }, + }) +} + +func testAccCheckCertExists(n string, cert *iam.ServerCertificate) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Server Cert ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).iamconn + describeOpts := &iam.GetServerCertificateInput{ + ServerCertificateName: aws.String(rs.Primary.Attributes["name"]), + } + resp, err := conn.GetServerCertificate(describeOpts) + if err != nil { + return err + } + + *cert = *resp.ServerCertificate + + return nil + } +} + +func testAccCheckAWSServerCertAttributes(cert *iam.ServerCertificate) resource.TestCheckFunc { + return func(s *terraform.State) error { + if !strings.HasPrefix(*cert.ServerCertificateMetadata.ServerCertificateName, "terraform-test-cert") { + return fmt.Errorf("Bad Server Cert Name: %s", *cert.ServerCertificateMetadata.ServerCertificateName) + } + + if *cert.CertificateBody != strings.TrimSpace(certBody) { + return fmt.Errorf("Bad Server Cert body\n\t expected: %s\n\tgot: %s\n", certBody, *cert.CertificateBody) + } + return nil + } +} + +func testAccCheckIAMServerCertificateDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).iamconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_iam_server_certificate" { + continue + } + + // Try to find the Cert + opts := &iam.GetServerCertificateInput{ + ServerCertificateName: aws.String(rs.Primary.Attributes["name"]), + } + resp, err := conn.GetServerCertificate(opts) + if err == nil { + if resp.ServerCertificate != nil { + return fmt.Errorf("Error: Server Cert still exists") + } + + return nil + } + + } + + return nil +} + +var certBody = fmt.Sprintf(` +-----BEGIN CERTIFICATE----- +MIIExDCCA6ygAwIBAgIJALX7Jt7ddT3eMA0GCSqGSIb3DQEBBQUAMIGcMQswCQYD +VQQGEwJVUzERMA8GA1UECBMITWlzc291cmkxETAPBgNVBAcTCENvbHVtYmlhMRIw +EAYDVQQKEwlIYXNoaUNvcnAxEjAQBgNVBAsTCVRlcnJhZm9ybTEbMBkGA1UEAxMS +d3d3Lm5vdGV4YW1wbGUuY29tMSIwIAYJKoZIhvcNAQkBFhNjbGludEBoYXNoaWNv +cnAuY29tMB4XDTE1MDUyNjE0MzA1MloXDTE4MDUyNTE0MzA1MlowgZwxCzAJBgNV +BAYTAlVTMREwDwYDVQQIEwhNaXNzb3VyaTERMA8GA1UEBxMIQ29sdW1iaWExEjAQ +BgNVBAoTCUhhc2hpQ29ycDESMBAGA1UECxMJVGVycmFmb3JtMRswGQYDVQQDExJ3 +d3cubm90ZXhhbXBsZS5jb20xIjAgBgkqhkiG9w0BCQEWE2NsaW50QGhhc2hpY29y +cC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCownyOIKXBbYxh +PynVAw30eaJj2OmilFJagwGeFHMT0rErCodY8lAQsPz6gj83NC9D4MzDt1H+GmoR +MSDphJEUxxTxvaNWTTN5sZ9WvE+sbw5YkkTXc4DmVsVMoa3urQO20f0tcHXyULj0 +sXbtG+q/QhKxqeFjYON46Z6l7x32d/cj4mIcXwLpIf+W2wpvXCKAc8851skJ+O9W +UW0/h/ivwwkKfzGfiObL16IUaq+fxwnkYt3fUI2Z4rSKAULMEcquzfKr3JR6wkeI +J66ZSb6fMNlCPGPcINDhzwSgGRpqRqeuRl4Z9m2fZaaYVltHqjwDH1tKr+3qXFnv +nZmq7pzJAgMBAAGjggEFMIIBATAdBgNVHQ4EFgQUO8bEvPq+V/rtnlhTxQDusR7o +n6QwgdEGA1UdIwSByTCBxoAUO8bEvPq+V/rtnlhTxQDusR7on6ShgaKkgZ8wgZwx +CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNaXNzb3VyaTERMA8GA1UEBxMIQ29sdW1i +aWExEjAQBgNVBAoTCUhhc2hpQ29ycDESMBAGA1UECxMJVGVycmFmb3JtMRswGQYD +VQQDExJ3d3cubm90ZXhhbXBsZS5jb20xIjAgBgkqhkiG9w0BCQEWE2NsaW50QGhh +c2hpY29ycC5jb22CCQC1+ybe3XU93jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB +BQUAA4IBAQBsJ/NP1uBYm+8ejrpUu2mipT5JfBahpiUxef5BeubSrSM3zmdrtLLA ++DdDkrt0AfOaasBXMTEwrR3NunBAmn/6PX0r/PAjlqk/tOVBnASC9t3cmi88fO10 +gQw+se86MiCr/hTavq2YTQZ652+ksjxeQwyHIzKrYS/rRGPKKHX70H5Asb1CY44p +/GRyLvAckzZ1Gp64ym6XCLTS53wOur6wLX1/lqshBo2utUmm/2a/XF4psSDx/k2J +E2oHzGoJ2F/+QkiXHzvPcUXRFVhXkQnZDocCv/nhcEwNkN9Z1OxCNqsZw+FiJm2E +FVSdVaOstOHOVllblhWxvjm55a44feFX +-----END CERTIFICATE-----`) + +var testAccIAMServerCertConfig = fmt.Sprintf(` +resource "aws_iam_server_certificate" "test_cert" { + name = "terraform-test-cert-%d" + certificate_body = <aws_iam_role_policy + > + aws_iam_server_certificate + + > aws_iam_user