Skip to content

Commit 0c66bb4

Browse files
modular-magicianSarahFrench
authored andcommitted
Add ceritificateManagerCertificates field to ComputeRegionTargetHttpsProxy resource (#10011) (#17365)
[upstream:1470d00e90e813f69d09aa9f55589884cbc27900] Signed-off-by: Modular Magician <[email protected]>
1 parent 0c837d4 commit 0c66bb4

4 files changed

+232
-18
lines changed

.changelog/10011.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
compute: added `certificate_manager_certificates` field to `google_compute_region_target_https_proxy` resource
3+
```

google/services/compute/resource_compute_region_target_https_proxy.go

+120-12
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"fmt"
2222
"log"
2323
"reflect"
24+
"regexp"
25+
"strings"
2426
"time"
2527

2628
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
@@ -64,24 +66,26 @@ first character must be a lowercase letter, and all following
6466
characters must be a dash, lowercase letter, or digit, except the last
6567
character, which cannot be a dash.`,
6668
},
67-
"ssl_certificates": {
68-
Type: schema.TypeList,
69-
Required: true,
70-
Description: `A list of RegionSslCertificate resources that are used to authenticate
71-
connections between users and the load balancer. Currently, exactly
72-
one SSL certificate must be specified.`,
73-
Elem: &schema.Schema{
74-
Type: schema.TypeString,
75-
DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName,
76-
},
77-
},
7869
"url_map": {
7970
Type: schema.TypeString,
8071
Required: true,
8172
DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName,
8273
Description: `A reference to the RegionUrlMap resource that defines the mapping from URL
8374
to the RegionBackendService.`,
8475
},
76+
"certificate_manager_certificates": {
77+
Type: schema.TypeList,
78+
Optional: true,
79+
DiffSuppressFunc: tpgresource.CompareResourceNames,
80+
Description: `URLs to certificate manager certificate resources that are used to authenticate connections between users and the load balancer.
81+
Currently, you may specify up to 15 certificates. Certificate manager certificates do not apply when the load balancing scheme is set to INTERNAL_SELF_MANAGED.
82+
sslCertificates and certificateManagerCertificates fields can not be defined together.
83+
Accepted format is '//certificatemanager.googleapis.com/projects/{project}/locations/{location}/certificates/{resourceName}' or just the self_link 'projects/{project}/locations/{location}/certificates/{resourceName}'`,
84+
Elem: &schema.Schema{
85+
Type: schema.TypeString,
86+
},
87+
ConflictsWith: []string{"ssl_certificates"},
88+
},
8589
"description": {
8690
Type: schema.TypeString,
8791
Optional: true,
@@ -97,6 +101,18 @@ to the RegionBackendService.`,
97101
Description: `The Region in which the created target https proxy should reside.
98102
If it is not provided, the provider region is used.`,
99103
},
104+
"ssl_certificates": {
105+
Type: schema.TypeList,
106+
Optional: true,
107+
Description: `URLs to SslCertificate resources that are used to authenticate connections between users and the load balancer.
108+
At least one SSL certificate must be specified. Currently, you may specify up to 15 SSL certificates.
109+
sslCertificates do not apply when the load balancing scheme is set to INTERNAL_SELF_MANAGED.`,
110+
Elem: &schema.Schema{
111+
Type: schema.TypeString,
112+
DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName,
113+
},
114+
ConflictsWith: []string{"certificate_manager_certificates"},
115+
},
100116
"ssl_policy": {
101117
Type: schema.TypeString,
102118
Optional: true,
@@ -151,6 +167,12 @@ func resourceComputeRegionTargetHttpsProxyCreate(d *schema.ResourceData, meta in
151167
} else if v, ok := d.GetOkExists("name"); !tpgresource.IsEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
152168
obj["name"] = nameProp
153169
}
170+
certificateManagerCertificatesProp, err := expandComputeRegionTargetHttpsProxyCertificateManagerCertificates(d.Get("certificate_manager_certificates"), d, config)
171+
if err != nil {
172+
return err
173+
} else if v, ok := d.GetOkExists("certificate_manager_certificates"); !tpgresource.IsEmptyValue(reflect.ValueOf(certificateManagerCertificatesProp)) && (ok || !reflect.DeepEqual(v, certificateManagerCertificatesProp)) {
174+
obj["certificateManagerCertificates"] = certificateManagerCertificatesProp
175+
}
154176
sslCertificatesProp, err := expandComputeRegionTargetHttpsProxySslCertificates(d.Get("ssl_certificates"), d, config)
155177
if err != nil {
156178
return err
@@ -176,6 +198,11 @@ func resourceComputeRegionTargetHttpsProxyCreate(d *schema.ResourceData, meta in
176198
obj["region"] = regionProp
177199
}
178200

201+
obj, err = resourceComputeRegionTargetHttpsProxyEncoder(d, meta, obj)
202+
if err != nil {
203+
return err
204+
}
205+
179206
url, err := tpgresource.ReplaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/targetHttpsProxies")
180207
if err != nil {
181208
return err
@@ -266,6 +293,18 @@ func resourceComputeRegionTargetHttpsProxyRead(d *schema.ResourceData, meta inte
266293
return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("ComputeRegionTargetHttpsProxy %q", d.Id()))
267294
}
268295

296+
res, err = resourceComputeRegionTargetHttpsProxyDecoder(d, meta, res)
297+
if err != nil {
298+
return err
299+
}
300+
301+
if res == nil {
302+
// Decoding the object has resulted in it being gone. It may be marked deleted
303+
log.Printf("[DEBUG] Removing ComputeRegionTargetHttpsProxy because it no longer exists.")
304+
d.SetId("")
305+
return nil
306+
}
307+
269308
if err := d.Set("project", project); err != nil {
270309
return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err)
271310
}
@@ -282,6 +321,9 @@ func resourceComputeRegionTargetHttpsProxyRead(d *schema.ResourceData, meta inte
282321
if err := d.Set("name", flattenComputeRegionTargetHttpsProxyName(res["name"], d, config)); err != nil {
283322
return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err)
284323
}
324+
if err := d.Set("certificate_manager_certificates", flattenComputeRegionTargetHttpsProxyCertificateManagerCertificates(res["certificateManagerCertificates"], d, config)); err != nil {
325+
return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err)
326+
}
285327
if err := d.Set("ssl_certificates", flattenComputeRegionTargetHttpsProxySslCertificates(res["sslCertificates"], d, config)); err != nil {
286328
return fmt.Errorf("Error reading RegionTargetHttpsProxy: %s", err)
287329
}
@@ -318,9 +360,15 @@ func resourceComputeRegionTargetHttpsProxyUpdate(d *schema.ResourceData, meta in
318360

319361
d.Partial(true)
320362

321-
if d.HasChange("ssl_certificates") {
363+
if d.HasChange("certificate_manager_certificates") || d.HasChange("ssl_certificates") {
322364
obj := make(map[string]interface{})
323365

366+
certificateManagerCertificatesProp, err := expandComputeRegionTargetHttpsProxyCertificateManagerCertificates(d.Get("certificate_manager_certificates"), d, config)
367+
if err != nil {
368+
return err
369+
} else if v, ok := d.GetOkExists("certificate_manager_certificates"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, certificateManagerCertificatesProp)) {
370+
obj["certificateManagerCertificates"] = certificateManagerCertificatesProp
371+
}
324372
sslCertificatesProp, err := expandComputeRegionTargetHttpsProxySslCertificates(d.Get("ssl_certificates"), d, config)
325373
if err != nil {
326374
return err
@@ -511,6 +559,10 @@ func flattenComputeRegionTargetHttpsProxyName(v interface{}, d *schema.ResourceD
511559
return v
512560
}
513561

562+
func flattenComputeRegionTargetHttpsProxyCertificateManagerCertificates(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
563+
return v
564+
}
565+
514566
func flattenComputeRegionTargetHttpsProxySslCertificates(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
515567
if v == nil {
516568
return v
@@ -547,6 +599,31 @@ func expandComputeRegionTargetHttpsProxyName(v interface{}, d tpgresource.Terraf
547599
return v, nil
548600
}
549601

602+
func expandComputeRegionTargetHttpsProxyCertificateManagerCertificates(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
603+
if v == nil {
604+
return nil, nil
605+
}
606+
l := v.([]interface{})
607+
req := make([]interface{}, 0, len(l))
608+
for _, raw := range l {
609+
if raw == nil {
610+
return nil, fmt.Errorf("Invalid value for certificate_manager_certificates: nil")
611+
}
612+
if strings.HasPrefix(raw.(string), "//") || strings.HasPrefix(raw.(string), "https://") {
613+
// Any full URL will be passed to the API request (regardless of the resource type). This is to allow self_links of CertificateManagerCeritificate resources.
614+
// If the full URL is an invalid reference, that should be handled by the API.
615+
req = append(req, raw.(string))
616+
} else if reg, _ := regexp.Compile("projects/(.*)/locations/(.*)/certificates/(.*)"); reg.MatchString(raw.(string)) {
617+
// If the input is the id pattern of CertificateManagerCertificate resource, a prefix will be added to construct the full URL before constructing the API request.
618+
self_link := "https://certificatemanager.googleapis.com/v1/" + raw.(string)
619+
req = append(req, self_link)
620+
} else {
621+
return nil, fmt.Errorf("Invalid value for certificate_manager_certificates: %v is an invalid format for a certificateManagerCertificate resource", raw.(string))
622+
}
623+
}
624+
return req, nil
625+
}
626+
550627
func expandComputeRegionTargetHttpsProxySslCertificates(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
551628
l := v.([]interface{})
552629
req := make([]interface{}, 0, len(l))
@@ -586,3 +663,34 @@ func expandComputeRegionTargetHttpsProxyRegion(v interface{}, d tpgresource.Terr
586663
}
587664
return f.RelativeLink(), nil
588665
}
666+
667+
func resourceComputeRegionTargetHttpsProxyEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {
668+
669+
if _, ok := obj["certificateManagerCertificates"]; ok {
670+
// The field certificateManagerCertificates should not be included in the API request, and it should be renamed to `sslCertificates`
671+
// The API does not allow using both certificate manager certificates and sslCertificates. If that changes
672+
// in the future, the encoder logic should change accordingly because this will mean that both fields are no longer mutual exclusive.
673+
log.Printf("[DEBUG] converting the field CertificateManagerCertificates to sslCertificates before sending the request")
674+
obj["sslCertificates"] = obj["certificateManagerCertificates"]
675+
delete(obj, "certificateManagerCertificates")
676+
}
677+
return obj, nil
678+
}
679+
680+
func resourceComputeRegionTargetHttpsProxyDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) {
681+
// Since both sslCertificates and certificateManagerCertificates maps to the same API field (sslCertificates), we need to check the types
682+
// of certificates that exist in the array and decide whether to change the field to certificateManagerCertificate or not.
683+
// The decoder logic depends on the fact that the API does not allow mixed type of certificates and it returns
684+
// certificate manager certificates in the format of //certificatemanager.googleapis.com/projects/*/locations/*/certificates/*
685+
if sslCertificates, ok := res["sslCertificates"].([]interface{}); ok && len(sslCertificates) > 0 {
686+
regPat, _ := regexp.Compile("//certificatemanager.googleapis.com/projects/(.*)/locations/(.*)/certificates/(.*)")
687+
688+
if regPat.MatchString(sslCertificates[0].(string)) {
689+
// It is enough to check only the type of one of the provided certificates beacuse all the certificates should be the same type.
690+
log.Printf("[DEBUG] The field sslCertificates contains certificateManagerCertificates, the field name will be converted to certificateManagerCertificates")
691+
res["certificateManagerCertificates"] = res["sslCertificates"]
692+
delete(res, "sslCertificates")
693+
}
694+
}
695+
return res, nil
696+
}

google/services/compute/resource_compute_region_target_https_proxy_generated_test.go

+58
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,64 @@ resource "google_compute_region_health_check" "default" {
114114
`, context)
115115
}
116116

