Skip to content

Commit 874650e

Browse files
modular-magicianSarahFrench
authored andcommitted
Add CV user guide page (#8135) (#5768)
* Add CV guide page * Fix h2 headers in markdown * Fix HCL formatting * Fix name of `plantimestamp` function Signed-off-by: Modular Magician <[email protected]>
1 parent 71aff59 commit 874650e

File tree

2 files changed

+226
-0
lines changed

2 files changed

+226
-0
lines changed

.changelog/8135.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:none
2+
3+
```
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
---
2+
page_title: "Using Terraform Cloud's Continuous Validation feature with the Google Provider"
3+
description: |-
4+
Continuous validation helps identify issues immediately and continuously instead of waiting until customers encounter problems. This guide shows how continuous validation can be used with the Google provider.
5+
---
6+
7+
# Using Terraform Cloud's Continuous Validation feature with the Google Provider
8+
9+
The Continuous Validation feature in Terraform Cloud (TFC) allows users to make assertions about their infrastructure between applied runs. This helps users to identify issues at the time they first appear and avoid situations where a change is only identified during a future terraform plan/apply or once it causes a user-facing problem.
10+
11+
Users can add checks to their Terraform configuration using an HCL language feature called [check{} blocks](https://developer.hashicorp.com/terraform/language/checks). Check blocks contain assertions that are defined with a custom condition expression and an error message. When the condition expression evaluates to true the check passes, but when the expression evaluates to false Terraform will show a warning message that includes the user-defined error message.
12+
13+
Custom conditions can be created using data from Terraform providers’ resources and data sources. Data can also be combined from multiple sources; for example, you can use checks to monitor expirable resources by comparing a resource’s expiration date attribute to the current time returned by Terraform’s built-in time functions. These include the [plantimestamp function](https://developer.hashicorp.com/terraform/language/functions/plantimestamp), which was added in Terraform 1.5.
14+
15+
For more information about continuous validation visit the [Workspace Health page in the Terraform Cloud documentation](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/health#continuous-validation).
16+
17+
Below, this guide shows examples of how data returned by the Google provider can be used to define checks in your Terraform configuration. In each example it is assumed that the Google provider is configured with a default project, region, and zone.
18+
19+
~> Check blocks and the plantime function are available in Terraform 1.5 and later
20+
21+
## Example - Assert a VM is in a running state (`google_compute_instance`)
22+
23+
VM instances provisioned using Compute Engine can pass through several states as part of the [VM instance lifecycle](https://cloud.google.com/compute/docs/instances/instance-life-cycle). Once a VM is provisioned it could experience an error, or a user could suspend or stop that VM in the Google Cloud console, without that change being detected until the next Terraform plan is generated. Continuous validation can be used to assert the state of a VM and detect if there are any unexpected status changes that occur out-of-band.
24+
25+
The example below shows how a check block can be used to assert that a VM is in the running state.
26+
27+
You can force the check to fail in this example by provisioning the VM, manually stopping it in the Google Cloud console, and then triggering a health check in TFC. The check will fail and report that the VM is not running.
28+
29+
```hcl
30+
data "google_compute_network" "default" {
31+
name = "default"
32+
}
33+
resource "google_compute_instance" "vm_instance" {
34+
name = "my-instance"
35+
machine_type = "f1-micro"
36+
boot_disk {
37+
initialize_params {
38+
image = "debian-cloud/debian-11"
39+
}
40+
}
41+
network_interface {
42+
network = data.google_compute_network.default.name
43+
access_config {
44+
}
45+
}
46+
}
47+
check "check_vm_status" {
48+
data "google_compute_instance" "vm_instance" {
49+
name = google_compute_instance.vm_instance.name
50+
}
51+
assert {
52+
condition = data.google_compute_instance.vm_instance.current_status == "RUNNING"
53+
error_message = format("Provisioned VMs should be in a RUNNING status, instead the VM `%s` has status: %s",
54+
data.google_compute_instance.vm_instance.name,
55+
data.google_compute_instance.vm_instance.current_status
56+
)
57+
}
58+
}
59+
```
60+
61+
## Example - Check if a certificate will expire within a certain timeframe (`google_privateca_certificate`)
62+
63+
Certificates can be provisioned using either the Certificate Manager, Certificate Authority Service (‘Private CA’), and Compute Engine APIs. In this example we provision a certificate via the Certificate Authority Service that has a user-supplied [lifetime argument](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/privateca_certificate#lifetime). After the lifetime duration passes the certificate is automatically deleted in GCP. By creating a check that asserts the certificate’s expiration date is more than 30 days away we can be notified by TFC health checks when the certificate is approaching expiration and needs manual intervention.
64+
65+
In the example below we provision a certificate with a lifetime of 30 days and 2 minutes (see `local.month_and_2min_in_second_duration`) and create a check that asserts certificates should be valid for the next month (see `local.month_in_hour_duration`).
66+
67+
We can see the check begin to fail by waiting 2 minutes after the certificate is provisioned and then triggering a health check in TFC. The check will fail and report that the certificate is due to expire in less than a month.
68+
69+
```hcl
70+
locals {
71+
month_in_hour_duration = "${24 * 30}h"
72+
month_and_2min_in_second_duration = "${(60 * 60 * 24 * 30) + (60 * 2)}s"
73+
}
74+
resource "tls_private_key" "example" {
75+
algorithm = "RSA"
76+
}
77+
resource "tls_cert_request" "example" {
78+
private_key_pem = tls_private_key.example.private_key_pem
79+
subject {
80+
common_name = "example.com"
81+
organization = "ACME Examples, Inc"
82+
}
83+
}
84+
resource "google_privateca_ca_pool" "default" {
85+
name = "my-ca-pool"
86+
location = "us-central1"
87+
tier = "ENTERPRISE"
88+
publishing_options {
89+
publish_ca_cert = true
90+
publish_crl = true
91+
}
92+
labels = {
93+
terraform = true
94+
}
95+
issuance_policy {
96+
baseline_values {
97+
ca_options {
98+
is_ca = false
99+
}
100+
key_usage {
101+
base_key_usage {
102+
digital_signature = true
103+
key_encipherment = true
104+
}
105+
extended_key_usage {
106+
server_auth = true
107+
}
108+
}
109+
}
110+
}
111+
}
112+
resource "google_privateca_certificate_authority" "test-ca" {
113+
Deletion_protection = false
114+
certificate_authority_id = "my-authority"
115+
location = google_privateca_ca_pool.default.location
116+
pool = google_privateca_ca_pool.default.name
117+
config {
118+
subject_config {
119+
subject {
120+
country_code = "us"
121+
organization = "google"
122+
organizational_unit = "enterprise"
123+
locality = "mountain view"
124+
province = "california"
125+
street_address = "1600 amphitheatre parkway"
126+
postal_code = "94109"
127+
common_name = "my-certificate-authority"
128+
}
129+
}
130+
x509_config {
131+
ca_options {
132+
is_ca = true
133+
}
134+
key_usage {
135+
base_key_usage {
136+
cert_sign = true
137+
crl_sign = true
138+
}
139+
extended_key_usage {
140+
server_auth = true
141+
}
142+
}
143+
}
144+
}
145+
type = "SELF_SIGNED"
146+
key_spec {
147+
algorithm = "RSA_PKCS1_4096_SHA256"
148+
}
149+
}
150+
resource "google_privateca_certificate" "default" {
151+
name = "my-certificate"
152+
pool = google_privateca_ca_pool.default.name
153+
certificate_authority = google_privateca_certificate_authority.test-ca.certificate_authority_id
154+
location = google_privateca_ca_pool.default.location
155+
lifetime = local.month_and_2min_in_second_duration # lifetime is 2mins over the threshold in the check block below
156+
pem_csr = tls_cert_request.example.cert_request_pem
157+
}
158+
check "check_certificate_state" {
159+
assert {
160+
condition = timecmp(plantimestamp(), timeadd(
161+
google_privateca_certificate.default.certificate_description[0].subject_description[0].not_after_time,
162+
"-${local.month_in_hour_duration}")) < 0
163+
error_message = format("Provisioned certificates should be valid for at least 30 days, but `%s`is due to expire on `%s`.",
164+
google_privateca_certificate.default.name,
165+
166+
google_privateca_certificate.default.certificate_description[0].subject_description[0].not_after_time
167+
)
168+
}
169+
}
170+
```
171+
172+
## Example - Validate the status of a Cloud Function (`google_cloudfunctions2_function`)
173+
174+
Cloud Functions can have multiple statuses depending on issues that occur during deployment or triggering the function. These are: ACTIVE, FAILED, DEPLOYING, DELETING
175+
176+
In the example below we create a 2nd generation cloud function that uses source code stored as a .zip file in a GCS bucket. A .zip file containing the files needed by the function is uploaded by Terraform from the local machine. In the check we use the `google_cloudfunctions2_function` data source’s [state attribute](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloudfunctions2_function#state) to access the function’s state and assert that the function is active.
177+
178+
```hcl
179+
resource "google_storage_bucket" "bucket" {
180+
name = "my-bucket"
181+
location = "US"
182+
uniform_bucket_level_access = true
183+
}
184+
resource "google_storage_bucket_object" "object" {
185+
name = "function-source.zip"
186+
bucket = google_storage_bucket.bucket.name
187+
source = "./function-source.zip"
188+
}
189+
resource "google_cloudfunctions2_function" "my-function" {
190+
name = "my-function"
191+
location = "us-central1"
192+
description = "a new function"
193+
build_config {
194+
runtime = "nodejs12"
195+
entry_point = "helloHttp"
196+
source {
197+
storage_source {
198+
bucket = google_storage_bucket.bucket.name
199+
object = google_storage_bucket_object.object.name
200+
}
201+
}
202+
}
203+
service_config {
204+
max_instance_count = 1
205+
available_memory = "1536Mi"
206+
timeout_seconds = 30
207+
}
208+
}
209+
check "check_cf_state" {
210+
data "google_cloudfunctions2_function" "my-function" {
211+
name = google_cloudfunctions2_function.my-function.name
212+
location = google_cloudfunctions2_function.my-function.location
213+
}
214+
assert {
215+
condition = data.google_cloudfunctions2_function.my-function.state == "ACTIVE"
216+
error_message = format("Provisioned Cloud Functions should be in an ACTIVE state, instead the function `%s` has state: %s",
217+
data.google_cloudfunctions2_function.my-function.name,
218+
data.google_cloudfunctions2_function.my-function.state
219+
)
220+
}
221+
}
222+
```
223+

0 commit comments

Comments
 (0)