diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 234a9c90e..81dbc1fe6 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -219,6 +219,9 @@ func Provider() terraform.ResourceProvider { "aws_lambda_permission": resourceAwsLambdaPermission(), "aws_launch_configuration": resourceAwsLaunchConfiguration(), "aws_lb_cookie_stickiness_policy": resourceAwsLBCookieStickinessPolicy(), + "aws_load_balancer_policy": resourceAwsLoadBalancerPolicy(), + "aws_load_balancer_backend_server_policy": resourceAwsLoadBalancerBackendServerPolicies(), + "aws_load_balancer_listener_policy": resourceAwsLoadBalancerListenerPolicies(), "aws_main_route_table_association": resourceAwsMainRouteTableAssociation(), "aws_nat_gateway": resourceAwsNatGateway(), "aws_network_acl": resourceAwsNetworkAcl(), diff --git a/builtin/providers/aws/resource_aws_load_balancer_backend_server_policy.go b/builtin/providers/aws/resource_aws_load_balancer_backend_server_policy.go new file mode 100644 index 000000000..325c4fd1a --- /dev/null +++ b/builtin/providers/aws/resource_aws_load_balancer_backend_server_policy.go @@ -0,0 +1,138 @@ +package aws + +import ( + "fmt" + "strconv" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/elb" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsLoadBalancerBackendServerPolicies() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsLoadBalancerBackendServerPoliciesCreate, + Read: resourceAwsLoadBalancerBackendServerPoliciesRead, + Update: resourceAwsLoadBalancerBackendServerPoliciesCreate, + Delete: resourceAwsLoadBalancerBackendServerPoliciesDelete, + + Schema: map[string]*schema.Schema{ + "load_balancer_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "policy_names": &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Set: schema.HashString, + }, + + "instance_port": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + }, + }, + } +} + +func resourceAwsLoadBalancerBackendServerPoliciesCreate(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbconn + + loadBalancerName := d.Get("load_balancer_name") + + policyNames := []*string{} + if v, ok := d.GetOk("policy_names"); ok { + policyNames = expandStringList(v.(*schema.Set).List()) + } + + setOpts := &elb.SetLoadBalancerPoliciesForBackendServerInput{ + LoadBalancerName: aws.String(loadBalancerName.(string)), + InstancePort: aws.Int64(int64(d.Get("instance_port").(int))), + PolicyNames: policyNames, + } + + if _, err := elbconn.SetLoadBalancerPoliciesForBackendServer(setOpts); err != nil { + return fmt.Errorf("Error setting LoadBalancerPoliciesForBackendServer: %s", err) + } + + d.SetId(fmt.Sprintf("%s:%s", *setOpts.LoadBalancerName, strconv.FormatInt(*setOpts.InstancePort, 10))) + return resourceAwsLoadBalancerBackendServerPoliciesRead(d, meta) +} + +func resourceAwsLoadBalancerBackendServerPoliciesRead(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbconn + + loadBalancerName, instancePort := resourceAwsLoadBalancerBackendServerPoliciesParseId(d.Id()) + + describeElbOpts := &elb.DescribeLoadBalancersInput{ + LoadBalancerNames: []*string{aws.String(loadBalancerName)}, + } + + describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts) + + if err != nil { + if ec2err, ok := err.(awserr.Error); ok { + if ec2err.Code() == "LoadBalancerNotFound" { + d.SetId("") + return fmt.Errorf("LoadBalancerNotFound: %s", err) + } + } + return fmt.Errorf("Error retrieving ELB description: %s", err) + } + + if len(describeResp.LoadBalancerDescriptions) != 1 { + return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions) + } + + lb := describeResp.LoadBalancerDescriptions[0] + + policyNames := []*string{} + for _, backendServer := range lb.BackendServerDescriptions { + if instancePort != strconv.Itoa(int(*backendServer.InstancePort)) { + continue + } + + for _, name := range backendServer.PolicyNames { + policyNames = append(policyNames, name) + } + } + + d.Set("load_balancer_name", loadBalancerName) + d.Set("instance_port", instancePort) + d.Set("policy_names", flattenStringList(policyNames)) + + return nil +} + +func resourceAwsLoadBalancerBackendServerPoliciesDelete(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbconn + + loadBalancerName, instancePort := resourceAwsLoadBalancerBackendServerPoliciesParseId(d.Id()) + + instancePortInt, err := strconv.ParseInt(instancePort, 10, 64) + if err != nil { + return fmt.Errorf("Error parsing instancePort as integer: %s", err) + } + + setOpts := &elb.SetLoadBalancerPoliciesForBackendServerInput{ + LoadBalancerName: aws.String(loadBalancerName), + InstancePort: aws.Int64(instancePortInt), + PolicyNames: []*string{}, + } + + if _, err := elbconn.SetLoadBalancerPoliciesForBackendServer(setOpts); err != nil { + return fmt.Errorf("Error setting LoadBalancerPoliciesForBackendServer: %s", err) + } + + d.SetId("") + return nil +} + +func resourceAwsLoadBalancerBackendServerPoliciesParseId(id string) (string, string) { + parts := strings.SplitN(id, ":", 2) + return parts[0], parts[1] +} diff --git a/builtin/providers/aws/resource_aws_load_balancer_backend_server_policy_test.go b/builtin/providers/aws/resource_aws_load_balancer_backend_server_policy_test.go new file mode 100644 index 000000000..b52be936d --- /dev/null +++ b/builtin/providers/aws/resource_aws_load_balancer_backend_server_policy_test.go @@ -0,0 +1,388 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/elb" + + tlsprovider "github.com/hashicorp/terraform/builtin/providers/tls" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSLoadBalancerBackendServerPolicy_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: map[string]terraform.ResourceProvider{ + "aws": testAccProvider, + "tls": tlsprovider.Provider(), + }, + CheckDestroy: testAccCheckAWSLoadBalancerBackendServerPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSLoadBalancerBackendServerPolicyConfig_basic0, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.test-pubkey-policy0"), + testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.test-backend-auth-policy0"), + testAccCheckAWSLoadBalancerBackendServerPolicyState("test-aws-policies-lb", "test-backend-auth-policy0", true), + ), + }, + resource.TestStep{ + Config: testAccAWSLoadBalancerBackendServerPolicyConfig_basic1, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.test-pubkey-policy0"), + testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.test-pubkey-policy1"), + testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.test-backend-auth-policy0"), + testAccCheckAWSLoadBalancerBackendServerPolicyState("test-aws-policies-lb", "test-backend-auth-policy0", true), + ), + }, + resource.TestStep{ + Config: testAccAWSLoadBalancerBackendServerPolicyConfig_basic2, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLoadBalancerBackendServerPolicyState("test-aws-policies-lb", "test-backend-auth-policy0", false), + ), + }, + }, + }) +} + +func policyInBackendServerPolicies(str string, list []string) bool { + for _, v := range list { + if v == str { + return true + } + } + return false +} + +func testAccCheckAWSLoadBalancerBackendServerPolicyDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).elbconn + + for _, rs := range s.RootModule().Resources { + switch { + case rs.Type == "aws_load_balancer_policy": + loadBalancerName, policyName := resourceAwsLoadBalancerBackendServerPoliciesParseId(rs.Primary.ID) + out, err := conn.DescribeLoadBalancerPolicies( + &elb.DescribeLoadBalancerPoliciesInput{ + LoadBalancerName: aws.String(loadBalancerName), + PolicyNames: []*string{aws.String(policyName)}, + }) + if err != nil { + if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") { + continue + } + return err + } + if len(out.PolicyDescriptions) > 0 { + return fmt.Errorf("Policy still exists") + } + case rs.Type == "aws_load_balancer_backend_policy": + loadBalancerName, policyName := resourceAwsLoadBalancerBackendServerPoliciesParseId(rs.Primary.ID) + out, err := conn.DescribeLoadBalancers( + &elb.DescribeLoadBalancersInput{ + LoadBalancerNames: []*string{aws.String(loadBalancerName)}, + }) + if err != nil { + if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "LoadBalancerNotFound") { + continue + } + return err + } + for _, backendServer := range out.LoadBalancerDescriptions[0].BackendServerDescriptions { + policyStrings := []string{} + for _, pol := range backendServer.PolicyNames { + policyStrings = append(policyStrings, *pol) + } + if policyInBackendServerPolicies(policyName, policyStrings) { + return fmt.Errorf("Policy still exists and is assigned") + } + } + default: + continue + } + } + return nil +} + +func testAccCheckAWSLoadBalancerBackendServerPolicyState(loadBalancerName string, loadBalancerBackendAuthPolicyName string, assigned bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + elbconn := testAccProvider.Meta().(*AWSClient).elbconn + + loadBalancerDescription, err := elbconn.DescribeLoadBalancers(&elb.DescribeLoadBalancersInput{ + LoadBalancerNames: []*string{aws.String(loadBalancerName)}, + }) + if err != nil { + return err + } + + for _, backendServer := range loadBalancerDescription.LoadBalancerDescriptions[0].BackendServerDescriptions { + policyStrings := []string{} + for _, pol := range backendServer.PolicyNames { + policyStrings = append(policyStrings, *pol) + } + if policyInBackendServerPolicies(loadBalancerBackendAuthPolicyName, policyStrings) != assigned { + if assigned { + return fmt.Errorf("Policy no longer assigned %s not in %+v", loadBalancerBackendAuthPolicyName, policyStrings) + } else { + return fmt.Errorf("Policy exists and is assigned") + } + } + } + + return nil + } +} + +const testAccAWSLoadBalancerBackendServerPolicyConfig_basic0 = ` +resource "tls_private_key" "example0" { + algorithm = "RSA" +} + +resource "tls_self_signed_cert" "test-cert0" { + key_algorithm = "RSA" + private_key_pem = "${tls_private_key.example0.private_key_pem}" + + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } + + validity_period_hours = 12 + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] +} + +resource "aws_iam_server_certificate" "test-iam-cert0" { + name_prefix = "test_cert_" + certificate_body = "${tls_self_signed_cert.test-cert0.cert_pem}" + private_key = "${tls_private_key.example0.private_key_pem}" +} + +resource "aws_elb" "test-lb" { + name = "test-aws-policies-lb" + availability_zones = ["us-west-2a"] + + listener { + instance_port = 443 + instance_protocol = "https" + lb_port = 443 + lb_protocol = "https" + ssl_certificate_id = "${aws_iam_server_certificate.test-iam-cert0.arn}" + } + + tags { + Name = "tf-acc-test" + } +} + +resource "aws_load_balancer_policy" "test-pubkey-policy0" { + load_balancer_name = "${aws_elb.test-lb.name}" + policy_name = "test-pubkey-policy0" + policy_type_name = "PublicKeyPolicyType" + policy_attribute = { + name = "PublicKey" + value = "${replace(replace(replace(tls_private_key.example0.public_key_pem, "\n", ""), "-----BEGIN PUBLIC KEY-----", ""), "-----END PUBLIC KEY-----", "")}" + } +} + +resource "aws_load_balancer_policy" "test-backend-auth-policy0" { + load_balancer_name = "${aws_elb.test-lb.name}" + policy_name = "test-backend-auth-policy0" + policy_type_name = "BackendServerAuthenticationPolicyType" + policy_attribute = { + name = "PublicKeyPolicyName" + value = "${aws_load_balancer_policy.test-pubkey-policy0.policy_name}" + } +} + +resource "aws_load_balancer_backend_server_policy" "test-backend-auth-policies-443" { + load_balancer_name = "${aws_elb.test-lb.name}" + instance_port = 443 + policy_names = [ + "${aws_load_balancer_policy.test-backend-auth-policy0.policy_name}" + ] +} +` + +const testAccAWSLoadBalancerBackendServerPolicyConfig_basic1 = ` +resource "tls_private_key" "example0" { + algorithm = "RSA" +} + +resource "tls_self_signed_cert" "test-cert0" { + key_algorithm = "RSA" + private_key_pem = "${tls_private_key.example0.private_key_pem}" + + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } + + validity_period_hours = 12 + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] +} + +resource "tls_private_key" "example1" { + algorithm = "RSA" +} + +resource "tls_self_signed_cert" "test-cert1" { + key_algorithm = "RSA" + private_key_pem = "${tls_private_key.example1.private_key_pem}" + + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } + + validity_period_hours = 12 + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] +} + +resource "aws_iam_server_certificate" "test-iam-cert0" { + name_prefix = "test_cert_" + certificate_body = "${tls_self_signed_cert.test-cert0.cert_pem}" + private_key = "${tls_private_key.example0.private_key_pem}" +} + +resource "aws_elb" "test-lb" { + name = "test-aws-policies-lb" + availability_zones = ["us-west-2a"] + + listener { + instance_port = 443 + instance_protocol = "https" + lb_port = 443 + lb_protocol = "https" + ssl_certificate_id = "${aws_iam_server_certificate.test-iam-cert0.arn}" + } + + tags { + Name = "tf-acc-test" + } +} + +resource "aws_load_balancer_policy" "test-pubkey-policy0" { + load_balancer_name = "${aws_elb.test-lb.name}" + policy_name = "test-pubkey-policy0" + policy_type_name = "PublicKeyPolicyType" + policy_attribute = { + name = "PublicKey" + value = "${replace(replace(replace(tls_private_key.example0.public_key_pem, "\n", ""), "-----BEGIN PUBLIC KEY-----", ""), "-----END PUBLIC KEY-----", "")}" + } +} + +resource "aws_load_balancer_policy" "test-pubkey-policy1" { + load_balancer_name = "${aws_elb.test-lb.name}" + policy_name = "test-pubkey-policy1" + policy_type_name = "PublicKeyPolicyType" + policy_attribute = { + name = "PublicKey" + value = "${replace(replace(replace(tls_private_key.example1.public_key_pem, "\n", ""), "-----BEGIN PUBLIC KEY-----", ""), "-----END PUBLIC KEY-----", "")}" + } +} + +resource "aws_load_balancer_policy" "test-backend-auth-policy0" { + load_balancer_name = "${aws_elb.test-lb.name}" + policy_name = "test-backend-auth-policy0" + policy_type_name = "BackendServerAuthenticationPolicyType" + policy_attribute = { + name = "PublicKeyPolicyName" + value = "${aws_load_balancer_policy.test-pubkey-policy1.policy_name}" + } +} + +resource "aws_load_balancer_backend_server_policy" "test-backend-auth-policies-443" { + load_balancer_name = "${aws_elb.test-lb.name}" + instance_port = 443 + policy_names = [ + "${aws_load_balancer_policy.test-backend-auth-policy0.policy_name}" + ] +} +` + +const testAccAWSLoadBalancerBackendServerPolicyConfig_basic2 = ` +resource "tls_private_key" "example0" { + algorithm = "RSA" +} + +resource "tls_self_signed_cert" "test-cert0" { + key_algorithm = "RSA" + private_key_pem = "${tls_private_key.example0.private_key_pem}" + + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } + + validity_period_hours = 12 + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] +} + +resource "tls_private_key" "example1" { + algorithm = "RSA" +} + +resource "tls_self_signed_cert" "test-cert1" { + key_algorithm = "RSA" + private_key_pem = "${tls_private_key.example1.private_key_pem}" + + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } + + validity_period_hours = 12 + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] +} + +resource "aws_iam_server_certificate" "test-iam-cert0" { + name_prefix = "test_cert_" + certificate_body = "${tls_self_signed_cert.test-cert0.cert_pem}" + private_key = "${tls_private_key.example0.private_key_pem}" +} + +resource "aws_elb" "test-lb" { + name = "test-aws-policies-lb" + availability_zones = ["us-west-2a"] + + listener { + instance_port = 443 + instance_protocol = "https" + lb_port = 443 + lb_protocol = "https" + ssl_certificate_id = "${aws_iam_server_certificate.test-iam-cert0.arn}" + } + + tags { + Name = "tf-acc-test" + } +} +` diff --git a/builtin/providers/aws/resource_aws_load_balancer_listener_policy.go b/builtin/providers/aws/resource_aws_load_balancer_listener_policy.go new file mode 100644 index 000000000..d1c8cacbb --- /dev/null +++ b/builtin/providers/aws/resource_aws_load_balancer_listener_policy.go @@ -0,0 +1,138 @@ +package aws + +import ( + "fmt" + "strconv" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/elb" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsLoadBalancerListenerPolicies() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsLoadBalancerListenerPoliciesCreate, + Read: resourceAwsLoadBalancerListenerPoliciesRead, + Update: resourceAwsLoadBalancerListenerPoliciesCreate, + Delete: resourceAwsLoadBalancerListenerPoliciesDelete, + + Schema: map[string]*schema.Schema{ + "load_balancer_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "policy_names": &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Set: schema.HashString, + }, + + "load_balancer_port": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + }, + }, + } +} + +func resourceAwsLoadBalancerListenerPoliciesCreate(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbconn + + loadBalancerName := d.Get("load_balancer_name") + + policyNames := []*string{} + if v, ok := d.GetOk("policy_names"); ok { + policyNames = expandStringList(v.(*schema.Set).List()) + } + + setOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{ + LoadBalancerName: aws.String(loadBalancerName.(string)), + LoadBalancerPort: aws.Int64(int64(d.Get("load_balancer_port").(int))), + PolicyNames: policyNames, + } + + if _, err := elbconn.SetLoadBalancerPoliciesOfListener(setOpts); err != nil { + return fmt.Errorf("Error setting LoadBalancerPoliciesOfListener: %s", err) + } + + d.SetId(fmt.Sprintf("%s:%s", *setOpts.LoadBalancerName, strconv.FormatInt(*setOpts.LoadBalancerPort, 10))) + return resourceAwsLoadBalancerListenerPoliciesRead(d, meta) +} + +func resourceAwsLoadBalancerListenerPoliciesRead(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbconn + + loadBalancerName, loadBalancerPort := resourceAwsLoadBalancerListenerPoliciesParseId(d.Id()) + + describeElbOpts := &elb.DescribeLoadBalancersInput{ + LoadBalancerNames: []*string{aws.String(loadBalancerName)}, + } + + describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts) + + if err != nil { + if ec2err, ok := err.(awserr.Error); ok { + if ec2err.Code() == "LoadBalancerNotFound" { + d.SetId("") + return fmt.Errorf("LoadBalancerNotFound: %s", err) + } + } + return fmt.Errorf("Error retrieving ELB description: %s", err) + } + + if len(describeResp.LoadBalancerDescriptions) != 1 { + return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions) + } + + lb := describeResp.LoadBalancerDescriptions[0] + + policyNames := []*string{} + for _, listener := range lb.ListenerDescriptions { + if loadBalancerPort != strconv.Itoa(int(*listener.Listener.LoadBalancerPort)) { + continue + } + + for _, name := range listener.PolicyNames { + policyNames = append(policyNames, name) + } + } + + d.Set("load_balancer_name", loadBalancerName) + d.Set("load_balancer_port", loadBalancerPort) + d.Set("policy_names", flattenStringList(policyNames)) + + return nil +} + +func resourceAwsLoadBalancerListenerPoliciesDelete(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbconn + + loadBalancerName, loadBalancerPort := resourceAwsLoadBalancerListenerPoliciesParseId(d.Id()) + + loadBalancerPortInt, err := strconv.ParseInt(loadBalancerPort, 10, 64) + if err != nil { + return fmt.Errorf("Error parsing loadBalancerPort as integer: %s", err) + } + + setOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{ + LoadBalancerName: aws.String(loadBalancerName), + LoadBalancerPort: aws.Int64(loadBalancerPortInt), + PolicyNames: []*string{}, + } + + if _, err := elbconn.SetLoadBalancerPoliciesOfListener(setOpts); err != nil { + return fmt.Errorf("Error setting LoadBalancerPoliciesOfListener: %s", err) + } + + d.SetId("") + return nil +} + +func resourceAwsLoadBalancerListenerPoliciesParseId(id string) (string, string) { + parts := strings.SplitN(id, ":", 2) + return parts[0], parts[1] +} diff --git a/builtin/providers/aws/resource_aws_load_balancer_listener_policy_test.go b/builtin/providers/aws/resource_aws_load_balancer_listener_policy_test.go new file mode 100644 index 000000000..b8f9816a8 --- /dev/null +++ b/builtin/providers/aws/resource_aws_load_balancer_listener_policy_test.go @@ -0,0 +1,233 @@ +package aws + +import ( + "fmt" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/elb" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSLoadBalancerListenerPolicy_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLoadBalancerListenerPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSLoadBalancerListenerPolicyConfig_basic0, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.magic-cookie-sticky"), + testAccCheckAWSLoadBalancerListenerPolicyState("test-aws-policies-lb", int64(80), "magic-cookie-sticky-policy", true), + ), + }, + resource.TestStep{ + Config: testAccAWSLoadBalancerListenerPolicyConfig_basic1, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.magic-cookie-sticky"), + testAccCheckAWSLoadBalancerListenerPolicyState("test-aws-policies-lb", int64(80), "magic-cookie-sticky-policy", true), + ), + }, + resource.TestStep{ + Config: testAccAWSLoadBalancerListenerPolicyConfig_basic2, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLoadBalancerListenerPolicyState("test-aws-policies-lb", int64(80), "magic-cookie-sticky-policy", false), + ), + }, + }, + }) +} + +func policyInListenerPolicies(str string, list []string) bool { + for _, v := range list { + if v == str { + return true + } + } + return false +} + +func testAccCheckAWSLoadBalancerListenerPolicyDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).elbconn + + for _, rs := range s.RootModule().Resources { + switch { + case rs.Type == "aws_load_balancer_policy": + loadBalancerName, policyName := resourceAwsLoadBalancerListenerPoliciesParseId(rs.Primary.ID) + out, err := conn.DescribeLoadBalancerPolicies( + &elb.DescribeLoadBalancerPoliciesInput{ + LoadBalancerName: aws.String(loadBalancerName), + PolicyNames: []*string{aws.String(policyName)}, + }) + if err != nil { + if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") { + continue + } + return err + } + if len(out.PolicyDescriptions) > 0 { + return fmt.Errorf("Policy still exists") + } + case rs.Type == "aws_load_listener_policy": + loadBalancerName, _ := resourceAwsLoadBalancerListenerPoliciesParseId(rs.Primary.ID) + out, err := conn.DescribeLoadBalancers( + &elb.DescribeLoadBalancersInput{ + LoadBalancerNames: []*string{aws.String(loadBalancerName)}, + }) + if err != nil { + if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "LoadBalancerNotFound") { + continue + } + return err + } + policyNames := []string{} + for k, _ := range rs.Primary.Attributes { + if strings.HasPrefix(k, "policy_names.") && strings.HasSuffix(k, ".name") { + value_key := fmt.Sprintf("%s.value", strings.TrimSuffix(k, ".name")) + policyNames = append(policyNames, rs.Primary.Attributes[value_key]) + } + } + for _, policyName := range policyNames { + for _, listener := range out.LoadBalancerDescriptions[0].ListenerDescriptions { + policyStrings := []string{} + for _, pol := range listener.PolicyNames { + policyStrings = append(policyStrings, *pol) + } + if policyInListenerPolicies(policyName, policyStrings) { + return fmt.Errorf("Policy still exists and is assigned") + } + } + } + default: + continue + } + } + return nil +} + +func testAccCheckAWSLoadBalancerListenerPolicyState(loadBalancerName string, loadBalancerListenerPort int64, loadBalancerListenerPolicyName string, assigned bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + elbconn := testAccProvider.Meta().(*AWSClient).elbconn + + loadBalancerDescription, err := elbconn.DescribeLoadBalancers(&elb.DescribeLoadBalancersInput{ + LoadBalancerNames: []*string{aws.String(loadBalancerName)}, + }) + if err != nil { + return err + } + + for _, listener := range loadBalancerDescription.LoadBalancerDescriptions[0].ListenerDescriptions { + if *listener.Listener.LoadBalancerPort != loadBalancerListenerPort { + continue + } + policyStrings := []string{} + for _, pol := range listener.PolicyNames { + policyStrings = append(policyStrings, *pol) + } + if policyInListenerPolicies(loadBalancerListenerPolicyName, policyStrings) != assigned { + if assigned { + return fmt.Errorf("Policy no longer assigned %s not in %+v", loadBalancerListenerPolicyName, policyStrings) + } else { + return fmt.Errorf("Policy exists and is assigned") + } + } + } + + return nil + } +} + +const testAccAWSLoadBalancerListenerPolicyConfig_basic0 = ` +resource "aws_elb" "test-lb" { + name = "test-aws-policies-lb" + availability_zones = ["us-west-2a"] + + listener { + instance_port = 80 + instance_protocol = "http" + lb_port = 80 + lb_protocol = "http" + } + + tags { + Name = "tf-acc-test" + } +} + +resource "aws_load_balancer_policy" "magic-cookie-sticky" { + load_balancer_name = "${aws_elb.test-lb.name}" + policy_name = "magic-cookie-sticky-policy" + policy_type_name = "AppCookieStickinessPolicyType" + policy_attribute = { + name = "CookieName" + value = "magic_cookie" + } +} + +resource "aws_load_balancer_listener_policy" "test-lb-listener-policies-80" { + load_balancer_name = "${aws_elb.test-lb.name}" + load_balancer_port = 80 + policy_names = [ + "${aws_load_balancer_policy.magic-cookie-sticky.policy_name}", + ] +} +` + +const testAccAWSLoadBalancerListenerPolicyConfig_basic1 = ` +resource "aws_elb" "test-lb" { + name = "test-aws-policies-lb" + availability_zones = ["us-west-2a"] + + listener { + instance_port = 80 + instance_protocol = "http" + lb_port = 80 + lb_protocol = "http" + } + + tags { + Name = "tf-acc-test" + } +} + +resource "aws_load_balancer_policy" "magic-cookie-sticky" { + load_balancer_name = "${aws_elb.test-lb.name}" + policy_name = "magic-cookie-sticky-policy" + policy_type_name = "AppCookieStickinessPolicyType" + policy_attribute = { + name = "CookieName" + value = "unicorn_cookie" + } +} + +resource "aws_load_balancer_listener_policy" "test-lb-listener-policies-80" { + load_balancer_name = "${aws_elb.test-lb.name}" + load_balancer_port = 80 + policy_names = [ + "${aws_load_balancer_policy.magic-cookie-sticky.policy_name}" + ] +} +` + +const testAccAWSLoadBalancerListenerPolicyConfig_basic2 = ` +resource "aws_elb" "test-lb" { + name = "test-aws-policies-lb" + availability_zones = ["us-west-2a"] + + listener { + instance_port = 80 + instance_protocol = "http" + lb_port = 80 + lb_protocol = "http" + } + + tags { + Name = "tf-acc-test" + } +} +` diff --git a/builtin/providers/aws/resource_aws_load_balancer_policy.go b/builtin/providers/aws/resource_aws_load_balancer_policy.go new file mode 100644 index 000000000..8305cf992 --- /dev/null +++ b/builtin/providers/aws/resource_aws_load_balancer_policy.go @@ -0,0 +1,352 @@ +package aws + +import ( + "fmt" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/elb" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsLoadBalancerPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsLoadBalancerPolicyCreate, + Read: resourceAwsLoadBalancerPolicyRead, + Update: resourceAwsLoadBalancerPolicyUpdate, + Delete: resourceAwsLoadBalancerPolicyDelete, + + Schema: map[string]*schema.Schema{ + "load_balancer_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "policy_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "policy_type_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "policy_attribute": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "value": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + } +} + +func resourceAwsLoadBalancerPolicyCreate(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbconn + + attributes := []*elb.PolicyAttribute{} + if attributedata, ok := d.GetOk("policy_attribute"); ok { + attributeSet := attributedata.(*schema.Set).List() + for _, attribute := range attributeSet { + data := attribute.(map[string]interface{}) + attributes = append(attributes, &elb.PolicyAttribute{ + AttributeName: aws.String(data["name"].(string)), + AttributeValue: aws.String(data["value"].(string)), + }) + } + } + + lbspOpts := &elb.CreateLoadBalancerPolicyInput{ + LoadBalancerName: aws.String(d.Get("load_balancer_name").(string)), + PolicyName: aws.String(d.Get("policy_name").(string)), + PolicyTypeName: aws.String(d.Get("policy_type_name").(string)), + PolicyAttributes: attributes, + } + + if _, err := elbconn.CreateLoadBalancerPolicy(lbspOpts); err != nil { + return fmt.Errorf("Error creating LoadBalancerPolicy: %s", err) + } + + d.SetId(fmt.Sprintf("%s:%s", + *lbspOpts.LoadBalancerName, + *lbspOpts.PolicyName)) + return resourceAwsLoadBalancerPolicyRead(d, meta) +} + +func resourceAwsLoadBalancerPolicyRead(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbconn + + loadBalancerName, policyName := resourceAwsLoadBalancerPolicyParseId(d.Id()) + + request := &elb.DescribeLoadBalancerPoliciesInput{ + LoadBalancerName: aws.String(loadBalancerName), + PolicyNames: []*string{aws.String(policyName)}, + } + + getResp, err := elbconn.DescribeLoadBalancerPolicies(request) + if err != nil { + if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "PolicyNotFound" { + d.SetId("") + return nil + } + return fmt.Errorf("Error retrieving policy: %s", err) + } + + if len(getResp.PolicyDescriptions) != 1 { + return fmt.Errorf("Unable to find policy %#v", getResp.PolicyDescriptions) + } + + policyDesc := getResp.PolicyDescriptions[0] + policyTypeName := policyDesc.PolicyTypeName + policyAttributes := policyDesc.PolicyAttributeDescriptions + + attributes := []map[string]string{} + for _, a := range policyAttributes { + pair := make(map[string]string) + pair["name"] = *a.AttributeName + pair["value"] = *a.AttributeValue + if (*policyTypeName == "SSLNegotiationPolicyType") && (*a.AttributeValue == "false") { + continue + } + attributes = append(attributes, pair) + } + + d.Set("policy_name", policyName) + d.Set("policy_type_name", policyTypeName) + d.Set("load_balancer_name", loadBalancerName) + d.Set("policy_attribute", attributes) + + return nil +} + +func resourceAwsLoadBalancerPolicyUpdate(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbconn + reassignments := Reassignment{} + + loadBalancerName, policyName := resourceAwsLoadBalancerPolicyParseId(d.Id()) + + assigned, err := resourceAwsLoadBalancerPolicyAssigned(policyName, loadBalancerName, elbconn) + if err != nil { + return fmt.Errorf("Error determining assignment status of Load Balancer Policy %s: %s", policyName, err) + } + + if assigned { + reassignments, err = resourceAwsLoadBalancerPolicyUnassign(policyName, loadBalancerName, elbconn) + if err != nil { + return fmt.Errorf("Error unassigning Load Balancer Policy %s: %s", policyName, err) + } + } + + request := &elb.DeleteLoadBalancerPolicyInput{ + LoadBalancerName: aws.String(loadBalancerName), + PolicyName: aws.String(policyName), + } + + if _, err := elbconn.DeleteLoadBalancerPolicy(request); err != nil { + return fmt.Errorf("Error deleting Load Balancer Policy %s: %s", d.Id(), err) + } + + err = resourceAwsLoadBalancerPolicyCreate(d, meta) + + for _, listenerAssignment := range reassignments.listenerPolicies { + if _, err := elbconn.SetLoadBalancerPoliciesOfListener(listenerAssignment); err != nil { + return fmt.Errorf("Error setting LoadBalancerPoliciesOfListener: %s", err) + } + } + + for _, backendServerAssignment := range reassignments.backendServerPolicies { + if _, err := elbconn.SetLoadBalancerPoliciesForBackendServer(backendServerAssignment); err != nil { + return fmt.Errorf("Error setting LoadBalancerPoliciesForBackendServer: %s", err) + } + } + + return resourceAwsLoadBalancerPolicyRead(d, meta) +} + +func resourceAwsLoadBalancerPolicyDelete(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbconn + + loadBalancerName, policyName := resourceAwsLoadBalancerPolicyParseId(d.Id()) + + assigned, err := resourceAwsLoadBalancerPolicyAssigned(policyName, loadBalancerName, elbconn) + if err != nil { + return fmt.Errorf("Error determining assignment status of Load Balancer Policy %s: %s", policyName, err) + } + + if assigned { + _, err := resourceAwsLoadBalancerPolicyUnassign(policyName, loadBalancerName, elbconn) + if err != nil { + return fmt.Errorf("Error unassigning Load Balancer Policy %s: %s", policyName, err) + } + } + + request := &elb.DeleteLoadBalancerPolicyInput{ + LoadBalancerName: aws.String(loadBalancerName), + PolicyName: aws.String(policyName), + } + + if _, err := elbconn.DeleteLoadBalancerPolicy(request); err != nil { + return fmt.Errorf("Error deleting Load Balancer Policy %s: %s", d.Id(), err) + } + + d.SetId("") + return nil +} + +func resourceAwsLoadBalancerPolicyParseId(id string) (string, string) { + parts := strings.SplitN(id, ":", 2) + return parts[0], parts[1] +} + +func resourceAwsLoadBalancerPolicyAssigned(policyName, loadBalancerName string, elbconn *elb.ELB) (bool, error) { + describeElbOpts := &elb.DescribeLoadBalancersInput{ + LoadBalancerNames: []*string{aws.String(loadBalancerName)}, + } + + describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts) + + if err != nil { + if ec2err, ok := err.(awserr.Error); ok { + if ec2err.Code() == "LoadBalancerNotFound" { + return false, nil + } + } + return false, fmt.Errorf("Error retrieving ELB description: %s", err) + } + + if len(describeResp.LoadBalancerDescriptions) != 1 { + return false, fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions) + } + + lb := describeResp.LoadBalancerDescriptions[0] + assigned := false + for _, backendServer := range lb.BackendServerDescriptions { + for _, name := range backendServer.PolicyNames { + if policyName == *name { + assigned = true + break + } + } + } + + for _, listener := range lb.ListenerDescriptions { + for _, name := range listener.PolicyNames { + if policyName == *name { + assigned = true + break + } + } + } + + return assigned, nil +} + +type Reassignment struct { + backendServerPolicies []*elb.SetLoadBalancerPoliciesForBackendServerInput + listenerPolicies []*elb.SetLoadBalancerPoliciesOfListenerInput +} + +func resourceAwsLoadBalancerPolicyUnassign(policyName, loadBalancerName string, elbconn *elb.ELB) (Reassignment, error) { + reassignments := Reassignment{} + + describeElbOpts := &elb.DescribeLoadBalancersInput{ + LoadBalancerNames: []*string{aws.String(loadBalancerName)}, + } + + describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts) + + if err != nil { + if ec2err, ok := err.(awserr.Error); ok { + if ec2err.Code() == "LoadBalancerNotFound" { + return reassignments, nil + } + } + return reassignments, fmt.Errorf("Error retrieving ELB description: %s", err) + } + + if len(describeResp.LoadBalancerDescriptions) != 1 { + return reassignments, fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions) + } + + lb := describeResp.LoadBalancerDescriptions[0] + + for _, backendServer := range lb.BackendServerDescriptions { + policies := []*string{} + + for _, name := range backendServer.PolicyNames { + if policyName != *name { + policies = append(policies, name) + } + } + + if len(backendServer.PolicyNames) != len(policies) { + setOpts := &elb.SetLoadBalancerPoliciesForBackendServerInput{ + LoadBalancerName: aws.String(loadBalancerName), + InstancePort: aws.Int64(*backendServer.InstancePort), + PolicyNames: policies, + } + + reassignOpts := &elb.SetLoadBalancerPoliciesForBackendServerInput{ + LoadBalancerName: aws.String(loadBalancerName), + InstancePort: aws.Int64(*backendServer.InstancePort), + PolicyNames: backendServer.PolicyNames, + } + + reassignments.backendServerPolicies = append(reassignments.backendServerPolicies, reassignOpts) + + _, err = elbconn.SetLoadBalancerPoliciesForBackendServer(setOpts) + if err != nil { + return reassignments, fmt.Errorf("Error Setting Load Balancer Policies for Backend Server: %s", err) + } + } + } + + for _, listener := range lb.ListenerDescriptions { + policies := []*string{} + + for _, name := range listener.PolicyNames { + if policyName != *name { + policies = append(policies, name) + } + } + + if len(listener.PolicyNames) != len(policies) { + setOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{ + LoadBalancerName: aws.String(loadBalancerName), + LoadBalancerPort: aws.Int64(*listener.Listener.LoadBalancerPort), + PolicyNames: policies, + } + + reassignOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{ + LoadBalancerName: aws.String(loadBalancerName), + LoadBalancerPort: aws.Int64(*listener.Listener.LoadBalancerPort), + PolicyNames: listener.PolicyNames, + } + + reassignments.listenerPolicies = append(reassignments.listenerPolicies, reassignOpts) + + _, err = elbconn.SetLoadBalancerPoliciesOfListener(setOpts) + if err != nil { + return reassignments, fmt.Errorf("Error Setting Load Balancer Policies of Listener: %s", err) + } + } + } + + return reassignments, nil +} diff --git a/builtin/providers/aws/resource_aws_load_balancer_policy_test.go b/builtin/providers/aws/resource_aws_load_balancer_policy_test.go new file mode 100644 index 000000000..9fe60cd96 --- /dev/null +++ b/builtin/providers/aws/resource_aws_load_balancer_policy_test.go @@ -0,0 +1,240 @@ +package aws + +import ( + "fmt" + "strconv" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/elb" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSLoadBalancerPolicy_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLoadBalancerPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSLoadBalancerPolicyConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.test-policy"), + ), + }, + }, + }) +} + +func TestAccAWSLoadBalancerPolicy_updateWhileAssigned(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLoadBalancerPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSLoadBalancerPolicyConfig_updateWhileAssigned0, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.test-policy"), + ), + }, + resource.TestStep{ + Config: testAccAWSLoadBalancerPolicyConfig_updateWhileAssigned1, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.test-policy"), + ), + }, + }, + }) +} + +func testAccCheckAWSLoadBalancerPolicyDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).elbconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_load_balancer_policy" { + continue + } + + loadBalancerName, policyName := resourceAwsLoadBalancerPolicyParseId(rs.Primary.ID) + out, err := conn.DescribeLoadBalancerPolicies( + &elb.DescribeLoadBalancerPoliciesInput{ + LoadBalancerName: aws.String(loadBalancerName), + PolicyNames: []*string{aws.String(policyName)}, + }) + if err != nil { + if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") { + continue + } + return err + } + + if len(out.PolicyDescriptions) > 0 { + return fmt.Errorf("Policy still exists") + } + } + return nil +} + +func testAccCheckAWSLoadBalancerPolicyState(elbResource string, policyResource string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[elbResource] + if !ok { + return fmt.Errorf("Not found: %s", elbResource) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + policy, ok := s.RootModule().Resources[policyResource] + if !ok { + return fmt.Errorf("Not found: %s", policyResource) + } + + elbconn := testAccProvider.Meta().(*AWSClient).elbconn + loadBalancerName, policyName := resourceAwsLoadBalancerPolicyParseId(policy.Primary.ID) + loadBalancerPolicies, err := elbconn.DescribeLoadBalancerPolicies(&elb.DescribeLoadBalancerPoliciesInput{ + LoadBalancerName: aws.String(loadBalancerName), + PolicyNames: []*string{aws.String(policyName)}, + }) + + if err != nil { + return err + } + + for _, loadBalancerPolicy := range loadBalancerPolicies.PolicyDescriptions { + if *loadBalancerPolicy.PolicyName == policyName { + if *loadBalancerPolicy.PolicyTypeName != policy.Primary.Attributes["policy_type_name"] { + return fmt.Errorf("PolicyTypeName does not match") + } + policyAttributeCount, err := strconv.Atoi(policy.Primary.Attributes["policy_attribute.#"]) + if err != nil { + return err + } + if len(loadBalancerPolicy.PolicyAttributeDescriptions) != policyAttributeCount { + return fmt.Errorf("PolicyAttributeDescriptions length mismatch") + } + policyAttributes := make(map[string]string) + for k, v := range policy.Primary.Attributes { + if strings.HasPrefix(k, "policy_attribute.") && strings.HasSuffix(k, ".name") { + key := v + value_key := fmt.Sprintf("%s.value", strings.TrimSuffix(k, ".name")) + policyAttributes[key] = policy.Primary.Attributes[value_key] + } + } + for _, policyAttribute := range loadBalancerPolicy.PolicyAttributeDescriptions { + if *policyAttribute.AttributeValue != policyAttributes[*policyAttribute.AttributeName] { + return fmt.Errorf("PollicyAttribute Value mismatch %s != %s: %s", *policyAttribute.AttributeValue, policyAttributes[*policyAttribute.AttributeName], policyAttributes) + } + } + } + } + + return nil + } +} + +const testAccAWSLoadBalancerPolicyConfig_basic = ` +resource "aws_elb" "test-lb" { + name = "test-aws-policies-lb" + availability_zones = ["us-west-2a"] + + listener { + instance_port = 80 + instance_protocol = "http" + lb_port = 80 + lb_protocol = "http" + } + + tags { + Name = "tf-acc-test" + } +} + +resource "aws_load_balancer_policy" "test-policy" { + load_balancer_name = "${aws_elb.test-lb.name}" + policy_name = "test-policy-policy" + policy_type_name = "AppCookieStickinessPolicyType" + policy_attribute = { + name = "CookieName" + value = "magic_cookie" + } +} +` + +const testAccAWSLoadBalancerPolicyConfig_updateWhileAssigned0 = ` +resource "aws_elb" "test-lb" { + name = "test-aws-policies-lb" + availability_zones = ["us-west-2a"] + + listener { + instance_port = 80 + instance_protocol = "http" + lb_port = 80 + lb_protocol = "http" + } + + tags { + Name = "tf-acc-test" + } +} + +resource "aws_load_balancer_policy" "test-policy" { + load_balancer_name = "${aws_elb.test-lb.name}" + policy_name = "test-policy-policy" + policy_type_name = "AppCookieStickinessPolicyType" + policy_attribute = { + name = "CookieName" + value = "magic_cookie" + } +} + +resource "aws_load_balancer_listener_policy" "test-lb-test-policy-80" { + load_balancer_name = "${aws_elb.test-lb.name}" + load_balancer_port = 80 + policy_names = [ + "${aws_load_balancer_policy.test-policy.policy_name}" + ] +} +` + +const testAccAWSLoadBalancerPolicyConfig_updateWhileAssigned1 = ` +resource "aws_elb" "test-lb" { + name = "test-aws-policies-lb" + availability_zones = ["us-west-2a"] + + listener { + instance_port = 80 + instance_protocol = "http" + lb_port = 80 + lb_protocol = "http" + } + + tags { + Name = "tf-acc-test" + } +} + +resource "aws_load_balancer_policy" "test-policy" { + load_balancer_name = "${aws_elb.test-lb.name}" + policy_name = "test-policy-policy" + policy_type_name = "AppCookieStickinessPolicyType" + policy_attribute = { + name = "CookieName" + value = "unicorn_cookie" + } +} + +resource "aws_load_balancer_listener_policy" "test-lb-test-policy-80" { + load_balancer_name = "${aws_elb.test-lb.name}" + load_balancer_port = 80 + policy_names = [ + "${aws_load_balancer_policy.test-policy.policy_name}" + ] +} +` diff --git a/builtin/providers/aws/resource_aws_rds_cluster_instance.go b/builtin/providers/aws/resource_aws_rds_cluster_instance.go index 0ea5f13a2..72914b14d 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_instance.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_instance.go @@ -97,6 +97,18 @@ func resourceAwsRDSClusterInstance() *schema.Resource { ForceNew: true, }, + "monitoring_role_arn": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "monitoring_interval": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 0, + }, + "tags": tagsSchema(), }, } @@ -128,6 +140,14 @@ func resourceAwsRDSClusterInstanceCreate(d *schema.ResourceData, meta interface{ createOpts.DBSubnetGroupName = aws.String(attr.(string)) } + if attr, ok := d.GetOk("monitoring_role_arn"); ok { + createOpts.MonitoringRoleArn = aws.String(attr.(string)) + } + + if attr, ok := d.GetOk("monitoring_interval"); ok { + createOpts.MonitoringInterval = aws.Int64(int64(attr.(int))) + } + log.Printf("[DEBUG] Creating RDS DB Instance opts: %s", createOpts) resp, err := conn.CreateDBInstance(createOpts) if err != nil { @@ -207,6 +227,14 @@ func resourceAwsRDSClusterInstanceRead(d *schema.ResourceData, meta interface{}) d.Set("identifier", db.DBInstanceIdentifier) d.Set("storage_encrypted", db.StorageEncrypted) + if db.MonitoringInterval != nil { + d.Set("monitoring_interval", db.MonitoringInterval) + } + + if db.MonitoringRoleArn != nil { + d.Set("monitoring_role_arn", db.MonitoringRoleArn) + } + if len(db.DBParameterGroups) > 0 { d.Set("db_parameter_group_name", db.DBParameterGroups[0].DBParameterGroupName) } @@ -245,6 +273,18 @@ func resourceAwsRDSClusterInstanceUpdate(d *schema.ResourceData, meta interface{ } + if d.HasChange("monitoring_role_arn") { + d.SetPartial("monitoring_role_arn") + req.MonitoringRoleArn = aws.String(d.Get("monitoring_role_arn").(string)) + requestUpdate = true + } + + if d.HasChange("monitoring_interval") { + d.SetPartial("monitoring_interval") + req.MonitoringInterval = aws.Int64(int64(d.Get("monitoring_interval").(int))) + requestUpdate = true + } + log.Printf("[DEBUG] Send DB Instance Modification request: %#v", requestUpdate) if requestUpdate { log.Printf("[DEBUG] DB Instance Modification request: %#v", req) diff --git a/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go b/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go index 81e79f488..212fab1e5 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go @@ -187,6 +187,25 @@ func testAccCheckAWSClusterInstanceExists(n string, v *rds.DBInstance) resource. } } +func TestAccAWSCluster_withInstanceEnhancedMonitor(t *testing.T) { + var v rds.DBInstance + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSClusterDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSClusterInstanceEnhancedMonitor(acctest.RandInt()), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSClusterInstanceExists("aws_rds_cluster_instance.cluster_instances", &v), + testAccCheckAWSDBClusterInstanceAttributes(&v), + ), + }, + }, + }) +} + // Add some random to the name, to avoid collision func testAccAWSClusterInstanceConfig(n int) string { return fmt.Sprintf(` @@ -281,3 +300,64 @@ resource "aws_db_parameter_group" "bar" { } `, n, n, n, n) } + +func testAccAWSClusterInstanceEnhancedMonitor(n int) string { + return fmt.Sprintf(` +resource "aws_rds_cluster" "default" { + cluster_identifier = "tf-aurora-cluster-test-%d" + availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"] + database_name = "mydb" + master_username = "foo" + master_password = "mustbeeightcharaters" +} + +resource "aws_rds_cluster_instance" "cluster_instances" { + identifier = "tf-cluster-instance-%d" + cluster_identifier = "${aws_rds_cluster.default.id}" + instance_class = "db.r3.large" + db_parameter_group_name = "${aws_db_parameter_group.bar.name}" + monitoring_interval = "60" + monitoring_role_arn = "${aws_iam_role.tf_enhanced_monitor_role.arn}" +} + +resource "aws_iam_role" "tf_enhanced_monitor_role" { + name = "tf_enhanced_monitor_role-%d" + assume_role_policy = < wu-tang-pubkey +``` + +This example shows how to enable backend authentication for an ELB as well as customize the TLS settings. + +## Argument Reference + +The following arguments are supported: + +* `load_balancer_name` - (Required) The load balancer to attach the policy to. +* `policy_names` - (Required) List of Policy Names to apply to the backend server. +* `instance_port` - (Required) The instance port to apply the policy to. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the policy. +* `load_balancer_name` - The load balancer on which the policy is defined. +* `instance_port` - The backend port the policies are applied to diff --git a/website/source/docs/providers/aws/r/load_balancer_listener_policy.html.markdown b/website/source/docs/providers/aws/r/load_balancer_listener_policy.html.markdown new file mode 100644 index 000000000..5248ee6e2 --- /dev/null +++ b/website/source/docs/providers/aws/r/load_balancer_listener_policy.html.markdown @@ -0,0 +1,73 @@ +--- +layout: "aws" +page_title: "AWS: aws_load_balancer_listener_policy" +sidebar_current: "docs-aws-resource-load-balancer-listener-policy" +description: |- + Attaches a load balancer policy to an ELB Listener. +--- + +# aws\_elb\_load\_balancer\_listener\_policy + +Attaches a load balancer policy to an ELB Listener. + + +## Example Usage + +``` +resource "aws_elb" "wu-tang" { + name = "wu-tang" + availability_zones = ["us-east-1a"] + + listener { + instance_port = 443 + instance_protocol = "http" + lb_port = 443 + lb_protocol = "https" + ssl_certificate_id = "arn:aws:iam::000000000000:server-certificate/wu-tang.net" + } + + tags { + Name = "wu-tang" + } +} + +resource "aws_load_balancer_policy" "wu-tang-ssl" { + load_balancer_name = "${aws_elb.wu-tang.name}" + policy_name = "wu-tang-ssl" + policy_type_name = "SSLNegotiationPolicyType" + policy_attribute = { + name = "ECDHE-ECDSA-AES128-GCM-SHA256" + value = "true" + } + policy_attribute = { + name = "Protocol-TLSv1.2" + value = "true" + } +} + +resource "aws_load_balancer_listener_policy" "wu-tang-listener-policies-443" { + load_balancer_name = "${aws_elb.wu-tang.name}" + load_balancer_port = 443 + policy_names = [ + "${aws_load_balancer_policy.wu-tang-ssl.policy_name}" + ] +} +``` + +This example shows how to customize the TLS settings of an HTTPS listener. + +## Argument Reference + +The following arguments are supported: + +* `load_balancer_name` - (Required) The load balancer to attach the policy to. +* `load_balancer_port` - (Required) The load balancer listener port to apply the policy to. +* `policy_names` - (Required) List of Policy Names to apply to the backend server. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the policy. +* `load_balancer_name` - The load balancer on which the policy is defined. +* `load_balancer_port` - The load balancer listener port the policies are applied to diff --git a/website/source/docs/providers/aws/r/load_balancer_policy.html.markdown b/website/source/docs/providers/aws/r/load_balancer_policy.html.markdown new file mode 100644 index 000000000..04978aaef --- /dev/null +++ b/website/source/docs/providers/aws/r/load_balancer_policy.html.markdown @@ -0,0 +1,108 @@ +--- +layout: "aws" +page_title: "AWS: aws_load_balancer_policy" +sidebar_current: "docs-aws-resource-load-balancer-policy" +description: |- + Provides a load balancer policy, which can be attached to an ELB listener or backend server. +--- + +# aws\_elb\_load\_balancer\_policy + +Provides a load balancer policy, which can be attached to an ELB listener or backend server. + +## Example Usage + +``` +resource "aws_elb" "wu-tang" { + name = "wu-tang" + availability_zones = ["us-east-1a"] + + listener { + instance_port = 443 + instance_protocol = "http" + lb_port = 443 + lb_protocol = "https" + ssl_certificate_id = "arn:aws:iam::000000000000:server-certificate/wu-tang.net" + } + + tags { + Name = "wu-tang" + } +} + +resource "aws_load_balancer_policy" "wu-tang-ca-pubkey-policy" { + load_balancer_name = "${aws_elb.wu-tang.name}" + policy_name = "wu-tang-ca-pubkey-policy" + policy_type_name = "PublicKeyPolicyType" + policy_attribute = { + name = "PublicKey" + value = "${file("wu-tang-pubkey")}" + } +} + +resource "aws_load_balancer_policy" "wu-tang-root-ca-backend-auth-policy" { + load_balancer_name = "${aws_elb.wu-tang.name}" + policy_name = "wu-tang-root-ca-backend-auth-policy" + policy_type_name = "BackendServerAuthenticationPolicyType" + policy_attribute = { + name = "PublicKeyPolicyName" + value = "${aws_load_balancer_policy.wu-tang-root-ca-pubkey-policy.policy_name}" + } +} + +resource "aws_load_balancer_policy" "wu-tang-ssl" { + load_balancer_name = "${aws_elb.wu-tang.name}" + policy_name = "wu-tang-ssl" + policy_type_name = "SSLNegotiationPolicyType" + policy_attribute = { + name = "ECDHE-ECDSA-AES128-GCM-SHA256" + value = "true" + } + policy_attribute = { + name = "Protocol-TLSv1.2" + value = "true" + } +} + +resource "aws_load_balancer_backend_server_policy" "wu-tang-backend-auth-policies-443" { + load_balancer_name = "${aws_elb.wu-tang.name}" + instance_port = 443 + policy_names = [ + "${aws_load_balancer_policy.wu-tang-root-ca-backend-auth-policy.policy_name}" + ] +} + +resource "aws_load_balancer_listener_policy" "wu-tang-listener-policies-443" { + load_balancer_name = "${aws_elb.wu-tang.name}" + load_balancer_port = 443 + policy_names = [ + "${aws_load_balancer_policy.wu-tang-ssl.policy_name}" + ] +} +``` + +Where the file `pubkey` in the current directoy contains only the _public key_ of the certificate. + +``` +cat wu-tang-ca.pem | openssl x509 -pubkey -noout | grep -v '\-\-\-\-' | tr -d '\n' > wu-tang-pubkey +``` + +This example shows how to enable backend authentication for an ELB as well as customize the TLS settings. + +## Argument Reference + +The following arguments are supported: + +* `load_balancer_name` - (Required) The load balancer on which the policy is defined. +* `policy_name` - (Required) The name of the load balancer policy. +* `policy_type_name` - (Required) The policy type. +* `policy_attribute` - (Optional) Policy attribute to apply to the policy. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the policy. +* `policy_name` - The name of the stickiness policy. +* `policy_type_name` - The policy type of the policy. +* `load_balancer_name` - The load balancer on which the policy is defined. diff --git a/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown b/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown index 79fca6445..1a3dba1fd 100644 --- a/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown +++ b/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown @@ -66,6 +66,10 @@ details on controlling this property. * `apply_immediately` - (Optional) Specifies whether any database modifications are applied immediately, or during the next maintenance window. Default is`false`. * `storage_encrypted` - (Optional) Specifies whether the DB cluster instance is encrypted. The default is `false` if not specified. +* `monitoring_role_arn` - (Optional) The ARN for the IAM role that permits RDS to send +enhanced monitoring metrics to CloudWatch Logs. You can find more information on the [AWS Documentation](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Monitoring.html) +what IAM permissions are needed to allow Enhanced Monitoring for RDS Instances. +* `monitoring_interval` - (Optional) The interval, in seconds, between points when Enhanced Monitoring metrics are collected for the DB instance. To disable collecting Enhanced Monitoring metrics, specify 0. The default is 0. Valid Values: 0, 1, 5, 10, 15, 30, 60. * `kms_key_id` - (Optional) The ARN for the KMS encryption key. When specifying `kms_key_id`, `storage_encrypted` needs to be set to true * `tags` - (Optional) A mapping of tags to assign to the instance.