Skip to content

Commit 7aeae02

Browse files
authored
Update forward_ssh_connectivity.private_key to be mutable (#13307)
1 parent 4fb4461 commit 7aeae02

File tree

2 files changed

+264
-1
lines changed

2 files changed

+264
-1
lines changed

mmv1/products/datastream/ConnectionProfile.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,6 @@ properties:
498498
type: String
499499
description: |
500500
SSH private key.
501-
immutable: true
502501
sensitive: true
503502
conflicts:
504503
- forward_ssh_connectivity.0.password

mmv1/third_party/terraform/services/datastream/resource_datastream_connection_profile_test.go

+264
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package datastream_test
22

33
import (
4+
"fmt"
45
"testing"
6+
"time"
57

68
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
79
"github.com/hashicorp/terraform-provider-google/google/acctest"
@@ -76,6 +78,114 @@ func TestAccDatastreamConnectionProfile_update(t *testing.T) {
7678
})
7779
}
7880

81+
func TestAccDatastreamConnectionProfile_sshKey_update(t *testing.T) {
82+
t.Parallel()
83+
84+
context := map[string]interface{}{
85+
"random_suffix": acctest.RandString(t, 10),
86+
}
87+
88+
randomPubKey1 := `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCjXhptfWIrtflLZ1WeOsjCfHSEKvui0fdNXTqpqIA+2NNlFjwKS4mV3bDJIRlC5FdWG/D5LW4kvSmcTx1eSLUcvqw3i3F73Ii35AR1Rid1bY0LCBYUUgkDKyvZgDzrM7g+MwBtthoud8Axt9/bh28qtzSVNvWfxIYsa2CwtqlkZr5c6Qb6N2B9kxW8WFsCnoAeBaZDMq+LVBRsRJvBBrJm/qhMNPd07Al7wGLEnNPWmwjFT7B12sMjNr7ZNLfI9VckEyUSx3AGBFH7RImeYiWb6vZA9v5DE7kBrCoHtJK5IN9dvqEWXrrDT7RTFXd55xQqT70eZiIDNz1nexDw8ZCn user`
89+
randomPrivKey1 := `-----BEGIN OPENSSH PRIVATE KEY-----
90+
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
91+
NhAAAAAwEAAQAAAQEAo14abX1iK7X5S2dVnjrIwnx0hCr7otH3TV06qaiAPtjTZRY8CkuJ
92+
ld2wySEZQuRXVhvw+S1uJL0pnE8dXki1HL6sN4txe9yIt+QEdUYndW2NCwgWFFIJAysr2Y
93+
A86zO4PjMAbbYaLnfAMbff24dvKrc0lTb1n8SGLGtgsLapZGa+XOkG+jdgfZMVvFhbAp6A
94+
HgWmQzKvi1QUbESbwQayZv6oTDT3dOwJe8BixJzT1psIxU+wddrDIza+2TS3yPVXJBMlEs
95+
dwBgRR+0SJnmIlm+r2QPb+QxO5AawqB7SSuSDfXb6hFl66w0+0UxV3eecUKk+9HmYiAzc9
96+
Z3sQ8PGQpwAAA8B2IBoLdiAaCwAAAAdzc2gtcnNhAAABAQCjXhptfWIrtflLZ1WeOsjCfH
97+
SEKvui0fdNXTqpqIA+2NNlFjwKS4mV3bDJIRlC5FdWG/D5LW4kvSmcTx1eSLUcvqw3i3F7
98+
3Ii35AR1Rid1bY0LCBYUUgkDKyvZgDzrM7g+MwBtthoud8Axt9/bh28qtzSVNvWfxIYsa2
99+
CwtqlkZr5c6Qb6N2B9kxW8WFsCnoAeBaZDMq+LVBRsRJvBBrJm/qhMNPd07Al7wGLEnNPW
100+
mwjFT7B12sMjNr7ZNLfI9VckEyUSx3AGBFH7RImeYiWb6vZA9v5DE7kBrCoHtJK5IN9dvq
101+
EWXrrDT7RTFXd55xQqT70eZiIDNz1nexDw8ZCnAAAAAwEAAQAAAQAnvU5kb+mfhGaeBwb2
102+
tIn9dVTKicIoezbTJOiOOKTppMjXgC8euf0/7WuBoYGJmg38rlNR6dEvMqyaj0wvkTQtR9
103+
yQrmTuoljHkrna5TPYBswWcOMeEk6K7Md/4wfulugsiS+DgJah0xN3hKj5t9o848/wtCvP
104+
r3iL+ZrNocFW4Ju+QrArFWTLFuJL4uc69ykgWE7I5Qkm+3Lg6aSoNazMzCu9rCblduetJq
105+
EilQ6AOkv68xTOQ1EDIQc8xr6u6GCUvVVBwYaR3cYV6fWeLWJATqUODkEXdDZfgUerf4Io
106+
3KirdRf0YFyJiHJh4AqWd76jWCkhCwrREx0lfMCZghoxAAAAgHwOfMJtd4wOug2BPKu0SA
107+
HSwQ+yTTibg2xuENstd8akJC3VsU5GC8pngNAyoFpSt3QDlLpvqPqXVJSkkMbUtnPO0SIR
108+
5ffMB97kFvNkMNDUIalwxR9DV1CMPTAnTO7NSfO8UUKRjKivpmpS6ptMjxUM0hPoDBebhx
109+
P37In1a2jDAAAAgQDVCaoMFjHRGds1JaVjm6YviR0C2OsE55GOS7cW+I3SE63DumfHsN8i
110+
r/u5oEQUelaauYVmi9tT3L4lReFX2tYqtyE0mbPUXcY5XfmBxBsjW1sQ6YyHlN/vGLgo33
111+
NZZFpIg2FknTzM4qeddfbyKuqAJX27f7RrSZCf+WrJUKDWqwAAAIEAxFAn6d9na7uHnb31
112+
TQ8PoTvkH7fwugXuG7ACLCTl3PpOSGPQAPI8rCaGOMd+uU1Jyjt3TcdPYlNAtiFQCxWLMH
113+
RNFfeqviC85H6WzQNezNj45QqKTf5gRdHVu2NMRwn2pJjRgdIvsUaL1AY4sC0AivoEMlpx
114+
rQYvdaDG7KsYXfUAAAAEdXNlcgECAwQFBgc=
115+
-----END OPENSSH PRIVATE KEY-----`
116+
117+
randomPubKey2 := `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDmc1i/FqnVtYsTzb6LmoUGom8ISnfRCPTIFf3LLIyRFgO+qD6Dnqn5p2lLE8ksdooAGJ+EyJtV5c+3kYGnjzzH4TlB2pkt562BntrggvJ98sELQbHEDiemiLnJqqIESk5FcSXdcJ/UX/AdkbXLjSR5M8+cGGqKSb0HSnKfOWkjWwZwp/JwbvyWPIJ6IQNKzAS5HVU/J+u8ezhPd1iBdezvAuPlihpjMGQg1KW3APZoELS6/BSMpXcvDy+TwuggEPPZ0Up09BJRtqesHiZur6CnqUIzJcCWCfi5C8IfHzlhawry+iA1V5Lh06Mz7OaySXpf902RITfh+KcLxcSSMmPl user`
118+
randomPrivKey2 := `-----BEGIN OPENSSH PRIVATE KEY-----
119+
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
120+
NhAAAAAwEAAQAAAQEA5nNYvxap1bWLE82+i5qFBqJvCEp30Qj0yBX9yyyMkRYDvqg+g56p
121+
+adpSxPJLHaKABifhMibVeXPt5GBp488x+E5QdqZLeetgZ7a4ILyffLBC0GxxA4npoi5ya
122+
qiBEpORXEl3XCf1F/wHZG1y40keTPPnBhqikm9B0pynzlpI1sGcKfycG78ljyCeiEDSswE
123+
uR1VPyfrvHs4T3dYgXXs7wLj5YoaYzBkINSltwD2aBC0uvwUjKV3Lw8vk8LoIBDz2dFKdP
124+
QSUbanrB4mbq+gp6lCMyXAlgn4uQvCHx85YWsK8vogNVeS4dOjM+zmskl6X/dNkSE34fin
125+
C8XEkjJj5QAAA8CppfYQqaX2EAAAAAdzc2gtcnNhAAABAQDmc1i/FqnVtYsTzb6LmoUGom
126+
8ISnfRCPTIFf3LLIyRFgO+qD6Dnqn5p2lLE8ksdooAGJ+EyJtV5c+3kYGnjzzH4TlB2pkt
127+
562BntrggvJ98sELQbHEDiemiLnJqqIESk5FcSXdcJ/UX/AdkbXLjSR5M8+cGGqKSb0HSn
128+
KfOWkjWwZwp/JwbvyWPIJ6IQNKzAS5HVU/J+u8ezhPd1iBdezvAuPlihpjMGQg1KW3APZo
129+
ELS6/BSMpXcvDy+TwuggEPPZ0Up09BJRtqesHiZur6CnqUIzJcCWCfi5C8IfHzlhawry+i
130+
A1V5Lh06Mz7OaySXpf902RITfh+KcLxcSSMmPlAAAAAwEAAQAAAQEAq2opHRpSgfBj3vsv
131+
PNBXGrRAOr6JmSc8TIhvG22rsU/awTqMJYMjk9v+6iVxgm06ARBPt4kwYhhrBXRqKKTW5S
132+
aWXHGpdwfZe40Z6d39Wcnz5debzuVogOs6ptMRaHeM+QJM1AYuHN6v0I7N1vbJpo3vY4CV
133+
3v8yZ/XshJtDpVNqHFuCh1r07aW4NlqoTy5TEvWD1VPCqAVwTLWuNMfWRGYbwqJrRUxuu3
134+
6vqddE8yMONYMwVRKPADj0DTi3i+LK3v6QfJlxb09EhqJPOOXM+fBVzUWkUXlPjvMP4uUH
135+
/zRrGscSI93n0V/H3/XTOJTskdEZUEFpeFbUXIphloCKEQAAAIEA9CJapVXG9HcKimXX3I
136+
OQdwPoKONM52KnAoWjGO1N5ECydjz2yHQkNJNLFwAUefmKVy0/ce0EdyEJjoHKvCwoTWL6
137+
3CPlWQY+7pk0Fr62iT7UjjGwCtmHB6B5G4qUlsBkVN3WCwfmBwYrziRR+qcS8hSS7m37Uy
138+
rMbGGIHHVGPzIAAACBAP6ouUUlIN7jLdLxyApj1Cx7oW7Gp33j3goXn82WVv6+ubPJymVD
139+
u7zmoWWVegOngoPlR1q/mHBGoB1Ec1Im5IaN5qzVrxVKraJz5Q1XRc/azpkYb1FaDFBW2O
140+
iDaP5PHvNQpYcmE82Dg8bUqa7tYIUgq2vqHJdBZC5IvnYnGrWbAAAAgQDnqf2DVITbK5jK
141+
UJqEmni0YE8PD3PuPGRWLmZeOcxshHR1nQIeUoXWAhCS9G7Rl5Kdr1IXzSln22OvUXMPmE
142+
gZLd7QJVyRQ0bXhYf8nIs/UGhjq83OSoS4iSwHeZ1CrKWmVP74/+Na6fDdfJ65Z8+I4ktM
143+
QC3v6moZVb2wrgGkfwAAAAR1c2VyAQIDBAU=
144+
-----END OPENSSH PRIVATE KEY-----`
145+
146+
acctest.VcrTest(t, resource.TestCase{
147+
PreCheck: func() { acctest.AccTestPreCheck(t) },
148+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
149+
ExternalProviders: map[string]resource.ExternalProvider{
150+
"time": {
151+
Source: "registry.terraform.io/hashicorp/time",
152+
},
153+
},
154+
CheckDestroy: testAccCheckDatastreamConnectionProfileDestroyProducer(t),
155+
Steps: []resource.TestStep{
156+
{
157+
Config: testAccDatastreamConnectionProfile_sshKey_update(context, true, randomPrivKey1, randomPubKey1),
158+
},
159+
{
160+
ResourceName: "google_datastream_connection_profile.ssh_connectivity_profile",
161+
ImportState: true,
162+
ImportStateVerify: true,
163+
ImportStateVerifyIgnore: []string{"connection_profile_id", "location", "create_without_validation", "forward_ssh_connectivity.0.private_key", "postgresql_profile.0.password"},
164+
},
165+
{
166+
PreConfig: func() {
167+
fmt.Println("Waiting before proceeding to the next step...")
168+
time.Sleep(150 * time.Second) // Delay before the next step
169+
},
170+
Config: testAccDatastreamConnectionProfile_sshKey_update(context, true, randomPrivKey2, randomPubKey2),
171+
},
172+
{
173+
ResourceName: "google_datastream_connection_profile.ssh_connectivity_profile",
174+
ImportState: true,
175+
ImportStateVerify: true,
176+
ImportStateVerifyIgnore: []string{"connection_profile_id", "location", "create_without_validation", "forward_ssh_connectivity.0.private_key", "postgresql_profile.0.password"},
177+
},
178+
{
179+
PreConfig: func() {
180+
fmt.Println("Waiting before proceeding to the next step...")
181+
time.Sleep(150 * time.Second) // Delay before the next step
182+
},
183+
Config: testAccDatastreamConnectionProfile_sshKey_update(context, false, randomPrivKey2, randomPubKey2),
184+
},
185+
},
186+
})
187+
}
188+
79189
func testAccDatastreamConnectionProfile_update(context map[string]interface{}) string {
80190
return acctest.Nprintf(`
81191
resource "google_datastream_connection_profile" "default" {
@@ -247,3 +357,157 @@ resource "google_datastream_connection_profile" "mysql_con_profile" {
247357
}
248358
`, context)
249359
}
360+
361+
func testAccDatastreamConnectionProfile_sshKey_update(context map[string]interface{}, preventDestroy bool, private_key string, public_key string) string {
362+
context["lifecycle_block"] = ""
363+
if preventDestroy {
364+
context["lifecycle_block"] = `
365+
lifecycle {
366+
prevent_destroy = true
367+
}`
368+
}
369+
context["private_key"] = private_key
370+
context["public_key"] = public_key
371+
372+
return acctest.Nprintf(`
373+
resource "google_compute_network" "default" {
374+
name = "tf-test-datastream-ssh%{random_suffix}"
375+
}
376+
377+
resource "google_sql_database_instance" "instance" {
378+
depends_on = [google_compute_instance.default]
379+
name = "tf-test-my-database-instance%{random_suffix}"
380+
database_version = "POSTGRES_14"
381+
region = "us-central1"
382+
settings {
383+
tier = "db-f1-micro"
384+
ip_configuration {
385+
ipv4_enabled = true
386+
387+
authorized_networks {
388+
value = google_compute_instance.default.network_interface.0.access_config.0.nat_ip
389+
}
390+
}
391+
}
392+
393+
deletion_protection = "false"
394+
}
395+
396+
resource "google_sql_database" "db" {
397+
depends_on = [google_sql_database_instance.instance]
398+
instance = google_sql_database_instance.instance.name
399+
name = "db"
400+
}
401+
402+
resource "google_sql_user" "user" {
403+
depends_on = [google_sql_database_instance.instance]
404+
name = "user"
405+
instance = google_sql_database_instance.instance.name
406+
password = "password%{random_suffix}"
407+
}
408+
409+
resource "google_compute_instance" "default" {
410+
name = "tf-test-instance-%{random_suffix}"
411+
machine_type = "e2-small"
412+
zone = "us-central1-a"
413+
boot_disk {
414+
initialize_params {
415+
image = "debian-11-bullseye-v20241009"
416+
}
417+
}
418+
419+
network_interface {
420+
network = google_compute_network.default.name
421+
access_config {}
422+
}
423+
424+
metadata = {
425+
"ssh-keys" = "user:%{public_key}"
426+
}
427+
428+
metadata_startup_script = <<-EOT
429+
#!/bin/bash
430+
echo "Updating SSHD config for SSH forwarding..."
431+
432+
# Backup sshd_config
433+
echo "AllowTcpForwarding yes" >> /etc/ssh/sshd_config
434+
echo "PasswordAuthentication no" >> /etc/ssh/sshd_config
435+
echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config
436+
echo "AuthorizedKeysFile .ssh/authorized_keys" >> /etc/ssh/sshd_config
437+
438+
# Restart SSH service
439+
systemctl restart sshd
440+
EOT
441+
442+
tags = ["ssh-host"]
443+
444+
depends_on = [google_compute_firewall.ssh, google_compute_firewall.datastream_sql_access]
445+
446+
}
447+
448+
resource "time_sleep" "ssh_host_wait" {
449+
depends_on = [google_compute_instance.default]
450+
create_duration = "12m"
451+
}
452+
453+
resource "google_compute_firewall" "ssh" {
454+
name = "tf-test-%{random_suffix}"
455+
network = google_compute_network.default.name
456+
457+
allow {
458+
protocol = "tcp"
459+
ports = ["22"]
460+
}
461+
462+
direction = "INGRESS"
463+
priority = 1000
464+
source_ranges = ["34.71.242.81", "34.72.28.29", "34.67.6.157", "34.67.234.134", "34.72.239.218"]
465+
466+
target_tags = ["ssh-host"]
467+
}
468+
469+
resource "google_compute_firewall" "datastream_sql_access" {
470+
name = "datastream-to-cloudsql-%{random_suffix}"
471+
network = google_compute_network.default.name
472+
473+
allow {
474+
protocol = "tcp"
475+
ports = ["5432"]
476+
}
477+
478+
direction = "INGRESS"
479+
priority = 1000
480+
source_ranges = ["34.71.242.81", "34.72.28.29", "34.67.6.157", "34.67.234.134", "34.72.239.218"] #Datastream IPs
481+
482+
}
483+
484+
resource "google_datastream_connection_profile" "ssh_connectivity_profile" {
485+
display_name = "Source connection profile"
486+
location = "us-central1"
487+
connection_profile_id = "tf-test-pg-profile%{random_suffix}"
488+
489+
postgresql_profile {
490+
hostname = google_sql_database_instance.instance.public_ip_address
491+
username = google_sql_user.user.name
492+
password = google_sql_user.user.password
493+
database = google_sql_database.db.name
494+
port = 5432
495+
}
496+
497+
forward_ssh_connectivity {
498+
hostname = google_compute_instance.default.network_interface.0.access_config.0.nat_ip
499+
username = google_sql_user.user.name
500+
port = 22
501+
private_key = <<EOT
502+
%{private_key}
503+
EOT
504+
}
505+
506+
depends_on = [time_sleep.ssh_host_wait]
507+
timeouts {
508+
create = "10m"
509+
}
510+
%{lifecycle_block}
511+
}
512+
`, context)
513+
}

0 commit comments

Comments
 (0)