Skip to content

Commit b8d6a4f

Browse files
Add VPC network support to Security Gateway terraform (#13581) (#22514)
[upstream:942681e33983068cf74fe9e5087e0cdf26d65b64] Signed-off-by: Modular Magician <[email protected]>
1 parent 3c174a6 commit b8d6a4f

File tree

5 files changed

+312
-0
lines changed

5 files changed

+312
-0
lines changed

.changelog/13581.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
beyondcorp: Added `upstreams` fields to `google_beyondcorp_application` resource
3+
```

google/services/beyondcorp/resource_beyondcorp_application.go

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,49 @@ Hostname and Ports - ("abc.com" and "22"), ("abc.com" and "22,33") etc`,
110110
Description: `Optional. An arbitrary user-provided name for the Application resource.
111111
Cannot exceed 64 characters.`,
112112
},
113+
"upstreams": {
114+
Type: schema.TypeList,
115+
Optional: true,
116+
Description: `Optional. List of which upstream resource(s) to forward traffic to.`,
117+
Elem: &schema.Resource{
118+
Schema: map[string]*schema.Schema{
119+
"egress_policy": {
120+
Type: schema.TypeList,
121+
Optional: true,
122+
Description: `Optional. Routing policy information.`,
123+
MaxItems: 1,
124+
Elem: &schema.Resource{
125+
Schema: map[string]*schema.Schema{
126+
"regions": {
127+
Type: schema.TypeList,
128+
Required: true,
129+
Description: `Required. List of regions where the application sends traffic to.`,
130+
Elem: &schema.Schema{
131+
Type: schema.TypeString,
132+
},
133+
},
134+
},
135+
},
136+
},
137+
"network": {
138+
Type: schema.TypeList,
139+
Optional: true,
140+
Description: `Network to forward traffic to.`,
141+
MaxItems: 1,
142+
Elem: &schema.Resource{
143+
Schema: map[string]*schema.Schema{
144+
"name": {
145+
Type: schema.TypeString,
146+
Required: true,
147+
Description: `Required. Network name is of the format:
148+
'projects/{project}/global/networks/{network}'`,
149+
},
150+
},
151+
},
152+
},
153+
},
154+
},
155+
},
113156
"create_time": {
114157
Type: schema.TypeString,
115158
Computed: true,
@@ -156,6 +199,12 @@ func resourceBeyondcorpApplicationCreate(d *schema.ResourceData, meta interface{
156199
} else if v, ok := d.GetOkExists("endpoint_matchers"); !tpgresource.IsEmptyValue(reflect.ValueOf(endpointMatchersProp)) && (ok || !reflect.DeepEqual(v, endpointMatchersProp)) {
157200
obj["endpointMatchers"] = endpointMatchersProp
158201
}
202+
upstreamsProp, err := expandBeyondcorpApplicationUpstreams(d.Get("upstreams"), d, config)
203+
if err != nil {
204+
return err
205+
} else if v, ok := d.GetOkExists("upstreams"); !tpgresource.IsEmptyValue(reflect.ValueOf(upstreamsProp)) && (ok || !reflect.DeepEqual(v, upstreamsProp)) {
206+
obj["upstreams"] = upstreamsProp
207+
}
159208

160209
url, err := tpgresource.ReplaceVars(d, config, "{{BeyondcorpBasePath}}projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications?applicationId={{application_id}}")
161210
if err != nil {
@@ -278,6 +327,9 @@ func resourceBeyondcorpApplicationRead(d *schema.ResourceData, meta interface{})
278327
if err := d.Set("endpoint_matchers", flattenBeyondcorpApplicationEndpointMatchers(res["endpointMatchers"], d, config)); err != nil {
279328
return fmt.Errorf("Error reading Application: %s", err)
280329
}
330+
if err := d.Set("upstreams", flattenBeyondcorpApplicationUpstreams(res["upstreams"], d, config)); err != nil {
331+
return fmt.Errorf("Error reading Application: %s", err)
332+
}
281333
if err := d.Set("name", flattenBeyondcorpApplicationName(res["name"], d, config)); err != nil {
282334
return fmt.Errorf("Error reading Application: %s", err)
283335
}
@@ -316,6 +368,12 @@ func resourceBeyondcorpApplicationUpdate(d *schema.ResourceData, meta interface{
316368
} else if v, ok := d.GetOkExists("endpoint_matchers"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, endpointMatchersProp)) {
317369
obj["endpointMatchers"] = endpointMatchersProp
318370
}
371+
upstreamsProp, err := expandBeyondcorpApplicationUpstreams(d.Get("upstreams"), d, config)
372+
if err != nil {
373+
return err
374+
} else if v, ok := d.GetOkExists("upstreams"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, upstreamsProp)) {
375+
obj["upstreams"] = upstreamsProp
376+
}
319377

320378
url, err := tpgresource.ReplaceVars(d, config, "{{BeyondcorpBasePath}}projects/{{project}}/locations/global/securityGateways/{{security_gateways_id}}/applications/{{application_id}}")
321379
if err != nil {
@@ -333,6 +391,10 @@ func resourceBeyondcorpApplicationUpdate(d *schema.ResourceData, meta interface{
333391
if d.HasChange("endpoint_matchers") {
334392
updateMask = append(updateMask, "endpointMatchers")
335393
}
394+
395+
if d.HasChange("upstreams") {
396+
updateMask = append(updateMask, "upstreams")
397+
}
336398
// updateMask is a URL parameter but not present in the schema, so ReplaceVars
337399
// won't set it
338400
url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
@@ -487,6 +549,59 @@ func flattenBeyondcorpApplicationEndpointMatchersPorts(v interface{}, d *schema.
487549
return v
488550
}
489551

552+
func flattenBeyondcorpApplicationUpstreams(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
553+
if v == nil {
554+
return v
555+
}
556+
l := v.([]interface{})
557+
transformed := make([]interface{}, 0, len(l))
558+
for _, raw := range l {
559+
original := raw.(map[string]interface{})
560+
if len(original) < 1 {
561+
// Do not include empty json objects coming back from the api
562+
continue
563+
}
564+
transformed = append(transformed, map[string]interface{}{
565+
"egress_policy": flattenBeyondcorpApplicationUpstreamsEgressPolicy(original["egressPolicy"], d, config),
566+
"network": flattenBeyondcorpApplicationUpstreamsNetwork(original["network"], d, config),
567+
})
568+
}
569+
return transformed
570+
}
571+
func flattenBeyondcorpApplicationUpstreamsEgressPolicy(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
572+
if v == nil {
573+
return nil
574+
}
575+
original := v.(map[string]interface{})
576+
if len(original) == 0 {
577+
return nil
578+
}
579+
transformed := make(map[string]interface{})
580+
transformed["regions"] =
581+
flattenBeyondcorpApplicationUpstreamsEgressPolicyRegions(original["regions"], d, config)
582+
return []interface{}{transformed}
583+
}
584+
func flattenBeyondcorpApplicationUpstreamsEgressPolicyRegions(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
585+
return v
586+
}
587+
588+
func flattenBeyondcorpApplicationUpstreamsNetwork(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
589+
if v == nil {
590+
return nil
591+
}
592+
original := v.(map[string]interface{})
593+
if len(original) == 0 {
594+
return nil
595+
}
596+
transformed := make(map[string]interface{})
597+
transformed["name"] =
598+
flattenBeyondcorpApplicationUpstreamsNetworkName(original["name"], d, config)
599+
return []interface{}{transformed}
600+
}
601+
func flattenBeyondcorpApplicationUpstreamsNetworkName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
602+
return v
603+
}
604+
490605
func flattenBeyondcorpApplicationName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
491606
return v
492607
}
@@ -535,3 +650,78 @@ func expandBeyondcorpApplicationEndpointMatchersHostname(v interface{}, d tpgres
535650
func expandBeyondcorpApplicationEndpointMatchersPorts(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
536651
return v, nil
537652
}
653+
654+
func expandBeyondcorpApplicationUpstreams(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
655+
l := v.([]interface{})
656+
req := make([]interface{}, 0, len(l))
657+
for _, raw := range l {
658+
if raw == nil {
659+
continue
660+
}
661+
original := raw.(map[string]interface{})
662+
transformed := make(map[string]interface{})
663+
664+
transformedEgressPolicy, err := expandBeyondcorpApplicationUpstreamsEgressPolicy(original["egress_policy"], d, config)
665+
if err != nil {
666+
return nil, err
667+
} else if val := reflect.ValueOf(transformedEgressPolicy); val.IsValid() && !tpgresource.IsEmptyValue(val) {
668+
transformed["egressPolicy"] = transformedEgressPolicy
669+
}
670+
671+
transformedNetwork, err := expandBeyondcorpApplicationUpstreamsNetwork(original["network"], d, config)
672+
if err != nil {
673+
return nil, err
674+
} else if val := reflect.ValueOf(transformedNetwork); val.IsValid() && !tpgresource.IsEmptyValue(val) {
675+
transformed["network"] = transformedNetwork
676+
}
677+
678+
req = append(req, transformed)
679+
}
680+
return req, nil
681+
}
682+
683+
func expandBeyondcorpApplicationUpstreamsEgressPolicy(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
684+
l := v.([]interface{})
685+
if len(l) == 0 || l[0] == nil {
686+
return nil, nil
687+
}
688+
raw := l[0]
689+
original := raw.(map[string]interface{})
690+
transformed := make(map[string]interface{})
691+
692+
transformedRegions, err := expandBeyondcorpApplicationUpstreamsEgressPolicyRegions(original["regions"], d, config)
693+
if err != nil {
694+
return nil, err
695+
} else if val := reflect.ValueOf(transformedRegions); val.IsValid() && !tpgresource.IsEmptyValue(val) {
696+
transformed["regions"] = transformedRegions
697+
}
698+
699+
return transformed, nil
700+
}
701+
702+
func expandBeyondcorpApplicationUpstreamsEgressPolicyRegions(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
703+
return v, nil
704+
}
705+
706+
func expandBeyondcorpApplicationUpstreamsNetwork(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
707+
l := v.([]interface{})
708+
if len(l) == 0 || l[0] == nil {
709+
return nil, nil
710+
}
711+
raw := l[0]
712+
original := raw.(map[string]interface{})
713+
transformed := make(map[string]interface{})
714+
715+
transformedName, err := expandBeyondcorpApplicationUpstreamsNetworkName(original["name"], d, config)
716+
if err != nil {
717+
return nil, err
718+
} else if val := reflect.ValueOf(transformedName); val.IsValid() && !tpgresource.IsEmptyValue(val) {
719+
transformed["name"] = transformedName
720+
}
721+
722+
return transformed, nil
723+
}
724+
725+
func expandBeyondcorpApplicationUpstreamsNetworkName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
726+
return v, nil
727+
}

google/services/beyondcorp/resource_beyondcorp_application_generated_meta.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ fields:
1616
- field: 'security_gateways_id'
1717
provider_only: true
1818
- field: 'update_time'
19+
- field: 'upstreams.egress_policy.regions'
20+
- field: 'upstreams.network.name'

google/services/beyondcorp/resource_beyondcorp_application_generated_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,59 @@ resource "google_beyondcorp_application" "example" {
7373
`, context)
7474
}
7575

76+
func TestAccBeyondcorpApplication_beyondcorpSecurityGatewayApplicationVpcExample(t *testing.T) {
77+
t.Parallel()
78+
79+
context := map[string]interface{}{
80+
"random_suffix": acctest.RandString(t, 10),
81+
}
82+
83+
acctest.VcrTest(t, resource.TestCase{
84+
PreCheck: func() { acctest.AccTestPreCheck(t) },
85+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
86+
CheckDestroy: testAccCheckBeyondcorpApplicationDestroyProducer(t),
87+
Steps: []resource.TestStep{
88+
{
89+
Config: testAccBeyondcorpApplication_beyondcorpSecurityGatewayApplicationVpcExample(context),
90+
},
91+
{
92+
ResourceName: "google_beyondcorp_application.example",
93+
ImportState: true,
94+
ImportStateVerify: true,
95+
ImportStateVerifyIgnore: []string{"application_id", "security_gateways_id"},
96+
},
97+
},
98+
})
99+
}
100+
101+
func testAccBeyondcorpApplication_beyondcorpSecurityGatewayApplicationVpcExample(context map[string]interface{}) string {
102+
return acctest.Nprintf(`
103+
data "google_project" "project" {}
104+
105+
resource "google_beyondcorp_security_gateway" "default" {
106+
security_gateway_id = "default%{random_suffix}"
107+
display_name = "My Security Gateway resource"
108+
hubs { region = "us-central1" }
109+
}
110+
111+
resource "google_beyondcorp_application" "example" {
112+
security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id
113+
application_id = "tf-test-my-vm-service%{random_suffix}"
114+
endpoint_matchers {
115+
hostname = "my-vm-service.com"
116+
}
117+
upstreams {
118+
egress_policy {
119+
regions = ["us-central1"]
120+
}
121+
network {
122+
name = "projects/${data.google_project.project.project_id}/global/networks/default"
123+
}
124+
}
125+
}
126+
`, context)
127+
}
128+
76129
func testAccCheckBeyondcorpApplicationDestroyProducer(t *testing.T) func(s *terraform.State) error {
77130
return func(s *terraform.State) error {
78131
for name, rs := range s.RootModule().Resources {

website/docs/r/beyondcorp_application.html.markdown

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,39 @@ resource "google_beyondcorp_application" "example" {
4848
}
4949
}
5050
```
51+
<div class = "oics-button" style="float: right; margin: 0 0 -15px">
52+
<a href="https://console.cloud.google.com/cloudshell/open?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2Fterraform-google-modules%2Fdocs-examples.git&cloudshell_image=gcr.io%2Fcloudshell-images%2Fcloudshell%3Alatest&cloudshell_print=.%2Fmotd&cloudshell_tutorial=.%2Ftutorial.md&cloudshell_working_dir=beyondcorp_security_gateway_application_vpc&open_in_editor=main.tf" target="_blank">
53+
<img alt="Open in Cloud Shell" src="//gstatic.com/cloudssh/images/open-btn.svg" style="max-height: 44px; margin: 32px auto; max-width: 100%;">
54+
</a>
55+
</div>
56+
## Example Usage - Beyondcorp Security Gateway Application Vpc
57+
58+
59+
```hcl
60+
data "google_project" "project" {}
61+
62+
resource "google_beyondcorp_security_gateway" "default" {
63+
security_gateway_id = "default"
64+
display_name = "My Security Gateway resource"
65+
hubs { region = "us-central1" }
66+
}
67+
68+
resource "google_beyondcorp_application" "example" {
69+
security_gateways_id = google_beyondcorp_security_gateway.default.security_gateway_id
70+
application_id = "my-vm-service"
71+
endpoint_matchers {
72+
hostname = "my-vm-service.com"
73+
}
74+
upstreams {
75+
egress_policy {
76+
regions = ["us-central1"]
77+
}
78+
network {
79+
name = "projects/${data.google_project.project.project_id}/global/networks/default"
80+
}
81+
}
82+
}
83+
```
5184

5285
## Argument Reference
5386

@@ -99,10 +132,41 @@ The following arguments are supported:
99132
Optional. An arbitrary user-provided name for the Application resource.
100133
Cannot exceed 64 characters.
101134

135+
* `upstreams` -
136+
(Optional)
137+
Optional. List of which upstream resource(s) to forward traffic to.
138+
Structure is [documented below](#nested_upstreams).
139+
102140
* `project` - (Optional) The ID of the project in which the resource belongs.
103141
If it is not provided, the provider project is used.
104142

105143

144+
<a name="nested_upstreams"></a>The `upstreams` block supports:
145+
146+
* `egress_policy` -
147+
(Optional)
148+
Optional. Routing policy information.
149+
Structure is [documented below](#nested_upstreams_upstreams_egress_policy).
150+
151+
* `network` -
152+
(Optional)
153+
Network to forward traffic to.
154+
Structure is [documented below](#nested_upstreams_upstreams_network).
155+
156+
157+
<a name="nested_upstreams_upstreams_egress_policy"></a>The `egress_policy` block supports:
158+
159+
* `regions` -
160+
(Required)
161+
Required. List of regions where the application sends traffic to.
162+
163+
<a name="nested_upstreams_upstreams_network"></a>The `network` block supports:
164+
165+
* `name` -
166+
(Required)
167+
Required. Network name is of the format:
168+
`projects/{project}/global/networks/{network}`
169+
106170
## Attributes Reference
107171

108172
In addition to the arguments listed above, the following computed attributes are exported:

0 commit comments

Comments
 (0)