In this article, we will take a look on some Advanced Edge Gateway actions available to the tenant (client) and how to work with it via API using PowerShell. You can find links to the official documentation at the end of the article. In short, though, you can do the same things through the API as you do through the web interface for managing your cloud.
According to the role-based access model, changes are available to the client in the section Services > Firewall / NAT / VPN / ... For example, the following settings are available for view only, as they are changed by C4Y engineers:
- Rate Limits
- IP Allocations
- General > Name / Edge Gateway Configuration / ...
To help you understand the official documentation, we'll show how to work through the API by adding a DNAT rule to connect to a Windows server with RDP.
0 - preparation
First, we need to specify the data to work with - cloud, tenant (vOrg), Organization Administrator account and the name of Edge we are going to work with
#Requires -Version 7.2 # скрипт разрабатывался и тестировался с интерпретатором "C:\Program Files\PowerShell\7\pwsh.exe"
$Auth = @{
'cloud' = 'vcd.cloud4y.ru' # облако, с которым собираемся работать по API
'tenant' = 'demo-tenant' # имя вашей вОрг, определить можно по URL https://vcd.cloud4y.ru/tenant/demo-tenant/vdcs
'user' = 'administrator' # имя учётной записи с ролью 'Organization Administrator'
'password' = '3CQvyk3aZ0ZVCav4U' # пароль учётной записи
}
$Edge = 'demo-tenant_K41_VDC_Edge' # имя Edge, с которым вы собираетесь работать через API
$DemoAddingNatRule = $true # $true - добавить новое NAT-правило для демонстрации, $false - перечислить существующие правила
1 - open a session
The general principle of working with the cloud via API is as follows:
- find out the latest current version of the API
- open a work session, performing basic authorization in the cloud in your tenant with username and password
- for all other queries, use the token of the session opened at the previous step as authorization
$rApiVer = Invoke-WebRequest -UseBasicParsing -Method 'Get' -Headers @{"Accept"="application/*+json"} -Uri ('https://{0}/api/versions' -f $Auth['cloud'])
$rApiVerJson = [System.Text.Encoding]::UTF8.GetString($rApiVer.Content) | ConvertFrom-Json
$ApiVer = ($rApiVerJson.versionInfo | Where-Object {$_.deprecated -eq $false} | Select-Object -Last 1).version
$objCredential = '{0}@{1}:{2}' -f $Auth['user'], $Auth['tenant'], $Auth['password'] # Login@vOrg:Password
$CredentialBytes = [System.Text.Encoding]::UTF8.GetBytes($objCredential)
$CredentialBase64 = [System.Convert]::ToBase64String($CredentialBytes)
$HeadersAuthBasic = @{
'Accept' = 'application/*+json;version={0};multisite=global' -f $ApiVer
'Authorization' = 'Basic {0}' -f $CredentialBase64
}
$rAuth = Invoke-WebRequest -UseBasicParsing -Method 'Post' -Headers $HeadersAuthBasic -Uri ('https://{0}/api/sessions' -f $Auth['cloud'])
$rAuthBody = [System.Text.Encoding]::UTF8.GetString($rAuth.Content) | ConvertFrom-Json
$AuthHeaders = @{
'Accept' = 'application/*+json;version={0};multisite=global' -f $ApiVer
'Authorization' = '{0} {1}' -f ($rAuth.Headers['X-VMWARE-VCLOUD-TOKEN-TYPE'] -join ''), ($rAuth.Headers['X-VMWARE-VCLOUD-ACCESS-TOKEN'] -join '')
}
$uriNsx = $rAuthBody.link | Where-Object {$_.rel -eq 'nsx'} | Select-Object -ExpandProperty 'href'
$uriQuery = $rAuthBody.link | Where-Object {$_.href -match 'query' -and $_.type -match 'json'} | Select-Object -ExpandProperty 'href'
$uriSession = $rAuthBody.href
"`n{0}`nApiVer`t`t`t{1}`nQuery Service URL`t{2}`nNSX URL`t`t`t{3}`nHeaders`n{4}" -f 'step 1 - open session', $ApiVer, $uriQuery, $uriNsx, ($AuthHeaders | ConvertTo-Json) | Out-Default
step 1 - open session
ApiVer 35.0
Query Service URL https://vcd.cloud4y.ru/api/query
NSX URL https://vcd.cloud4y.ru/network
Headers
{
"Accept": "application/*+json;version=35.0;multisite=global",
"Authorization": "Bearer eyJhb...Zpa9FQ"
}
2 - Query Service
Query Service allows you to get basic information, including the ID of cloud objects: vOrg, vDC, VMs, networks, Edge Gateway, etc. Then, knowing the object ID, you can get detailed object properties, including links to change, via API.
Let's look at the valid queries and select those related to the router.
# посмотреть все возможные типы запросов можно так:
$rPossibleQueriesList = Invoke-WebRequest -UseBasicParsing -Method 'Get' -Headers $AuthHeaders -Uri $uriQuery
$rPossibleQueriesListBody = [System.Text.Encoding]::UTF8.GetString($rPossibleQueriesList.Content) | ConvertFrom-Json
"`n{0}" -f 'step 2 - list of queries' | Out-Default
$rPossibleQueriesListBody.link | Where-Object { $_.type -match 'json' -and $_.href -match '=records' } | Select-Object -Property 'name', 'href' | Sort-Object -Property 'name' | Out-Default
# а так мы узнаем допустимые запросы по Edge Gateway, которые вернут информацию в json-формате в records-представлении
$rPossibleQueriesListBody.link | Where-Object { $_.type -match 'json' -and $_.href -match '=records' -and $_.name -match 'gate' } | Format-List | Out-Default
step 2 - list of queries
name href
---- ----
adminApiDefinition https://vcd.cloud4y.ru/api/query?type=adminApiDefinition&format=records
adminFileDescriptor https://vcd.cloud4y.ru/api/query?type=adminFileDescriptor&format=records
adminService https://vcd.cloud4y.ru/api/query?type=adminService&format=records
apiDefinition https://vcd.cloud4y.ru/api/query?type=apiDefinition&format=records
catalog https://vcd.cloud4y.ru/api/query?type=catalog&format=records
catalogItem https://vcd.cloud4y.ru/api/query?type=catalogItem&format=records
disk https://vcd.cloud4y.ru/api/query?type=disk&format=records
edgeGateway https://vcd.cloud4y.ru/api/query?type=edgeGateway&format=records
event https://vcd.cloud4y.ru/api/query?type=event&format=records
externalLocalization https://vcd.cloud4y.ru/api/query?type=externalLocalization&format=records
fileDescriptor https://vcd.cloud4y.ru/api/query?type=fileDescriptor&format=records
fromCloudTunnel https://vcd.cloud4y.ru/api/query?type=fromCloudTunnel&format=records
gatewayUplinks https://vcd.cloud4y.ru/api/query?type=gatewayUplinks&format=records
group https://vcd.cloud4y.ru/api/query?type=group&format=records
media https://vcd.cloud4y.ru/api/query?type=media&format=records
organization https://vcd.cloud4y.ru/api/query?type=organization&format=records
orgNetwork https://vcd.cloud4y.ru/api/query?type=orgNetwork&format=records
orgVdc https://vcd.cloud4y.ru/api/query?type=orgVdc&format=records
orgVdcNetwork https://vcd.cloud4y.ru/api/query?type=orgVdcNetwork&format=records
orgVdcStorageProfile https://vcd.cloud4y.ru/api/query?type=orgVdcStorageProfile&format=records
orgVdcTemplate https://vcd.cloud4y.ru/api/query?type=orgVdcTemplate&format=records
right https://vcd.cloud4y.ru/api/query?type=right&format=records
role https://vcd.cloud4y.ru/api/query?type=role&format=records
service https://vcd.cloud4y.ru/api/query?type=service&format=records
strandedUser https://vcd.cloud4y.ru/api/query?type=strandedUser&format=records
task https://vcd.cloud4y.ru/api/query?type=task&format=records
toCloudTunnel https://vcd.cloud4y.ru/api/query?type=toCloudTunnel&format=records
user https://vcd.cloud4y.ru/api/query?type=user&format=records
vApp https://vcd.cloud4y.ru/api/query?type=vApp&format=records
vAppNetwork https://vcd.cloud4y.ru/api/query?type=vAppNetwork&format=records
vAppOrgVdcNetworkRelation https://vcd.cloud4y.ru/api/query?type=vAppOrgVdcNetworkRelation&format=records
vAppTemplate https://vcd.cloud4y.ru/api/query?type=vAppTemplate&format=records
vm https://vcd.cloud4y.ru/api/query?type=vm&format=records
vmDiskRelation https://vcd.cloud4y.ru/api/query?type=vmDiskRelation&format=records
otherAttributes :
href : https://vcd.cloud4y.ru/api/query?type=edgeGateway&format=records
id :
name : edgeGateway
type : application/vnd.vmware.vcloud.query.records+json
model :
rel : down
vCloudExtension : {}
otherAttributes :
href : https://vcd.cloud4y.ru/api/query?type=gatewayUplinks&format=records
id :
name : gatewayUplinks
type : application/vnd.vmware.vcloud.query.records+json
model :
rel : down
vCloudExtension : {}
3 - query the Edge in its vOrg (tenant)
The appropriate request type is edgeGateway, so we find out the ID required for further work with the router. For more details about the parameters defined below, see the official documentation available at the end of this article. If you have several VDCs in your Org, you can either get the list of all your routers, or specify the required one immediately using 'filter' = 'name==*edge_name*'
.
$params = [ordered] @{
'format' = 'records'
'type' = 'edgeGateway'
'page' = 1
'pageSize' = 128
# 'filter' = 'name==*{0}*' -f $Edge
}
$paramsStr = ($params.GetEnumerator() | ForEach-Object {$_.Key + '=' + $_.Value}) -join '&'
$rTenantEdges = Invoke-WebRequest -UseBasicParsing -Method 'Get' -Headers $AuthHeaders -Uri ('{0}?{1}' -f $uriQuery, $paramsStr)
$rTenantEdgesBody = [System.Text.Encoding]::UTF8.GetString($rTenantEdges.Content) | ConvertFrom-Json
"`n{0}" -f 'step 3 - edge list' | Out-Default
$rTenantEdgesBody.record | Select-Object -Property 'name', 'href' | Out-Default
step 3 - edge list
name href
---- ----
demo-tenant_M14_VDC_k8s_Edge https://vcd.cloud4y.ru/api/admin/edgeGateway/1e995b68-e864-45b0-bdb5-6e4fa2d4d487
demo-tenant_K41_VDC_Edge https://vcd.cloud4y.ru/api/admin/edgeGateway/b056f3c9-3712-46c6-98d1-a6e5eea2b99f
4 - Checking the Edge
Before moving on, you need to check if your router is an Advanced router, this will determine the way to proceed. If it is Advanced, you need to use the NSX API. If not, you can work with it as described in the article Changing EDGE settings with the vCloud API.
$uriEdge = $rTenantEdgesBody.record | Where-Object {$_.name -match $Edge} | Select-Object -ExpandProperty 'href'
$rEdgeFullInfo = Invoke-WebRequest -UseBasicParsing -Method 'Get' -Headers $AuthHeaders -Uri $uriEdge
$rEdgeFullInfoBody = [System.Text.Encoding]::UTF8.GetString($rEdgeFullInfo.Content) | ConvertFrom-Json
"`n{0}" -f 'step 4 - check target edge "advanced" property' | Out-Default
"`n'{0}' is{1} Advanced because 'AdvancedNetworkingEnabled' is {2}" -f $Edge, (' not' * [int][bool] (-not $rEdgeFullInfoBody.configuration.advancedNetworkingEnabled)), $rEdgeFullInfoBody.configuration.advancedNetworkingEnabled | Out-Default
# Verify that the Edge Gateway is not an Advanced Gateway
if ($rEdgeFullInfoBody.configuration.advancedNetworkingEnabled)
{
@(
'using the VMware Cloud Director API to configure Edge Gateway services can produce unexpected results'
'Use "VMware Cloud Director API for NSX Programming Guide" instead:'
' https://developer.vmware.com/docs/12789/vmware-cloud-director-api-for-nsx-programming-guide'
' https://vdc-download.vmware.com/vmwb-repository/dcr-public/6c72dc6b-b5c5-4563-b781-189f11fd7d51/ba3dd30d-4355-4b4f-91ee-a041a51decda/vmware_cloud_director_nsx_api_guide_35_0.pdf'
) | Out-Default
}
else
{
@(
'Use "VMware Cloud Director API Programming Guide":'
' https://vdc-download.vmware.com/vmwb-repository/dcr-public/715b0387-34d7-4568-b2d8-d11454c52d51/944f905e-fa4e-4005-be7d-19c3cea70ffd/vmware_cloud_director_sp_api_guide_35_0.pdf#unique_93'
' https://developer.vmware.com/docs/12676/vmware-cloud-director-api-programming-guide/GUID-1E7274A7-57D3-488F-9EFF-1D097FFE61A8.html'
' https://client.cloud4y.ru/knowledgebase.php?action=displayarticle&catid=19&id=665'
) | Out-Default
"`n{0}`n{1}" -f 'step 4 - edge full view', $uriEdge
$rEdgeFullInfoBody.link | Where-Object { $_.type -notmatch 'xml' } | Select-Object -Property 'rel', 'type', 'href' | Format-Table -AutoSize
$rEdgeFullInfoBody | ConvertTo-Json -Depth 13 | Out-File -FilePath ('./{0}.json' -f $rEdgeFullInfoBody.name)
}
step 4 - check target edge "advanced" property
'demo-tenant_K41_VDC_Edge' is Advanced because 'AdvancedNetworkingEnabled' is True
using the VMware Cloud Director API to configure Edge Gateway services can produce unexpected results
Use "VMware Cloud Director API for NSX Programming Guide" instead:
https://developer.vmware.com/docs/12789/vmware-cloud-director-api-for-nsx-programming-guide
https://vdc-download.vmware.com/vmwb-repository/dcr-public/6c72dc6b-b5c5-4563-b781-189f11fd7d51/ba3dd30d-4355-4b4f-91ee-a041a51decda/vmware_cloud_director_nsx_api_guide_35_0.pdf
5 - Configuring Advanced Edge Gateway
$id = $uriEdge -split '/' | Select-Object -Last 1
$rNat = Invoke-WebRequest -UseBasicParsing -Method 'Get' -Headers $AuthHeaders -Uri ('{0}/edges/{1}/nat/config' -f $uriNsx, $id)
$rNat = $rNat.Content | ConvertFrom-Json
$rNat | ConvertTo-Json -Depth 13 | Out-File -FilePath ('./{0}_nat.json' -f $rEdgeFullInfoBody.name)
if ($DemoAddingNatRule)
{
$rNat.rules.natRulesDtos += [PSCustomObject] @{
# ruleId = 0
# ruleTag = 0
loggingEnabled = $false
enabled = $false
description = 'DEMO - JUST ADDING ONE MORE DISABLING NAT RULE, ALLOWING RDP OVER UDP'
translatedAddress = '192.168.0.123'
ruleType = 'user'
action = 'dnat'
vnic = '0'
originalAddress = '176.53.183.149'
dnatMatchSourceAddress = 'any'
protocol = 'udp'
originalPort = '3389'
translatedPort = '3389'
dnatMatchSourcePort = 'any'
}
$rNat.rules.natRulesDtos | Format-Table -AutoSize
$rNatMod = Invoke-WebRequest -UseBasicParsing -Method 'Put' -Headers $AuthHeaders -Uri ('{0}/edges/{1}/nat/config' -f $uriNsx, $id) -Body ($rNat | ConvertTo-Json -Depth 13) -ContentType "application/*+json;charset=UTF-8"
$rNatMod | Out-Default
}
else
{
$rNat.rules.natRulesDtos | Format-Table -AutoSize | Out-Default
}
ruleId ruleTag loggingEnabled enabled description translatedAddress ruleT
ype
------ ------- -------------- ------- ----------- ----------------- -----
196609 196609 False True allow Internet access for demo-tenant_K41_VDC_LAN 176.53.183.149 user
196610 196610 False True allow RDP to unexisting VM 192.168.0.123 user
False False DEMO - JUST ADDING ONE MORE DISABLING NAT RULE, ALLOWING RDP OVER UDP 192.168.0.123 user
StatusCode : 204
StatusDescription : No Content
Content : {}
RawContent : HTTP/1.1 204 No Content
Connection: close
X-VMWARE-VCLOUD-REQUEST-ID: ff2d5311-a0fa-477e-83e2-30750618e20b
Strict-Transport-Security: max-age=31536000
X-XSS-Protection: 1; mode=block
X-Content-Ty...
Headers : {[Connection, close], [X-VMWARE-VCLOUD-REQUEST-ID, ff2d5311-a0fa-477e-83e2-30750618e20b], [Strict-Transport-Sec
urity, max-age=31536000], [X-XSS-Protection, 1; mode=block]...}
RawContentLength : 0
Resources