Invoking VMware Cloud on AWS REST API calls from Terraform

5 minute read
Advanced
1

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.

profile picture
AWS
EXPERT
published a month ago211 views