117+
func TestAccComputeRegionTargetHttpsProxy_regionTargetHttpsProxyCertificateManagerCertificateExample(t *testing.T) {
118+
t.Parallel()
119+
120+
context := map[string]interface{}{
121+
"random_suffix": acctest.RandString(t, 10),
122+
}
123+
124+
acctest.VcrTest(t, resource.TestCase{
125+
PreCheck: func() { acctest.AccTestPreCheck(t) },
126+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
127+
CheckDestroy: testAccCheckComputeRegionTargetHttpsProxyDestroyProducer(t),
128+
Steps: []resource.TestStep{
129+
{
130+
Config: testAccComputeRegionTargetHttpsProxy_regionTargetHttpsProxyCertificateManagerCertificateExample(context),
131+
},
132+
{
133+
ResourceName: "google_compute_region_target_https_proxy.default",
134+
ImportState: true,
135+
ImportStateVerify: true,
136+
ImportStateVerifyIgnore: []string{"ssl_policy", "url_map", "region"},
137+
},
138+
},
139+
})
140+
}
141+
142+
func testAccComputeRegionTargetHttpsProxy_regionTargetHttpsProxyCertificateManagerCertificateExample(context map[string]interface{}) string {
143+
return acctest.Nprintf(`
144+
resource "google_compute_region_target_https_proxy" "default" {
145+
name = "tf-test-target-http-proxy%{random_suffix}"
146+
url_map = google_compute_region_url_map.default.id
147+
certificate_manager_certificates = ["//certificatemanager.googleapis.com/${google_certificate_manager_certificate.default.id}"] # [google_certificate_manager_certificate.default.id] is also acceptable
148+
}
149+
150+
resource "google_certificate_manager_certificate" "default" {
151+
name = "tf-test-my-certificate%{random_suffix}"
152+
location = "us-central1"
153+
self_managed {
154+
pem_certificate = file("test-fixtures/cert.pem")
155+
pem_private_key = file("test-fixtures/private-key.pem")
156+
}
157+
}
158+
159+
resource "google_compute_region_url_map" "default" {
160+
name = "tf-test-url-map%{random_suffix}"
161+
default_service = google_compute_region_backend_service.default.id
162+
region = "us-central1"
163+
}
164+
165+
resource "google_compute_region_backend_service" "default" {
166+
name = "tf-test-backend-service%{random_suffix}"
167+
region = "us-central1"
168+
protocol = "HTTPS"
169+
timeout_sec = 30
170+
load_balancing_scheme = "INTERNAL_MANAGED"
171+
}
172+
`, context)
173+
}
174+
117175
func testAccCheckComputeRegionTargetHttpsProxyDestroyProducer(t *testing.T) func(s *terraform.State) error {
118176
return func(s *terraform.State) error {
119177
for name, rs := range s.RootModule().Resources {

website/docs/r/compute_region_target_https_proxy.html.markdown

+51-6
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,44 @@ resource "google_compute_region_health_check" "default" {
9494
}
9595
}
9696
```
97+
<div class = "oics-button" style="float: right; margin: 0 0 -15px">
98+
<a href="https://console.cloud.google.com/cloudshell/open?cloudshell_git_repo=https%3A%2F%2Fgithub.jpy.wang%2Fterraform-google-modules%2Fdocs-examples.git&cloudshell_working_dir=region_target_https_proxy_certificate_manager_certificate&cloudshell_image=gcr.io%2Fcloudshell-images%2Fcloudshell%3Alatest&open_in_editor=main.tf&cloudshell_print=.%2Fmotd&cloudshell_tutorial=.%2Ftutorial.md" target="_blank">
99+
<img alt="Open in Cloud Shell" src="//gstatic.com/cloudssh/images/open-btn.svg" style="max-height: 44px; margin: 32px auto; max-width: 100%;">
100+
</a>
101+
</div>
102+
## Example Usage - Region Target Https Proxy Certificate Manager Certificate
103+
104+
105+
```hcl
106+
resource "google_compute_region_target_https_proxy" "default" {
107+
name = "target-http-proxy"
108+
url_map = google_compute_region_url_map.default.id
109+
certificate_manager_certificates = ["//certificatemanager.googleapis.com/${google_certificate_manager_certificate.default.id}"] # [google_certificate_manager_certificate.default.id] is also acceptable
110+
}
111+
112+
resource "google_certificate_manager_certificate" "default" {
113+
name = "my-certificate"
114+
location = "us-central1"
115+
self_managed {
116+
pem_certificate = file("test-fixtures/cert.pem")
117+
pem_private_key = file("test-fixtures/private-key.pem")
118+
}
119+
}
120+
121+
resource "google_compute_region_url_map" "default" {
122+
name = "url-map"
123+
default_service = google_compute_region_backend_service.default.id
124+
region = "us-central1"
125+
}
126+
127+
resource "google_compute_region_backend_service" "default" {
128+
name = "backend-service"
129+
region = "us-central1"
130+
protocol = "HTTPS"
131+
timeout_sec = 30
132+
load_balancing_scheme = "INTERNAL_MANAGED"
133+
}
134+
```
97135

98136
## Argument Reference
99137

@@ -110,12 +148,6 @@ The following arguments are supported:
110148
characters must be a dash, lowercase letter, or digit, except the last
111149
character, which cannot be a dash.
112150

113-
* `ssl_certificates` -
114-
(Required)
115-
A list of RegionSslCertificate resources that are used to authenticate
116-
connections between users and the load balancer. Currently, exactly
117-
one SSL certificate must be specified.
118-
119151
* `url_map` -
120152
(Required)
121153
A reference to the RegionUrlMap resource that defines the mapping from URL
@@ -129,6 +161,19 @@ The following arguments are supported:
129161
(Optional)
130162
An optional description of this resource.
131163

164+
* `certificate_manager_certificates` -
165+
(Optional)
166+
URLs to certificate manager certificate resources that are used to authenticate connections between users and the load balancer.
167+
Currently, you may specify up to 15 certificates. Certificate manager certificates do not apply when the load balancing scheme is set to INTERNAL_SELF_MANAGED.
168+
sslCertificates and certificateManagerCertificates fields can not be defined together.
169+
Accepted format is `//certificatemanager.googleapis.com/projects/{project}/locations/{location}/certificates/{resourceName}` or just the self_link `projects/{project}/locations/{location}/certificates/{resourceName}`
170+
171+
* `ssl_certificates` -
172+
(Optional)
173+
URLs to SslCertificate resources that are used to authenticate connections between users and the load balancer.
174+
At least one SSL certificate must be specified. Currently, you may specify up to 15 SSL certificates.
175+
sslCertificates do not apply when the load balancing scheme is set to INTERNAL_SELF_MANAGED.
176+
132177
* `ssl_policy` -
133178
(Optional)
134179
A reference to the Region SslPolicy resource that will be associated with

0 commit comments

Comments
 (0)