Invoking VMware Cloud on AWS REST API calls from Terraform
Demonstrates using the Terraform http datasource to directly invoke the VMware Cloud on AWS API
I needed to programmatically retrieve the source NAT (SNAT) IPs for my SDDC - there is one SNAT for the management gateway (MGW) and one SNAT for the compute gateway (CGW). I needed those values as variables for use in a Terraform script. The values can be found in the sddc-user-config
API call. You can use Terraform's http datasource to retrieve data from a REST API.
To accelerate development, I relied on all of the VMC API work already done in Python in the SDDC Import/Export for VMware Cloud on AWS Fling.
Service URLs
First, I need service URLs. I copy the values from the Fling and add them to locals.tf
main.tf
locals { prod_url = "https://vmc.vmware.com" csp_prod_url = "https://console.cloud.vmware.com" }
Input Variables
I need 3 variables - a refresh token, an org ID, and an SDDC ID.
variables.tf
variable "vmc_org_id" { type = string description = "The VMware Cloud long organization identifier (eg: 01234567-89ab-cdef-0123-456789abcdef)." } variable "vmc_sddc_id" { type = string description = "The VMware Cloud long SDDC identifier (eg: 01234567-89ab-cdef-0123-456789abcdef)." } variable "vmc_refresh_token" { type = string description = "The VMware Cloud Services API token. This token is scoped within the organization." sensitive = true }
Now I need to populate my variables. Org ID and SDDC ID are somewhat sensitive, but not as sensitive as an API token. I decide I will store 2 values in tfvars, and the API token as an environment variable
terraform.tfvars
vmc_org_id = "01234567-89ab-cdef-0123-456789abcdef" vmc_sddc_id = "31234567-89ab-cdef-0123-456789abcdef"
I generate a VMware Cloud API token, then save it as a Terraform environment variable.
Shell (Mac/Linux)
export TF_VAR_vmc_refresh_token="<insert API token>"
PowerShell (Windows)
$env:TF_VAR_vmc_refresh_token = "<insert API token>"
HTTP functions
When I was developing these functions, I first wrote vmc_access_token
, then stored the resulting access_token
in a local variable. Then back to main for the next function, and back to locals for saving the results. Rather than going back and forth between main.tf
and locals.tf
in this post, I'm going to show you all of the file contents together.
main.tf
vmc_access_token
First, exchange the refresh token for an access token.
data "http" "vmc_access_token" { url = "${local.csp_prod_url}/csp/gateway/am/api/auth/api-tokens/authorize?refresh_token=${var.vmc_refresh_token}" method = "POST" request_headers = { Content-Type = "application/json", Accept = "application/json" } lifecycle { postcondition { condition = contains([200], self.status_code) error_message = "Status code invalid" } } }
vmc_nsxt_proxy
Retrieve the NSX-T reverse proxy URL
data "http" "vmc_nsxt_proxy" { url = "${local.prod_url}/vmc/api/orgs/${var.vmc_org_id}/sddcs/${var.vmc_sddc_id}" method = "GET" request_headers = { Content-Type = "application/json", Accept = "application/json", csp-auth-token = local.access_token } lifecycle { postcondition { condition = contains([200], self.status_code) error_message = "Status code invalid" } } }
vmc_sddc_user_config
Retrieve the SDDC User Config data
data "http" "vmc_sddc_user_config" { url = "${local.proxy_url_short}/api/orgs/${var.vmc_org_id}/sddcs/${var.vmc_sddc_id}/cloud-service/api/v1/infra/sddc-user-config" method = "GET" request_headers = { Content-Type = "application/json", Accept = "application/json", csp-auth-token = local.access_token } lifecycle { postcondition { condition = contains([200], self.status_code) error_message = "Status code invalid" } } }
Saving data in local variables
I add another section of code to locals.tf
to extract specific keys from the JSON payload and save them into local variables.
locals.tf
locals { access_token = sensitive(jsondecode(data.http.vmc_access_token.response_body)["access_token"]) proxy_url = jsondecode(data.http.vmc_nsxt_proxy.response_body)["resource_config"]["nsx_api_public_endpoint_url"] proxy_url_short = trimsuffix(local.proxy_url,"/sks-nsxt-manager") cgw_snat_ip = jsondecode(data.http.vmc_sddc_user_config.response_body)["cgw_snat_ip"] mgw_snat_ip = jsondecode(data.http.vmc_sddc_user_config.response_body)["mgw_snat_ip"] }
Outputs
To get the values displayed, I add the variables to outputs.tf
. I didn't need to display the proxy URLs but I added them here anyway
outputs.tf
output "proxy_url" { value = local.proxy_url } output "proxy_url_short" { value = local.proxy_url_short } output "cgw_snat_ip" { value = local.cgw_snat_ip } output "mgw_snat_ip" { value = local.mgw_snat_ip }
Results
I run the script with terraform init
and terraform apply
C:\tf> terraform init Initializing the backend... Initializing provider plugins... - Finding latest version of hashicorp/http... - Installing hashicorp/http v3.2.1... - Installed hashicorp/http v3.2.1 (signed by HashiCorp) Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary
C:\tf> terraform apply [output redacted] Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes Apply complete! Resources: 0 added, 0 changed, 0 destroyed. Outputs: cgw_snat_ip = "204.74.108.1" mgw_snat_ip = "204.74.108.2" proxy_url = "https://nsx-x-x-x-x.rp.vmwarevmc.com/vmc/reverse-proxy/api/orgs/[orgid]/sddcs/[sddcid]/sks-nsxt-manager" proxy_url_short = "https://nsx-x-x-x-x.rp.vmwarevmc.com/vmc/reverse-proxy/api/orgs/[orgid]/sddcs/[sddcid]"
Conclusion
This sample code only prints the values to the screen. With the values stored in a local variable, you can use them elsewhere in the Terraform script. I don't write much Terraform, but it was relatively simple (with a lot of Googling) to translate the existing Fling code from Python to Terraform's HCL.
Relevant content
- Accepted Answerasked 3 years agolg...
- asked 7 months agolg...
- Accepted Answerasked 3 years agolg...
- AWS OFFICIALUpdated 6 months ago
- AWS OFFICIALUpdated 7 months ago
- AWS OFFICIALUpdated 9 months ago