Skip to content

Commit 6bdd3d4

Browse files
provider: automatically generate the *_wo and *_wo_version for write-only arguments + compute: added write-only support for shared_secret argument for google_compute_vpn_tunnel resource (#14933) (#10788)
[upstream:e9c7b3bf64eca845688218688261064fbff27f2b] Signed-off-by: Modular Magician <[email protected]>
1 parent 81a1c95 commit 6bdd3d4

File tree

7 files changed

+285
-19
lines changed

7 files changed

+285
-19
lines changed

.changelog/14933.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
compute: added `shared_secret_wo` and `shared_secret_wo_version` fields for `google_compute_vpn_tunnel` resource, enabling write-only management of the shared secret.
3+
```

google-beta/services/compute/resource_compute_vpn_tunnel.go

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -172,14 +172,6 @@ must be a lowercase letter, and all following characters must
172172
be a dash, lowercase letter, or digit,
173173
except the last character, which cannot be a dash.`,
174174
},
175-
"shared_secret": {
176-
Type: schema.TypeString,
177-
Required: true,
178-
ForceNew: true,
179-
Description: `Shared secret used to set the secure session between the Cloud VPN
180-
gateway and the peer VPN gateway.`,
181-
Sensitive: true,
182-
},
183175
"cipher_suite": {
184176
Type: schema.TypeList,
185177
Optional: true,
@@ -383,6 +375,32 @@ Only IPv4 is supported.`,
383375
DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName,
384376
Description: `URL of router resource to be used for dynamic routing.`,
385377
},
378+
"shared_secret": {
379+
Type: schema.TypeString,
380+
Optional: true,
381+
ForceNew: true,
382+
Description: `Shared secret used to set the secure session between the Cloud VPN
383+
gateway and the peer VPN gateway.`,
384+
Sensitive: true,
385+
ExactlyOneOf: []string{"shared_secret", "shared_secret_wo"},
386+
},
387+
"shared_secret_wo": {
388+
Type: schema.TypeString,
389+
Optional: true,
390+
Description: `Shared secret used to set the secure session between the Cloud VPN
391+
gateway and the peer VPN gateway.
392+
Note: This property is write-only and will not be read from the API. For more info see [updating write-only attributes](/docs/providers/google/guides/using_write_only_attributes.html#updating-write-only-attributes)`,
393+
WriteOnly: true,
394+
ExactlyOneOf: []string{"shared_secret", "shared_secret_wo"},
395+
RequiredWith: []string{"shared_secret_wo_version"},
396+
},
397+
"shared_secret_wo_version": {
398+
Type: schema.TypeString,
399+
Optional: true,
400+
ForceNew: true,
401+
Description: `Triggers update of shared_secret_wo write-only. For more info see [updating write-only attributes](/docs/providers/google/guides/using_write_only_attributes.html#updating-write-only-attributes)`,
402+
RequiredWith: []string{"shared_secret_wo"},
403+
},
386404
"target_vpn_gateway": {
387405
Type: schema.TypeString,
388406
Optional: true,
@@ -564,6 +582,18 @@ func resourceComputeVpnTunnelCreate(d *schema.ResourceData, meta interface{}) er
564582
} else if v, ok := d.GetOkExists("cipher_suite"); !tpgresource.IsEmptyValue(reflect.ValueOf(cipherSuiteProp)) && (ok || !reflect.DeepEqual(v, cipherSuiteProp)) {
565583
obj["cipherSuite"] = cipherSuiteProp
566584
}
585+
sharedSecretWoProp, err := expandComputeVpnTunnelSharedSecretWo(tpgresource.GetRawConfigAttributeAsString(d, "shared_secret_wo"), d, config)
586+
if err != nil {
587+
return err
588+
} else if v, ok := d.GetOkExists("shared_secret_wo"); !tpgresource.IsEmptyValue(reflect.ValueOf(sharedSecretWoProp)) && (ok || !reflect.DeepEqual(v, sharedSecretWoProp)) {
589+
obj["sharedSecret"] = sharedSecretWoProp
590+
}
591+
sharedSecretWoVersionProp, err := expandComputeVpnTunnelSharedSecretWoVersion(d.Get("shared_secret_wo_version"), d, config)
592+
if err != nil {
593+
return err
594+
} else if v, ok := d.GetOkExists("shared_secret_wo_version"); !tpgresource.IsEmptyValue(reflect.ValueOf(sharedSecretWoVersionProp)) && (ok || !reflect.DeepEqual(v, sharedSecretWoVersionProp)) {
595+
obj["sharedSecretWoVersion"] = sharedSecretWoVersionProp
596+
}
567597
effectiveLabelsProp, err := expandComputeVpnTunnelEffectiveLabels(d.Get("effective_labels"), d, config)
568598
if err != nil {
569599
return err
@@ -800,6 +830,9 @@ func resourceComputeVpnTunnelRead(d *schema.ResourceData, meta interface{}) erro
800830
if err := d.Set("cipher_suite", flattenComputeVpnTunnelCipherSuite(res["cipherSuite"], d, config)); err != nil {
801831
return fmt.Errorf("Error reading VpnTunnel: %s", err)
802832
}
833+
if err := d.Set("shared_secret_wo_version", flattenComputeVpnTunnelSharedSecretWoVersion(res["sharedSecretWoVersion"], d, config)); err != nil {
834+
return fmt.Errorf("Error reading VpnTunnel: %s", err)
835+
}
803836
if err := d.Set("terraform_labels", flattenComputeVpnTunnelTerraformLabels(res["labels"], d, config)); err != nil {
804837
return fmt.Errorf("Error reading VpnTunnel: %s", err)
805838
}
@@ -1214,6 +1247,10 @@ func flattenComputeVpnTunnelCipherSuitePhase2Pfs(v interface{}, d *schema.Resour
12141247
return schema.NewSet(schema.HashString, v.([]interface{}))
12151248
}
12161249

1250+
func flattenComputeVpnTunnelSharedSecretWoVersion(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
1251+
return d.Get("shared_secret_wo_version")
1252+
}
1253+
12171254
func flattenComputeVpnTunnelTerraformLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
12181255
if v == nil {
12191256
return v
@@ -1474,6 +1511,14 @@ func expandComputeVpnTunnelCipherSuitePhase2Pfs(v interface{}, d tpgresource.Ter
14741511
return v, nil
14751512
}
14761513

1514+
func expandComputeVpnTunnelSharedSecretWo(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
1515+
return v, nil
1516+
}
1517+
1518+
func expandComputeVpnTunnelSharedSecretWoVersion(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
1519+
return v, nil
1520+
}
1521+
14771522
func expandComputeVpnTunnelEffectiveLabels(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) {
14781523
if v == nil {
14791524
return map[string]string{}, nil

google-beta/services/compute/resource_compute_vpn_tunnel_generated_meta.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ fields:
3131
- field: 'router'
3232
- field: 'shared_secret'
3333
- field: 'shared_secret_hash'
34+
- field: 'shared_secret_wo'
35+
api_field: 'shared_secret'
36+
- field: 'shared_secret_wo_version'
37+
provider_only: true
3438
- field: 'target_vpn_gateway'
3539
- field: 'terraform_labels'
3640
provider_only: true

google-beta/services/compute/resource_compute_vpn_tunnel_generated_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func TestAccComputeVpnTunnel_vpnTunnelBasicExample(t *testing.T) {
4949
ResourceName: "google_compute_vpn_tunnel.tunnel1",
5050
ImportState: true,
5151
ImportStateVerify: true,
52-
ImportStateVerifyIgnore: []string{"labels", "peer_external_gateway", "peer_gcp_gateway", "region", "router", "shared_secret", "target_vpn_gateway", "terraform_labels", "vpn_gateway"},
52+
ImportStateVerifyIgnore: []string{"labels", "peer_external_gateway", "peer_gcp_gateway", "region", "router", "shared_secret", "shared_secret_wo", "target_vpn_gateway", "terraform_labels", "vpn_gateway"},
5353
},
5454
},
5555
})
@@ -141,7 +141,7 @@ func TestAccComputeVpnTunnel_vpnTunnelCipherSuiteExample(t *testing.T) {
141141
ResourceName: "google_compute_vpn_tunnel.tunnel1",
142142
ImportState: true,
143143
ImportStateVerify: true,
144-
ImportStateVerifyIgnore: []string{"labels", "peer_external_gateway", "peer_gcp_gateway", "region", "router", "shared_secret", "target_vpn_gateway", "terraform_labels", "vpn_gateway"},
144+
ImportStateVerifyIgnore: []string{"labels", "peer_external_gateway", "peer_gcp_gateway", "region", "router", "shared_secret", "shared_secret_wo", "target_vpn_gateway", "terraform_labels", "vpn_gateway"},
145145
},
146146
},
147147
})

