Broadcom.Community.VCFLicensing.psm1
|
# Author: William Lam # Description: PowerShell Module to automate between Broadcom VCF Business Service Console (BSC) and VCF Operations 9.x # --- Helper Functions for Part Construction --- Function Get-MultipartFormDataPart { param ( [string] $Name, [string] $Value, [string] $Boundary ) # The string must be fully formatted before converting to bytes $part = "" $part += "--$Boundary`r`n" $part += "Content-Disposition: form-data; name=`"$Name`"`r`n" $part += "`r`n" $part += "$Value`r`n" # Using ASCII for headers and boundaries is standard/safer than UTF8 return [System.Text.Encoding]::ASCII.GetBytes($part) } Function Get-MultipartFilePart { param ( [string] $Name, [string] $FilePath, [string] $Boundary ) $fileName = [System.IO.Path]::GetFileName($FilePath) $contentBytes = [System.IO.File]::ReadAllBytes($FilePath) $contentType = "application/octet-stream" $partHeader = "" # Add an extra CRLF before the boundary if it's the second part $partHeader += "`r`n--$Boundary`r`n" $partHeader += "Content-Disposition: form-data; name=`"$Name`"; filename=`"$fileName`"`r`n" $partHeader += "Content-Type: $contentType`r`n" $partHeader += "`r`n" # Blank line separates headers from content # Convert header parts to ASCII bytes $headerBytes = [System.Text.Encoding]::ASCII.GetBytes($partHeader) $trailerBytes = [System.Text.Encoding]::ASCII.GetBytes("`r`n") # CRLF after file content # Combine: Header Bytes + File Content Bytes + Trailer Bytes $combined = New-Object System.Byte[] ($headerBytes.Length + $contentBytes.Length + $trailerBytes.Length) [Array]::Copy($headerBytes, 0, $combined, 0, $headerBytes.Length) [Array]::Copy($contentBytes, 0, $combined, $headerBytes.Length, $contentBytes.Length) [Array]::Copy($trailerBytes, 0, $combined, $headerBytes.Length + $contentBytes.Length, $trailerBytes.Length) return $combined } # --- End Helper Functions --- Function Invoke-MultipartUpload { param( [Parameter(Mandatory=$true)] [string]$Uri, [Parameter(Mandatory=$true)] [Hashtable]$Headers, [Parameter(Mandatory=$true)] [string]$FilePath, [Parameter(Mandatory=$false)] [string]$NameValue = $null, # Optional text field required by license-mgmt V2 registration (e.g. MANUAL) [Parameter(Mandatory=$false)] [string]$RegistrationMode = $null, # multipart form-data name for the binary part (default file; e.g. challenge for verification upload) [Parameter(Mandatory=$false)] [string]$FileFieldName = 'file', [Parameter(Mandatory=$false)] [switch]$SkipCertCheck, # Return 4xx/5xx responses instead of throwing (PS 6+: Invoke-WebRequest -SkipHttpErrorCheck; PS 5.1: WebException catch) [Parameter(Mandatory=$false)] [switch]$SkipHttpErrorCheck, [Parameter(Mandatory=$false)] [boolean]$Troubleshoot=$false ) $NameFieldName = "name" $RegistrationModeField = "registration_mode" # Input validation if (-not (Test-Path $FilePath)) { Write-Error "File not found: $FilePath" return } # 1. --- Generate Unique Boundary --- $Boundary = "----PowerShellBoundary$([Guid]::NewGuid().ToString().Replace('-', ''))" # 2. --- Define Content-Type Header --- $ContentType = "multipart/form-data; boundary=$Boundary" $Headers["Content-Type"] = $ContentType # --- DEBUG INFO --- if($Troubleshoot) { Write-Host "--- DEBUG INFO ---" Write-Host "URI: $Uri" Write-Host "Generated Boundary: $Boundary" Write-Host "Content-Type Header: $ContentType" Write-Host "File Path: $FilePath" Write-Host "Name Value: $(if ($NameValue) {"$NameValue (Included)"} else {"N/A (Skipped)"})" Write-Host "Registration Mode: $(if ($RegistrationMode) {"$RegistrationMode (Included)"} else {"N/A (Skipped)"})" Write-Host "File form-data name: $FileFieldName" Write-Host "SkipCertificateCheck: $SkipCertCheck" Write-Host "SkipHttpErrorCheck: $SkipHttpErrorCheck" Write-Host "------------------" } # 3. --- Build the full body as a stream of bytes --- $bodyStream = New-Object System.IO.MemoryStream try { # A. Optional registration_mode (before name/file; matches browser multipart order) if ($RegistrationMode) { $regModeBytes = Get-MultipartFormDataPart -Name $RegistrationModeField -Value $RegistrationMode -Boundary $Boundary $bodyStream.Write($regModeBytes, 0, $regModeBytes.Length) } # B. Conditional: Add the "name" part (Text Field) if ($NameValue) { $nameBytes = Get-MultipartFormDataPart -Name $NameFieldName -Value $NameValue -Boundary $Boundary $bodyStream.Write($nameBytes, 0, $nameBytes.Length) } # C. Add the binary file part (form name from FileFieldName) $filePartBytes = @() if ($NameValue -or $RegistrationMode) { $filePartBytes = Get-MultipartFilePart -Name $FileFieldName -FilePath $FilePath -Boundary $Boundary } else { # Manual assembly for file-only upload (first part) $fileName = [System.IO.Path]::GetFileName($FilePath) $contentType = "application/octet-stream" $partHeader = "" $partHeader += "--$Boundary`r`n" $partHeader += "Content-Disposition: form-data; name=`"$FileFieldName`"; filename=`"$fileName`"`r`n" $partHeader += "Content-Type: $contentType`r`n" $partHeader += "`r`n" # Blank line separates headers from content $headerBytes = [System.Text.Encoding]::ASCII.GetBytes($partHeader) $contentBytes = [System.IO.File]::ReadAllBytes($FilePath) $trailerBytes = [System.Text.Encoding]::ASCII.GetBytes("`r`n") # CRLF after file content $combined = New-Object System.Byte[] ($headerBytes.Length + $contentBytes.Length + $trailerBytes.Length) [Array]::Copy($headerBytes, 0, $combined, 0, $headerBytes.Length) [Array]::Copy($contentBytes, 0, $combined, $headerBytes.Length, $contentBytes.Length) [Array]::Copy($trailerBytes, 0, $combined, $headerBytes.Length + $contentBytes.Length, $trailerBytes.Length) $filePartBytes = $combined } $bodyStream.Write($filePartBytes, 0, $filePartBytes.Length) # D. Add the closing boundary $closing = "--$Boundary--`r`n" $closingBytes = [System.Text.Encoding]::ASCII.GetBytes($closing) $bodyStream.Write($closingBytes, 0, $closingBytes.Length) # Reset stream position to the beginning for reading by Invoke-WebRequest $bodyStream.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null # 4. --- Execute the Request --- # (Splatting logic unchanged) $IWRParams = @{ Uri = $Uri Method = 'Post' Headers = $Headers Body = $bodyStream } if ($SkipCertCheck) { $IWRParams.Add('SkipCertificateCheck', $true) } $Response = $null if ($SkipHttpErrorCheck) { if ($PSVersionTable.PSVersion.Major -ge 6) { $IWRParams['SkipHttpErrorCheck'] = $true $Response = Invoke-WebRequest @IWRParams } else { try { $Response = Invoke-WebRequest @IWRParams } catch { $ex = $_.Exception if ($ex.Response) { $r = $ex.Response try { $code = [int]$r.StatusCode $sr = New-Object System.IO.StreamReader($r.GetResponseStream()) $bodyText = $sr.ReadToEnd() $Response = [pscustomobject]@{ StatusCode = $code; Content = $bodyText } } catch { throw } } else { throw } } } } else { $Response = Invoke-WebRequest @IWRParams } if ($Response.StatusCode -ge 200 -and $Response.StatusCode -lt 300) { Write-Host "Upload successful. HTTP Status Code: $($Response.StatusCode)" } return $Response } catch { Write-Error "Upload failed: $($_.Exception.Message)" if ($_.Exception.Response -is [System.Net.HttpWebResponse]) { $ErrorResponse = $_.Exception.Response $StreamReader = New-Object System.IO.StreamReader($ErrorResponse.GetResponseStream()) $ErrorBody = $StreamReader.ReadToEnd() Write-Error "Internal Server Error (500) Body:" Write-Error $ErrorBody } return $_ } finally { if ($bodyStream) { $bodyStream.Dispose() } } } Function Connect-VcfBsc { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Connect to the VCF Business Service Console .DESCRIPTION This cmdlet creates $global:bscConnection object containing valid access token .PARAMETER ClientId The OAuth Client ID generated from VCF Business Service Console UI .PARAMETER SecretId The OAuth Secret ID generated from VCF Business Service Console UI .EXAMPLE Connect-VcfBsc -ClientID $ClientId -SecretId $SecretId #> Param ( [Parameter(Mandatory=$true)][String]$ClientId, [Parameter(Mandatory=$true)][String]$SecretId, [Switch]$Troubleshoot ) $body = @{ "grant_type" = "client_credentials" "client_id" = $ClientId "client_secret" = $SecretId } $uri = "https://eapi.broadcom.com/vcf/generateToken" $method = "POST" if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" Write-Host -ForegroundColor cyan "[DEBUG]`n$($body | Out-String)`n" } try { $requests = Invoke-WebRequest -Uri $uri -Method $method -Headers @{"Content-Type" = "application/x-www-form-urlencoded"} -Body $body } catch { Write-Error "Error in requesting Access Token" Write-Error "`n($_.Exception.Message)`n" break } if($requests.StatusCode -eq 200) { $accessToken=($requests.Content | ConvertFrom-Json).access_token $headers = @{ "Authorization" = "Bearer $accessToken" } $global:bscConnection = new-object PSObject -Property @{ 'headers' = $headers } $global:bscConnection | Out-Null } else { Write-Host "Something went wrong with auth" } } Function Register-VcfOperations { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Register VCF Operations 9.0.x instance given a registration file .DESCRIPTION This cmdlet register VCF Operations instance given a registration file .PARAMETER TenantId The BSC Tenant ID (retrieved through VCF BSC UI) .PARAMETER RegistrationFile The filename of the exported registration file from VCF Operations .PARAMETER Name The human friendly label to use for the registered VCF Operations in VCF BSC .PARAMETER RegistrationMode Multipart field registration_mode (e.g. MANUAL). Use empty string to omit for legacy APIs. .EXAMPLE $VCF_OPERATIONS_REGISTRATION_LABEL="vcf01.vcf.lab" $VCF_OPERATIONS_REGISTRATION_FILE="Registration-vcf01.vcf.lab-2025-12-16T15_03_43Z.data" Register-VcfOperations -TenantId $VCF_BSC_TENANT_ID -RegistrationFile $VCF_OPERATIONS_REGISTRATION_FILE -Name $VCF_OPERATIONS_REGISTRATION_LABEL #> Param ( [Parameter(Mandatory=$true)][String]$TenantId, [Parameter(Mandatory=$true)][String]$RegistrationFile, [Parameter(Mandatory=$true)][String]$Name, [Parameter(Mandatory=$false)][String]$RegistrationMode = 'MANUAL', [Switch]$Troubleshoot ) $headers = @{ "Authorization" = $global:bscConnection.Headers.Authorization } $uri = "https://eapi.broadcom.com/vcf/license-mgmt/api/v1/tenants/${TenantId}/appliance-registration/upload" Write-Host "Uploading VCF Operations (${VCF_OPERATIONS_REGISTRATION_LABEL}) Registration File to Broadcom Business Service Console ..." try { if($Troubleshoot) { $requests = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $RegistrationFile -NameValue $Name -RegistrationMode $RegistrationMode -Troubleshoot $true } else { $requests = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $RegistrationFile -NameValue $Name -RegistrationMode $RegistrationMode } } catch { Write-Error "Error in registering VCF Operations" Write-Error "`n($_.Exception.Message)`n" break } if($requests.StatusCode -eq 200) { ($requests.Content | ConvertFrom-Json) } } Function Register-VcfOperations2 { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Register VCF Operations 9.1 instance given a registration file .DESCRIPTION This cmdlet register VCF Operations 9.1 instance given a registration file. If the API returns status DELETED, the same registration file is posted again to the v2 asset activate endpoint (multipart file only) and that JSON response is returned. .PARAMETER TenantId The BSC Tenant ID (retrieved through VCF BSC UI) .PARAMETER RegistrationFile The filename of the exported registration file from VCF Operations .PARAMETER Name The human friendly label to use for the registered VCF Operations in VCF BSC .PARAMETER RegistrationMode Multipart field registration_mode; defaults to MANUAL. Use empty string to omit. .EXAMPLE $VCF_OPERATIONS_REGISTRATION_LABEL="vcf01.vcf.lab" $VCF_OPERATIONS_REGISTRATION_FILE="Registration-vcf01.vcf.lab-2025-12-16T15_03_43Z.data" Register-VcfOperations2 -TenantId $VCF_BSC_TENANT_ID -RegistrationFile $VCF_OPERATIONS_REGISTRATION_FILE -Name $VCF_OPERATIONS_REGISTRATION_LABEL #> Param ( [Parameter(Mandatory=$true)][String]$TenantId, [Parameter(Mandatory=$true)][String]$RegistrationFile, [Parameter(Mandatory=$true)][String]$Name, [Parameter(Mandatory=$false)][String]$RegistrationMode = 'MANUAL', [Switch]$Troubleshoot ) $headers = @{ "Authorization" = $global:bscConnection.Headers.Authorization } $uri = "https://eapi.broadcom.com/vcf/license-mgmt/api/v2/tenants/${TenantId}/registrations" Write-Host "Uploading VCF Operations (${VCF_OPERATIONS_REGISTRATION_LABEL}) Registration File to Broadcom Business Service Console ..." try { if($Troubleshoot) { $requests = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $RegistrationFile -NameValue $Name -RegistrationMode $RegistrationMode -Troubleshoot $true } else { $requests = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $RegistrationFile -NameValue $Name -RegistrationMode $RegistrationMode } } catch { Write-Error "Error in registering VCF Operations" Write-Error "`n($_.Exception.Message)`n" break } if($requests.StatusCode -eq 200) { $result = $requests.Content | ConvertFrom-Json if ($result.status -eq 'DELETED' -and $result.asset_id) { $activateUri = "https://eapi.broadcom.com/vcf/license-mgmt/api/v2/tenants/${TenantId}/assets/$($result.asset_id)?activate=true" Write-Host "Registration returned status DELETED; re-activating asset $($result.asset_id) ..." $headersActivate = @{ "Authorization" = $global:bscConnection.Headers.Authorization } try { if($Troubleshoot) { $activateReq = Invoke-MultipartUpload -Uri $activateUri -Headers $headersActivate -FilePath $RegistrationFile -Troubleshoot $true } else { $activateReq = Invoke-MultipartUpload -Uri $activateUri -Headers $headersActivate -FilePath $RegistrationFile } } catch { Write-Error "Error in re-activating VCF Operations asset" Write-Error "`n($_.Exception.Message)`n" break } if ($activateReq.StatusCode -eq 200) { ($activateReq.Content | ConvertFrom-Json) | Select-Object -Property * -ExcludeProperty id | Out-Host return $result.asset_id } else { $result | Select-Object -Property * -ExcludeProperty id | Out-Host return $result.asset_id } } else { $result | Select-Object -Property * -ExcludeProperty id | Out-Host return $result.asset_id } } } Function Download-VcfBscVerificationFile { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Download generated verification file from VCF Business Service Console .DESCRIPTION This cmdlet downloads generated verification file from VCF Business Service Console .PARAMETER TenantId The BSC Tenant ID (retrieved through VCF BSC UI) .PARAMETER VcfOperationsId The registered VCF Operations Id .PARAMETER VerificationFile The filename where the verification file will be saved .EXAMPLE $VCF_VERIFICATION_FILE="verification-flt-ops01a.rainpole.io___2026-04-06_18-03-00-948Z.verification" Download-VcfBscVerificationFile -TenantId $VCF_BSC_TENANT_ID -VcfOperationsId $VCF_BSC_OPERATIONS_REGISTRATION_ID -VerificationFile $VCF_VERIFICATION_FILE #> Param ( [Parameter(Mandatory=$true)][String]$TenantId, [Parameter(Mandatory=$true)][String]$VcfOperationsId, [Parameter(Mandatory=$true)][String]$VerificationFile, [Switch]$Troubleshoot ) $headers = @{ "Authorization" = $global:bscConnection.Headers.Authorization "Content-Type"="application/json" "Accept"="application/json" } $uri = "https://eapi.broadcom.com/vcf/license-mgmt/api/v1/tenants/${TenantId}/assets/${VcfOperationsId}/child-asset-registration/challenges/download" $method = "GET" if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" } try { Write-Host "Downloading Verification file $VerificationFile ...`n" $results = Invoke-WebRequest -Method $method -Uri $uri -Headers $headers -OutFile $VerificationFile } catch { Write-Error "Error in downloading BSC Verification File" Write-Error "`n($_.Exception.Message)`n" break } } Function Upload-VcfBscConfirmationFile { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Upload the confirmation file from VCF Operations to Broadcom Business Service Console .DESCRIPTION After Import-VcfOperationsVerificationFile, VCF Operations returns a confirmation payload (e.g. *.confirmation). That file must be posted for challenges/upload with multipart field name "file" — not to the VCF Operations /challenge endpoint. .PARAMETER TenantId The BSC Tenant ID (retrieved through VCF BSC UI) .PARAMETER VcfOperationsId The registered VCF Operations / asset Id (same as used for Download-VcfBscVerificationFile) .PARAMETER ConfirmationFile Path to the confirmation file saved from Download-VcfOperationsConfirmationFile .PARAMETER Troubleshoot Emit multipart debug details .EXAMPLE Upload-VcfBscConfirmationFile -TenantId $VCF_BSC_TENANT_ID -VcfOperationsId $VCF_BSC_OPERATIONS_REGISTRATION_ID -ConfirmationFile $VCF_CONFIRMATION_FILE #> Param ( [Parameter(Mandatory=$true)][String]$TenantId, [Parameter(Mandatory=$true)][String]$VcfOperationsId, [Parameter(Mandatory=$true)][String]$ConfirmationFile, [Switch]$Troubleshoot ) $headers = @{ "Authorization" = $global:bscConnection.Headers.Authorization "Accept" = "application/json" } $uri = "https://eapi.broadcom.com/vcf/license-mgmt/api/v1/tenants/${TenantId}/assets/${VcfOperationsId}/child-asset-registration/challenges/upload" if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - POST`n$uri`n" } try { Write-Host "Uploading confirmation file ($ConfirmationFile) to Broadcom Business Service Console ...`n" if($Troubleshoot) { $requests = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $ConfirmationFile -Troubleshoot $true } else { $requests = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $ConfirmationFile } } catch { Write-Error "Error uploading confirmation file to BSC" Write-Error "`n($_.Exception.Message)`n" break } if ($requests.StatusCode -eq 200) { ($requests.Content | ConvertFrom-Json) } } Function Get-VcfBscLicense { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Returns the list of licenses from VCF Business Service Console .DESCRIPTION This cmdlet returns the list of licenses from VCF Business Service Console .PARAMETER TenantId The BSC Tenant ID (retrieved through VCF BSC UI) .PARAMETER Name The name of the license label to filter results .EXAMPLE Get-VcfBscLicense -TenantId $VCF_BSC_TENANT_ID $VCF_LICENSE_NAME="wlam-vcf" Get-VcfBscLicense -TenantId $VCF_BSC_TENANT_ID -Name $VCF_LICENSE_NAME #> Param ( [Parameter(Mandatory=$true)][String]$TenantId, [Parameter(Mandatory=$false)][String]$Name, [Switch]$Troubleshoot ) $headers = @{ "Authorization" = $global:bscConnection.Headers.Authorization "Content-Type"="application/json" "Accept"="application/json" } $uri = "https://eapi.broadcom.com/vcf/license-mgmt/api/v1/tenants/${TenantId}/allocations/search" $method = "POST" <# TODO Look into server side filtering $payload = [ordered]@{ "filters" = @( [ordered]@{ "key" = "NAME" "operator" = "EQUALS" "value" = $Name } ) } $body = $payload | ConvertTo-Json #> if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" Write-Host -ForegroundColor cyan "[DEBUG]`n$($body | Out-String)`n" } try { $results = Invoke-WebRequest -Method $method -Uri $uri -Headers $headers } catch { Write-Error "Error in retrieving BSC License" Write-Error "`n($_.Exception.Message)`n" break } if($results.StatusCode -eq 200) { $licenses = ($results.Content | ConvertFrom-Json).results if ($PSBoundParameters.ContainsKey("Name")){ $licenses = $licenses | where {$_.name -eq $Name} } return $licenses | select id, name, quantity, status, product } } Function Set-VcfBscLicense { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Associate or Disassociate license to registered VCF Operations instance in VCF Business Service Console .DESCRIPTION This cmdlet associates or disassociates license to registered VCF Operations instance in VCF Business Service Console .PARAMETER TenantId The BSC Tenant ID (retrieved through VCF BSC UI) .PARAMETER VcfOperationsId The Id of the registered VCF Operations (returned from Register-VcfOperations) .PARAMETER LicenseIds List of license IDs (returned from Get-VcfBscCLicense) .PARAMETER Operation Associate or Disassociate .EXAMPLE $VCF_BSC_OPERATIONS_REGISTRATION_ID="f8d3966c-9a82-3cf6-e797-2392b311ed23" $VCF_BSC_LICENSE_IDS=@("efaa38b7-08ac-452f-929b-d30f6d37fba5","fc711690-f8d3-4209-a06e-529c39979251") Set-VcfBscLicense -TenantId $VCF_BSC_TENANT_ID -VcfOperationsId $VCF_BSC_OPERATIONS_REGISTRATION_ID -LicenseIds $VCF_BSC_LICENSE_IDS -Operation Associate #> Param ( [Parameter(Mandatory=$true)][String]$TenantId, [Parameter(Mandatory=$true)][String]$VcfOperationsId, [Parameter(Mandatory=$true)][String[]]$LicenseIds, [Parameter(Mandatory=$true)][ValidateSet("Associate","Dissociate")]$Operation, [Switch]$Troubleshoot ) $headers = @{ "Authorization" = $global:bscConnection.Headers.Authorization "Content-Type"="application/json" "Accept"="application/json" } $uri = "https://eapi.broadcom.com/vcf/license-mgmt/api/v1/tenants/${TenantId}/licenses" $method = "POST" if($Operation -eq "Associate") { $payload = [ordered]@{ operation = "ASSOCIATE" license_associate_request = @{ vcf_ops_id = $VcfOperationsId ids = @($LicenseIds) } } } else { $payload = [ordered]@{ operation = "DISSOCIATE" license_dissociate_request = @{ vcf_ops_id = $VcfOperationsId ids = @($LicenseIds) } } } $body = $payload | ConvertTo-Json if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" Write-Host -ForegroundColor cyan "[DEBUG]`n$($body | Out-String)`n" } Write-Host "$Operation license(s) to VCF Operations Id: $VcfOperationsId ...`n" try { $results = Invoke-WebRequest -Method $method -Uri $uri -Headers $headers -Body $body } catch { Write-Error "Error in $operation BSC License" Write-Error "`n($_.Exception.Message)`n" $results break } if($results.StatusCode -eq 200) { return ($results.Content | ConvertFrom-Json).results | select id, name, quantity, status, product } } Function Set-VcfBscLicense2 { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Associate or Disassociate license to registered VCF Operations instance in VCF Business Service Console .DESCRIPTION This cmdlet associates or disassociates license to registered VCF Operations instance in VCF Business Service Console .PARAMETER TenantId The BSC Tenant ID (retrieved through VCF BSC UI) .PARAMETER LicenseServerId The Id of the license server (returned from Import-VcfOperationsConfirmationFile) .PARAMETER LicenseIds List of license IDs (returned from Get-VcfBscCLicense) .PARAMETER Operation Associate or Disassociate .EXAMPLE $VCF_LICENSE_SERVER_BSC_ID="077bf2c4-ddb3-4f2e-a593-a8451909e1b6" $VCF_BSC_LICENSE_IDS=@("4088324b-5809-41dd-83f2-e33d0ad8a758","bcc0b0a6-69ba-4f24-95ec-aaf4cf60a557) Set-VcfBscLicense2 -TenantId $VCF_BSC_TENANT_ID -LicenseServerId $VCF_LICENSE_SERVER_BSC_ID -LicenseIds $VCF_BSC_LICENSE_IDS -Operation Associate #> Param ( [Parameter(Mandatory=$true)][String]$TenantId, [Parameter(Mandatory=$true)][String]$LicenseServerId, [Parameter(Mandatory=$true)][String[]]$LicenseIds, [Parameter(Mandatory=$true)][ValidateSet("Associate","Dissociate")]$Operation, [Switch]$Troubleshoot ) $headers = @{ "Authorization" = $global:bscConnection.Headers.Authorization "Content-Type"="application/json" "Accept"="application/json" } $uri = "https://eapi.broadcom.com/vcf/license-mgmt/api/v1/tenants/${TenantId}/assets/${LicenseServerId}/allocations" $method = "POST" if($Operation -eq "Associate") { $payload = [ordered]@{ allocationIds = $LicenseIds action = "ASSOCIATE" } } else { $payload = [ordered]@{ allocationIds = $LicenseIds action = "DISSOCIATE" } } $body = $payload | ConvertTo-Json if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" Write-Host -ForegroundColor cyan "[DEBUG]`n$($body | Out-String)`n" } Write-Host "$Operation license(s) to License Server Id: $LicenseServerId ...`n" try { $results = Invoke-WebRequest -Method $method -Uri $uri -Headers $headers -Body $body } catch { Write-Error "Error in $operation BSC License" Write-Error "`n($_.Exception.Message)`n" $results break } if($results.StatusCode -eq 200) { return ($results.Content | ConvertFrom-Json).results | select id, name, quantity, status, product } } Function Download-VcfBscLicense { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Download generated license from VCF Business Service Console .DESCRIPTION This cmdlet downloads generated license from VCF Business Service Console .PARAMETER TenantId The BSC Tenant ID (retrieved through VCF BSC UI) .PARAMETER VcfOperationsId The registered VCF Operations Id .PARAMETER LicenseFile The filename where the license file will be saved .EXAMPLE $VCF_LICENSE_FILE="vcf01.vcf.lab.lic" Download-VcfBscLicense -TenantId $VCF_BSC_TENANT_ID -VcfOperationsId $VCF_BSC_OPERATIONS_REGISTRATION_ID -LicenseFile $VCF_LICENSE_FILE #> Param ( [Parameter(Mandatory=$true)][String]$TenantId, [Parameter(Mandatory=$true)][String]$VcfOperationsId, [Parameter(Mandatory=$true)][String]$LicenseFile, [Switch]$Troubleshoot ) $headers = @{ "Authorization" = $global:bscConnection.Headers.Authorization "Content-Type"="application/json" "Accept"="application/json" } $uri = "https://eapi.broadcom.com/vcf/license-mgmt/api/v1/tenants/${TenantId}/vcf-ops/${VcfOperationsId}/licenses/download" $method = "GET" if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" } try { Write-Host "Downloading license file $LicenseFile ...`n" $results = Invoke-WebRequest -Method $method -Uri $uri -Headers $headers -OutFile $LicenseFile return $LicenseFile } catch { Write-Error "Error in downloading BSC License" Write-Error "`n($_.Exception.Message)`n" break } } Function Connect-VcfOperations { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Connect to VCF Operations .DESCRIPTION This cmdlet creates $global:vcfOpsConnection object containing valid access token .PARAMETER Fqdn IP Address/Hostname of VCF Operations .PARAMETER User The username to login to VCF Operations .PARAMETER Password The password to login to VCF Operations .EXAMPLE $VCF_OPERATIONS_HOSTNAME="vcf01.vcf.lab" $VCF_OPERATIONS_USERNAME="admin" $VCF_OPERATIONS_PASSWORD='' Connect-VcfOperations -Fqdn $VCF_OPERATIONS_HOSTNAME -User $VCF_OPERATIONS_USERNAME -Password $VCF_OPERATIONS_PASSWORD #> Param ( [Parameter(Mandatory=$true)][String]$Fqdn, [Parameter(Mandatory=$true)][String]$User, [Parameter(Mandatory=$true)][String]$Password, [Switch]$Troubleshoot ) $payload = @{ username = $User password = $Password authSource = "local" } $body = $payload | ConvertTo-Json $uri = "https://${Fqdn}/suite-api/api/auth/token/acquire" $method = "POST" if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" Write-Host -ForegroundColor cyan "[DEBUG]`n$($body | Out-String)`n" } try { $requests = Invoke-WebRequest -Uri $uri -Method $method -Headers @{"Content-Type" = "application/json";"Accept" = "application/json"} -Body $body -SkipCertificateCheck } catch { Write-Error "Error in requesting Access Token" Write-Error "`n($_.Exception.Message)`n" break } if($requests.StatusCode -eq 200) { $accessToken=($requests.Content | ConvertFrom-Json).token $headers = @{ "Authorization" = "OpsToken $accessToken" "Content-Type" = "application/json" "Accept" = "application/json" "X-Ops-API-use-unsupported" = "true" } $global:vcfOpsConnection = new-object PSObject -Property @{ 'Server' = $Fqdn 'headers' = $headers } $global:vcfOpsConnection | Out-Null } else { Write-Host "Something went wrong with auth" } } Function Download-VcfOperationsRegistrationFile { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Download registration file from VCF Operations .DESCRIPTION This cmdlet downloads registration file from VCF Operations .EXAMPLE Download-VcfOperationsRegistrationFile #> Param ( [Switch]$Troubleshoot ) $uri = "https://$($global:vcfOpsConnection.server)/suite-api/internal/extension/vcf-license-cloud-integration/registration/offline/request" $method = "POST" if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" } try { $requests = Invoke-WebRequest -Method $method -Uri $uri -Headers $global:vcfOpsConnection.headers -SkipCertificateCheck } catch { Write-Error "Error in downloading registration file" Write-Error "`n($_.Exception.Message)`n" break } if($requests.StatusCode -eq 200) { $result = ($requests.Content | ConvertFrom-Json) $RegistrationFile = $result.fileName $RegistrationData = $result.jwsEncodedData Write-Host "Successfully downloaded registration file $RegistrationFile`n" $RegistrationData | Out-File -FilePath $RegistrationFile return $RegistrationFile } } Function Import-VcfOperationsVerificationFile { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Import the BSC verification (*.verification) file into VCF Operations .DESCRIPTION Mirrors licensing_utils.import_verification_file_to_vcfops: POSTs the verification file to license-servers/challenge, then treats a 200 body as a confirmation JWT (plain text, not JSON) to ConfirmationOutFile. HTTP 400 with body containing "Invalid/Empty Challenge file" means the verification was already imported previously; a message is shown and the cmdlet returns without writing a file. Do not use the confirmation file (*.confirmation) from Download-VcfOperationsConfirmationFile here — that file must be uploaded to BSC with Upload-VcfBscConfirmationFile instead. .PARAMETER VerificationFile Path to the *.verification file from BSC (not *.confirmation) .PARAMETER FileFieldName Multipart form field name for the upload (default challenge). .PARAMETER ConfirmationOutFile Path to write the confirmation JWT. If omitted, defaults to confirmation-<VCF Ops FQDN>__<UTC timestamp>.confirmation in the same folder as the verification file (e.g. confirmation-flt-ops01a.rainpole.io__2026-04-06T21_09_01.257Z.confirmation). .EXAMPLE $VCF_VERIFICATION_FILE="verification-flt-ops01a.rainpole.io___2026-04-06_18-03-00-948Z.verification" Import-VcfOperationsVerificationFile -VerificationFile $VCF_VERIFICATION_FILE #> Param ( [Parameter(Mandatory=$true)][String]$VerificationFile, [Parameter(Mandatory=$false)][String]$FileFieldName = 'challenge', [Parameter(Mandatory=$false)][String]$ConfirmationOutFile = $null, [Switch]$Troubleshoot ) if ($VerificationFile -like '*.confirmation') { Write-Warning "This path looks like a confirmation file (*.confirmation). Import-VcfOperationsVerificationFile expects the BSC verification file (*.verification). To complete the handshake, use Upload-VcfBscConfirmationFile for the confirmation file." } if (-not $ConfirmationOutFile) { $vfDir = Split-Path -Parent $VerificationFile if ([string]::IsNullOrEmpty($vfDir)) { $vfDir = '.' } $hostPart = $global:vcfOpsConnection.server if ([string]::IsNullOrWhiteSpace($hostPart)) { $hostPart = 'vcf-operations' } $ts = [DateTime]::UtcNow.ToString('yyyy-MM-ddTHH_mm_ss.fff') + 'Z' $confirmName = "confirmation-${hostPart}__${ts}.confirmation" $ConfirmationOutFile = Join-Path $vfDir $confirmName } $uri = "https://$($global:vcfOpsConnection.server)/suite-api/internal/extension/vcf-license-cloud-integration/license-servers/challenge" $method = "POST" $headers = @{ "Authorization" = $global:vcfOpsConnection.headers.authorization "X-Ops-API-use-unsupported" = "true" "Accept" = "application/json, text/plain, */*" } if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" } try { Write-Host "Importing Verification File ($VerificationFile) to VCF Operations ...`n" if($Troubleshoot) { $results = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $VerificationFile -FileFieldName $FileFieldName -SkipCertCheck -SkipHttpErrorCheck -Troubleshoot $true } else { $results = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $VerificationFile -FileFieldName $FileFieldName -SkipCertCheck -SkipHttpErrorCheck -Troubleshoot $false } } catch { Write-Error "Error in importing verification file" Write-Error "`n($_.Exception.Message)`n" break } if ($results -is [System.Management.Automation.ErrorRecord]) { Write-Error "Verification import failed: $($results.Exception.Message)" break } if ($null -eq $results.StatusCode) { Write-Error "Verification import failed: no HTTP response." break } if ($results.StatusCode -eq 400 -and $results.Content -like '*Invalid/Empty Challenge file*') { Write-Host "Verification file was already imported in a previous attempt (Invalid/Empty Challenge file).`n" return } if ($results.StatusCode -ne 200) { Write-Error "Verification import failed: HTTP $($results.StatusCode). Response: $($results.Content)" break } $confirmationJwt = $results.Content.Trim() if ([string]::IsNullOrWhiteSpace($confirmationJwt)) { Write-Error "Verification import returned empty confirmation JWT." break } $utf8NoBom = New-Object System.Text.UTF8Encoding $false [System.IO.File]::WriteAllText($ConfirmationOutFile, $confirmationJwt, $utf8NoBom) Write-Host "Confirmation file saved to $ConfirmationOutFile`n" return $ConfirmationOutFile } Function Import-VcfOperationsConfirmationFile { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Import generated confirmation file from VCF Operations into VCF Business Service Console .DESCRIPTION This cmdlet imports generated confirmation file from VCF Operations into VCF Business Service Console .PARAMETER TenantId The BSC Tenant ID (retrieved through VCF BSC UI) .PARAMETER ConfirmationFile The name of the confirmation file to import .EXAMPLE $VCF_OPERATIONS_CONFIRMATION_FILE="confirmation-flt-ops01a.rainpole.io___2026-04-06T21_09_01.257Z.confirmation" Import-VcfOperationsConfirmationFile -TenantId $VCF_BSC_TENANT_ID -VcfOperationsId $VCF_BSC_OPERATIONS_REGISTRATION_ID -ConfirmationFile $VCF_OPERATIONS_CONFIRMATION_FILE #> Param ( [Parameter(Mandatory=$true)][String]$TenantId, [Parameter(Mandatory=$true)][String]$VcfOperationsId, [Parameter(Mandatory=$true)][String]$ConfirmationFile, [Switch]$Troubleshoot ) $uri = "https://eapi.broadcom.com/vcf/license-mgmt/api/v1/tenants/${TenantId}/assets/${VcfOperationsId}/child-asset-registration/challenges/upload" $method = "POST" $headers = @{ "Authorization" = $global:bscConnection.Headers.Authorization "Accept" = "application/json" } if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" } try { Write-Host "Uploading confirmation file ($ConfirmationFile) to Broadcom Business Service Console ...`n" if($Troubleshoot) { $results = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $ConfirmationFile -Troubleshoot $true } else { $results = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $ConfirmationFile } } catch { Write-Error "Error in importing confirmation file" Write-Error "`n($_.Exception.Message)`n" break } if ($results.StatusCode -eq 200) { ($results.Content | ConvertFrom-Json).child_assets | select asset_type, asset_id | Out-Host return (($results.Content | ConvertFrom-Json).child_assets).asset_id } } Function Import-VcfOperationsLicenseFile { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Import generated license file from VCF Business Service Console into VCF Operations .DESCRIPTION This cmdlet imports generated license file from VCF Business Service Console into VCF Operations .PARAMETER LicenseFile The name of the license file to import .EXAMPLE $VCF_LICENSE_FILE="/Users/wlam/Documents/cursor/ops-license/flt-ops01a.rainpole.io.lic" Import-VcfOperationsLicenseFile -LicenseFile $VCF_LICENSE_FILE #> Param ( [Parameter(Mandatory=$true)][String]$LicenseFile, [Switch]$Troubleshoot ) $uri = "https://$($global:vcfOpsConnection.server)/suite-api/internal/extension/vcf-license-cloud-integration/registration/offline/response" $method = "POST" $headers = @{ "Authorization" = $global:vcfOpsConnection.headers.authorization "X-Ops-API-use-unsupported" = "true" "Accept" = "application/json, text/plain, */*" } if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" } try { Write-Host "Importing License File ($LicenseFile) to VCF Operations ...`n" if($Troubleshoot) { $results = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $LicenseFile -SkipCertCheck -Troubleshoot $true } else { $results = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $LicenseFile -SkipCertCheck } } catch { Write-Error "Error in importing license file" Write-Error "`n($_.Exception.Message)`n" break } } Function Download-VcfOperationsUsageFile { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Download usage file from VCF Operations .DESCRIPTION This cmdlet downloads usage file from VCF Operations .EXAMPLE Download-VcfOperationsUsageFile #> Param ( [Switch]$Troubleshoot ) # Get the current date/time as a DateTimeOffset object $NowOffset = [DateTimeOffset]::Now # STARTDATE: Tomorrow's date in Unix milliseconds $StartTimeMilliseconds = $NowOffset.AddDays(1).ToUnixTimeMilliseconds() # ENDDATE: One month from now in Unix milliseconds $EndTimeMilliseconds = $NowOffset.AddMonths(1).ToUnixTimeMilliseconds() $uri = "https://$($global:vcfOpsConnection.server)/suite-api/internal/extension/vcf-license-cloud-integration/usage/offline/report?startDate=${StartTimeMilliseconds}&endDate=${EndTimeMilliseconds}" $method = "POST" if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" } try { Write-Host "Downloading Usage File ..." $requests = Invoke-WebRequest -Method $method -Uri $uri -Headers $global:vcfOpsConnection.headers -SkipCertificateCheck } catch { Write-Error "Error in downloading usage file" Write-Error "`n($_.Exception.Message)`n" break } if($requests.StatusCode -eq 200) { $result = ($requests.Content | ConvertFrom-Json) $UsageFile = $result.fileName.Replace(" ", "") $UsageData = $result.gzipJwsEncodedData Write-Host "Successfully downloaded usage file $UsageFile`n" $ContentBytes = [System.Convert]::FromBase64String($UsageData) [System.IO.File]::WriteAllBytes($UsageFile, $ContentBytes) } } Function Import-VcfOperationsUsageFile { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Import generated usage file from VCF Operations into VCF Business Service Console .DESCRIPTION This cmdlet imports generated usage file from VCF Operations into VCF Business Service Console .PARAMETER TenantId The BSC Tenant ID (retrieved through VCF BSC UI) .PARAMETER UsageFile The name of the usage file to import .EXAMPLE $VCF_OPERATIONS_USAGE_FILE="Usage-vcf01.vcf.lab-2025-12-16T21_09_14Z.gzip" Import-VcfOperationsUsageFile -TenantId $VCF_BSC_TENANT_ID -UsageFile $VCF_OPERATIONS_USAGE_FILE #> Param ( [Parameter(Mandatory=$true)][String]$TenantId, [Parameter(Mandatory=$true)][String]$UsageFile, [Switch]$Troubleshoot ) $uri = "https://eapi.broadcom.com/vcf/license-usage/api/v1/tenants/${TenantId}/license-usage/upload" $method = "POST" $headers = @{ "Authorization" = $global:bscConnection.Headers.Authorization } if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" } try { Write-Host "Importing Usage File ($UsageFile) to VCF Operations ...`n" if($Troubleshoot) { $results = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $UsageFile -Troubleshoot $true } else { $results = Invoke-MultipartUpload -Uri $uri -Headers $headers -FilePath $UsageFile } } catch { Write-Error "Error in importing usage file" Write-Error "`n($_.Exception.Message)`n" break } } Function Get-VcfOperationsEntitlements { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS List license entitlements that have been imported into VCF Operations .DESCRIPTION This cmdlet lists license entitlements that have been imported into VCF Operations .EXAMPLE Get-VcfOperationsEntitlements #> Param ( [Switch]$Troubleshoot ) $uri = "https://$($global:vcfOpsConnection.server)/suite-api/internal/extension/vcf-entitlement/entitlements/query" $method = "POST" if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" } try { $requests = Invoke-WebRequest -Uri $uri -Method $method -Headers $global:vcfOpsConnection.headers -SkipCertificateCheck } catch { Write-Error "Error in retrieving license entitlements" Write-Error "`n($_.Exception.Message)`n" break } if($requests.StatusCode -eq 200) { $entitlements = (($requests.Content | ConvertFrom-Json).entitlementWithVcentersDetails).entitlementInfo $results = @() foreach($entitlement in $entitlements) { $tmp = [pscustomobject] [ordered]@{ Name = $entitlement.name Id = $entitlement.id Product = $entitlement.productDisplayName Type = $entitlement.type UsedCapacity = $entitlement.usage AllocatedCapacity = $entitlement.capacity } $results+=$tmp } } return $results | Sort-Object -Property Name } Function Get-VcfOperationsVcenters { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS List vCenter Servers managed by VCF Operations .DESCRIPTION This cmdlet lists vCenter Servers managed by VCF Operations .EXAMPLE Get-VcfOperationsVcenters #> Param ( [Switch]$Troubleshoot ) $uri = "https://$($global:vcfOpsConnection.server)/suite-api/internal/extension/vcf-entitlement/vcenter-systems/query?page=0&pageSize=10" $method = "POST" if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" } try { $requests = Invoke-WebRequest -Uri $uri -Method $method -Headers $global:vcfOpsConnection.headers -SkipCertificateCheck } catch { Write-Error "Error in retrieving license entitlements" Write-Error "`n($_.Exception.Message)`n" break } if($requests.StatusCode -eq 200) { $vcenters = ($requests.Content | ConvertFrom-Json).vcenterSystems $results = @() foreach($vcenter in $vcenters) { $tmp = [pscustomobject] [ordered]@{ "vCenter" = $vcenter.vcenterInfo.host "Id" = $vcenter.vcenterInfo.id "ManagedByVCFInstance" = $vcenter.vcenterInfo.vcfAdapterName "PrimaryLicenseName" = $vcenter.entitlementName "PrimaryLicenseProduct" = $vcenter.entitlementProduct "PrimaryLicenseUsedCapacity" = $vcenter.licensedUsage "AddOnLicenseName" = $vcenter.addOns "FullyLicensed" = $vcenter.unlicensedUsage -eq 0 ? $true : $false } $results+=$tmp } } return $results | Sort-Object -Property Name } Function Get-VcfOperationsVcenters2 { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS List vCenter Servers managed by VCF Operations .DESCRIPTION This cmdlet lists vCenter Servers managed by VCF Operations .EXAMPLE Get-VcfOperationsVcenters2 #> Param ( [Switch]$Troubleshoot ) $uri = "https://$($global:vcfOpsConnection.server)/suite-api/internal/extension/vcf-entitlement/vcenter-systems/query?page=0&pageSize=10" $method = "POST" if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" } try { $requests = Invoke-WebRequest -Uri $uri -Method $method -Headers $global:vcfOpsConnection.headers -SkipCertificateCheck } catch { Write-Error "Error in retrieving license entitlements" Write-Error "`n($_.Exception.Message)`n" break } if($requests.StatusCode -eq 200) { $vcenters = ($requests.Content | ConvertFrom-Json).vcenterSystems $results = @() foreach($vcenter in $vcenters) { $licenseProductFamilies = $vcenters.productFamilies $licenseInfo = @() foreach($licenseProductFamily in $licenseProductFamilies) { $license = [pscustomobject] [ordered]@{ "Family" = $licenseProductFamily.productFamily "Type" = $licenseProductFamily.type "LicensedUsage" = $licenseProductFamily.licensedUsage "UnlicensedUsage" = $licenseProductFamily.unlicensedUsage "AllocationId" = $licenseProductFamily.licenses.allocationId "LicenseName" = $licenseProductFamily.licenses.name } $licenseInfo+=$license } $tmp = [pscustomobject] [ordered]@{ "vCenter" = $vcenter.host "Id" = $vcenter.id "ManagedByVCFInstance" = $vcenter.vcfAdapterName "Version" = $vcenter.version "Expiration" = $vcenter.expirationTimestamp "FullyLicensed" = $vcenter.state -eq "LICENSED"? $true : $false "Licenses" = $licenseInfo } $results+=$tmp } } return $results | Sort-Object -Property Name } Function Set-VcfOperationsLicenseAssignment2 { <# .NOTES =========================================================================== Created by: William Lam Organization: Broadcom Blog: http://www.williamlam.com Twitter: @lamw =========================================================================== .SYNOPSIS Assign license entitlement(s) to a vCenter Server managed by VCF Operations .DESCRIPTION This cmdlet assigns license entitlement(s) to a vCenter Server managed by VCF Operations .PARAMETER VcenterId vCenter Server ID (returned from Get-VcfOperationsVcenters) .PARAMETER LicenseIds .EXAMPLE $VCENTER_ID="abc6b4a7-2d61-4da0-9338-e1193148be7b" $VCF_LICENSE_ID="4088324b-5809-41dd-83f2-e33d0ad8a758" $VSAN_LICENSE_ID="bcc0b0a6-69ba-4f24-95ec-aaf4cf60a557" Set-VcfOperationsLicenseAssignment2 -VcenterId $VCENTER_ID -LicenseId $VCF_LICENSE_ID Set-VcfOperationsLicenseAssignment2 -VcenterId $VCENTER_ID -LicenseId $VSAN_LICENSE_ID #> Param ( [Parameter(Mandatory=$true)][String]$VcenterId, [Parameter(Mandatory=$true)][String]$LicenseId, [Switch]$Troubleshoot ) $uri = "https://$($global:vcfOpsConnection.server)/suite-api/internal/extension/vcf-entitlement/assign" $method = "POST" $vc = Get-VcfOperationsVcenters2 | where {$_.id -eq $VcenterId} $payload = @( [ordered]@{ vcenter = [ordered]@{ id = $VcenterId adapterName = $vc.vCenter host = $vc.vCenter } allocationIds = @($LicenseId) } ) $body = ConvertTo-Json -InputObject @($payload) if($Troubleshoot) { Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$uri`n" Write-Host -ForegroundColor cyan "[DEBUG]`n$($body | Out-String)`n" } try { $requests = Invoke-WebRequest -Uri $uri -Method $method -Headers $global:vcfOpsConnection.headers -Body $body -SkipCertificateCheck } catch { Write-Error "Error in assigning license entitlements" Write-Error "`n($_.Exception.Message)`n" break } if($requests.StatusCode -eq 200) { ($requests.Content | ConvertFrom-Json) } } |