diff --git a/.changelog/6443.txt b/.changelog/6443.txt new file mode 100644 index 0000000000..4f9dd03d5b --- /dev/null +++ b/.changelog/6443.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +compute: added `certificate_map` to `compute_target_ssl_proxy` resource +``` diff --git a/google-beta/resource_compute_target_ssl_proxy.go b/google-beta/resource_compute_target_ssl_proxy.go index 236121295d..04e69a1679 100644 --- a/google-beta/resource_compute_target_ssl_proxy.go +++ b/google-beta/resource_compute_target_ssl_proxy.go @@ -59,16 +59,13 @@ first character must be a lowercase letter, and all following characters must be a dash, lowercase letter, or digit, except the last character, which cannot be a dash.`, }, - "ssl_certificates": { - Type: schema.TypeList, - Required: true, - Description: `A list of SslCertificate resources that are used to authenticate -connections between users and the load balancer. At least one -SSL certificate must be specified.`, - Elem: &schema.Schema{ - Type: schema.TypeString, - DiffSuppressFunc: compareSelfLinkOrResourceName, - }, + "certificate_map": { + Type: schema.TypeString, + Optional: true, + Description: `A reference to the CertificateMap resource uri that identifies a certificate map +associated with the given target proxy. This field can only be set for global target proxies. +Accepted format is '//certificatemanager.googleapis.com/projects/{project}/locations/{location}/certificateMaps/{resourceName}'.`, + ExactlyOneOf: []string{"ssl_certificates", "certificate_map"}, }, "description": { Type: schema.TypeString, @@ -84,6 +81,18 @@ SSL certificate must be specified.`, the backend. Default value: "NONE" Possible values: ["NONE", "PROXY_V1"]`, Default: "NONE", }, + "ssl_certificates": { + Type: schema.TypeList, + Optional: true, + Description: `A list of SslCertificate resources that are used to authenticate +connections between users and the load balancer. At least one +SSL certificate must be specified.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, + ExactlyOneOf: []string{"ssl_certificates", "certificate_map"}, + }, "ssl_policy": { Type: schema.TypeString, Optional: true, @@ -155,6 +164,12 @@ func resourceComputeTargetSslProxyCreate(d *schema.ResourceData, meta interface{ } else if v, ok := d.GetOkExists("ssl_certificates"); !isEmptyValue(reflect.ValueOf(sslCertificatesProp)) && (ok || !reflect.DeepEqual(v, sslCertificatesProp)) { obj["sslCertificates"] = sslCertificatesProp } + certificateMapProp, err := expandComputeTargetSslProxyCertificateMap(d.Get("certificate_map"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("certificate_map"); !isEmptyValue(reflect.ValueOf(certificateMapProp)) && (ok || !reflect.DeepEqual(v, certificateMapProp)) { + obj["certificateMap"] = certificateMapProp + } sslPolicyProp, err := expandComputeTargetSslProxySslPolicy(d.Get("ssl_policy"), d, config) if err != nil { return err @@ -263,6 +278,9 @@ func resourceComputeTargetSslProxyRead(d *schema.ResourceData, meta interface{}) if err := d.Set("ssl_certificates", flattenComputeTargetSslProxySslCertificates(res["sslCertificates"], d, config)); err != nil { return fmt.Errorf("Error reading TargetSslProxy: %s", err) } + if err := d.Set("certificate_map", flattenComputeTargetSslProxyCertificateMap(res["certificateMap"], d, config)); err != nil { + return fmt.Errorf("Error reading TargetSslProxy: %s", err) + } if err := d.Set("ssl_policy", flattenComputeTargetSslProxySslPolicy(res["sslPolicy"], d, config)); err != nil { return fmt.Errorf("Error reading TargetSslProxy: %s", err) } @@ -392,6 +410,40 @@ func resourceComputeTargetSslProxyUpdate(d *schema.ResourceData, meta interface{ return err } } + if d.HasChange("certificate_map") { + obj := make(map[string]interface{}) + + certificateMapProp, err := expandComputeTargetSslProxyCertificateMap(d.Get("certificate_map"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("certificate_map"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, certificateMapProp)) { + obj["certificateMap"] = certificateMapProp + } + + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/global/targetSslProxies/{{name}}/setCertificateMap") + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return fmt.Errorf("Error updating TargetSslProxy %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating TargetSslProxy %q: %#v", d.Id(), res) + } + + err = computeOperationWaitTime( + config, res, project, "Updating TargetSslProxy", userAgent, + d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return err + } + } if d.HasChange("ssl_policy") { obj := make(map[string]interface{}) @@ -544,6 +596,10 @@ func flattenComputeTargetSslProxySslCertificates(v interface{}, d *schema.Resour return convertAndMapStringArr(v.([]interface{}), ConvertSelfLinkToV1) } +func flattenComputeTargetSslProxyCertificateMap(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func flattenComputeTargetSslProxySslPolicy(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { return v @@ -587,6 +643,10 @@ func expandComputeTargetSslProxySslCertificates(v interface{}, d TerraformResour return req, nil } +func expandComputeTargetSslProxyCertificateMap(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandComputeTargetSslProxySslPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { f, err := parseGlobalFieldValue("sslPolicies", v.(string), "project", d, config, true) if err != nil { diff --git a/google-beta/resource_compute_target_ssl_proxy_test.go b/google-beta/resource_compute_target_ssl_proxy_test.go index 8bc8ee59ec..2f881b6544 100644 --- a/google-beta/resource_compute_target_ssl_proxy_test.go +++ b/google-beta/resource_compute_target_ssl_proxy_test.go @@ -6,6 +6,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + compute "google.golang.org/api/compute/v0.beta" ) func TestAccComputeTargetSslProxy_update(t *testing.T) { @@ -17,6 +19,9 @@ func TestAccComputeTargetSslProxy_update(t *testing.T) { backend2 := fmt.Sprintf("tssl-test-%s", randString(t, 10)) hc := fmt.Sprintf("tssl-test-%s", randString(t, 10)) + resourceSuffix := randString(t, 10) + var proxy compute.TargetSslProxy + vcrTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -25,22 +30,42 @@ func TestAccComputeTargetSslProxy_update(t *testing.T) { { Config: testAccComputeTargetSslProxy_basic1(target, sslPolicy, cert1, backend1, hc), Check: resource.ComposeTestCheckFunc( - testAccCheckComputeTargetSslProxy( - t, "google_compute_target_ssl_proxy.foobar", "NONE", cert1), + testAccCheckComputeTargetSslProxyExists( + t, "google_compute_target_ssl_proxy.foobar", &proxy), + testAccCheckComputeTargetSslProxyHeader(t, "NONE", &proxy), + testAccCheckComputeTargetSslProxyHasSslCertificate(t, cert1, &proxy), ), }, { Config: testAccComputeTargetSslProxy_basic2(target, sslPolicy, cert1, cert2, backend1, backend2, hc), Check: resource.ComposeTestCheckFunc( - testAccCheckComputeTargetSslProxy( - t, "google_compute_target_ssl_proxy.foobar", "PROXY_V1", cert2), + testAccCheckComputeTargetSslProxyExists( + t, "google_compute_target_ssl_proxy.foobar", &proxy), + testAccCheckComputeTargetSslProxyHeader(t, "PROXY_V1", &proxy), + testAccCheckComputeTargetSslProxyHasSslCertificate(t, cert2, &proxy), + ), + }, + { + Config: testAccComputeTargetSslProxy_certificateMap1(resourceSuffix), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeTargetSslProxyExists( + t, "google_compute_target_ssl_proxy.with_certificate_map", &proxy), + testAccCheckComputeTargetSslProxyHasCertificateMap(t, "certificatemap-test-1-"+resourceSuffix, &proxy), + ), + }, + { + Config: testAccComputeTargetSslProxy_certificateMap2(resourceSuffix), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeTargetSslProxyExists( + t, "google_compute_target_ssl_proxy.with_certificate_map", &proxy), + testAccCheckComputeTargetSslProxyHasCertificateMap(t, "certificatemap-test-2-"+resourceSuffix, &proxy), ), }, }, }) } -func testAccCheckComputeTargetSslProxy(t *testing.T, n, proxyHeader, sslCert string) resource.TestCheckFunc { +func testAccCheckComputeTargetSslProxyExists(t *testing.T, n string, proxy *compute.TargetSslProxy) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -64,15 +89,44 @@ func testAccCheckComputeTargetSslProxy(t *testing.T, n, proxyHeader, sslCert str return fmt.Errorf("TargetSslProxy not found") } - if found.ProxyHeader != proxyHeader { - return fmt.Errorf("Wrong proxy header. Expected '%s', got '%s'", proxyHeader, found.ProxyHeader) + *proxy = *found + + return nil + } +} + +func testAccCheckComputeTargetSslProxyHeader(t *testing.T, proxyHeader string, proxy *compute.TargetSslProxy) resource.TestCheckFunc { + return func(s *terraform.State) error { + if proxy.ProxyHeader != proxyHeader { + return fmt.Errorf("Wrong proxy header. Expected '%s', got '%s'", proxyHeader, proxy.ProxyHeader) } + return nil + } +} + +func testAccCheckComputeTargetSslProxyHasSslCertificate(t *testing.T, cert string, proxy *compute.TargetSslProxy) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := googleProviderConfig(t) + certURL := fmt.Sprintf(canonicalSslCertificateTemplate, config.Project, cert) - foundCertName := GetResourceNameFromSelfLink(found.SslCertificates[0]) - if foundCertName != sslCert { - return fmt.Errorf("Wrong ssl certificates. Expected '%s', got '%s'", sslCert, foundCertName) + for _, sslCertificate := range proxy.SslCertificates { + if ConvertSelfLinkToV1(sslCertificate) == certURL { + return nil + } } + return fmt.Errorf("Ssl certificate not found: expected'%s'", certURL) + } +} + +func testAccCheckComputeTargetSslProxyHasCertificateMap(t *testing.T, certificateMap string, proxy *compute.TargetSslProxy) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := googleProviderConfig(t) + wantCertMapURL := fmt.Sprintf(canonicalCertificateMapTemplate, config.Project, certificateMap) + gotCertMapURL := ConvertSelfLinkToV1(proxy.CertificateMap) + if wantCertMapURL != gotCertMapURL { + return fmt.Errorf("certificate map not found: got %q, want %q", gotCertMapURL, wantCertMapURL) + } return nil } } @@ -169,3 +223,116 @@ resource "google_compute_health_check" "zero" { } `, target, sslPolicy, sslCert1, sslCert2, backend1, backend2, hc) } + +func testAccComputeTargetSslProxy_certificateMap1(id string) string { + return fmt.Sprintf(` +resource "google_compute_target_ssl_proxy" "with_certificate_map" { + description = "Resource created for Terraform acceptance testing" + name = "ssl-proxy-%s" + backend_service = google_compute_backend_service.foo.self_link + certificate_map = "//certificatemanager.googleapis.com/${google_certificate_manager_certificate_map.map1.id}" +} + +resource "google_compute_backend_service" "foo" { + name = "backend-service-%s" + protocol = "SSL" + health_checks = [google_compute_health_check.zero.self_link] +} + +resource "google_compute_health_check" "zero" { + name = "health-check-%s" + check_interval_sec = 1 + timeout_sec = 1 + tcp_health_check { + port = "443" + } +} + +resource "google_certificate_manager_certificate_map" "map1" { + name = "certificatemap-test-1-%s" +} +resource "google_certificate_manager_certificate_map_entry" "map_entry" { + name = "certificatemapentry-test-%s" + map = google_certificate_manager_certificate_map.map1.name + certificates = [google_certificate_manager_certificate.certificate.id] + matcher = "PRIMARY" +} + +resource "google_certificate_manager_certificate" "certificate" { + name = "certificate-test-%s" + scope = "DEFAULT" + managed { + domains = [ + google_certificate_manager_dns_authorization.instance.domain, + ] + dns_authorizations = [ + google_certificate_manager_dns_authorization.instance.id, + ] + } +} + +resource "google_certificate_manager_dns_authorization" "instance" { + name = "dnsauthorization-test-%s" + domain = "mysite.com" +} +`, id, id, id, id, id, id, id) +} + +func testAccComputeTargetSslProxy_certificateMap2(id string) string { + return fmt.Sprintf(` +resource "google_compute_target_ssl_proxy" "with_certificate_map" { + description = "Resource created for Terraform acceptance testing" + name = "ssl-proxy-%s" + backend_service = google_compute_backend_service.foo.self_link + certificate_map = "//certificatemanager.googleapis.com/${google_certificate_manager_certificate_map.map2.id}" +} + +resource "google_compute_backend_service" "foo" { + name = "backend-service-%s" + protocol = "SSL" + health_checks = [google_compute_health_check.zero.self_link] +} + +resource "google_compute_health_check" "zero" { + name = "health-check-%s" + check_interval_sec = 1 + timeout_sec = 1 + tcp_health_check { + port = "443" + } +} + +resource "google_certificate_manager_certificate_map" "map1" { + name = "certificatemap-test-1-%s" +} + +resource "google_certificate_manager_certificate_map" "map2" { + name = "certificatemap-test-2-%s" +} + +resource "google_certificate_manager_certificate_map_entry" "map_entry" { + name = "certificatemapentry-test-%s" + map = google_certificate_manager_certificate_map.map1.name + certificates = [google_certificate_manager_certificate.certificate.id] + matcher = "PRIMARY" +} + +resource "google_certificate_manager_certificate" "certificate" { + name = "certificate-test-%s" + scope = "DEFAULT" + managed { + domains = [ + google_certificate_manager_dns_authorization.instance.domain, + ] + dns_authorizations = [ + google_certificate_manager_dns_authorization.instance.id, + ] + } +} + +resource "google_certificate_manager_dns_authorization" "instance" { + name = "dnsauthorization-test-%s" + domain = "mysite.com" +} +`, id, id, id, id, id, id, id, id) +} diff --git a/website/docs/r/compute_target_ssl_proxy.html.markdown b/website/docs/r/compute_target_ssl_proxy.html.markdown index 91ab5fb9e2..35f31d9aee 100644 --- a/website/docs/r/compute_target_ssl_proxy.html.markdown +++ b/website/docs/r/compute_target_ssl_proxy.html.markdown @@ -89,12 +89,6 @@ The following arguments are supported: (Required) A reference to the BackendService resource. -* `ssl_certificates` - - (Required) - A list of SslCertificate resources that are used to authenticate - connections between users and the load balancer. At least one - SSL certificate must be specified. - - - - @@ -110,6 +104,18 @@ The following arguments are supported: Default value is `NONE`. Possible values are `NONE` and `PROXY_V1`. +* `ssl_certificates` - + (Optional) + A list of SslCertificate resources that are used to authenticate + connections between users and the load balancer. At least one + SSL certificate must be specified. + +* `certificate_map` - + (Optional) + A reference to the CertificateMap resource uri that identifies a certificate map + associated with the given target proxy. This field can only be set for global target proxies. + Accepted format is `//certificatemanager.googleapis.com/projects/{project}/locations/{location}/certificateMaps/{resourceName}`. + * `ssl_policy` - (Optional) A reference to the SslPolicy resource that will be associated with