google-beta/services/compute/resource_compute_vpn_tunnel_test.go

Lines changed: 168 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ import (
2121
"testing"
2222

2323
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
24+
"github.com/hashicorp/terraform-plugin-testing/plancheck"
2425
"github.com/hashicorp/terraform-provider-google-beta/google-beta/acctest"
2526
"github.com/hashicorp/terraform-provider-google-beta/google-beta/envvar"
2627
)
2728

2829
func TestAccComputeVpnTunnel_regionFromGateway(t *testing.T) {
2930
t.Parallel()
3031
region := "us-central1"
32+
suffix := acctest.RandString(t, 10)
3133
if envvar.GetTestRegionFromEnv() == region {
3234
// Make sure we choose a region that isn't the provider default
3335
// in order to test getting the region from the gateway and not the
@@ -41,7 +43,7 @@ func TestAccComputeVpnTunnel_regionFromGateway(t *testing.T) {
4143
CheckDestroy: testAccCheckComputeVpnTunnelDestroyProducer(t),
4244
Steps: []resource.TestStep{
4345
{
44-
Config: testAccComputeVpnTunnel_regionFromGateway(acctest.RandString(t, 10), region),
46+
Config: testAccComputeVpnTunnel_regionFromGateway(suffix, region),
4547
},
4648
{
4749
ResourceName: "google_compute_vpn_tunnel.foobar",
@@ -56,14 +58,60 @@ func TestAccComputeVpnTunnel_regionFromGateway(t *testing.T) {
5658
func TestAccComputeVpnTunnel_router(t *testing.T) {
5759
t.Parallel()
5860

61+
suffix := acctest.RandString(t, 10)
5962
router := fmt.Sprintf("tf-test-tunnel-%s", acctest.RandString(t, 10))
6063
acctest.VcrTest(t, resource.TestCase{
6164
PreCheck: func() { acctest.AccTestPreCheck(t) },
6265
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
6366
CheckDestroy: testAccCheckComputeVpnTunnelDestroyProducer(t),
6467
Steps: []resource.TestStep{
6568
{
66-
Config: testAccComputeVpnTunnelRouter(acctest.RandString(t, 10), router),
69+
Config: testAccComputeVpnTunnelRouter(suffix, router),
70+
},
71+
{
72+
ResourceName: "google_compute_vpn_tunnel.foobar",
73+
ImportState: true,
74+
ImportStateVerify: true,
75+
ImportStateVerifyIgnore: []string{"shared_secret", "detailed_status"},
76+
},
77+
},
78+
})
79+
}
80+
81+
func TestAccComputeVpnTunnel_routerWithSharedSecretWo_update(t *testing.T) {
82+
t.Parallel()
83+
84+
router := fmt.Sprintf("tf-test-tunnel-%s", acctest.RandString(t, 10))
85+
suffix := acctest.RandString(t, 10)
86+
acctest.VcrTest(t, resource.TestCase{
87+
PreCheck: func() { acctest.AccTestPreCheck(t) },
88+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
89+
CheckDestroy: testAccCheckComputeVpnTunnelDestroyProducer(t),
90+
Steps: []resource.TestStep{
91+
{
92+
Config: testAccComputeVpnTunnelRouterWithSharedSecretWo(suffix, router),
93+
Check: resource.ComposeTestCheckFunc(
94+
resource.TestCheckNoResourceAttr("google_compute_vpn_tunnel.foobar", "shared_secret_wo"),
95+
resource.TestCheckResourceAttr("google_compute_vpn_tunnel.foobar", "shared_secret_wo_version", "1"),
96+
),
97+
},
98+
{
99+
ResourceName: "google_compute_vpn_tunnel.foobar",
100+
ImportState: true,
101+
ImportStateVerify: true,
102+
ImportStateVerifyIgnore: []string{"shared_secret", "detailed_status"},
103+
},
104+
{
105+
Config: testAccComputeVpnTunnelRouterWithSharedSecretWo_update(suffix, router),
106+
ConfigPlanChecks: resource.ConfigPlanChecks{
107+
PreApply: []plancheck.PlanCheck{
108+
plancheck.ExpectResourceAction("google_compute_vpn_tunnel.foobar", plancheck.ResourceActionDestroyBeforeCreate),
109+
},
110+
},
111+
Check: resource.ComposeTestCheckFunc(
112+
resource.TestCheckNoResourceAttr("google_compute_vpn_tunnel.foobar", "shared_secret_wo"),
113+
resource.TestCheckResourceAttr("google_compute_vpn_tunnel.foobar", "shared_secret_wo_version", "2"),
114+
),
67115
},
68116
{
69117
ResourceName: "google_compute_vpn_tunnel.foobar",
@@ -78,13 +126,14 @@ func TestAccComputeVpnTunnel_router(t *testing.T) {
78126
func TestAccComputeVpnTunnel_defaultTrafficSelectors(t *testing.T) {
79127
t.Parallel()
80128

129+
suffix := acctest.RandString(t, 10)
81130
acctest.VcrTest(t, resource.TestCase{
82131
PreCheck: func() { acctest.AccTestPreCheck(t) },
83132
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
84133
CheckDestroy: testAccCheckComputeVpnTunnelDestroyProducer(t),
85134
Steps: []resource.TestStep{
86135
{
87-
Config: testAccComputeVpnTunnelDefaultTrafficSelectors(acctest.RandString(t, 10)),
136+
Config: testAccComputeVpnTunnelDefaultTrafficSelectors(suffix),
88137
},
89138
{
90139
ResourceName: "google_compute_vpn_tunnel.foobar",
@@ -217,6 +266,122 @@ resource "google_compute_vpn_tunnel" "foobar" {
217266
`, suffix, router)
218267
}
219268

269+
func testAccComputeVpnTunnelRouterWithSharedSecretWo(suffix, router string) string {
270+
return fmt.Sprintf(`
271+
resource "google_compute_network" "foobar" {
272+
name = "tf-test-%[1]s"
273+
auto_create_subnetworks = false
274+
}
275+
276+
resource "google_compute_subnetwork" "foobar" {
277+
name = "tf-test-subnetwork-%[1]s"
278+
network = google_compute_network.foobar.self_link
279+
ip_cidr_range = "10.0.0.0/16"
280+
region = "us-central1"
281+
}
282+
283+
resource "google_compute_address" "foobar" {
284+
name = "tf-test-%[1]s"
285+
region = google_compute_subnetwork.foobar.region
286+
}
287+
288+
resource "google_compute_ha_vpn_gateway" "foobar" {
289+
name = "tf-test-%[1]s"
290+
network = google_compute_network.foobar.self_link
291+
region = google_compute_subnetwork.foobar.region
292+
}
293+
294+
resource "google_compute_external_vpn_gateway" "external_gateway" {
295+
name = "external-gateway-%[1]s"
296+
redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT"
297+
description = "An externally managed VPN gateway"
298+
interface {
299+
id = 0
300+
ip_address = "8.8.8.8"
301+
}
302+
}
303+
304+
resource "google_compute_router" "foobar" {
305+
name = "%[2]s"
306+
region = google_compute_subnetwork.foobar.region
307+
network = google_compute_network.foobar.self_link
308+
bgp {
309+
asn = 64514
310+
}
311+
}
312+
313+
resource "google_compute_vpn_tunnel" "foobar" {
314+
name = "tf-test-%[1]s"
315+
region = google_compute_subnetwork.foobar.region
316+
vpn_gateway = google_compute_ha_vpn_gateway.foobar.id
317+
peer_external_gateway = google_compute_external_vpn_gateway.external_gateway.id
318+
peer_external_gateway_interface = 0
319+
shared_secret_wo = "I am write only, and should not be written to state"
320+
shared_secret_wo_version = 1
321+
router = google_compute_router.foobar.self_link
322+
vpn_gateway_interface = 0
323+
}
324+
`, suffix, router)
325+
}
326+
327+
func testAccComputeVpnTunnelRouterWithSharedSecretWo_update(suffix, router string) string {
328+
return fmt.Sprintf(`
329+
resource "google_compute_network" "foobar" {
330+
name = "tf-test-%[1]s"
331+
auto_create_subnetworks = false
332+
}
333+
334+
resource "google_compute_subnetwork" "foobar" {
335+
name = "tf-test-subnetwork-%[1]s"
336+
network = google_compute_network.foobar.self_link
337+
ip_cidr_range = "10.0.0.0/16"
338+
region = "us-central1"
339+
}
340+
341+
resource "google_compute_address" "foobar" {
342+
name = "tf-test-%[1]s"
343+
region = google_compute_subnetwork.foobar.region
344+
}
345+
346+
resource "google_compute_ha_vpn_gateway" "foobar" {
347+
name = "tf-test-%[1]s"
348+
network = google_compute_network.foobar.self_link
349+
region = google_compute_subnetwork.foobar.region
350+
}
351+
352+
resource "google_compute_external_vpn_gateway" "external_gateway" {
353+
name = "external-gateway-%[1]s"
354+
redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT"
355+
description = "An externally managed VPN gateway"
356+
interface {
357+
id = 0
358+
ip_address = "8.8.8.8"
359+
}
360+
}
361+
362+
resource "google_compute_router" "foobar" {
363+
name = "%[2]s"
364+
region = google_compute_subnetwork.foobar.region
365+
network = google_compute_network.foobar.self_link
366+
bgp {
367+
asn = 64514
368+
}
369+
}
370+
371+
resource "google_compute_vpn_tunnel" "foobar" {
372+
name = "tf-test-%[1]s"
373+
region = google_compute_subnetwork.foobar.region
374+
vpn_gateway = google_compute_ha_vpn_gateway.foobar.id
375+
peer_external_gateway = google_compute_external_vpn_gateway.external_gateway.id
376+
peer_external_gateway_interface = 0
377+
shared_secret_wo = "This is another secret, but still write only"
378+
shared_secret_wo_version = 2
379+
router = google_compute_router.foobar.self_link
380+
vpn_gateway_interface = 0
381+
}
382+
`, suffix, router)
383+
}
384+
220385
func testAccComputeVpnTunnelDefaultTrafficSelectors(suffix string) string {
221386
return fmt.Sprintf(`
222387
resource "google_compute_network" "foobar" {

google-beta/tpgresource/utils.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,3 +928,33 @@ func ReducedPrefixedUniqueId(prefix string) string {
928928
date := uniqueId[2:8]
929929
return prefix + date + counter
930930
}
931+
932+
// GetRawConfigAttributeAsString retrieves an attribute directly from the raw config
933+
// This is useful for retrieving values that are not directly accessible via the
934+
// standard schema.ResourceData.Get method, such as write-only attributes.
935+
func GetRawConfigAttributeAsString(d *schema.ResourceData, key string) string {
936+
// see https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/write-only-arguments#retrieving-write-only-values
937+
parts := strings.Split(key, ".")
938+
939+
var path cty.Path
940+
if len(parts) > 0 {
941+
path = cty.GetAttrPath(parts[0])
942+
}
943+
944+
for i := 1; i < len(parts); i++ {
945+
part := parts[i]
946+
947+
if index, err := strconv.Atoi(part); err == nil {
948+
path = path.IndexInt(index)
949+
} else {
950+
path = path.GetAttr(part)
951+
}
952+
}
953+
954+
woCty, diags := d.GetRawConfigAt(path)
955+
if len(diags) == 0 && !woCty.IsNull() && woCty.Type().Equals(cty.String) {
956+
return woCty.AsString()
957+
}
958+
959+
return ""
960+
}

0 commit comments

Comments
 (0)