VCommanderRestClient.psm1

#######################################################################################################################
# Publisher: Embotics Corporation #
# Copyright: © 2018 Embotics Corporation. All rights reserved. #
# Client version: 2.9.1 #
# Required VCommander module version: 1.1 #
# Compatible vCommander versions: 7.1.0 and above #
#######################################################################################################################

##############################################################
# Module variables
##############################################################
$Script:ROOT_PATH = "rest/v2"
$Script:clientVersion = "2.9.1"
$Script:vcommanderModuleVersion = "1.1"
$Script:compatibleVCmdrVersions = "7.0.0 and above"
$Script:clientInfo = New-Object PSObject
$Script:restAPIServiceInfo = New-Object PSObject
$Script:internalClientModuleName = "VCommanderInternalRestClient"
$Script:loggingModuleName = "PowerShellLogging"
$Script:initialized = $false
$Script:XML_OUTPUT = $false  #by default, we expect everything to be in Powershell object
$Script:ErrorActionPreference = "Stop"
$Script:DTOTemplateMap = @{} # a map of DTO templates
$Script:DATE_FORMAT = "yyyyMMdd"
$Script:DATE_TIME_FORMAT ="yyyyMMdd-HHmmss"
$Script:SLEEP_TIME_MILLIS = 5000

##############################################################
# Global variables
##############################################################
$Global:SERVICE_HOST ="localhost"
$Global:BASE_SERVICE_URI_PATTERN = "https://{service_host}/webservices/services/$Script:ROOT_PATH"
$Global:CREDENTIAL = $null
$Global:ORGANIZATION_ID = $null

#Configure global variables
$Global:Embotics = New-Object PSObject
Add-Member -InputObject $Global:Embotics -MemberType NoteProperty -Name "ROOT_PATH" -Value $Script:ROOT_PATH -Force
Add-Member -InputObject $Global:Embotics -MemberType NoteProperty -Name "REQUEST_HEADERS" -Value @{} -Force

#Load the .Net assembly so we can use System.Web.HttpUtility for URL encoding
Add-Type -AssemblyName System.Web

<#
.SYNOPSIS
Set up the REST client. To be used if the REST client will be used by a human. If automating, global variables need to be adjusted/set.

.DESCRIPTION
Set up the REST client

.EXAMPLE
Initialize-Configuration
#>

function Initialize-Configuration {

    $Script:initialized = $false #Reset
    #Set up correct URI
    Write-Host "Enter the hostname of your vCommander."
    Write-Host "If you are using a non-standard port, use a colon (prod.example.com:8443)."
    $Global:SERVICE_HOST = Read-Host "vCommander REST service"
    $Global:BASE_SERVICE_URI = $Global:BASE_SERVICE_URI_PATTERN.Replace("{service_host}",$Global:SERVICE_HOST)
    
    #Get user credential
    $Global:CREDENTIAL = $host.ui.PromptForCredential("Need credentials", "Please enter a vCommander user account.", "", "")

    $ignoreSSLError = Read-Host "Ignore untrusted SSL certificate error? (y/n)"

    if ($ignoreSSLError -ieq 'y') {
        VCommander\Set-IgnoreSslErrors
        Write-Host "Client configured to ignore untrusted SSL certificate error."
    }

    $result = Initialize-Client #In case user don't call Connect-Client
    if (!$result) {
        throw "Unable to initialize client. Please run with -Debug switch to determine cause."
    }

    Write-Host "============================================================================================="
    $clientInfo | fl
    Write-Host "============================================================================================="

    if ($loggingModuleLoaded) {
        Write-Host -foregroundcolor green 'To enable file logging: $logFile = Enable-LogFile -Path $env:temp\vCmdr-RESTApi.log'
        Write-Host -foregroundcolor green 'To disable file logging: $logFile | Disable-LogFile'
        Write-Host -foregroundcolor green 'It is recommended to disable file logging before the script exits.'
    }

    Write-Host "To begin, call the function Connect-Client."
}

<#
.SYNOPSIS
Initialize the client

.DESCRIPTION
Initialize the client

#>

function Initialize-Client {
    [cmdletbinding()]
    Param()

    Write-Debug "Initalizing client..."

    if ($Script:initialized) {    
        $true
        return
    }

    $internalClientModuleLoaded = $false
    $loggingModuleLoaded = $false
    
    Write-Debug "Checking for $Script:internalClientModuleName module"
    $internalRestClientModule = Get-Module -ListAvailable $Script:internalClientModuleName
    if($internalRestClientModule) {
        Write-Debug "$($Script:internalClientModuleName) detected."
        If (-not (Get-Module -name $Script:internalClientModuleName)) {
            Write-Debug "Importing $($Script:internalClientModuleName)."
            Import-Module -Name $Script:internalClientModuleName -Prefix "internal_"
        } else {
            #Re-import to get latest changes
            Write-Debug "Re-importing $($Script:internalClientModuleName)."
            Remove-Module $Script:internalClientModuleName
            Import-Module -Name $Script:internalClientModuleName -Prefix "internal_"
        }

        Write-Debug "$($Script:internalClientModuleName) module imported."
        Initialize-internal_Module
        
        $internalClientModuleLoaded = $true
    } else {
        Write-Debug "$($Script:internalClientModuleName) not present. OK."
        $internalClientModuleLoaded = $false
    }

    Write-Debug "Checking for $Script:loggingModuleName module"
    $loggingModule = Get-Module -ListAvailable $Script:loggingModuleName
    if($loggingModule) {
        try {
            Write-Debug "$($Script:loggingModuleName) detected."
            If (-not (Get-Module -name $Script:loggingModuleName)) {
                Write-Debug "Importing $($Script:loggingModuleName)."
                Import-Module -Name $Script:loggingModuleName
                Write-Debug "$($Script:loggingModuleName) module imported."
            }
            $loggingModuleLoaded = $true
        } catch {}
    } else {
        Write-Debug "$($Script:loggingModuleName) not present. OK."
        $loggingModuleLoaded = $false
    }
        
    Update-ClientInfo -internalModuleExists $internalClientModuleLoaded -loggingModelExists $loggingModuleLoaded
    $Script:initialized = $true
    $true
}

<#
.SYNOPSIS
Modify client info

.DESCRIPTION
Modify client info

#>

function Update-ClientInfo {
    [cmdletbinding()]
    Param(
        [Bool] $internalModuleExists = $false,
        [Bool] $loggingModelExists = $false
    )    
    
    try {
        Add-Member -InputObject $clientInfo -MemberType NoteProperty -Name "Name" -Value "PowerShell REST client v$($Script:clientVersion)" -Force
        Add-Member -InputObject $Script:clientInfo -MemberType NoteProperty -Name "Supported vCommander versions" -Value $($Script:compatibleVCmdrVersions) -Force
        Add-Member -InputObject $Script:clientInfo -MemberType NoteProperty -Name "VCommander module version" -Value $Script:vcommanderModuleVersion -Force
        Add-Member -InputObject $Script:clientInfo -MemberType NoteProperty -Name "PowerShell version" -Value $($PSVERSIONTable.PSVersion.Major) -Force
        Add-Member -InputObject $Script:clientInfo -MemberType NoteProperty -Name "PowerShell module directory" -Value $($PSHOME) -Force
        Add-Member -InputObject $Script:clientInfo -MemberType NoteProperty -Name "Service URI" -Value $(Get-BaseServiceURI) -Force
        Add-Member -InputObject $Script:clientInfo -MemberType NoteProperty -Name "Client account userId" -Value $($Global:CREDENTIAL.UserName) -Force    
        if ($internalModuleExists) {
            Add-Member -InputObject $Script:clientInfo -MemberType NoteProperty -Name $Script:internalClientModuleName -Value "Loaded" -Force
        }

        if ($loggingModelExists) {
            Add-Member -InputObject $Script:clientInfo -MemberType NoteProperty -Name $Script:loggingModuleName -Value "Loaded" -Force
        }

        Add-Member -InputObject $Script:clientInfo -MemberType NoteProperty -Name "Available security protocols" -Value $([System.Net.SecurityProtocolType].GetEnumNames()) -Force    
        Add-Member -InputObject $Script:clientInfo -MemberType NoteProperty -Name "Security protocols Used" -Value $([System.Net.ServicePointManager]::securityprotocol) -Force
        
        #Figuring out .Net framework version
        $netFrameworkVersions = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse | Get-ItemProperty -name Version -EA 0 | Where { $_.PSChildName -match '^(?!S)\p{L}'} | Select Version -ExpandProperty "Version" | Select -Unique
        Add-Member -InputObject $Script:clientInfo -MemberType NoteProperty -Name "Installed .Net Framework versions" -Value ($netFrameworkVersions | Out-String) -Force
    } catch {
        #ignore and move on
    }
}

<#
.SYNOPSIS
Modify REST API service info

.DESCRIPTION
Modify REST API service info

#>

function Update-RESTAPIServiceInfo {
    [cmdletbinding()]
    Param(
        [String] $serviceVersion = ""
    )
    
    Add-Member -InputObject $Script:restAPIServiceInfo -MemberType NoteProperty -Name "Version" -Value "$serviceVersion" -Force
}


<#
.SYNOPSIS
Return REST API Service info

.DESCRIPTION
Return REST API Service info

#>

function Get-ServiceAPIInfo {
    $Script:restAPIServiceInfo
}

<#
.SYNOPSIS
Modify the base service URI

.DESCRIPTION
Modify the base service URI

.EXAMPLE
Set-BaseServiceURI https://localhost/webservices/services/rest/v2
#>

function Set-BaseServiceURI {
    [cmdletbinding()]
    Param(
        [String] $uri = $(Throw "Provide a base service URI.")
    )    
    
    $Global:BASE_SERVICE_URI = $uri
    Write-Debug "Base URI changed to $uri"
}

<#
.SYNOPSIS
Get the base service URI

.DESCRIPTION
Get the base service URI

.EXAMPLE
Get-BaseServiceURI
#>

function Get-BaseServiceURI {
    $Global:BASE_SERVICE_URI
}

<#
.SYNOPSIS
Establish a connection with the REST service

.DESCRIPTION
Establish a connection with the REST service

.EXAMPLE

#Set hostname of vcommander server where {service_host} is the hostname
$Global:BASE_SERVICE_URI = https://{service_host}/webservices/services/rest/v2

#Set username and password
$username = "superuser"
$password = "secret"
$secPasswd = ConvertTo-SecureString $password -AsPlainText -Force

#Create creds file
$creds = New-Object System.Management.Automation.PSCredential ($username, $secPasswd)
$Global:CREDENTIAL = $creds

Connect-Client

.EXAMPLE

#Create Credentials File
$MyCredentials=GET-CREDENTIAL | EXPORT-CLIXML C:\Scriptfolder\SecureCredentials.xml

#Get Credential File
$MyCredentials=IMPORT-CLIXML "C:\Powershell Scripts\SecureCredentials.xml"
$Global:CREDENTIAL = $MyCredentials

#Set hostname of vcommander server where {service_host} is the hostname
$Global:BASE_SERVICE_URI = https://{service_host}/webservices/services/rest/v2

Connect-Client

#>

function Connect-Client {
    [cmdletbinding()]
    Param()

    Write-Debug "Checking for suitable security protocol prior to communicating with server."
    #vCommander is dropping support for TLSv1.0 so it's best we warn user
    if ( ([System.Net.SecurityProtocolType]::Tls11 -eq $null) -and ([System.Net.SecurityProtocolType]::Tls12 -eq $null)) {
        throw "The current installed .NET Framework doesn't support TLSv1.2 or TLSv1.1. Client requires security protocol TLSv1.2 or TLSv1.1 to communicate with server. Security protocols supported on this system: $([System.net.securityprotocoltype].GetEnumNames())"
    }
    
    # Explicitly set the protocol the client should use.
    [System.Net.ServicePointManager]::securityprotocol = "Tls12,Tls11"

    $result = Initialize-Client
    if (!$result) {
        throw "Unable to initialize client. Please run with -Debug switch to determine cause."
    }

    Write-Debug "Attemping to connect client ..."
    if ($Global:CREDENTIAL -eq $null) {
        Throw "Client has not been initialized or preconfigured. Please configure the global CREDENTIAL variable."
    }
    
    #Clear any cache if the user re-login
    Clear-Cache
    
    Get-SecurityToken $Global:CREDENTIAL
    Write-Debug "Client connected."

    $true
}


<#
.SYNOPSIS
Establish a connection with the REST service

.DESCRIPTION
Establish a connection with the REST service

.PARAMETER url
URL of the vCommander webservice (required)

.PARAMETER cred
vCommander credentials as a PowerShell credential object (required)

.PARAMETER ssltrustall
Use the -ssltrustall flag to trust self-signed certificates

.EXAMPLE
$credentials = Get-Credential
Connect-Client2 -url "https://vcommander/webservices/services/rest/v2" -cred $credentials -ssltrustall -organizationId 123

.EXAMPLE
#Create a credential file and Load it and connect without an organization

#Create Credentials File
$MyCredentials=GET-CREDENTIAL | EXPORT-CLIXML C:\Powershell Scripts\SecureCredentials.xml

#Get Credential File
$MyCredentials=IMPORT-CLIXML "C:\Powershell Scripts\SecureCredentials.xml"

Connect-Client2 -url "https://vcommander/webservices/services/rest/v2" -cred $MyCredentials -ssltrustall

#>

function Connect-Client2 {
    [cmdletbinding()]
    Param(
        [String] $url = $(Throw "Specify the webservice URL. Example https://vcommander/webservices/services/rest/v2"),
        [System.Management.Automation.CredentialAttribute()] $credentials = $(Throw "Specify vCommander credentials"),
        [switch] $ssltrustall = $false,
        [Long] $organizationId = $null
    )

    $Global:BASE_SERVICE_URI = $url
    $Global:CREDENTIAL = $credentials
    $Global:ORGANIZATION_ID = $null

    # Explicitly set the protocol the client should use.
    [System.Net.ServicePointManager]::securityprotocol = "Tls12,Tls11"
    
    if ($ssltrustall) {
        VCommander\Set-IgnoreSslErrors
    }
    
    if ($organizationId) {
        $Global:ORGANIZATION_ID = $organizationId
    }

    if ($verbose) {
        Connect-Client
    } else {
        Connect-Client | Out-Null
    }
}


<#
.SYNOPSIS
Get the base service URI

.DESCRIPTION
Get the base service URI

.EXAMPLE
Get-BaseServiceURI
#>

function Get-BaseServiceURI {
    $Global:BASE_SERVICE_URI
}


<#
.SYNOPSIS
Terminate a connection with the REST service

.DESCRIPTION
Terminate a connection with the REST service

.EXAMPLE
Disconnect-Client

#>

function Disconnect-Client {
    [cmdletbinding()]
    Param()
    
    if ($Global:Embotics.REQUEST_HEADERS.securityToken -eq $null) {
        return
    }
    
    Write-Debug "Logging out $($Global:Embotics.REQUEST_HEADERS.securityToken)"
    
    $fullURI = "$Global:BASE_SERVICE_URI/sessions/$($Global:Embotics.REQUEST_HEADERS.securityToken)"
    Write-Debug "URI is: $fullURI"
    
    Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method DELETE | Out-Null

    $Global:ORGANIZATION_ID = $null
}

<#
.SYNOPSIS
Switch to another organization by logging out and logging back in using the global credentials

.DESCRIPTION
Switch to another organization by logging out and logging back in using the global credentials

.EXAMPLE
Switch-Organization -organizationId 123

#>

function Switch-Organization {
    [cmdletbinding()]
    Param(
        [Long] $organizationId = $(Throw "Specify the organization ID")
    )
    
    Write-Debug "Switching organization to $($organizationId)"
    
    Disconnect-Client

    $Global:ORGANIZATION_ID = $organizationId
    Connect-Client
}

<#
.SYNOPSIS
Log in to get a security token

.DESCRIPTION
Log in to get a security token for subsequent requests

.EXAMPLE
Get-SecurityToken $credential

#>

function Get-SecurityToken {
    [CmdletBinding()]
    Param(
        [System.Management.Automation.PSCredential] $credential = $(Throw "Provide a PSCredential object containing the credential for authentication against the REST service.")
    )    

    Set-PSDebug -Strict
    Set-StrictMode -version Latest
    
    Write-Debug "Logging in as '$($credential.UserName)' to get security token."
    
    $fullURI = "$Global:BASE_SERVICE_URI/sessions"
    Write-Debug "URI is: $fullURI"
    
    #Constructing request body containing login credentials
    $tokenString = "$($Global:CREDENTIAL.GetNetworkCredential().UserName):$($Global:CREDENTIAL.GetNetworkCredential().Password)"
    if ($Global:ORGANIZATION_ID) {
        $tokenString += ":$($Global:ORGANIZATION_ID)"
    }
    $requestBody = VCommander\ConvertTo-Base64 $tokenString

    #Logging in
    try {
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Body $requestBody -ContentType "text/plain" -Method POST
    } catch {
        throw "Unable to communicate with vCommander REST service to acquire security token due to: $($_)";
    }

    Write-Debug "'$($credential.UserName)' logged in."
    Write-Debug "Security token assigned: $($requestResponse.Headers.securityToken)"
        
    #Applicable only for vCmdr 5.2+ exclusive
    try {
        Write-Debug "REST API service version: $($requestResponse.Headers."Embotics-REST-API-Version")"
        Update-RESTAPIServiceInfo -serviceVersion $($requestResponse.Headers."Embotics-REST-API-Version")
    } catch {
    }

    #Extract security token and keep track of it in HTTP request header
    $Global:Embotics.REQUEST_HEADERS = @{ securitytoken = $requestResponse.Headers.securityToken; }
    
    #Keep track of credential for use in keeping session alive
    $Global:CREDENTIAL = $credential
}

<#
.SYNOPSIS
Update the license with the specified key

.DESCRIPTION
Update the license with the specified key

.EXAMPLE
$result = Set-LicenseKey -key <<your key goes here>>
#>

function Set-LicenseKey {
    [cmdletbinding()]
    Param(
        [String] $key = $(Throw "Provide the license key.")
    )    
    
    
    $fullURI = "$Global:BASE_SERVICE_URI/license"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive

      try{
        $equestResponse  = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $key -ContentType "text/plain"
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    
    Convert-RestResponseOutput $requestResponse.RawContent | Out-null    
}

<#
.SYNOPSIS
Retrieve a managed object

.DESCRIPTION
Retrieve a managed object

.PARAMETER id
The managed object ID

.PARAMETER type
The managed object type

.PARAMETER name
The managed object name

.EXAMPLE
$mobject = Get-ManagedObject -id $managedObjectId -type "DATACENTER"

.EXAMPLE
$mobject = Get-ManagedObject -type "DATACENTER" -name "DC 1"

#>

function Get-ManagedObject {
    [cmdletbinding()]
    Param(
        [Long] $id = -1,# Managed object ID
        [String] $type = "", #Managed object type
        [String] $name = "" #Managed object name
    )
    
    
    
    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafeName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/managedobjects?id=$id&type=$type&name=$urlSafeName"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a managed object using ID and type

.DESCRIPTION
Retrieve a managed object using ID and type

.PARAMETER id
The managed object ID

.PARAMETER type
The managed object type

.EXAMPLE
$mobject = Get-ManagedObjectById -id $managedObjectId -type "DATACENTER"

#>

function Get-ManagedObjectById {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the managed object ID."),        
        [String] $type = $(Throw "Provide the managed object type.")
    )
    
    Get-ManagedObject -id $id -type $type
}

<#
.SYNOPSIS
Retrieve a managed object using ID and name

.DESCRIPTION
Retrieve a managed object using ID and name

.PARAMETER id
The managed object ID

.PARAMETER name
The managed object name

.EXAMPLE
$mobject = Get-ManagedObjectByName -type "DATACENTER" -name "DC 1"

#>

function Get-ManagedObjectByName {
    [cmdletbinding()]
    Param(
        [String] $name= $(Throw "Provide the name of the managed object."),        
        [String] $type = $(Throw "Provide the managed object type.")
    )
    
    Get-ManagedObject -name $name -type $type
}


<#
.SYNOPSIS
Retrieve a collection of network zones

.DESCRIPTION
Retrieve a collection of network zones

.EXAMPLE
$networkZones = Get-NetworkZones

#>

function Get-NetworkZones {
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/networkzones"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
        
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a network zone by ID

.DESCRIPTION
Retrieve a network zone by ID

.EXAMPLE
$ps = Get-NetworkZoneById -Id 24555

#>

function Get-NetworkZoneById {
    [cmdletbinding()]
    Param(
        [Long] $Id = $(Throw "Provide the network zone ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/networkzones/$Id"
    Write-Debug "URI is: $fullURI"
     
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
        
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a network zone by name

.DESCRIPTION
Retrieve a network zone by name

.EXAMPLE
$networkZone = Get-NetworkZoneByName -name "DMZ"

#>

function Get-NetworkZoneByName {
    [cmdletbinding()]
    Param(
        [String] $name = $(Throw "Provide the network zone name.")
    )
    
    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafeName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/networkzones/name/$urlSafeName"
    Write-Debug "URI is: $fullURI"
     
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
        
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Create a new network zone

.DESCRIPTION
Create a new network zone

.EXAMPLE

$networkZoneDTO = Get-DTOTemplateObject -DTOTagName "NetworkZone"
$networkZoneDTO.NetworkZone.name = "Prod 2"

$result = New-NetworkZone -dto $networkZoneDTO

#>

function New-NetworkZone { 
    [cmdletbinding()]
    Param(
        $dto = $(Throw "Provide the network zone DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/networkzones"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $dto
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    }catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    
    $true
}

<#
.SYNOPSIS
Modify a network zone

.DESCRIPTION
Modify a network zone

.EXAMPLE

$networkZoneDTO = Get-NetworkZoneByName -name "Prod 2"
$networkZoneDTO.NetworkZone.name = "Prod 2 Modified"

$result = Update-NetworkZone -Id $networkZoneDTO.NetworkZone.id -dto $networkZoneDTO

#>

function Update-NetworkZone {
    [cmdletbinding()]
    Param(
        [Long] $Id = $(Throw "Provide the ID of the network zone."),
        $dto = $(Throw "Provide the network zone DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/networkzones/$Id"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $dto
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method PUT -Body $xml_dto -ContentType "application/xml"
    }catch{
            Write-Error (Convert-ErrorForUser $_)
            Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Remove a network zone

.DESCRIPTION
Remove a network zone

.EXAMPLE
$networkZoneDTO = Get-NetworkZoneByName -name "Prod 2 Modified"
$result = Remove-NetworkZone -Id $networkZoneDTO.NetworkZone.id
#>

function Remove-NetworkZone {
    [cmdletbinding()]
    Param(
        [Long] $Id = $(Throw "Provide the ID of the network zone.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/networkzones/$id"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method DELETE
    }catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    
    $true
}

<#
.SYNOPSIS
Assign the specified network zone to a network

.DESCRIPTION
Assign the specified network zone to a network

.EXAMPLE

#Retrieve the network tag
$networkTagDTO = Get-NetworkZoneByName "DMZ"

#Retrieve the management server so we can filter down to the correct network
$msCollection = Get-ManagedObjectByName -name "manta" -type "MANAGEMENTSERVER"
$managementServer = $msCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the datacenter so we can filter down to the correct network
$datacenters = Get-Datacenters -msId $managementServer.id
$dc = $datacenters.DatacenterCollection.Datacenters | Where-Object { $_.name -match "Engineering"}

#Retrieve a network
$networks = Get-AvailableNetworks -dcid $dc.id
$network1 = $networks.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "Dev Network"}

$result = Set-NetworkZoneToNetwork -networkId $network1.id -networkZone $networkTagDTO
#>

function Set-NetworkZoneToNetwork {
    [cmdletbinding()]
    Param(
        [Long] $networkId = $(Throw "Provide the ID of the network."),
        $networkZone = $(Throw "Provide the network zone.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/networks/$networkId/action/applynetworkzone"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $networkZone
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}


<#
.SYNOPSIS
Assign the specified network zone to a subnet

.DESCRIPTION
Assign the specified network zone to a subnet

.EXAMPLE
#
###################################################################################
# Applying a network zone to a virtual cloud subnet
###################################################################################

#Retrieve the network tag
$networkTagDTO = Get-NetworkZoneByName "DMZ"

#Retrieve the management server so we can filter down to the correct network
$msCollection = Get-ManagedObjectByName -name "it@example.com" -type "MANAGEMENTSERVER"
$managementServer = $msCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the regions
$regions = Get-Regions
$regionReference = $regions.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "us-west-2"}
$region = Get-RegionById -id $regionReference.id

#Retrieve the virtual cloud
$vcid = $region.Region.VirtualClouds | Where-Object {$_.displayName -match "vpc-3b793253"} | select -ExpandProperty "id"

#Retrieve a subnet
$subnets = Get-VirtualCloudSubnets -regionId $regionReference.id -vcid $vcid
$subnetId = $subnets.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "subnet-da584db8"} | select -ExpandProperty "id"

$result = Set-NetworkZoneToSubnet -regionId $regionReference.id -vcId $vcid -subnetId $subnetId -networkZone $networkTagDTO


.EXAMPLE
#
###################################################################################
# Applying a network zone to a region subnet
###################################################################################

#Retrieve the network tag
$networkTagDTO = Get-NetworkZoneByName "DMZ"

#Retrieve the management server so we can filter down to the correct network
$msCollection = Get-ManagedObjectByName -name "it@example.com" -type "MANAGEMENTSERVER"
$managementServer = $msCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the regions
$regions = Get-Regions
$regionReference = $regions.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "us-west-2"}
$region = Get-RegionById -id $regionReference.id

#Retrieve a subnet
$subnets = Get-RegionSubnets -id $regionReference.id
$subnetId = $subnets.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "subnet-a697efce"} | select -ExpandProperty "id"

$result = Set-NetworkZoneToSubnet -regionId $regionReference.id -subnetId $subnetId -networkZone $networkTagDTO

#>

function Set-NetworkZoneToSubnet {
    [cmdletbinding()]
    Param(
        [Long] $regionId= $(Throw "Provide the region ID."),
        [Long] $vcId=-1,
        [Long] $subnetId = $(Throw "Provide the subnet ID."),
        $networkZone = $(Throw "Provide the network zone.")
    )

    if ($vcId > 0) {
        $fullURI = "$Global:BASE_SERVICE_URI/regions/$regionId/virtualclouds/$vcId/subnets/$subnetId/action/applynetworkzone"
    } else {
        $fullURI = "$Global:BASE_SERVICE_URI/regions/$regionId/subnets/$subnetId/action/applynetworkzone"
    }
    
    Write-Debug "URI is: $fullURI"
   
    $xml_dto = Convert-ObjectToXml $networkZone
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}
    

<#
.SYNOPSIS
Retrieve a collection of IP pools

.DESCRIPTION
Retrieve a collection of IP pools

.EXAMPLE
$pools = Get-IpPools

#>

function Get-IpPools {
    [cmdletbinding()]
    Param()
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/ippools"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve an IP pool by name

.DESCRIPTION
Retrieve an IP pool by name

.EXAMPLE
$pool = Get-IpPoolByName -name "Pool #2"

#>

function Get-IpPoolByName {
    [cmdletbinding()]
    Param(
        [String] $name = $(Throw "Provide the name of the IP pool.")
    )
    
    
    
    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafeName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/ippools/name/$urlSafeName"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}


<#
.SYNOPSIS
Retrieve an IP pool by ID

.DESCRIPTION
Retrieve an IP pool by ID

.EXAMPLE
$ipPool = Get-IpPoolById -id 12584

#>

function Get-IpPoolById {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the IP pool.")
    )
        
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/ippools/$id "
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Create an IP pool

.DESCRIPTION
Create an IP pool

.EXAMPLE
Create an IP pool without specifying networks

#Retrieve the management server
$managementServers = Get-ManagementServers
$msid = $managementServers.ManagementServerCollection.ManagementServers | Where-Object {$_.name -match "manta"} | Select -ExpandProperty "id"

#Retrieve the datacenter
$datacenters = Get-Datacenters -msId $msid
$dc = $datacenters.DatacenterCollection.Datacenters | Where-Object { $_.name -match "Engineering"}

#Create an IP Pool template
$ipPoolDto = New-DTOTemplateObject -DTOTagName "IpPool"
$ipPoolDto.IpPool.name = "Pool #2"
$ipPoolDto.IpPool.freeIpWarningThreshold = 3

#Set up the datacenter
$ipPoolDto.IpPool.datacenter.id = $dc.id
$ipPoolDto.IpPool.datacenter.displayName = $dc.name #Not required

#Set up the network configuration
$ipPoolDto.IpPool.networkConfig.defaultGateway = "10.10.10.251"
$ipPoolDto.IpPool.networkConfig.subnetMask = "255.255.255.0"
$ipPoolDto.IpPool.networkConfig.dnsSuffix = "example.com"
$ipPoolDto.IpPool.networkConfig.primaryDns = "10.10.10.1"
$ipPoolDto.IpPool.networkConfig.secondaryDns = "10.10.10.2"

#Set up the IP range
$ipSlot1 = New-DTOTemplateObject -DTOTagName "Ipv4Range"
$ipSlot1.Ipv4Range.from = "10.10.10.3"
$ipSlot1.Ipv4Range.to = "10.10.10.10"

$ipSlot2 = New-DTOTemplateObject -DTOTagName "Ipv4Range"
$ipSlot2.Ipv4Range.from = "10.10.10.15"
$ipSlot2.Ipv4Range.to = "10.10.10.20"

$ipSlot3 = New-DTOTemplateObject -DTOTagName "Ipv4Range"
$ipSlot3.Ipv4Range.from = "10.10.10.30"
$ipSlot3.Ipv4Range.to = "10.10.10.50"

#Assign the IP slots to the range structure
$ipPoolDto.IpPool.range = @($ipSlot1.Ipv4Range, $ipSlot2.Ipv4Range, $ipSlot3.Ipv4Range)

#Create the IP pool
$taskInfo = New-IpPool -ipPoolDto $ipPoolDto


.EXAMPLE
Create an IP pool with networks

#Retrieve the management server
$managementServers = Get-ManagementServers
$msid = $managementServers.ManagementServerCollection.ManagementServers | Where-Object {$_.name -match "manta"} | Select -ExpandProperty "id"

#Retrieve the datacenter
$datacenters = Get-Datacenters -msId $msid
$dc = $datacenters.DatacenterCollection.Datacenters | Where-Object { $_.name -match "Engineering"}

#Create an IP Pool template
$ipPoolDto = New-DTOTemplateObject -DTOTagName "IpPool"
$ipPoolDto.IpPool.name = "Pool #2"
$ipPoolDto.IpPool.freeIpWarningThreshold = 3

#Set up the datacenter
$ipPoolDto.IpPool.datacenter.id = $dc.id
$ipPoolDto.IpPool.datacenter.displayName = $dc.name #Not required

#Set up the network configuration
$ipPoolDto.IpPool.networkConfig.defaultGateway = "10.10.10.251"
$ipPoolDto.IpPool.networkConfig.subnetMask = "255.255.255.0"
$ipPoolDto.IpPool.networkConfig.dnsSuffix = "example.com"
$ipPoolDto.IpPool.networkConfig.primaryDns = "10.10.10.1"
$ipPoolDto.IpPool.networkConfig.secondaryDns = "10.10.10.2"

#Set up the IP range
$ipSlot1 = New-DTOTemplateObject -DTOTagName "Ipv4Range"
$ipSlot1.Ipv4Range.from = "10.10.10.3"
$ipSlot1.Ipv4Range.to = "10.10.10.10"

$ipSlot2 = New-DTOTemplateObject -DTOTagName "Ipv4Range"
$ipSlot2.Ipv4Range.from = "10.10.10.15"
$ipSlot2.Ipv4Range.to = "10.10.10.20"

$ipSlot3 = New-DTOTemplateObject -DTOTagName "Ipv4Range"
$ipSlot3.Ipv4Range.from = "10.10.10.30"
$ipSlot3.Ipv4Range.to = "10.10.10.50"

#Assign the IP slots to the range structure
$ipPoolDto.IpPool.range = @($ipSlot1.Ipv4Range, $ipSlot2.Ipv4Range, $ipSlot3.Ipv4Range)

$networks = Get-AvailableNetworks -dcid $dc.id
$network1 = $networks.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "Dev Network"}
$network2 = $networks.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "PV Network"}

$poolNetworks = @($network1,$network2)
Add-Member -InputObject $ipPoolDto.IpPool -MemberType NoteProperty -Name "networks" -Value $poolNetworks -Force

#Create the IP pool
$taskInfo = New-IpPool -ipPoolDto $ipPoolDto
#>

function New-IpPool {
    [cmdletbinding()]
    Param(
        $ipPoolDto = $(Throw "Provide the IP pool DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/ippools"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $ipPoolDto
    
    Request-SessionAlive
   
    try{
        $requestResponse  = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
        
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Modify an IP pool

.DESCRIPTION
Modify an IP pool

.EXAMPLE
$pool = Get-IpPoolByName -name "Pool #2"
$pool.IpPool.name= "New Name"
#All other properties follow the same pattern as New-IpPool command.

$taskInfo = Update-IpPool -id $pool.IpPool.id -updatedPool $pool

#>

function Update-IpPool {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the IP pool."),
        $updatedPool = $(Throw "Provide the updated IP pool DTO.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/configurations/ippools/$id"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $updatedPool
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method PUT -Body $xml_dto -ContentType "application/xml"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Remove an IP pool

.DESCRIPTION
Remove an IP pool

.EXAMPLE

$pool = Get-IpPoolByName -name "Pool #2"
$taskInfo = Remove-IpPool -id $pool.IpPool.id

#>

function Remove-IpPool {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the IP pool.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/configurations/ippools/$id"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method DELETE
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    
    $true
}

<#
.SYNOPSIS
Retrieve a collection of deployment destinations

.DESCRIPTION
Retrieve a collection of deployment destinations

.PARAMETER msid
The management server ID. This restricts the retrieved deployment destinations to the specified management server.

.PARAMETER max
The max count to retrieve

.EXAMPLE
$deploymentDestinations = Get-DeploymentDestinations

#Get deployment Destinations with a managed system id and a max results

#Get the management server
$mobjectCollection = Get-ManagedObjectByName -name <managementservername> -type "MANAGEMENTSERVER"

#Get the Id of the management server
$managementServerId = $mobjectCollection.ManagedObjectCollection.managedObjects[0].id

#Get the deployment destination with a maxmimum of 1 result returned
$deploymentDestinations = Get-DeploymentDestinationDestinations -msid $managementServerId -max 1

.
#>

function Get-DeploymentDestinations {
    [cmdletbinding()]
    Param(
        [Long] $msid = -1,
        [Int] $max = 10
    )

    $fullURI = "$Global:BASE_SERVICE_URI/deploymentdestinations?msid=$msid&max=$max"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a deployment destination by ID

.DESCRIPTION
Retrieve a deployment destination by ID

.PARAMETER id
The deployment destination ID

.EXAMPLE
$deploymentDestination = Get-DeploymentDestinationById -id $ddId

#>

function Get-DeploymentDestinationById {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the deployment destination Id.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/deploymentdestinations/$id"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Activate a passive vCommander

.DESCRIPTION
Activate a passive vCommander

.PARAMETER quarantine
Defaults to true. If set to false, then as part of activation, the other system will not be quarantined.

.EXAMPLE
$activated = Invoke-Activate

.EXAMPLE
$activated = Invoke-Activate -quarantine $false

#>

function Invoke-Activate {
    [cmdletbinding()]
    Param(
        [bool] $quarantine = $true
    )
    $fullURI = "$Global:BASE_SERVICE_URI/configuration/activate?quarantine=$quarantine"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body "" -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
}

<#
.SYNOPSIS
Check the state of the vCommander service

.DESCRIPTION
Check the state of the vCommander service: whether the service is in active or stand-by mode, and whether it is healthy

.EXAMPLE
$systemState = Get-SystemState

#>

function Get-SystemState {
    [cmdletbinding()]
    Param()

    $fullURI = "$Global:BASE_SERVICE_URI/configuration/state"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}



<#
.SYNOPSIS
Retrieve deployment destinations assigned to an organization

.DESCRIPTION
Retrieve deployment destinations assigned to an organization

.PARAMETER orgid
The organization ID

.EXAMPLE
#Get an organization by name and retrieve the organization by name
$organization = Get-Organization -name "Default Organization"
$orgId = $organization.Organization.id
$deploymentDestinations = Get-DeploymentDestinationsByOrg -orgid $orgId

#>

function Get-DeploymentDestinationsByOrg {
    [cmdletbinding()]
    Param(
        [Long] $orgid = $(Throw "Provide the organization ID")
    )
    

    $fullURI = "$Global:BASE_SERVICE_URI/deploymentdestinations/assignments/orgs/$orgid"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a deployment destination by name

.DESCRIPTION
Retrieve a deployment destination by name

.PARAMETER name
The deployment destination name

.EXAMPLE
$deploymentDestination = Get-DeploymentDestinationByName -name "Deployment Default #1"

#>

function Get-DeploymentDestinationByName {
    [cmdletbinding()]
    Param(
        [String] $name = $(Throw "Provide name of the deployment destination.")
    )

    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafeName"
    
    
    $fullURI = "$Global:BASE_SERVICE_URI/deploymentdestinations/name/$urlSafeName"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

####################################################################################################################### #
# Policies #
#######################################################################################################################

<#
.SYNOPSIS
Retrieve a collection of policies

.DESCRIPTION
Retrieve a collection of policies

.PARAMETER max
The max count to retrieve

.EXAMPLE
$policies = Get-Policies -max 100
#>

function Get-Policies{
    [cmdletbinding()]
    Param(
        [Int] $max = 100
    )
    
    

    $fullURI = "$Global:BASE_SERVICE_URI/policies?max=$max"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a collection of policies of the specified type

.DESCRIPTION
Retrieve a collection of policies of the specified type

.EXAMPLE
$policies = PoliciesByType -type "COMPLIANCE" -max 20
#>

function Get-PoliciesByType {
    [cmdletbinding()]
    Param(        
        $type = $(Throw "Provide the type of the policy."),
        $max = 100 #Maximum number of policies to return
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/policies/type/$($type)?max=$max"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a policy by ID

.DESCRIPTION
Retrieve a policy by ID

.PARAMETER id
The policy ID

.EXAMPLE
$policy = Get-PolicyById -id $policyId

#>

function Get-PolicyById {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the policy ID.")
    )
    
    

    $fullURI = "$Global:BASE_SERVICE_URI/policies/$id"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a policy by name

.DESCRIPTION
Retrieve a policy by name

.PARAMETER name
The policy name

.EXAMPLE
$policy = Get-PolicyByName -name "Policy #1"

#>

function Get-PolicyByName {
    [cmdletbinding()]
    Param(
        [String] $name = $(Throw "Provide name of the policy.")
    )

    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafeName"

    $fullURI = "$Global:BASE_SERVICE_URI/policies/name/$urlSafeName"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Modify a policy

.DESCRIPTION
Modify a policy

.PARAMETER id
The policy ID

.PARAMETER dto
The policy DTO

.EXAMPLE
$policy = Get-PolicyByName "Compliances Policy"

#Change any number of properties. See the policy creation APIs for examples

#Changing the target to a management server
$mobjectCollection = Get-ManagedObjectByName -name "manta" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]
$policy.CompliancePolicy.targets = @($managementServer)
$policy.CompliancePolicy.global = $false

$updatedPolicy = Update-Policy -id $policy.CompliancePolicy.id -dto $policy
#>

function Update-Policy {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the policy ID."),
        $dto = $(Throw "Provide the policy DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/policies/$id"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $dto
    
    Request-SessionAlive
   
    try{
        $requestResponse  = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method PUT -Body $xml_dto -ContentType "application/xml"
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
        
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Remove a policy

.DESCRIPTION
Remove a policy

.EXAMPLE

#Get a Policy by name and remove the policy
$policy = Get-PolicyByName -name "Policy 1"
$polcyId = $policy.CompliancePolicy.id
$result = Remove-Policy -id $policyId
#>

function Remove-Policy {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the policy.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/policies/$id"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method DELETE
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    
    $true
}

<#
.SYNOPSIS
Create a policy

.DESCRIPTION
Create a policy

.PARAMETER dto
A policy DTO

.EXAMPLE
###################################################################################
# Create a Compliance Policy for a specific resource Pool with SLA and Cost Center requirements
# Action is 'notify only'
###################################################################################

#Retrieve management server
$dcCollection = Get-ManagedObjectByName -name "RESOURCEPOOL" -type "RESOURCEPOOL"
$dc = $dcCollection.ManagedObjectCollection.managedObjects[0]

#Create a Compliance Policy Template
$dto = New-DTOTemplateObject -DTOTagName "CompliancePolicy"
$dto.CompliancePolicy.name = "Compliance Policy"
$dto.CompliancePolicy.description = "Policy description"
$dto.CompliancePolicy.enabled = $true
$dto.CompliancePolicy.alertViaSNMP = $true
$dto.CompliancePolicy.allowsOverride = $true
$dto.CompliancePolicy.generateVCenterAlerts = $true
$dto.CompliancePolicy.gracePeriodInHrs = 7 # 1-24 hrs, or multiple of 24 hrs up to 168 hrs (7 days)
$dto.CompliancePolicy.treeViewType = "OPERATIONAL" #Or VMS_AND_TEMPLATES

#Specify target
$dto.CompliancePolicy.targets =@($dc)

#See New-EndOfLifePolicy API for example(s) on how to create policy workflow action
$dto.CompliancePolicy.action.type = "NOTIFY_ONLY"

#Retrieve a number of attributes
$costCenterManagedObjectCollection = Get-ManagedObjectByName -type "ATTRIBUTE" -name "Cost Center"
$costCenterReference = $costCenterManagedObjectCollection.ManagedObjectCollection.managedObjects[0]
    
$slaManagedObjectCollection = Get-ManagedObjectByName -type "ATTRIBUTE" -name "SLA"
$slaReference = $slaManagedObjectCollection.ManagedObjectCollection.managedObjects[0]

#Set up a compliance requirement
$complianceRequirementDTO = New-DTOTemplateObject -DTOTagName "ComplianceRequirement"
$complianceRequirementDTO.ComplianceRequirement.expiryDateRequired = $true
$complianceRequirementDTO.ComplianceRequirement.primaryOwnerRequired = $false
$complianceRequirementDTO.ComplianceRequirement.attributes = @($costCenterReference,$slaReference)
$dto.CompliancePolicy.complianceRequirement = $complianceRequirementDTO.ComplianceRequirement

$createdPolicy = New-CompliancePolicy -dto $dto
$createdPolicy = New-Policy -dto $dto
#>

function New-Policy {
    [cmdletbinding()]
    Param(
        $dto = $(Throw "Provide the policy DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/policies"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $dto
    
    Request-SessionAlive
   
    try{
        $requestResponse  = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
        
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Create an Unapproved VM policy

.DESCRIPTION
Create an Unapproved VM policy

.PARAMETER dto
A policy DTO

.EXAMPLE
#
###################################################################################
# Policy targeting a resource pool [Could use any target]
# Action is 'notify only'
###################################################################################

#Retrieve resource pool
$resourcePoolCollection = Get-ManagedObject -type "RESOURCEPOOL" -name "EmptyPool"
$resourcePoolReference = $resourcePoolCollection.ManagedObjectCollection.managedObjects[0]

$dto = New-DTOTemplateObject -DTOTagName "UnapprovedPolicy"
$dto.UnapprovedPolicy.name = "Policy #1"
$dto.UnapprovedPolicy.description = "Policy #1 description"
$dto.UnapprovedPolicy.enabled = $true
$dto.UnapprovedPolicy.alertViaSNMP = $true
$dto.UnapprovedPolicy.allowsOverride = $true

#Set action type
#See New-EndOfLifePolicy API for example(s) on how to create policy workflow action
$dto.UnapprovedPolicy.action.type = "NOTIFY_ONLY"

#Set target
$dto.UnapprovedPolicy.targets =@($resourcePoolReference)
$dto.UnapprovedPolicy.global = $false
$createdPolicy = New-UnapprovedPolicy -dto $dto

#>

function New-UnapprovedPolicy {
    [cmdletbinding()]
    Param(
        $dto = $(Throw "Provide the policy DTO.")
    )
    
    New-Policy -dto $dto
}

<#
.SYNOPSIS
Create a Suspect VM policy

.DESCRIPTION
Create a Suspect VM policy

.PARAMETER dto
A policy DTO

.EXAMPLE
#
###################################################################################
# Policy targeting a management server [Can use any target]
###################################################################################

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "manta" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

$dto = New-DTOTemplateObject -DTOTagName "SuspectPolicy"
$dto.SuspectPolicy.name = "Policy #2"
$dto.SuspectPolicy.description = "Policy #2 description"
$dto.SuspectPolicy.enabled = $true
$dto.SuspectPolicy.alertViaSNMP = $true
$dto.SuspectPolicy.allowsOverride = $true

#Set action type
#See New-EndOfLifePolicy API for example(s) on how to create policy workflow action
$dto.SuspectPolicy.action.type = "QUARANTINE_VM"

#Set target
$dto.SuspectPolicy.targets =@($managementServer)
$dto.SuspectPolicy.global = $false

$createdPolicy = New-SuspectPolicy -dto $dto

#>

function New-SuspectPolicy {
    [cmdletbinding()]
    Param(
        $dto = $(Throw "Provide the policy DTO.")
    )

    New-Policy -dto $dto
}

<#
.SYNOPSIS
Create an End of Life VM policy

.DESCRIPTION
Create an End of Life VM policy

.PARAMETER dto
A policy DTO

.EXAMPLE
#
###################################################################################
# Policy targeting a datacenter [can use any target] and executing
# a workflow as an action
###################################################################################

#Retrieve target datacenter
$dcCollection = Get-ManagedObjectByName -name "EmptyDC" -type "DATACENTER"
$dc = $dcCollection.ManagedObjectCollection.managedObjects[0]

#Find the correct workflow definition to use
$workflowDefinitions = Get-WorkflowDefinitionsByType -workflowType "VM"
$workflow = $workflowDefinitions.WorkflowDefinitionCollection.WorkflowDefinitions | Where-Object {$_.name -match "Service Workflow #1"}

#Create a policy workflow acion
#Use the workflow definition we just pulled
$policyWorkflowActionObject = New-DTOTemplateObject -DTOTagName "PolicyWorkflowAction"
$policyWorkflowActionObject.PolicyWorkflowAction.workflowDefinition = $workflow

$dto = New-DTOTemplateObject -DTOTagName "EOLPolicy"
$dto.EOLPolicy.name = "Policy #3"
$dto.EOLPolicy.description = "Policy #3 description"
$dto.EOLPolicy.enabled = $true
$dto.EOLPolicy.alertViaSNMP = $false
$dto.EOLPolicy.allowsOverride = $false
$dto.EOLPolicy.targets =@($dc)
$dto.EOLPolicy.global = $false

#Override simple policy action with a workflow action
$dto.EOLPolicy.action = $policyWorkflowActionObject.PolicyWorkflowAction

$createdPolicy = New-EndOfLifePolicy -dto $dto

.EXAMPLE
#
###################################################################################
# Policy targeting a managed system [can use any target]
# Action is 'remove VM from inventory'
###################################################################################

#Retrieve target
$moCollection = Get-ManagedObjectByName -name "vcenter01" -type "MANAGEMENTSERVER"
$target = $moCollection.ManagedObjectCollection.managedObjects[0]

$dto = New-DTOTemplateObject -DTOTagName "EOLPolicy"
$dto.EOLPolicy.name = "EOL Policy #2"
$dto.EOLPolicy.description = "EOL policy created using REST api"
$dto.EOLPolicy.enabled = $true
$dto.EOLPolicy.alertViaSNMP = $false
$dto.EOLPolicy.allowsOverride = $false
$dto.EOLPolicy.targets =@($target)
$dto.EOLPolicy.global = $false

#Set action type
$dto.EOLPolicy.action.type = "REMOVE_VM_FROM_INVENTORY"

$createdPolicy = New-EndOfLifePolicy -dto $dto

#>

function New-EndOfLifePolicy {
    [cmdletbinding()]
    Param(
        $dto = $(Throw "Provide the policy DTO.")
    )

    New-Policy -dto $dto
}

<#
.SYNOPSIS
Create a Default Ownership policy

.DESCRIPTION
Create a Default Ownership policy

.PARAMETER dto
A policy DTO

.EXAMPLE
#
###################################################################################
# Policy targeting a datacenter [can use any target] and executing
# a workflow as an action
###################################################################################

#Retrieve datacenter
$dcCollection = Get-ManagedObjectByName -name "EmptyDC" -type "DATACENTER"
$dc = $dcCollection.ManagedObjectCollection.managedObjects[0]

#Find the correct workflow definition to use
$workflowDefinitions = Get-WorkflowDefinitionsByType -workflowType "VM"
$workflow = $workflowDefinitions.WorkflowDefinitionCollection.WorkflowDefinitions | Where-Object {$_.name -match "Service Workflow #1"}

#Create a policy workflow acion
#Use the workflow definition we just pulled
$policyWorkflowActionObject = New-DTOTemplateObject -DTOTagName "PolicyWorkflowAction"
$policyWorkflowActionObject.PolicyWorkflowAction.workflowDefinition = $workflow

$dto = New-DTOTemplateObject -DTOTagName "DefaultOwnershipPolicy"
$dto.DefaultOwnershipPolicy.name = "Policy #4"
$dto.DefaultOwnershipPolicy.description = "Policy #4 description"
$dto.DefaultOwnershipPolicy.enabled = $true
$dto.DefaultOwnershipPolicy.treeViewType = "OPERATIONAL" #Or VMS_AND_TEMPLATES
$dto.DefaultOwnershipPolicy.allowsOverride = $false

#Override simple policy action with a workflow action
$dto.DefaultOwnershipPolicy.action = $policyWorkflowActionObject.PolicyWorkflowAction

#Specify target
$dto.DefaultOwnershipPolicy.targets =@($dc)
$dto.DefaultOwnershipPolicy.global = $false

#Create a user; the only information we need is the loginId
$user1DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user1DTO.OwnerInfo.loginId = "superuser"
$user1DTO.OwnerInfo.itContact = $false
$user1DTO.OwnerInfo.primary = $true

#Create a user; the only information we need is the loginId
$user2DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user2DTO.OwnerInfo.loginId = "manager"
$user2DTO.OwnerInfo.itContact = $true
$user2DTO.OwnerInfo.primary = $false

#Create a user; the only information we need is the loginId
$user3DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user3DTO.OwnerInfo.loginId = "bob@example.com"
$user3DTO.OwnerInfo.itContact = $false
$user3DTO.OwnerInfo.primary = $false

#Set the owners
$dto.DefaultOwnershipPolicy.owners = @($user1DTO.OwnerInfo, $user2DTO.OwnerInfo, $user3DTO.OwnerInfo)

#Grab the organization
$org = Get-OrganizationByName -name "Default Organization"
$dto.DefaultOwnershipPolicy.organization = $org.Organization

$createdPolicy = New-OwnershipPolicy -dto $dto


.EXAMPLE
#
###################################################################################
# Policy targeting a folder [can use any target]
# Using VMS_AND_TEMPLATES view
# with Notify Only action
###################################################################################

#Retrieve folder
$folderCollecions = Get-FoldersByName -name "VMDeploymentFolder"
$folder = $folderCollecions.FolderCollection.Folders[0]

$dto = New-DTOTemplateObject -DTOTagName "DefaultOwnershipPolicy"
$dto.DefaultOwnershipPolicy.name = "Policy #4"
$dto.DefaultOwnershipPolicy.description = "Policy #4 description"
$dto.DefaultOwnershipPolicy.enabled = $true
$dto.DefaultOwnershipPolicy.treeViewType = "VMS_AND_TEMPLATES" #Or OPERATIONAL
$dto.DefaultOwnershipPolicy.allowsOverride = $false
$dto.DefaultOwnershipPolicy.action.type = "NOTIFY_ONLY"

#Specify target
$dto.DefaultOwnershipPolicy.targets =@($folder)
$dto.DefaultOwnershipPolicy.targets = $false

#Create a user; the only information we need is the loginId
$user1DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user1DTO.OwnerInfo.loginId = "superuser"
$user1DTO.OwnerInfo.itContact = $false
$user1DTO.OwnerInfo.primary = $true

#Create a user; the only information we need is the loginId
$user2DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user2DTO.OwnerInfo.loginId = "manager"
$user2DTO.OwnerInfo.itContact = $true
$user2DTO.OwnerInfo.primary = $false

#Create a user; the only information we need is the loginId
$user3DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user3DTO.OwnerInfo.loginId = "bob@example.com"
$user3DTO.OwnerInfo.itContact = $false
$user3DTO.OwnerInfo.primary = $false

#Set the owners
$dto.DefaultOwnershipPolicy.owners = @($user1DTO.OwnerInfo, $user2DTO.OwnerInfo, $user3DTO.OwnerInfo)

#Grab the organization
$org = Get-OrganizationByName -name "Default Organization"
$dto.DefaultOwnershipPolicy.organization = $org.Organization

$createdPolicy = New-OwnershipPolicy -dto $dto

#>

function New-OwnershipPolicy {
    [cmdletbinding()]
    Param(
        $dto = $(Throw "Provide the policy DTO.")
    )

    New-Policy -dto $dto
}


<#
.SYNOPSIS
Create an Expired VM policy

.DESCRIPTION
Create an Expired VM policy

.PARAMETER dto
A policy DTO

.EXAMPLE
#
###################################################################################
# Policy targeting a datacenter [can use any target]
###################################################################################

#Retrieve target datacenter
$dcCollection = Get-ManagedObjectByName -name "EmptyDC" -type "DATACENTER"
$dc = $dcCollection.ManagedObjectCollection.managedObjects[0]

$dto = New-DTOTemplateObject -DTOTagName "ExpiryPolicy"
$dto.ExpiryPolicy.name = "Policy #5"
$dto.ExpiryPolicy.description = "Policy #5 description"
$dto.ExpiryPolicy.enabled = $true
$dto.ExpiryPolicy.targets =@($dc)
$dto.ExpiryPolicy.global = $false

#Set action - soon to expire
$soonToExpireAction = $dto.ExpiryPolicy.intervalActions | Where-Object {$_.interval -match "SOON_TO_EXPIRE"}
$soonToExpireAction.action.type = "NOTIFY_ONLY"

#Set action - expired
$expiredAction = $dto.ExpiryPolicy.intervalActions | Where-Object {$_.interval -eq "EXPIRED"}
$expiredAction.action.type ="SET_EOL"

#Set action - post-expired
#Find the correct workflow definition to use
$workflowDefinitions = Get-WorkflowDefinitionsByType -workflowType "VM"
$workflow = $workflowDefinitions.WorkflowDefinitionCollection.WorkflowDefinitions | Where-Object {$_.name -match "Service Workflow #1"}

#Create a policy workflow acion
#Use the workflow definition we just pulled
$policyWorkflowActionObject = New-DTOTemplateObject -DTOTagName "PolicyWorkflowAction"
$policyWorkflowActionObject.PolicyWorkflowAction.workflowDefinition = $workflow

$postExpiredAction = $dto.ExpiryPolicy.intervalActions | Where-Object {$_.interval -eq "POST_EXPIRED"}
$postExpiredAction.action = $policyWorkflowActionObject.PolicyWorkflowAction

#Set expiry groups
$expiryGroup = Get-GroupByName -groupType "EXPIRY_GROUP" -name "Default Expiry Group"
$dto.ExpiryPolicy.expiryGroup = $expiryGroup.Group
$dto.ExpiryPolicy.preExpiryLength = 8
$dto.ExpiryPolicy.postExpiryLength = 31

#Expiry extension
$dto.ExpiryPolicy.expiryExtension.extensionDays = 27
$dto.ExpiryPolicy.expiryExtension.vmStateToUnapproved = $false

#Set Expiry extension limit
Add-Member -InputObject $dto.ExpiryPolicy.expiryExtension -MemberType NoteProperty -Name "expiryExtensionLimit" -Value "3"

#Expiry notifications
$dto.ExpiryPolicy.expiryNotifications.alertViaSNMP = $true
$dto.ExpiryPolicy.expiryNotifications.emailITContact =$true
$dto.ExpiryPolicy.expiryNotifications.emailPrimaryOwner =$true
$dto.ExpiryPolicy.expiryNotifications.emailAllOwners = $false
$dto.ExpiryPolicy.expiryNotifications.emailSubject = "Email subject"
$dto.ExpiryPolicy.expiryNotifications.emailBody = "Email body"

$createdPolicy = New-ExpiryPolicy -dto $dto


.EXAMPLE
#
###################################################################################
# Policy targeting a datacenter [can use any target]
# Disable expiry extension and notifications
###################################################################################

#Retrieve target datacenter
$dcCollection = Get-ManagedObjectByName -name "EmptyDC" -type "DATACENTER"
$dc = $dcCollection.ManagedObjectCollection.managedObjects[0]

$dto = New-DTOTemplateObject -DTOTagName "ExpiryPolicy"
$dto.ExpiryPolicy.name = "Policy #5"
$dto.ExpiryPolicy.description = "Policy #5 description"
$dto.ExpiryPolicy.enabled = $true
$dto.ExpiryPolicy.targets =@($dc)
$dto.ExpiryPolicy.global = $false

#Set action - soon to expire
$soonToExpireAction = $dto.ExpiryPolicy.intervalActions | Where-Object {$_.interval -match "SOON_TO_EXPIRE"}
$soonToExpireAction.action.type = "NOTIFY_ONLY"

#Set action - expired
$expiredAction = $dto.ExpiryPolicy.intervalActions | Where-Object {$_.interval -eq "EXPIRED"}
$expiredAction.action.type ="SET_EOL"

#Set action - post-expired
#Find the correct workflow definition to use
$workflowDefinitions = Get-WorkflowDefinitionsByType -workflowType "VM"
$workflow = $workflowDefinitions.WorkflowDefinitionCollection.WorkflowDefinitions | Where-Object {$_.name -match "Service Workflow #1"}

#Create a policy workflow acion
#Use the workflow definition we just pulled
$policyWorkflowActionObject = New-DTOTemplateObject -DTOTagName "PolicyWorkflowAction"
$policyWorkflowActionObject.PolicyWorkflowAction.workflowDefinition = $workflow

$postExpiredAction = $dto.ExpiryPolicy.intervalActions | Where-Object {$_.interval -eq "POST_EXPIRED"}
$postExpiredAction.action = $policyWorkflowActionObject.PolicyWorkflowAction

#Set expiry groups
$expiryGroup = Get-GroupByName -groupType "EXPIRY_GROUP" -name "Default Expiry Group"
$dto.ExpiryPolicy.expiryGroup = $expiryGroup.Group
$dto.ExpiryPolicy.preExpiryLength = 8
$dto.ExpiryPolicy.postExpiryLength = 31

#Disable expiry extension
$dto.ExpiryPolicy.PSObject.Properties.Remove("expiryExtension")

#Disable expiry notifications
$dto.ExpiryPolicy.PSObject.Properties.Remove("expiryNotifications")

$createdPolicy = New-ExpiryPolicy -dto $dto
#>

function New-ExpiryPolicy {
    [cmdletbinding()]
    Param(
        $dto = $(Throw "Provide the policy DTO.")
    )

    New-Policy -dto $dto
}


<#
.SYNOPSIS
Create a Compliance policy

.DESCRIPTION
Create a Compliance policy

.PARAMETER dto
A policy DTO

.EXAMPLE
#
###################################################################################
# Policy targeting a datacenter [can use any target]
# Action is 'notify only'
# Required attributes: Two custom attributes + expiry date
###################################################################################

#Retrieve target datacenter
$dcCollection = Get-ManagedObjectByName -name "EmptyDC" -type "DATACENTER"
$target = $dcCollection.ManagedObjectCollection.managedObjects[0]

$dto = New-DTOTemplateObject -DTOTagName "CompliancePolicy"
$dto.CompliancePolicy.name = "Policy #7"
$dto.CompliancePolicy.description = "Policy #7 description"
$dto.CompliancePolicy.enabled = $true
$dto.CompliancePolicy.alertViaSNMP = $true
$dto.CompliancePolicy.allowsOverride = $true
$dto.CompliancePolicy.generateVCenterAlerts = $true
$dto.CompliancePolicy.gracePeriodInHrs = 7 # 1-24 hrs, or multiple of 24 hrs up to 168 hrs (7 days)
$dto.CompliancePolicy.treeViewType = "OPERATIONAL" #Or VMS_AND_TEMPLATES

#Specify target
$dto.CompliancePolicy.targets =@($target)
$dto.CompliancePolicy.global = $false

#Set action to be notify only
#See New-EndOfLifePolicy API for example(s) on how to create policy workflow action
$dto.CompliancePolicy.action.type = "NOTIFY_ONLY"

#Retrieve a number of attributes
$costCenterManagedObjectCollection = Get-ManagedObjectByName -type "ATTRIBUTE" -name "Cost Center"
$costCenterReference = $costCenterManagedObjectCollection.ManagedObjectCollection.managedObjects[0]
$costCenterReference.description = "" #Not need; if sending to server, we need to escape special characters

$slaManagedObjectCollection = Get-ManagedObjectByName -type "ATTRIBUTE" -name "SLA"
$slaReference = $slaManagedObjectCollection.ManagedObjectCollection.managedObjects[0]
$slaReference.description = "" #Not need; if sending to server, we need to escape special characters

#Set up a compliance requirement
$complianceRequirementDTO = New-DTOTemplateObject -DTOTagName "ComplianceRequirement"
$complianceRequirementDTO.ComplianceRequirement.expiryDateRequired = $true #setting this to true to make expiry date required
$complianceRequirementDTO.ComplianceRequirement.primaryOwnerRequired = $false
$complianceRequirementDTO.ComplianceRequirement.attributes = @($costCenterReference,$slaReference)
$dto.CompliancePolicy.complianceRequirement = $complianceRequirementDTO.ComplianceRequirement

$createdPolicy = New-CompliancePolicy -dto $dto

.EXAMPLE
#
###################################################################################
# Policy targeting a vApp [can use any target]
# Action is 'suspend vm'
# Required attributes: only primary owner
###################################################################################

#Retrieve target vApp
$vAppCollection = Get-ManagedObjectByName -name "vApp001" -type "VIRTUALAPP"
$target = $vAppCollection.ManagedObjectCollection.managedObjects[0]

$dto = New-DTOTemplateObject -DTOTagName "CompliancePolicy"
$dto.CompliancePolicy.name = "Policy #7"
$dto.CompliancePolicy.description = "Policy #7 description"
$dto.CompliancePolicy.enabled = $true
$dto.CompliancePolicy.alertViaSNMP = $true
$dto.CompliancePolicy.allowsOverride = $true
$dto.CompliancePolicy.generateVCenterAlerts = $true
$dto.CompliancePolicy.gracePeriodInHrs = 7 # 1-24 hrs, or multiple of 24 hrs up to 168 hrs (7 days)
$dto.CompliancePolicy.treeViewType = "OPERATIONAL" #Or VMS_AND_TEMPLATES

#Specify target
$dto.CompliancePolicy.targets =@($target)
$dto.CompliancePolicy.global = $false

#Set action to be 'suspend vm'
#See New-EndOfLifePolicy API for example(s) on how to create policy workflow action
$dto.CompliancePolicy.action.type = "SUSPEND_VM"

#Set up a compliance requirement
$complianceRequirementDTO = New-DTOTemplateObject -DTOTagName "ComplianceRequirement"
$complianceRequirementDTO.ComplianceRequirement.expiryDateRequired = $false
$complianceRequirementDTO.ComplianceRequirement.primaryOwnerRequired = $true #setting this to true to make primary owner required
$complianceRequirementDTO.ComplianceRequirement.PSObject.Properties.Remove("attributes") #must remove this attribute since no custom attributes are being selected
$dto.CompliancePolicy.complianceRequirement = $complianceRequirementDTO.ComplianceRequirement

$createdPolicy = New-CompliancePolicy -dto $dto
#>

function New-CompliancePolicy {
    [cmdletbinding()]
    Param(
        $dto = $(Throw "Provide the policy DTO.")
    )

    New-Policy -dto $dto
}


<#
.SYNOPSIS
Create a Default Attributes policy

.DESCRIPTION
Create a Default Attributes policy

.PARAMETER dto
A policy DTO

.EXAMPLE
#
###################################################################################
# Policy targeting a datacenter [can use any target]
# Action is 'notify only'
# Groups are specified by name
###################################################################################

#Retrieve target datacenter
$dcCollection = Get-ManagedObjectByName -name "EmptyDC" -type "DATACENTER"
$dc = $dcCollection.ManagedObjectCollection.managedObjects[0]

$dto = New-DTOTemplateObject -DTOTagName "DefaultAttributesPolicy"
$dto.DefaultAttributesPolicy.name = "Policy #6"
$dto.DefaultAttributesPolicy.description = "Policy #6 description"
$dto.DefaultAttributesPolicy.enabled = $true
$dto.DefaultAttributesPolicy.allowsOverride = $false

#Set to -1 for never expires
$dto.DefaultAttributesPolicy.daysToExpire = 25

#Specify target
$dto.DefaultAttributesPolicy.targets =@($dc)
$dto.DefaultAttributesPolicy.global = $false

#See New-EndOfLifePolicy API for example(s) on how to create policy workflow action
$dto.DefaultAttributesPolicy.action.type = "NOTIFY_ONLY"

#Set up groups
$scanGroup = Get-GroupByName -groupType "VM_SCAN_GROUP" -name "Guest OS Scan Group"
$powerScheduleGroup = Get-GroupByName -groupType "POWER_SCHEDULE_GROUP" -name "Power Schedule Group"
$expiryGroup = Get-GroupByName -groupType "EXPIRY_GROUP" -name "Expiry Group"
$maintenanceGroupDTO = Get-GroupByName -groupType "MAINTENANCE_GROUP" -name "Maintenance Group"
$rightsizingGroupDTO = Get-GroupByName -groupType "RIGHTSIZING_GROUP" -name "Rightsizing Group"

$dto.DefaultAttributesPolicy.scanGroup = $scanGroup.Group
$dto.DefaultAttributesPolicy.powerScheduleGroup = $powerScheduleGroup.Group
$dto.DefaultAttributesPolicy.expiryGroup = $expiryGroup.Group
$dto.DefaultAttributesPolicy.maintenanceGroup = $maintenanceGroupDTO.Group
$dto.DefaultAttributesPolicy.rightsizingGroup = $rightsizingGroupDTO.Group

$createdPolicy = New-AttributesPolicy -dto $dto

.EXAMPLE
#
###################################################################################
# Policy targeting a datacenter [can use any target]
# Action is 'notify only'
# Using default scan, power schedule and expiry groups
###################################################################################

#Retrieve target datacenter
$dcCollection = Get-ManagedObjectByName -name "EmptyDC" -type "DATACENTER"
$dc = $dcCollection.ManagedObjectCollection.managedObjects[0]

$dto = New-DTOTemplateObject -DTOTagName "DefaultAttributesPolicy"
$dto.DefaultAttributesPolicy.name = "Policy #6"
$dto.DefaultAttributesPolicy.description = "Policy #6 description"
$dto.DefaultAttributesPolicy.enabled = $true
$dto.DefaultAttributesPolicy.allowsOverride = $false

#Set to -1 for never expires
$dto.DefaultAttributesPolicy.daysToExpire = -1

#Specify target
$dto.DefaultAttributesPolicy.targets =@($dc)
$dto.DefaultAttributesPolicy.global = $false

#See New-EndOfLifePolicy API for example(s) on how to create policy workflow action
$dto.DefaultAttributesPolicy.action.type = "NOTIFY_ONLY"

#Using default groups
$dto.DefaultAttributesPolicy.PSObject.Properties.Remove("scanGroup")
$dto.DefaultAttributesPolicy.PSObject.Properties.Remove("powerScheduleGroup")
$dto.DefaultAttributesPolicy.PSObject.Properties.Remove("expiryGroup")
$dto.DefaultAttributesPolicy.PSObject.Properties.Remove("maintenanceGroup")
$dto.DefaultAttributesPolicy.PSObject.Properties.Remove("rightsizingGroup")


$createdPolicy = New-AttributesPolicy -dto $dto
#>

function New-AttributesPolicy {
    [cmdletbinding()]
    Param(
        $dto = $(Throw "Provide the policy DTO.")
    )

    New-Policy -dto $dto
}

####################################################################################################################### #
# Deployment Destinations #
#######################################################################################################################

<#
.SYNOPSIS
Create a deployment destination

.DESCRIPTION
Create a deployment destination

.PARAMETER ddDto
A deployment destination DTO

.EXAMPLE
$created = New-DeploymentDestination -ddDto $dto

#>


function New-DeploymentDestination {
    [cmdletbinding()]
    Param(
        $ddDto = $(Throw "Provide the deployment destination DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/deploymentdestinations"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $ddDto
    
    Request-SessionAlive
   
    try{
        $requestResponse  = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
        
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Create a vCenter deployment destination

.DESCRIPTION
Create a vCenter deployment destination

.PARAMETER ddDto
A deployment destination DTO

.EXAMPLE
#
###################################################################################
# Create a global vCenter deployment destination, deploying to a cluster,
# and connecting to a specific network
###################################################################################

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "manta" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the folder to deploy the VMs to
$folderCollection = Get-FoldersByName -name "Dev Infrastructure"
$folder = $folderCollection.FolderCollection.Folders[0]

#Retrieve the target (cluster)
$cluster = Get-ClusterByName -msId $managementServer.id -clusterName "Engineering Cluster"

#Retrieve the resource pool
#$rpCollection = Get-ManagedObject -name "Dev Infrastructure" -type "RESOURCEPOOL"
#$rp = $rpCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the host
#$hostCollection = Get-ManagedObject -name "paragon.example.com" -type "RUNTIMESERVER"
#$targetHost = $hostCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the network to connect the VM to
$networkCollection1 = Get-ManagedObject -name "Dev Network" -type "NETWORK"
$networkCollection2 = Get-ManagedObject -name "PV Network" -type "NETWORK"

$network1 = $networkCollection1.ManagedObjectCollection.managedObjects[0]
$network2 = $networkCollection2.ManagedObjectCollection.managedObjects[0]

#Retrieve the datastores
$dsCollection1 = Get-ManagedObject -name "Paragon" -type "DATASTORE"
$dsCollection2 = Get-ManagedObject -name "Renegade" -type "DATASTORE"

$ds1 = $dsCollection1.ManagedObjectCollection.managedObjects[0]
$ds2 = $dsCollection2.ManagedObjectCollection.managedObjects[0]

#Create a vCenter deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "VMWareDeploymentDestination"
$ddDTO.VMWareDeploymentDestination.name ="Default #1"
$ddDTO.VMWareDeploymentDestination.assignIpFromPool = $false
$ddDTO.VMWareDeploymentDestination.diskFormat = "THICK" #Possible values are THICK, THIN, and SAME_AS_SOURCE. See DiskFormat DTO.
$ddDTO.VMWareDeploymentDestination.peakCapacity = $true #to use average capacity, set to $false

#Specify the management server
$ddDTO.VMWareDeploymentDestination.managementServer = $managementServer

#Specify the folder
$ddDTO.VMWareDeploymentDestination.folder = $folder

#Specify the target
$ddDTO.VMWareDeploymentDestination.target = $cluster.Cluster

#Specify the network
$ddDTO.VMWareDeploymentDestination.PSObject.Properties.Remove('network')
Add-Member -InputObject $ddDTO.VMWareDeploymentDestination -MemberType NoteProperty -Name "networks" -Value @($network1, $network2) -Force

#Specify the datastores
$ddDTO.VMWareDeploymentDestination.datastores = @($ds1, $ds2)

#Create the deployment destination
$createDD = New-VMWareDeploymentDestination -ddDto $ddDTO



.EXAMPLE
#
###################################################################################
# Create a global vCenter deployment destination, putting VM in a datacenter
# Deploy to host and connect to the same network as the source
###################################################################################

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "manta" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the datacenter
$datacenterCollection = Get-ManagedObject -name "Engineering" -type "DATACENTER"
$dc = $datacenterCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the host
$hostCollection = Get-ManagedObject -name "paragon.example.com" -type "RUNTIMESERVER"
$targetHost = $hostCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the datastores
$dsCollection1 = Get-ManagedObject -name "Paragon" -type "DATASTORE"
$ds1 = $dsCollection1.ManagedObjectCollection.managedObjects[0]

#Create a VMware deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "VMWareDeploymentDestination"
$ddDTO.VMWareDeploymentDestination.name ="Default #2"
$ddDTO.VMWareDeploymentDestination.assignIpFromPool = $false
$ddDTO.VMWareDeploymentDestination.diskFormat = "THICK"
$ddDTO.VMWareDeploymentDestination.peakCapacity = $true

#Specify the management server
$ddDTO.VMWareDeploymentDestination.managementServer = $managementServer

#Specify the datacenter
$ddDTO.VMWareDeploymentDestination.PSObject.Properties.Remove("folder")
Add-Member -InputObject $ddDTO.VMWareDeploymentDestination -MemberType NoteProperty -Name "datacenter" -Value $dc -Force

#Specify the target
$ddDTO.VMWareDeploymentDestination.target = $targetHost

#Network is same as source
$ddDTO.VMWareDeploymentDestination.PSObject.Properties.Remove("network")

#Specify the datastores
$ddDTO.VMWareDeploymentDestination.datastores = @($ds1)

#Create the deployment destination
$createDD = New-VMWareDeploymentDestination -ddDto $ddDTO


.EXAMPLE
#
###################################################################################
# Create a vCenter deployment destination assigned to an organization and users,
# place VM into a folder, deploy to cluster, and connect to a specific network
###################################################################################

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "manta" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the folder to deploy the VMs to
$folderCollection = Get-FoldersByName -name "Dev Infrastructure"
$folder = $folderCollection.FolderCollection.Folders[0]

#Retrieve the target (cluster)
$cluster = Get-ClusterByName -msId $managementServer.id -clusterName "Engineering Cluster"

#Retrieve the network to connect the VM to
$networkCollection1 = Get-ManagedObject -name "Dev Network" -type "NETWORK"
$networkCollection2 = Get-ManagedObject -name "PV Network" -type "NETWORK"

$network1 = $networkCollection1.ManagedObjectCollection.managedObjects[0]
$network2 = $networkCollection2.ManagedObjectCollection.managedObjects[0]

#Retrieve the datastores
$dsCollection1 = Get-ManagedObject -name "Paragon" -type "DATASTORE"
$dsCollection2 = Get-ManagedObject -name "Renegade" -type "DATASTORE"

$ds1 = $dsCollection1.ManagedObjectCollection.managedObjects[0]
$ds2 = $dsCollection2.ManagedObjectCollection.managedObjects[0]

#Create a vCenter deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "VMWareDeploymentDestination"
$ddDTO.VMWareDeploymentDestination.name ="Default #3"
$ddDTO.VMWareDeploymentDestination.assignIpFromPool = $false
$ddDTO.VMWareDeploymentDestination.diskFormat = "THICK"
$ddDTO.VMWareDeploymentDestination.peakCapacity = $true

#Specify the management server
$ddDTO.VMWareDeploymentDestination.managementServer = $managementServer

#Specify the folder
$ddDTO.VMWareDeploymentDestination.folder = $folder

#Specify the target
$ddDTO.VMWareDeploymentDestination.target = $cluster.Cluster

#Specify the network
$ddDTO.VMWareDeploymentDestination.PSObject.Properties.Remove('network')
Add-Member -InputObject $ddDTO.VMWareDeploymentDestination -MemberType NoteProperty -Name "networks" -Value @($network1, $network2) -Force

#Specify the datastores
$ddDTO.VMWareDeploymentDestination.datastores = @($ds1, $ds2)

#Assign user(s)
#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.id = -1
$userDTO.OwnerInfo.loginId = "superuser"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null

$userDTO2 = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO2.OwnerInfo.id = -1
$userDTO2.OwnerInfo.loginId = "manager"
$userDTO2.OwnerInfo.itContact = $false
$userDTO2.OwnerInfo.primary = $false
$userDTO2.OwnerInfo.email = $null
$userDTO2.OwnerInfo.displayName = $null

$users = @($userDTO.OwnerInfo, $userDTO2.OwnerInfo)
Add-Member -InputObject $ddDTO.VMWareDeploymentDestination -MemberType NoteProperty -Name "users" -Value $users -Force


#Assign organization(s)
#Grab the organization
$org = Get-OrganizationByName -name "Default Organization"

$orgs = @($org.Organization)
Add-Member -InputObject $ddDTO.VMWareDeploymentDestination -MemberType NoteProperty -Name "organizations" -Value $orgs -Force

#Create the deployment destination
$createDD = New-VMWareDeploymentDestination -ddDto $ddDTO


.EXAMPLE
#
###################################################################################
# Create a global vCenter deployment destination, place VM into a folder,
# deploy to resource pool, and connect to the same network as the source,
# with fenced network using distributed switch
###################################################################################

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "manta" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the folder to deploy the VMs to
$folderCollection = Get-FoldersByName -name "Dev Infrastructure"
$folder = $folderCollection.FolderCollection.Folders[0]

#Retrieve the target
$rpCollection = Get-ManagedObject -name "Dev Infrastructure" -type "RESOURCEPOOL"
$rp = $rpCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the datastores
$dsCollection1 = Get-ManagedObject -name "Paragon" -type "DATASTORE"
$dsCollection2 = Get-ManagedObject -name "Renegade" -type "DATASTORE"

$ds1 = $dsCollection1.ManagedObjectCollection.managedObjects[0]
$ds2 = $dsCollection2.ManagedObjectCollection.managedObjects[0]

#Create a vCenter deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "VMWareDeploymentDestination"
$ddDTO.VMWareDeploymentDestination.name ="Default #4"
$ddDTO.VMWareDeploymentDestination.assignIpFromPool = $false
$ddDTO.VMWareDeploymentDestination.diskFormat = "SAME_AS_SOURCE"
$ddDTO.VMWareDeploymentDestination.peakCapacity = $false

#Specify the management server
$ddDTO.VMWareDeploymentDestination.managementServer = $managementServer

#Specify the folder
$ddDTO.VMWareDeploymentDestination.folder = $folder

#Specify the target
$ddDTO.VMWareDeploymentDestination.target = $rp

#Network is same as source
$ddDTO.VMWareDeploymentDestination.PSObject.Properties.Remove("network")

#Specify the datastores
$ddDTO.VMWareDeploymentDestination.datastores = @($ds1, $ds2)

#Retrieve the network
$networkCollection = Get-ManagedObject -name "PV Network" -type "NETWORK"
$network = $networkCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve distributed switch
$distributedSwitches = Get-DistributedSwitches -rpid $rp.id -switchname "dvsPvNetwork"
$distributedSwitch = $distributedSwitches.ManagedObjectReferenceCollection.ManagedObjectReferences[0]

#Create fenced network config
#Note: The external network must be configured with an IP Pool
$fencedNetworkConfig = New-DTOTemplateObject -DTOTagName "FencedNetworkConfig"
$fencedNetworkConfig.FencedNetworkConfig.externalNetwork = $network
$fencedNetworkConfig.FencedNetworkConfig.vlanIds = "1"
$fencedNetworkConfig.FencedNetworkConfig.distributedSwitch = $distributedSwitch

Add-Member -InputObject $ddDTO.VMWareDeploymentDestination -MemberType NoteProperty -Name "fencedNetworkConfig" -Value $fencedNetworkConfig.FencedNetworkConfig -Force

#Create the deployment destination
$createDD = New-VMWareDeploymentDestination -ddDto $ddDTO


.EXAMPLE
#
###################################################################################
# Create a global vCenter deployment destination, place VM into a folder,
# deploy to host, and connect to the same network as the source,
# with fenced network using standard switches
###################################################################################

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "autumnrain" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve hosts
$hostCollection = Get-RuntimeServers -msId $managementServer.id
$panoramixHost = $hostCollection.RuntimeServerCollection.RuntimeServers | Where-Object {$_.displayName -match "panoramix.example.com"}

#Retrieve the folder to deploy the VMs to
$folderCollection = Get-FoldersByName -name "Sanity"
$folder = $folderCollection.FolderCollection.Folders[0]

#Retrieve the datastores
$dsCollection1 = Get-ManagedObject -name "Panoramix" -type "DATASTORE"
$ds1 = $dsCollection1.ManagedObjectCollection.managedObjects[0]

#Create a vCenter deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "VMWareDeploymentDestination"
$ddDTO.VMWareDeploymentDestination.name ="Default #5"
$ddDTO.VMWareDeploymentDestination.assignIpFromPool = $false
$ddDTO.VMWareDeploymentDestination.diskFormat = "SAME_AS_SOURCE"
$ddDTO.VMWareDeploymentDestination.peakCapacity = $false

#Specify the management server
$ddDTO.VMWareDeploymentDestination.managementServer = $managementServer

#Specify the folder
$ddDTO.VMWareDeploymentDestination.folder = $folder

#Specify the target
$ddDTO.VMWareDeploymentDestination.target = $panoramixHost

#Network is same as source
$ddDTO.VMWareDeploymentDestination.PSObject.Properties.Remove("network")

#Specify the datastores
$ddDTO.VMWareDeploymentDestination.datastores = @($ds1)

#Retrieve the network
$networkCollection = Get-ManagedObject -name "Irina VM Network" -type "NETWORK"
$network = $networkCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve standard switches
$panoramixHostSwitches = Get-HostStandardSwitches -hostId $panoramixHost.id

#Just to check the switch exists
$panoramixHostSwitch0 = $panoramixHostSwitches.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "vSwitch0"}

#Create fenced network config
#Note: The external network must be configured with an IP Pool
$fencedNetworkConfig = New-DTOTemplateObject -DTOTagName "FencedNetworkConfig"
$fencedNetworkConfig.FencedNetworkConfig.externalNetwork = $network
$fencedNetworkConfig.FencedNetworkConfig.vlanIds = "1"

#Remove the distributed switches
$fencedNetworkConfig.FencedNetworkConfig.PSObject.Properties.Remove("distributedSwitch")

#Add standard switches
$switchBindingDTO = New-DTOTemplateObject -DTOTagName "SwitchBinding"
$switchBindingDTO.SwitchBinding.host = $panoramixHost
$switchBindingDTO.SwitchBinding.switchName = $panoramixHostSwitch0.displayName

$standardSwitchBindings = @($switchBindingDTO.SwitchBinding)
Add-Member -InputObject $fencedNetworkConfig.FencedNetworkConfig -MemberType NoteProperty -Name "standardSwitches" -Value $standardSwitchBindings -Force

#Add the network fencing module
Add-Member -InputObject $ddDTO.VMWareDeploymentDestination -MemberType NoteProperty -Name "fencedNetworkConfig" -Value $fencedNetworkConfig.FencedNetworkConfig -Force

#Create the deployment destination
$createDD = New-VMWareDeploymentDestination -ddDto $ddDTO

#>

function New-VMWareDeploymentDestination {
    [cmdletbinding()]
    Param(
        $ddDto = $(Throw "Provide the deployment destination DTO.")
    )
    
    New-DeploymentDestination -ddDto $ddDto
}


<#
.SYNOPSIS
Create an SCVMM deployment destination

.DESCRIPTION
Create an SCVMM deployment destination

.PARAMETER ddDto
A deployment destination DTO

.EXAMPLE
#
###################################################################################
# Create a global SCVMM deployment destination, deploying to a cluster,
# and connect to a specific network
###################################################################################

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "Blaze" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the target (cluster)
$cluster = Get-ClusterByName -msId $managementServer.id -clusterName "FuegoCluster.example.com"

# Logical network
$logicalNetworkCollection1 = Get-ManagedObject -name "PVNet20 Logical Network" -type "NETWORK"
$logicalNetworkCollection2 = Get-ManagedObject -name "PVNet21 Logical Network" -type "NETWORK"

$logicalNetwork1 = $logicalNetworkCollection1.ManagedObjectCollection.managedObjects[0]
$logicalnetwork2 = $logicalNetworkCollection2.ManagedObjectCollection.managedObjects[0]

#Retrieve the datastores
$dsCollection1 = Get-ManagedObject -name "Jazz.example.com\D:\" -type "DATASTORE"
$ds1 = $dsCollection1.ManagedObjectCollection.managedObjects[0]

#Create an SCVMM deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "SCVMMDeploymentDestination"
$ddDTO.SCVMMDeploymentDestination.name ="Default #1"
$ddDTO.SCVMMDeploymentDestination.diskFormat = "FIXED" #possible values are FIXED, DYNAMICALLY_EXPANDING, and SAME_AS_SOURCE. See DiskFormat DTO for options.
$ddDTO.SCVMMDeploymentDestination.peakCapacity = $true #"to use average capacity, set to $false"

#Specify the management server
$ddDTO.SCVMMDeploymentDestination.managementServer = $managementServer

#Specify the target
$ddDTO.SCVMMDeploymentDestination.target = $cluster.Cluster

#Specify the network
$ddDTO.SCVMMDeploymentDestination.PSObject.Properties.Remove('network')
$ddDTO.SCVMMDeploymentDestination.PSObject.Properties.Remove('logicalNetwork')
Add-Member -InputObject $ddDTO.SCVMMDeploymentDestination -MemberType NoteProperty -Name "logicalNetworks" -Value @($logicalNetwork1, $logicalnetwork2) -Force

#Specify the datastore
$ddDTO.SCVMMDeploymentDestination.datastores = @($ds1)

#Create the deployment destination
$createDD = New-SCVMMDeploymentDestination -ddDto $ddDTO

.EXAMPLE
#
###################################################################################
# Create an SCVMM deployment destination assigned to an organization and users,
# deploy to a host, and connect to the same network as the source
###################################################################################

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "Blaze" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the target
$hostCollection = Get-ManagedObject -name "Jazz.example.com" -type "RUNTIMESERVER"
$targetHost = $hostCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the datastores
$dsCollection1 = Get-ManagedObject -name "Jazz.example.com\D:\" -type "DATASTORE"
$ds1 = $dsCollection1.ManagedObjectCollection.managedObjects[0]

#Create an SCVMM deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "SCVMMDeploymentDestination"
$ddDTO.SCVMMDeploymentDestination.name ="Default #2"
$ddDTO.SCVMMDeploymentDestination.diskFormat = "SAME_AS_SOURCE"
$ddDTO.SCVMMDeploymentDestination.peakCapacity = $false

#Specify the management server
$ddDTO.SCVMMDeploymentDestination.managementServer = $managementServer

#Specify the target
$ddDTO.SCVMMDeploymentDestination.target = $targetHost

#Clear the network
$ddDTO.SCVMMDeploymentDestination.PSObject.Properties.Remove("network")
$ddDTO.SCVMMDeploymentDestination.PSObject.Properties.Remove("logicalNetwork")

#Specify the datastore
$ddDTO.SCVMMDeploymentDestination.datastores = @($ds1)

#Assign user(s)
#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.id = -1
$userDTO.OwnerInfo.loginId = "superuser"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null

$userDTO2 = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO2.OwnerInfo.id = -1
$userDTO2.OwnerInfo.loginId = "manager"
$userDTO2.OwnerInfo.itContact = $false
$userDTO2.OwnerInfo.primary = $false
$userDTO2.OwnerInfo.email = $null
$userDTO2.OwnerInfo.displayName = $null

$users = @($userDTO.OwnerInfo, $userDTO2.OwnerInfo)
Add-Member -InputObject $ddDTO.SCVMMDeploymentDestination -MemberType NoteProperty -Name "users" -Value $users -Force

#Assign organization(s)
#Grab the organization
$org = Get-OrganizationByName -name "Default Organization"

$orgs = @($org.Organization)
Add-Member -InputObject $ddDTO.SCVMMDeploymentDestination -MemberType NoteProperty -Name "organizations" -Value $orgs -Force

#Create the deployment destination
$createDD = New-SCVMMDeploymentDestination -ddDto $ddDTO

#>

function New-SCVMMDeploymentDestination {
    [cmdletbinding()]
    Param(
        $ddDto = $(Throw "Provide the deployment destination DTO.")
    )
    
    New-DeploymentDestination -ddDto $ddDto
}


<#
.SYNOPSIS
Create an AWS deployment destination

.DESCRIPTION
Create an AWS deployment destination

.PARAMETER ddDto
A deployment destination DTO

.EXAMPLE
#
###################################################################################
# Create deployment destination assigned to an organization and users,
# deploy to a virtual cloud
###################################################################################

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "it@example.com" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the target
$regionCollection = Get-ManagedObject -name "us-west-2" -type "REGION"
$targetRegion = $regionCollection.ManagedObjectCollection.managedObjects[0]
$virtualCloud = $targetRegion.VirtualClouds | Where-Object {$_.displayName -match "vpc-3b793253"}

#Subnet
$subnets = Get-VirtualCloudSubnets -regionId $targetRegion.id -vcid $virtualCloud.id
$subnet1 = $subnets.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "subnet-da584db8"}
$subnet2 = $subnets.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "subnet-df7d36b7"}

#Security group
$securityGroups = Get-VirtualCloudSecurityGroups -regionId $targetRegion.id -vcid $virtualCloud.id
$securityGroup = $securityGroups.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "default"}

#Create a deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "AWSDeploymentDestination"
$ddDTO.AWSDeploymentDestination.name="Default #3"
$ddDTO.AWSDeploymentDestination.keyPair="Embotics"

#Specify the management server
$ddDTO.AWSDeploymentDestination.managementServer = $managementServer

#Specify the target
$ddDTO.AWSDeploymentDestination.target = $virtualCloud

#Add subnet
Add-Member -InputObject $ddDTO.AWSDeploymentDestination -MemberType NoteProperty -Name "subnets" -Value @($subnet1,$subnet2) -Force

#Set up security group
$ddDTO.AWSDeploymentDestination.securityGroups = @($securityGroup)

#Assign user(s)
#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.id = -1
$userDTO.OwnerInfo.loginId = "superuser"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null

$userDTO2 = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO2.OwnerInfo.id = -1
$userDTO2.OwnerInfo.loginId = "manager"
$userDTO2.OwnerInfo.itContact = $false
$userDTO2.OwnerInfo.primary = $false
$userDTO2.OwnerInfo.email = $null
$userDTO2.OwnerInfo.displayName = $null

$users = @($userDTO.OwnerInfo, $userDTO2.OwnerInfo)
Add-Member -InputObject $ddDTO.AWSDeploymentDestination -MemberType NoteProperty -Name "users" -Value $users -Force

#Assign organization(s)
#Grab the organization
$org = Get-OrganizationByName -name "Default Organization"

$orgs = @($org.Organization)
Add-Member -InputObject $ddDTO.AWSDeploymentDestination -MemberType NoteProperty -Name "organizations" -Value $orgs -Force

#Create the deployment destination
$createDD = New-AWSDeploymentDestination -ddDto $ddDTO


.EXAMPLE
#
###################################################################################
# Assignment: global
# Target: EC2-Classic
# Availability zone: let the system decide
# Key pair: true
# Number of security groups: 1
###################################################################################

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "it@example.com" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the target
$regionCollection = Get-ManagedObject -name "us-west-2" -type "REGION"
$targetRegion = $regionCollection.ManagedObjectCollection.managedObjects[0]
$virtualCloudClassic = $targetRegion.ec2ClassicCloud

#Retrieve the security group
$securityGroups = Get-VirtualCloudSecurityGroups -regionId $targetRegion.id -vcid $virtualCloudClassic.id
$securityGroup1 = $securityGroups.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "default"}

#Create a deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "AWSDeploymentDestination"
$ddDTO.AWSDeploymentDestination.name="aws_dest02_REST"
$ddDTO.AWSDeploymentDestination.keyPair="ABC"

#Specify the management server
$ddDTO.AWSDeploymentDestination.managementServer = $managementServer

#Specify the target
#Since the target is a classic cloud, the available zone will be decided by the system
$ddDTO.AWSDeploymentDestination.target = $virtualCloudClassic

#Set up security groups
$ddDTO.AWSDeploymentDestination.securityGroups = @($securityGroup1)

#Create the deployment destination
$createDD = New-AWSDeploymentDestination -ddDto $ddDTO


.EXAMPLE
#
###################################################################################
# Assignment: global
# Target: EC2-Classic
# Availability zone: specific
# Key pair: true
# Number of security groups: 1
###################################################################################

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "it@example.com" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the target
$regionCollection = Get-ManagedObject -name "us-west-2" -type "REGION"
$targetRegion = $regionCollection.ManagedObjectCollection.managedObjects[0]
$virtualCloudClassic = $targetRegion.ec2ClassicCloud

#Find a suitable availability zone
$availabilityZones = Get-RegionAvailabilityZones -regionId $targetRegion.id
$specificZone = $availabilityZones.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "us-west-2b"}

#Retrieve the security group
$securityGroups = Get-VirtualCloudSecurityGroups -regionId $targetRegion.id -vcid $virtualCloudClassic.id
$securityGroup1 = $securityGroups.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "default"}

#Create a deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "AWSDeploymentDestination"
$ddDTO.AWSDeploymentDestination.name="aws_dest02_REST"
$ddDTO.AWSDeploymentDestination.keyPair="ABC"

#Specify the management server
$ddDTO.AWSDeploymentDestination.managementServer = $managementServer

#Specify the target
#We specified an availability zone
$ddDTO.AWSDeploymentDestination.target = $specificZone
Add-Member -InputObject $ddDTO.AWSDeploymentDestination -MemberType NoteProperty -Name "region" -Value $targetRegion -Force

#Set up security groups
$ddDTO.AWSDeploymentDestination.securityGroups = @($securityGroup1)

#Create the deployment destination
$createDD = New-AWSDeploymentDestination -ddDto $ddDTO

#>

function New-AWSDeploymentDestination {
    [cmdletbinding()]
    Param(
        $ddDto = $(Throw "Provide the deployment destination DTO.")
    )
    
    New-DeploymentDestination -ddDto $ddDto
}


<#
.SYNOPSIS
Create an Azure deployment destination

.DESCRIPTION
Create an Azure deployment destination

.PARAMETER ddDto
A deployment destination DTO

.EXAMPLE
# Create new Azure Deployment Destination
# Assignment: org and user
# Target: Region

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "corp_azure" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the target
$regionCollection = Get-ManagedObject -name "North Central US" -type "REGION"
$targetRegion = $regionCollection.ManagedObjectCollection.managedObjects[0]

#Create an Azure deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "AzureDeploymentDestination"
$ddDTO.AzureDeploymentDestination.name="azure_REST_01"

#Specify the management server
$ddDTO.AzureDeploymentDestination.managementServer = $managementServer

#Specify the target
$ddDTO.AzureDeploymentDestination.target = $targetRegion

#Assign user(s)
#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.id = -1
$userDTO.OwnerInfo.loginId = "user"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null

$users = @($userDTO.OwnerInfo)
Add-Member -InputObject $ddDTO.AzureDeploymentDestination -MemberType NoteProperty -Name "users" -Value $users -Force

#Assign organization(s)
#Grab the organization
$org = Get-OrganizationByName -name "Default Organization"

$orgs = @($org.Organization)
Add-Member -InputObject $ddDTO.AzureDeploymentDestination -MemberType NoteProperty -Name "organizations" -Value $orgs -Force

#Create the deployment destination
$createDD = New-AzureDeploymentDestination -ddDto $ddDTO

.EXAMPLE
# Create new Azure Deployment Destination
# Assignment: Global
# Target: Affinity Group

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "corp_azure" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the target
$regionCollection = Get-ManagedObject -name "azure-ag1" -type "DATACENTER"
$target = $regionCollection.ManagedObjectCollection.managedObjects[0]

#Create an Azure deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "AzureDeploymentDestination"
$ddDTO.AzureDeploymentDestination.name="azure_REST_02"

#Specify the management server
$ddDTO.AzureDeploymentDestination.managementServer = $managementServer

#Specify the target
$ddDTO.AzureDeploymentDestination.target = $target

#Create the deployment destination
$createDD = New-AzureDeploymentDestination -ddDto $ddDTO

.EXAMPLE
# Create new Azure Deployment Destination
# Assignment: Global
# Target: Virtual Network (must choose subnet)

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "corp_azure" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the target
$regionCollection = Get-ManagedObject -name "azu-vn1" -type "VIRTUAL_CLOUD"
$target = $regionCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the subnet
$regionCollection = Get-ManagedObject -name "North Central US" -type "REGION"
$targetRegion = $regionCollection.ManagedObjectCollection.managedObjects[0]
$subnets = Get-VirtualCloudSubnets -regionId $targetRegion.id -vcid $target.id
$subnet1 = $subnets.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "Subnet-1"}
$subnet2 = $subnets.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "Subnet-2"}

#Create an Azure deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "AzureDeploymentDestination"
$ddDTO.AzureDeploymentDestination.name="azure_REST_03"

#Specify the management server
$ddDTO.AzureDeploymentDestination.managementServer = $managementServer

#Specify the target
$ddDTO.AzureDeploymentDestination.target = $target

#Add subnet
Add-Member -InputObject $ddDTO.AzureDeploymentDestination -MemberType NoteProperty -Name "subnets" -Value @($subnet1,$subnet2) -Force

#Create the deployment destination
$createDD = New-AzureDeploymentDestination -ddDto $ddDTO

.EXAMPLE
# Create new Azure Deployment Destination
# Assignment: global
# Target: Cloud Service

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "corp_azure" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the target
$regionCollection = Get-ManagedObject -name "azu-cs1" -type "VIRTUALAPP"
$target = $regionCollection.ManagedObjectCollection.managedObjects[0]

#Create an Azure deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "AzureDeploymentDestination"
$ddDTO.AzureDeploymentDestination.name="azure_REST_04"

#Specify the management server
$ddDTO.AzureDeploymentDestination.managementServer = $managementServer

#Specify the target
$ddDTO.AzureDeploymentDestination.target = $target

#Create the deployment destination
$createDD = New-AzureDeploymentDestination -ddDto $ddDTO

.EXAMPLE
# Create new Azure Deployment Destination
# Assignment: global
# Target: Cloud Service under a Virtal Network (must choose subnet)

#Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "corp_azure" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the target
$regionCollection = Get-ManagedObject -name "azu-cs2" -type "VIRTUALAPP"
$target = $regionCollection.ManagedObjectCollection.managedObjects[0]

#Retrieve the subnet
$regionCollection = Get-ManagedObject -name "North Central US" -type "REGION"
$targetRegion = $regionCollection.ManagedObjectCollection.managedObjects[0]
$subnets = Get-RegionSubnets -id $targetRegion.id
$subnet1 = $subnets.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "Subnet-1"}
$subnet2 = $subnets.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "GatewaySubnet"}

#Create an Azure deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "AzureDeploymentDestination"
$ddDTO.AzureDeploymentDestination.name="azure_REST_05"

#Specify the management server
$ddDTO.AzureDeploymentDestination.managementServer = $managementServer

#Specify the target
$ddDTO.AzureDeploymentDestination.target = $target

#Add subnet
Add-Member -InputObject $ddDTO.AzureDeploymentDestination -MemberType NoteProperty -Name "subnets" -Value @($subnet1,$subnet2) -Force

#Create the deployment destination
$createDD = New-AzureDeploymentDestination -ddDto $ddDTO

#>

function New-AzureDeploymentDestination {
    [cmdletbinding()]
    Param(
        $ddDto = $(Throw "Provide the deployment destination DTO.")
    )
    
    New-DeploymentDestination -ddDto $ddDto
}

<#
.SYNOPSIS
Create a Microsoft Azure Resource Manager (ARM) deployment destination

.DESCRIPTION
Create a Microsoft Azure Resource Manager (ARM) deployment destination

.PARAMETER ddDto
A deployment destination DTO

.EXAMPLE
# Create new ARM Deployment Destination
# Target: Resource Group

# Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "vCommander4" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

# Retrieve the subnet by way of the region
$regionCollection = Get-ManagedObject -name "East US" -type "REGION"
$region = $regionCollection.ManagedObjectCollection.managedObjects[0]
$subnets = Get-RegionSubnets -id $region.id
$subnet = $subnets.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "default"}

# Get a security group from the region
$securityGroup = $region.securityGroups | Where-Object {$_.displayName -match "aks-agentpool-12344922-nsg"}

# Retrieve the resource group (target)
$resourceGroupCollection = Get-ManagedObject -name "east-group" -type "RESOURCE_GROUP"
$resourceGroup = $resourceGroupCollection.ManagedObjectCollection.managedObjects[0]

# Retrieve the datastore
$datastoreCollection = Get-ManagedObjectByName -name "cs2e26acb81f179x4898x8a2" -type "DATASTORE"
$datastore = $datastoreCollection.ManagedObjectCollection.ManagedObjectReferences[0]

# Retrieve the user
$user = (Get-Accounts).AccountCollection[0].Accounts | Where-Object { $_.userid -match "newuser" }

# Create the user DTO
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.loginId = $user.userid
$userDTO.OwnerInfo.email = $null

# Retrieve the organization
$organizations = Get-OrganizationReferences -name "Default Organization"
$organization = $organizations.ManagedObjectReferenceCollection.ManagedObjectReferences[0]

# Get a placement attribute
$city = Get-PlacementAttribute -name "City"

$cityReference = New-DTOTemplateObject -DTOTagName "ManagedObjectReference"
$cityReference.ManagedObjectReference.displayName = $city.SublistPlacementAttribute.name
$cityReference.ManagedObjectReference.id = $city.SublistPlacementAttribute.id
$cityReference.ManagedObjectReference.type = "PLACEMENT_ATTRIBUTE"

$appliedPlacementAttribute = New-DTOTemplateObject -DTOTagName "AppliedPlacementAttribute"
$appliedPlacementAttribute.AppliedPlacementAttribute.placementAttribute = $cityReference
$appliedPlacementAttribute.AppliedPlacementAttribute.allowedValues = @("Ottawa")

# Create and populate the ARM deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "ARMDeploymentDestination"
$ddDTO.ARMDeploymentDestination
$ddDTO.ARMDeploymentDestination.name ="Default #6"
$ddDTO.ARMDeploymentDestination.managementServer = $managementServer
$ddDTO.ARMDeploymentDestination.target = $resourceGroup
Add-Member -InputObject $ddDTO.ARMDeploymentDestination -MemberType NoteProperty -Name "subnets" -Value @($subnet) -Force
Add-Member -InputObject $ddDTO.ARMDeploymentDestination -MemberType NoteProperty -Name "datastores" -Value @($datastore.reference) -Force
Add-Member -InputObject $ddDTO.ARMDeploymentDestination -MemberType NoteProperty -Name "diagnosticsStorageAccount" -Value $datastore.reference -Force
Add-Member -InputObject $ddDTO.ARMDeploymentDestination -MemberType NoteProperty -Name "users" -Value @($userDTO.OwnerInfo) -Force
Add-Member -InputObject $ddDTO.ARMDeploymentDestination -MemberType NoteProperty -Name "organizations" -Value @($organization) -Force
Add-Member -InputObject $ddDTO.ARMDeploymentDestination -MemberType NoteProperty -Name "securityGroup" -Value $securityGroup -Force
Add-Member -InputObject $ddDTO.ARMDeploymentDestination -MemberType NoteProperty -Name "appliedPlacementAttributes" -Value @($appliedPlacementAttribute.AppliedPlacementAttribute) -Force

# Create the ARM deployment destination
$armDeploymentDestination = New-ARMDeploymentDestination -ddDto $ddDTO

.EXAMPLE
# Create new ARM Deployment Destination with default values
# Target: Resource Group

# Retrieve management server
$mobjectCollection = Get-ManagedObjectByName -name "vCommander4" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.managedObjects[0]

# Retrieve the subnet by way of the region
$regionCollection = Get-ManagedObject -name "East US" -type "REGION"
$region = $regionCollection.ManagedObjectCollection.managedObjects[0]
$subnets = Get-RegionSubnets -id $region.id
$subnet = $subnets.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "default"}

# Retrieve the resource group (target)
$resourceGroupCollection = Get-ManagedObject -name "east-group" -type "RESOURCE_GROUP"
$resourceGroup = $resourceGroupCollection.ManagedObjectCollection.managedObjects[0]

# Create and populate the ARM deployment destination DTO
$ddDTO = New-DTOTemplateObject -DTOTagName "ARMDeploymentDestination"
$ddDTO.ARMDeploymentDestination
$ddDTO.ARMDeploymentDestination.name ="Default #6"
$ddDTO.ARMDeploymentDestination.managementServer = $managementServer
$ddDTO.ARMDeploymentDestination.target = $resourceGroup
Add-Member -InputObject $ddDTO.ARMDeploymentDestination -MemberType NoteProperty -Name "subnets" -Value @($subnet) -Force

# Create the ARM deployment destination
$armDeploymentDestination = New-ARMDeploymentDestination -ddDto $ddDTO

#>

function New-ARMDeploymentDestination {
    [cmdletbinding()]
    Param(
        $ddDto = $(Throw "Provide the deployment destination DTO.")
    )
    
    New-DeploymentDestination -ddDto $ddDto
}

<#
.SYNOPSIS
Modify a deployment destination

.DESCRIPTION
Modify a deployment destination

.PARAMETER id
The deployment destination ID

.PARAMETER updatedDeploymentDestination
A deployment destination DTO

.EXAMPLE
$deploymentDestination = Get-DeploymentDestinationByName -name "Default #1"

#Change any other properties as needed
$deploymentDestination.VMWareDeploymentDestination.name = "Updated Default #1"

$ddId = $deploymentDestination.VMWareDeploymentDestination.id

$updatedDD = Update-DeploymentDestination -id $ddId -updatedDeploymentDestination $deploymentDestination

.EXAMPLE
# Apply a new allowed value to an existing placement attribute applied to a deployment destination

$deploymentDestination = Get-DeploymentDestinationByName -name "Default destination"

# Get the specific applied attribute we're adding an allowed value to
$appliedPlacementAttributes = $deploymentDestination.ARMDeploymentDestination.appliedPlacementAttributes
$cityPlacementAttribyte = $appliedPlacementAttributes | Where-Object { $_.placementAttribute.displayName -match "City" }
$cityPlacementAttribyte.allowedValues += "Ottawa"

# Update the modified deployment destination
$updatedDeploymentDestination = Update-DeploymentDestination -id $deploymentDestination.VMWareDeploymentDestination.id -updatedDeploymentDestination $deploymentDestination
#>

function Update-DeploymentDestination {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the deployment destination."),
        $updatedDeploymentDestination = $(Throw "Provide the updated deployment destination DTO.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/deploymentdestinations/$id"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $updatedDeploymentDestination
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method PUT -Body $xml_dto -ContentType "application/xml"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Remove a deployment destination

.DESCRIPTION
Remove a deployment destination

.PARAMETER id
The deployment destination ID

.EXAMPLE
#
###################################################################################
# Remove a VMware deployment destination
###################################################################################

$dd = Get-DeploymentDestinationByName -name "Default #1"
$result = Remove-DeploymentDestination -id $dd.VMWareDeploymentDestination.id

.EXAMPLE
#
###################################################################################
# Remove an SCVMM deployment destination
###################################################################################
$dd = Get-DeploymentDestinationByName -name "Default #2"
$result = Remove-DeploymentDestination -id $dd.SCVMMDeploymentDestination.id

.EXAMPLE
#
###################################################################################
# Remove an AWS deployment destination
###################################################################################
$dd = Get-DeploymentDestinationByName -name "Default #3"
$result = Remove-DeploymentDestination -id $dd.AWSDeploymentDestination.id
#>

function Remove-DeploymentDestination {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the deployment destination.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/deploymentdestinations/$id"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method DELETE
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    
    $true
}

<#
.SYNOPSIS
Retrieve a collection of projects

.DESCRIPTION
Retrieve a collection of projects

.PARAMETER max
The max count to retrieve

.EXAMPLE
$projects = Get-Projects

.EXAMPLE
#Return a maximum number of projects
$projects = Get-Projects -max 1

#>

function Get-Projects {
    [cmdletbinding()]
    Param(
        [Int] $max = 100
    )
    
    

    $fullURI = "$Global:BASE_SERVICE_URI/projects?max=$max"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a collection of regions

.DESCRIPTION
Retrieve a collection of regions

.PARAMETER projectid
The project ID. This restricts the retrieved regions to those within the specified project

.PARAMETER max
The max count to retrieve

.EXAMPLE
$regions = Get-Regions

.EXAMPLE

$regions = Get-Regions -projectid $projectId

#>

function Get-Regions {
    [cmdletbinding()]
    Param(
        [Long] $projectid = -1,
        [Int] $max = 10
    )
    
    

    $fullURI = "$Global:BASE_SERVICE_URI/regions?projectid=$projectid&max=$max"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a region

.DESCRIPTION
Retrieve a region

.PARAMETER id
The region ID

.EXAMPLE
$region = Get-RegionById -id $regionId

#>

function Get-RegionById {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the region ID.")
    )
    
    

    $fullURI = "$Global:BASE_SERVICE_URI/regions/$id"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve available zones for a region

.DESCRIPTION
Retrieve available zones for a region

.PARAMETER id
The region ID

.EXAMPLE
$availabilityZones = Get-RegionAvailabilityZones -regionId $regionId

#>

function Get-RegionAvailabilityZones {
    [cmdletbinding()]
    Param(
        [Long] $regionId = $(Throw "Provide the region ID.")
    )
    
    

    $fullURI = "$Global:BASE_SERVICE_URI/regions/$regionId/availabilityzones"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a region's subnets

.DESCRIPTION
Retrieve a region's subnets

.PARAMETER id
The region ID

.EXAMPLE
$subnets = Get-RegionSubnets -id $regionId

#>

function Get-RegionSubnets {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the region ID.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/regions/$id/subnets"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a virtual cloud subnet

.DESCRIPTION
Retrieve a virtual cloud subnet

.PARAMETER regionId
The region ID

.PARAMETER vcid
The virtual cloud ID

.EXAMPLE
$subnets = Get-VirtualCloudSubnets -regionId $regionId -vcid $vcid

.EXAMPLE
$regions = Get-Regions
$regionReference = $regions.ManagedObjectReferenceCollection.ManagedObjectReferences | Where-Object {$_.displayName -match "us-west-2"}
$region = Get-RegionById -id $regionReference.id
$vcid = $region.Region.VirtualClouds | Where-Object {$_.displayName -match "vpc-3b793253"} | select -ExpandProperty "id"
$subnets = Get-VirtualCloudSubnets -regionId $regionReference.id -vcid $vcid

#>

function Get-VirtualCloudSubnets {
    [cmdletbinding()]
    Param(
        [Long] $regionId= $(Throw "Provide the region ID."),
        [Long] $vcid= $(Throw "Provide the virtual cloud ID.")
    )
    
    

    $fullURI = "$Global:BASE_SERVICE_URI/regions/$regionId/virtualclouds/$vcid/subnets"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}


<#
.SYNOPSIS
Retrieve a virtual cloud security group

.DESCRIPTION
Retrieve a virtual cloud security group

.PARAMETER regionId
The region ID

.PARAMETER vcid
The virtual cloud ID

.EXAMPLE
$securityGroups = Get-VirtualCloudSecurityGroups -regionId $regionId -vcid $vcid

.EXAMPLE

$regionCollection = Get-ManagedObject -name "us-west-2" -type "REGION"
$regionId = $regionCollection.ManagedObjectCollection.managedObjects[0].id
$region = Get-RegionById -id $regionId
$vcid = $region.Region.VirtualClouds | Where-Object {$_.displayName -match "vpc-3b793253"} | select -ExpandProperty "id"
$securityGroups = Get-VirtualCloudSecurityGroups -regionId $regionId -vcid $vcid
#>

function Get-VirtualCloudSecurityGroups {
    [cmdletbinding()]
    Param(
        [Long] $regionId= $(Throw "Provide the region ID."),
        [Long] $vcid= $(Throw "Provide the virtual cloud ID.")
    )
    
    

    $fullURI = "$Global:BASE_SERVICE_URI/regions/$regionId/virtualclouds/$vcid/securitygroups"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a collection of standard switches accessible to a host

.DESCRIPTION
Retrieve a collection of standard switches accessible to a host

.PARAMETER hostid
The host ID

.EXAMPLE
$switches = Get-HostStandardSwitches -hostid $hostId

#>

function Get-HostStandardSwitches {
    [cmdletbinding()]
    Param(
        [Long] $hostId= $(Throw "Provide the host ID.")
    )
    
    

    $fullURI = "$Global:BASE_SERVICE_URI/hosts/$hostId/standardswitches"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a collection of distributed switches

.DESCRIPTION
Retrieve a collection of distributed switches

.PARAMETER rpid
The resource pool ID

.PARAMETER hostid
The host ID

.PARAMETER clusterid
The cluster ID

.PARAMETER switchname
The name of the switch

.EXAMPLE
$switches = Get-DistributedSwitches -hostid $hostId

#>

function Get-DistributedSwitches {
    [cmdletbinding()]
    Param(
        [Long] $rpid= -1,
        [Long] $hostid= -1,
        [Long] $clusterid= -1,
        [String] $switchname= ""
    )
    
    
    
    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($switchname)
    Write-Debug "Before encoding name: $switchname | After encode: $urlSafeName"
    

    $fullURI = "$Global:BASE_SERVICE_URI/distributedswitches?rpid=$rpid&hostid=$hostid&clusterid=$clusterid&switchname=$urlSafeName"
    
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve the available networks

.DESCRIPTION
Retrieve the available networks

.PARAMETER dcid
The datacenter ID

.PARAMETER hostid
The host ID

.PARAMETER clusterid
The cluster ID

.PARAMETER networkname
The network name

.EXAMPLE
$networks = Get-AvailableNetworks -dcid 1344

#>

function Get-AvailableNetworks {
    [cmdletbinding()]
    Param(
        [Long] $dcid = -1, #Provide the datacenter id
        [Long] $hostid = -1, #Provide the hostid id
        [Long] $clusterid = -1, #Provide the clusterid id
        [String] $networkname ="" #Network name
    )
    
    
    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($networkname)
    Write-Debug "Before encoding name: $networkname | After encode: $urlSafeName"

    $fullURI = "$Global:BASE_SERVICE_URI/networks?dcid=$dcid&hostid=$hostid&clusterid=$clusterid&networkname=$urlSafeName"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a collection of published services

.DESCRIPTION
Retrieve a collection of published services

.EXAMPLE
$collection = Get-PublishedServices

#>

function Get-PublishedServices {
    [cmdletbinding()]
    Param()
    #Set-PSDebug -Strict
    #Set-StrictMode -version Latest
    
    Write-Warning 'Deprecated as of release 2.3, replaced by Get-PublishedServiceReferences"'

    $fullURI = "$Global:BASE_SERVICE_URI/publishedservices"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a reference collection of published services

.DESCRIPTION
Retrieve a reference collection of published services

.PARAMETER name
Optional name of the published service

.EXAMPLE
$collection = Get-PublishedServiceReferences

.EXAMPLE
$collection = Get-PublishedServiceReferences -name "Latest Build"

#>

function Get-PublishedServiceReferences {
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory = $false)]
        [String] $name
    )
    #Set-PSDebug -Strict
    #Set-StrictMode -version Latest
    
    $fullURI = "$Global:BASE_SERVICE_URI/references/publishedservices"
    Write-Debug "URI is: $fullURI"

    if ($name -ne $null) {
        $urlSafeName = [System.Web.HttpUtility]::UrlEncode($name)
        Write-Debug "Before encoding name: $name | After encode: $urlSafeName"
        $fullURI += "?name=$($urlSafeName)"
    }
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a published service by ID

.DESCRIPTION
Retrieve a published service by ID

.EXAMPLE
$ps = Get-PublishedServiceById -psId 24555

#>

function Get-PublishedServiceById {
    [cmdletbinding()]
    Param(
        [Long] $psId = $(Throw "Provide the published service ID.")
    )
    
    
    $fullURI = "$Global:BASE_SERVICE_URI/publishedservices/$psId"
    Write-Debug "URI is: $fullURI"
     
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
        $Global:n = $requestResponse
    }
    catch{
        $Global:f = $_
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a published service by name

.DESCRIPTION
Retrieve a published service by name

.EXAMPLE
$ps = Get-PublishedServiceByName -name "Service #1"

#>

function Get-PublishedServiceByName {
    [cmdletbinding()]
    Param(
        [String] $name = $(Throw "Provide the published service name.")
    )
    
    
    $urlSafePSName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafePSName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/publishedservices/name/$urlSafePSName"
    Write-Debug "URI is: $fullURI"
     
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        $Global:k = $requestResponse
        $Global:j = $_
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Create a published service

.DESCRIPTION
Create a published service

.EXAMPLE
#
##### Creating a VMWare service with fenced component(s) #####
# With port forwarding #
# External router IP Static Assigned from IP pool #
# DHCP address mode #
##############################################################

#Locate the VM to use as published service component
$vms = Get-VMs -vmName "DevVM - 001"
$vm = $vms.VirtualMachineCollection.VirtualMachines[0]

#Create a published service component template
$psDTO = New-DTOTemplateObject -DTOTagName "PublishedService"

#Create a published service component, backed by a VM inventory
$psc = New-DTOTemplateObject -DTOTagName "PublishedServiceComponent"
$psc.PublishedServiceComponent.ref.id = $vm.id
$psc.PublishedServiceComponent.ref.type = $vm.type

$psDTO.PublishedService.serviceComponents = @()
$psDTO.PublishedService.serviceComponents += $psc.PublishedServiceComponent

#Create a published service component, backed by a VM inventory with an IAM Role
$psc = New-DTOTemplateObject -DTOTagName "PublishedServiceComponent"
$psc.PublishedServiceComponent.ref.id = $vm.id
$psc.PublishedServiceComponent.ref.type = $vm.type
$psc.PublishedServiceComponent.iamRole = "MyRole"

$psDTO.PublishedService.serviceComponents = @()
$psDTO.PublishedService.serviceComponents += $psc.PublishedServiceComponent
    
#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.id = -1
$userDTO.OwnerInfo.loginId = "superuser"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null
    
#Clear out user(s); add new one we just created
$psDTO.PublishedService.serviceUsers = @()
$psDTO.PublishedService.serviceUsers += $userDTO.OwnerInfo

#Grab one of the VM NICs
$vmNic1 = $vm.networkCards | Where-Object {$_.label -eq "Network adapter 1"}

#Set up network fencing service properties
$psDTO.PublishedService.fenced = $true
$psDTO.PublishedService.internalRouterIp = "192.168.1.1"
$psDTO.PublishedService.internalRouterSubnet = "255.255.255.0"
$psDTO.PublishedService.externalRouterStaticallyAssigned = $true

#Set up port forward pairs
$portForwardPairTemplate1 = New-DTOTemplateObject -DTOTagName "PortForwardPair"
$portForwardPairTemplate1.PortForwardPair.publicPort = 80
$portForwardPairTemplate1.PortForwardPair.privatePort = 8080

$portForwardPairTemplate2 = New-DTOTemplateObject -DTOTagName "PortForwardPair"
$portForwardPairTemplate2.PortForwardPair.publicPort = 443
$portForwardPairTemplate2.PortForwardPair.privatePort = 8443

#Set up component NIC parameters
$componentNICTemplate = New-DTOTemplateObject -DTOTagName "PublishedServiceComponentNIC"
$componentNICTemplate.PublishedServiceComponentNIC.accessMode = "OUT_ONLY"
$componentNICTemplate.PublishedServiceComponentNIC.addressMode="DHCP"
$componentNICTemplate.PublishedServiceComponentNIC.mac = $vmNic1.macAddress
$componentNICTemplate.PublishedServiceComponentNIC.hostName=""
$componentNICTemplate.PublishedServiceComponentNIC.portForwardPairs=@($portForwardPairTemplate1.PortForwardPair, $portForwardPairTemplate2.PortForwardPair)

#Add the component NIC to the published service component
Add-Member -InputObject $psc.PublishedServiceComponent -MemberType NoteProperty -Name "componentNics" -Value @($componentNICTemplate.PublishedServiceComponentNIC) -Force

# Apply a placement attribute
$city = Get-PlacementAttribute -name "City"

$cityReference = New-DTOTemplateObject -DTOTagName "ManagedObjectReference"
$cityReference.ManagedObjectReference.displayName = $city.SublistPlacementAttribute.name
$cityReference.ManagedObjectReference.id = $city.SublistPlacementAttribute.id
$cityReference.ManagedObjectReference.type = "PLACEMENT_ATTRIBUTE"

$appliedPlacementAttribute = New-DTOTemplateObject -DTOTagName "AppliedPlacementAttribute"
$appliedPlacementAttribute.AppliedPlacementAttribute.placementAttribute = $cityReference
$appliedPlacementAttribute.AppliedPlacementAttribute.allowedValues = @("Ottawa")

Add-Member -InputObject $psDTO.PublishedService -MemberType NoteProperty -Name "appliedPlacementAttributes" -Value @($appliedPlacementAttribute.AppliedPlacementAttribute) -Force

#Create the published service
$createPS = New-PublishedService -psDto $psDTO

.EXAMPLE
#
##### Creating a VMWare service with fenced component(s) #####
# No port forwarding #
# External router IP DHCP assigned #
# Statically Assigned address mode #
##############################################################

#Locate the VM to use as published service component
$vms = Get-VMs -vmName "DevVM - 001"
$vm = $vms.VirtualMachineCollection.VirtualMachines[0]

#Create a published service component template
$psDTO = New-DTOTemplateObject -DTOTagName "PublishedService"

#Create a published service component, backed by a VM inventory
$psc = New-DTOTemplateObject -DTOTagName "PublishedServiceComponent"
$psc.PublishedServiceComponent.ref.id = $vm.id
$psc.PublishedServiceComponent.ref.type = $vm.type

$psDTO.PublishedService.serviceComponents = @()
$psDTO.PublishedService.serviceComponents += $psc.PublishedServiceComponent
    
#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.id = -1
$userDTO.OwnerInfo.loginId = "superuser"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null
    
#Clear out user(s); add new one we just created
$psDTO.PublishedService.serviceUsers = @()
$psDTO.PublishedService.serviceUsers += $userDTO.OwnerInfo

#Grab one of the VM's NICs
$vmNic1 = $vm.networkCards | Where-Object {$_.label -eq "Network adapter 1"}

#Set up network fencing service properties
$psDTO.PublishedService.fenced = $true
$psDTO.PublishedService.internalRouterIp = "192.168.1.1"
$psDTO.PublishedService.internalRouterSubnet = "255.255.255.0"
$psDTO.PublishedService.externalRouterStaticallyAssigned = $false

#Set up component NIC parameters
$componentNICTemplate = New-DTOTemplateObject -DTOTagName "PublishedServiceComponentNIC"
$componentNICTemplate.PublishedServiceComponentNIC.accessMode = "OUT_ONLY"
$componentNICTemplate.PublishedServiceComponentNIC.addressMode="STATIC_PUSH"
$componentNICTemplate.PublishedServiceComponentNIC.staticIp = "192.168.1.2"
$componentNICTemplate.PublishedServiceComponentNIC.mac = $vmNic1.macAddress
$componentNICTemplate.PublishedServiceComponentNIC.hostName="test"
$componentNICTemplate.PublishedServiceComponentNIC.portForwardPairs=@()

#Add the component NIC to the published service component
Add-Member -InputObject $psc.PublishedServiceComponent -MemberType NoteProperty -Name "componentNics" -Value @($componentNICTemplate.PublishedServiceComponentNIC) -Force

#Create the published service
$createPS = New-PublishedService -psDto $psDTO

.EXAMPLE
#
##### Creating a VMWare service ############
# Single component #
# Assign request attributes to component #
############################################

#Locate the VM to use as published service component
$vms = Get-VMs -vmName "DevVM - 001"
$vm = $vms.VirtualMachineCollection.VirtualMachines[0]

#Create a published service component template
$psDTO = New-DTOTemplateObject -DTOTagName "PublishedService"
$psDTO.PublishedService.useStaticComponentForms = $false

#Create a published service component, backed by a VM inventory
$psc = New-DTOTemplateObject -DTOTagName "PublishedServiceComponent"
$psc.PublishedServiceComponent.ref.id = $vm.id
$psc.PublishedServiceComponent.ref.type = $vm.type

$psDTO.PublishedService.serviceComponents = @()
$psDTO.PublishedService.serviceComponents += $psc.PublishedServiceComponent
    
#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.id = -1
$userDTO.OwnerInfo.loginId = "superuser"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null
    
#Clear out user(s); add new one we just created
$psDTO.PublishedService.serviceUsers = @()
$psDTO.PublishedService.serviceUsers += $userDTO.OwnerInfo

$vmRequestAttribute1Template = New-DTOTemplateObject -DTOTagName "VMRequestAttribute"
$vmRequestAttribute2Template = New-DTOTemplateObject -DTOTagName "VMRequestAttribute"
$vmRequestAttribute3Template = New-DTOTemplateObject -DTOTagName "VMRequestAttribute"

#Attributes for the service component
$vmRequestAttribute1Template.VMRequestAttribute.attributeName = "SLA"
$vmRequestAttribute1Template.VMRequestAttribute.value = "Gold"
$vmRequestAttribute2Template.VMRequestAttribute.attributeName = "Cost Center"
$vmRequestAttribute2Template.VMRequestAttribute.value = "Toronto CS"
$vmRequestAttribute3Template.VMRequestAttribute.attributeName = "Primary Application"
$vmRequestAttribute3Template.VMRequestAttribute.value = "CIS App"

#Add the request attributes to service component
$requestAttributes = @($vmRequestAttribute1Template.VMRequestAttribute, $vmRequestAttribute2Template.VMRequestAttribute, $vmRequestAttribute3Template.VMRequestAttribute)
Add-Member -InputObject $psc.PublishedServiceComponent.serviceComponentSettings -MemberType NoteProperty -Name "requestAttributes" -Value $requestAttributes -Force

#Create the published service
$createPS = New-PublishedService -psDto $psDTO

.EXAMPLE
#
##### Creating a VMWare service ######
# Single component #
# Assign request form to component #
######################################

#Locate the VM to use as published servie component
$vms = Get-VMs -vmName "DevVM - 001"
$vm = $vms.VirtualMachineCollection.VirtualMachines[0]

#Create a published service component template
$psDTO = New-DTOTemplateObject -DTOTagName "PublishedService"
$psDTO.PublishedService.useStaticComponentForms = $false

#Create a published service component, backed by a VM inventory
$psc = New-DTOTemplateObject -DTOTagName "PublishedServiceComponent"
$psc.PublishedServiceComponent.ref.id = $vm.id
$psc.PublishedServiceComponent.ref.type = $vm.type

$psDTO.PublishedService.serviceComponents = @()
$psDTO.PublishedService.serviceComponents += $psc.PublishedServiceComponent
    
#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.id = -1
$userDTO.OwnerInfo.loginId = "superuser"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null

#Clear out user(s); add new one we just created
$psDTO.PublishedService.serviceUsers = @()
$psDTO.PublishedService.serviceUsers += $userDTO.OwnerInfo

$header = New-DTOTemplateObject -DTOTagName "RequestFormHeaderElement"
$header.RequestFormHeaderElement.label = "Header"
$header.RequestFormHeaderElement.tagName = "H4"

#Create a text input field element
$textFieldInputObject = New-DTOTemplateObject -DTOTagName "RequestFormTextFieldElement"
$textFieldInputObject.RequestFormTextFieldElement.label="My Text input"
$textFieldInputObject.RequestFormTextFieldElement.mandatory = $true
$textFieldInputObject.RequestFormTextFieldElement.maxChars = 200
$textFieldInputObject.RequestFormTextFieldElement.numOfLines = 20

#Create a memory element
$memory = New-DTOTemplateObject -DTOTagName "RequestFormMemoryElement"
$memory.RequestFormMemoryElement.label="Total Memory"
$memory.RequestFormMemoryElement.mandatory = $true
$memory.RequestFormMemoryElement.maxMemorySize = 2048

#Create a disk element
$disk = New-DTOTemplateObject -DTOTagName "RequestFormDiskElement"
$disk.RequestFormDiskElement.label= "Total Disks"
$disk.RequestFormDiskElement.mandatory = $true
$disk.RequestFormDiskElement.allowDiskAddition = $true
$disk.RequestFormDiskElement.allowDiskChange = $true
$disk.RequestFormDiskElement.maxNewDiskCount = 5
$disk.RequestFormDiskElement.maxNewDiskSizeInMB = 50

#Create CPU element
$cpu = New-DTOTemplateObject -DTOTagName "RequestFormCPUElement"
$cpu.RequestFormCPUElement.label = "CPU Count"
$cpu.RequestFormCPUElement.mandatory = $true
$cpu.RequestFormCPUElement.allowableValue = @("1","4","8")

#Create network form element
$devNetworkZone = Get-NetworkZoneByName "Dev"
$dmzNetworkZone = GEt-NetworkZoneByname "DMZ"

$networkElement = New-DTOTemplateObject -DTOTagName "RequestFormNetworkElement"
$networkElement.RequestFormNetworkElement.label="My Network"
$networkElement.RequestFormNetworkElement.maxNewNic = 5
$networkElement.RequestFormNetworkElement.allowNetworkAddition = $true
$networkElement.RequestFormNetworkElement.allowNetworkChange = $true
$networkElement.RequestFormNetworkElement.allowableNetworkZones = @($devNetworkZone.NetworkZone.name ,$dmzNetworkZone.NetworkZone.name )

#Create 'Upload Files' step
$fileUploadElementDTO = New-DTOTemplateObject -DTOTagName "RequestFormFileUploadElement"
$fileUploadElementDTO.RequestFormFileUploadElement.label = "Answer Files"
$fileUploadElementDTO.RequestFormFileUploadElement.mandatory=$true
$fileUploadElementDTO.RequestFormFileUploadElement.maxFileCount = 11

#Add other elements here

$formElements =@($header.RequestFormHeaderElement, $textFieldInputObject.RequestFormTextFieldElement, $memory.RequestFormMemoryElement, $disk.RequestFormDiskElement, $cpu.RequestFormCPUElement,$networkElement.RequestFormNetworkElement, $fileUploadElementDTO.RequestFormFileUploadElement)
Add-Member -InputObject $psc.PublishedServiceComponent.serviceComponentSettings -MemberType NoteProperty -Name "formElements" -Value $formElements -Force

#Create the published service
$createPS = New-PublishedService -psDto $psDTO

.EXAMPLE
#
##### Creating a VMWare service #####
# Single component #
# Set service component resources #
#####################################

#Locate the VM to use as published service component
$vms = Get-VMs -vmName "DevVM - 001"
$vm = $vms.VirtualMachineCollection.VirtualMachines[0]

#Create a published service component template
$psDTO = New-DTOTemplateObject -DTOTagName "PublishedService"

# Set static form to false
$psDTO.PublishedService.useStaticComponentForms = $false;

#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.id = -1
$userDTO.OwnerInfo.loginId = "superuser"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null

#Clear out user(s); add new one we just created
$psDTO.PublishedService.serviceUsers = @()
$psDTO.PublishedService.serviceUsers += $userDTO.OwnerInfo

#Create a published service component DTO, backed by a VM inventory
$psc = New-DTOTemplateServiceComponent -refId $vm.id -reftype $vm.type
$psDTO.PublishedService.serviceComponents = @($psc.PublishedServiceComponent)

#Change CPU count from default to new value
#Get the CPU Resource configuration
$cpu = $psc.PublishedServiceComponent.serviceComponentSettings.resources | Where-Object {$_.resourceType -match "CPUResource"}
$cpu.cpuCount = 4

#Change Memory from default to new value
#Get Memory Resource configuration
$memory = $psc.PublishedServiceComponent.serviceComponentSettings.resources | Where-Object {$_.resourceType -match "MemoryResource"}
$memory.memoryInMB = 1024 + $memory.MemoryResource.memoryInMB

#Add a disk to the existing list
#Get the Storage Resource configuration
$storage = $psc.PublishedServiceComponent.serviceComponentSettings.resources | Where-Object {$_.resourceType -match "StorageResource"}
#Create disk DTO
$disk = New-DTOTemplateObject -DTOTagName "DiskResource"
$disk.DiskResource.diskSizeInKB= 2097152
$disk.DiskResource.operation = "ADD_DISK"
$disk.DiskResource.storageTierName="Storage Tier 1"

$newDisks = @()
$newDisks += $storage.disks
$newDisks += $disk.DiskResource
$storage.disks = $newDisks

#Add more network adapters
#Get the Network Resource configuration
$network = $psc.PublishedServiceComponent.serviceComponentSettings.resources | Where-Object {$_.resourceType -match "NetworkResource"}
#Get the network zone
$devZone = Get-NetworkZoneByName -name "Dev"

#Create network adapter DTO
$networkAdapter = New-DTOTemplateObject "NetworkAdapterResource"
$networkAdapter.NetworkAdapterResource.networkZoneName = $devZone.NetworkZone.name

$newAdapters = @()
$newAdapters += $network.networkAdapters
$newAdapters += $networkAdapter.NetworkAdapterResource
$network.networkAdapters = $newAdapters

#Create the published service
$createPS = New-PublishedService -psDto $psDTO

.EXAMPLE
#
##### Creating a VMWare service ###################
# Single component #
# Set Puppet and Chef resources and form elements #
###################################################

#Locate the VM to use as published servie component
$vms = Get-VMs -vmName "MidgetTemplate"
$vm = $vms.VirtualMachineCollection.VirtualMachines[0]
    
#Create a published service component template
$psDTO = New-DTOTemplateObject -DTOTagName "PublishedService"
$psDTO.PublishedService.name = "FastProd 2"
$psDTO.PublishedService.useStaticComponentForms = $false

#Create a published service component DTO, backed by a VM inventory
$psc = New-DTOTemplateServiceComponent -refId $vm.id -reftype $vm.type

#Find the Chef resource and set properties
$chefResource = $psc.PublishedServiceComponent.serviceComponentSettings.resources | Where-Object {$_.resourceType -match "ChefResource"}

#Recipes and Roles fields have been deprecated. Please use runList field instead
#$recipes =@("7-Zip","apache2::god_monitor")
#$roles = @("linux","windows");
#Add-Member -InputObject $chefResource -MemberType NoteProperty -Name "recipes" -Value $recipes -Force
#Add-Member -InputObject $chefResource -MemberType NoteProperty -Name "roles" -Value $roles -Force

$chefResource.chefOrganization = "MyChefServer2"
$chefResource.environment="_default"

#Set Chef run-list as an ordered list of roles and recipes
$chefRunList =@("role[linux]", "recipe[7-zip]","recipe[apache2::god_monitor]")
Add-Member -InputObject $chefResource -MemberType NoteProperty -Name "runList" -Value $chefRunList -Force

#Find the Puppet resource and set properties
$puppetResource = $psc.PublishedServiceComponent.serviceComponentSettings.resources | Where-Object {$_.resourceType -match "PuppetResource"}
$puppetClasses =@("apt","acl")
$puppetGroups = @("Apache","Linux");
Add-Member -InputObject $puppetResource -MemberType NoteProperty -Name "classes" -Value $puppetClasses -Force
Add-Member -InputObject $puppetResource -MemberType NoteProperty -Name "groups" -Value $puppetGroups -Force

$psDTO.PublishedService.useStaticComponentForms = $false
$psDTO.PublishedService.serviceComponents = @($psc.PublishedServiceComponent)
    
#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.loginId = "superuser"
        
#Clear out user(s); add new one we just created
$psDTO.PublishedService.serviceUsers = @()
$psDTO.PublishedService.serviceUsers += $userDTO.OwnerInfo
    
#Retrieve the references to the organizations we are interested in
$orgsCollection1 = Get-OrganizationReferences -name "Default Organization"
$orgsCollection2 = Get-OrganizationReferences -name "TestOrg"
    
#Create a collection of organizations
$orgs = ($orgsCollection1.ManagedObjectReferenceCollection.ManagedObjectReferences[0], $orgsCollection2.ManagedObjectReferenceCollection.ManagedObjectReferences[0])
    
#Add organization collection to published service, as this object is not on the template
Add-Member -InputObject $psDTO.PublishedService -MemberType NoteProperty -Name "organizations" -Value $orgs -Force
    
# Add form elements
$cpu = New-DTOTemplateObject -DTOTagName "RequestFormCPUElement"
$cpu.RequestFormCPUElement.label = "CPU Count"
$cpu.RequestFormCPUElement.mandatory = $true
$cpu.RequestFormCPUElement.allowableValue = @("1","4","8")

$puppet = New-DTOTemplateObject -DTOTagName "RequestFormPuppetPickListElement"
$puppet.RequestFormPuppetPickListElement.label="PuppetGroup"
$puppet.RequestFormPuppetPickListElement.allowMultiple = $true
$puppet.RequestFormPuppetPickListElement.externalServerMetadata="PUPPET_GROUP"
$puppet.RequestFormPuppetPickListElement.mandatory = $true
$puppet.RequestFormPuppetPickListElement.allowOverrideDefaultValues = $false
$puppet.RequestFormPuppetPickListElement.availableValues=@("Apache","MySQL");

# The following commented lines add the Recipes form element. It cannot be used together with the Run-list element.
#$chef = New-DTOTemplateObject -DTOTagName "RequestFormChefPickListElement"
#$chef.RequestFormChefPickListElement.label="ChefRoles"
#$chef.RequestFormChefPickListElement.allowMultiple = $false
#$chef.RequestFormChefPickListElement.externalServerMetadata = "CHEF_ROLE"
#$chef.RequestFormChefPickListElement.mandatory = $true
#$chef.RequestFormChefPickListElement.allowOverrideDefaultValues = $true
#$chef.RequestFormChefPickListElement.availableValues=@("linux","windows")

# Add Chef Run-list element
$chefRunList = New-DTOTemplateObject -DTOTagName "RequestFormChefRunListElement"
$chefRunList.RequestFormChefRunListElement.availableValues = @("role[linux]", "recipe[7-zip]","recipe[apache2::god_monitor]")

$formElements =@($cpu.RequestFormCPUElement, $puppet.RequestFormPuppetPickListElement, $chefRunList.RequestFormChefRunListElement)
Add-Member -InputObject $psc.PublishedServiceComponent.serviceComponentSettings -MemberType NoteProperty -Name "formElements" -Value $formElements -Force

#Create the published service
$createPS = New-PublishedService -psDto $psDTO

.EXAMPLE
#
##### Creating a VMWare service ######
# Single component #
# Set service component groups #
######################################

#Locate the VM to use as published servie component
$vms = Get-VMs -vmName "DevVM - 001"
$vm = $vms.VirtualMachineCollection.VirtualMachines[0]

#Create a published service component template
$psDTO = New-DTOTemplateObject -DTOTagName "PublishedService"
$psDTO.PublishedService.useStaticComponentForms = $false

#Create a published service component DTO, backed by a VM inventory
$psc = New-DTOTemplateServiceComponent -refId $vm.id -reftype $vm.type

$psDTO.PublishedService.serviceComponents = @()
$psDTO.PublishedService.serviceComponents += $psc.PublishedServiceComponent
    
#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.id = -1
$userDTO.OwnerInfo.loginId = "superuser"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null

#Clear out user(s); add new one we just created
$psDTO.PublishedService.serviceUsers = @()
$psDTO.PublishedService.serviceUsers += $userDTO.OwnerInfo

# Set the group name for each group type to be added to the service component
$egName = "EG1"
$sgName = "SG1"
$psgName = "PSG1"
$mgName = "MG1"
$rgName = "RG1"

# Get all the groups in vCommander
$groups = Get-Groups

# Get the group object for the specified group names
$expiryGroup = $groups.GroupCollection.Groups | Where-Object {($_.groupType -eq "EXPIRY_GROUP") -and ($_.name -eq $egName)}
$scanGroup = $groups.GroupCollection.Groups | Where-Object {($_.groupType -eq "VM_SCAN_GROUP") -and ($_.name -eq $sgName)}
$powerScheduleGroup = $groups.GroupCollection.Groups | Where-Object {($_.groupType -eq "POWER_SCHEDULE_GROUP") -and ($_.name -eq $psgName)}
$maintenanceGroup = $groups.GroupCollection.Groups | Where-Object {($_.groupType -eq "MAINTENANCE_GROUP") -and ($_.name -eq $mgName)}
$rightsizingGroup = $groups.GroupCollection.Groups | Where-Object {($_.groupType -eq "RIGHTSIZING_GROUP") -and ($_.name -eq $rgName)}

#Add the request attributes to service component
$requestGroups = @($expiryGroup, $scanGroup, $powerScheduleGroup, $maintenanceGroup, $rightsizingGroup)
Add-Member -InputObject $psc.PublishedServiceComponent.serviceComponentSettings -MemberType NoteProperty -Name "groups" -Value $requestGroups -Force

#Create the published service
$createPS = New-PublishedService -psDto $psDTO

.EXAMPLE
# Create a service catalog entry based on an ARM template
    #Create a published service component template
    $psDTO = New-DTOTemplateObject -DTOTagName "PublishedService"
    $psDTO.PublishedService.name = "Example ARM Template Service"
    $psDTO.PublishedService.description = "Deploy into an existing resource group"

    #Settings controlling deployment
    $psDTO.PublishedService.cloudTemplateDeployType = "INCREMENTAL"
    $psDTO.PublishedService.deployType = "STANDALONE_VMS"

    #Publish this globally
    $psDTO.PublishedService.publishState = "PUBLISHED"
    $psDTO.PublishedService.serviceUsers = @()

    #Want to create a blueprint, not using (deprecated) static forms
    $psDTO.PublishedService.useStaticComponentForms = $false

    #Create a published service component
    $descriptor = New-DTOTemplateObject -DTOTagName "CloudTemplateDescriptor"
    $descriptor.CloudTemplateDescriptor.cloudTemplateUrl = "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/101-vm-simple-linux/azuredeploy.json"
    $descriptor.CloudTemplateDescriptor.cloudTemplateType = "ARM_TEMPLATE"

    $psc = Get-CloudTemplateServiceComponentTemplate -descriptor $descriptor
    
    #Update cost for the component
    $psc.PublishedServiceComponent.cost = 500

    #Update a couple of the parameter values - one will point to the form
    $psc.PublishedServiceComponent.cloudTemplateParameters[0].value = "12.04.5-LTS"
    $psc.PublishedServiceComponent.cloudTemplateParameters[1].value = "adminUser"
    $psc.PublishedServiceComponent.cloudTemplateParameters[2].value = "password"
    $psc.PublishedServiceComponent.cloudTemplateParameters[3].value = "#{form.inputField['DNS Prefix']}"

    #Create the form with input elements
    $dnsInputFormElement = New-DTOTemplateObject -DTOTagName "RequestFormTextFieldElement"
    $dnsInputFormElement.RequestFormTextFieldElement.label="DNS Prefix"
    $dnsInputFormElement.RequestFormTextFieldElement.mandatory = $true
    $dnsInputFormElement.RequestFormTextFieldElement.maxChars = 20
    $dnsInputFormElement.RequestFormTextFieldElement.numOfLines = 1

    $formElements =@($dnsInputFormElement.RequestFormTextFieldElement)
    Add-Member -InputObject $psc.PublishedServiceComponent.serviceComponentSettings -MemberType NoteProperty -Name "formElements" -Value $formElements -Force

    $psDTO.PublishedService.serviceComponents = @()
    $psDTO.PublishedService.serviceComponents += $psc.PublishedServiceComponent

    $createPS = New-PublishedService -psDto $psDTO


.EXAMPLE
# Create a service catalog entry based on an ARM template from a file
#Get the content of the JSON file
$jsonFile = 'C:\ARM Templates\Arm_Template_1.json'
$json = Get-Content $jsonFile -Raw

#Create a published service component template
$psDTO = New-DTOTemplateObject -DTOTagName "PublishedService"
$psDTO.PublishedService.name = "Example ARM Template Service"
$psDTO.PublishedService.description = "Deploy into an existing resource group"

#Settings controlling deployment
$psDTO.PublishedService.cloudTemplateDeployType = "INCREMENTAL"
$psDTO.PublishedService.deployType = "STANDALONE_VMS"

#Publish this globally
$psDTO.PublishedService.publishState = "PUBLISHED"
$psDTO.PublishedService.serviceUsers = @()

#Want to create a blueprint, not using (deprecated) static forms
$psDTO.PublishedService.useStaticComponentForms = $false

#Create a published service component
$descriptor = New-DTOTemplateObject -DTOTagName "CloudTemplateDescriptor"
$descriptor.CloudTemplateDescriptor.cloudTemplateType = "ARM_TEMPLATE"
$descriptor.CloudTemplateDescriptor.cloudTemplateBody = $json

$psc = Get-CloudTemplateServiceComponentTemplate -descriptor $descriptor

#Update cost for the component
$psc.PublishedServiceComponent.cost = 500

# Template Body Params - Update the parameter values - most will point to specific form elements
$param1 = $psc.PublishedServiceComponent.cloudTemplateParameters | Where-Object {$_.name -eq 'adminUsername'}
$param1.value = "#{form.inputField['Username']"
$param2 = $psc.PublishedServiceComponent.cloudTemplateParameters | Where-Object {$_.name -eq 'adminPassword'}
$param2.value = "#{form.inputField['Password']"
$param3 = $psc.PublishedServiceComponent.cloudTemplateParameters | Where-Object {$_.name -eq 'dnsLabelPrefix'}
$param3.value = "#{form.componentName}"
$param4 = $psc.PublishedServiceComponent.cloudTemplateParameters | Where-Object {$_.name -eq 'ubuntuOSVersion'}
$param4.value = "16.04.0-LTS"
$param5 = $psc.PublishedServiceComponent.cloudTemplateParameters | Where-Object {$_.name -eq 'vmName'}
$param5.value = "#{form.componentName}"

#Create the form with input elements
$usernameInputFormElement = New-DTOTemplateObject -DTOTagName "RequestFormTextFieldElement"
$usernameInputFormElement.RequestFormTextFieldElement.label="Username"
$usernameInputFormElement.RequestFormTextFieldElement.mandatory = $true
$usernameInputFormElement.RequestFormTextFieldElement.maxChars = 32
$usernameInputFormElement.RequestFormTextFieldElement.numOfLines = 1

$passwordInputFormElement = New-DTOTemplateObject -DTOTagName "RequestFormTextFieldElement"
$passwordInputFormElement.RequestFormTextFieldElement.label="Password"
$passwordInputFormElement.RequestFormTextFieldElement.mandatory = $true
$passwordInputFormElement.RequestFormTextFieldElement.maxChars = 128
$passwordInputFormElement.RequestFormTextFieldElement.numOfLines = 1
$passwordInputFormElement.RequestFormTextFieldElement.password = $true
    
$formElements =@($usernameInputFormElement.RequestFormTextFieldElement, $passwordInputFormElement.RequestFormTextFieldElement)
Add-Member -InputObject $psc.PublishedServiceComponent.serviceComponentSettings -MemberType NoteProperty -Name "formElements" -Value $formElements -Force

$psDTO.PublishedService.serviceComponents = @()
$psDTO.PublishedService.serviceComponents += $psc.PublishedServiceComponent

$createPS = New-PublishedService -psDto $psDTO

.EXAMPLE
# Create a service catalog entry based on an cloud formation template
    #Create a published service component template
    $psDTO = New-DTOTemplateObject -DTOTagName "PublishedService"
    $psDTO.PublishedService.name = "Example CloudFormation Template Service"
    $psDTO.PublishedService.description = "This is an example"

    #Publish this globally
    $psDTO.PublishedService.publishState = "PUBLISHED"
    $psDTO.PublishedService.serviceUsers = @()

    #Want to create a blueprint, not using (deprecated) static forms
    $psDTO.PublishedService.useStaticComponentForms = $false

    #Create a published service component
    $descriptor = New-DTOTemplateObject -DTOTagName "CloudTemplateDescriptor"
    $descriptor.CloudTemplateDescriptor.cloudTemplateUrl = "https://s3-us-west-2.amazonaws.com/cloudformation-templates-us-west-2/EC2InstanceWithSecurityGroupSample.template"
    $descriptor.CloudTemplateDescriptor.cloudTemplateType = "CLOUD_FORMATION"

    $psc = Get-CloudTemplateServiceComponentTemplate -descriptor $descriptor
    
    #Update cost for the component
    $psc.PublishedServiceComponent.cost = 500

    #Update a couple of the parameter values - one will point to the form
    $psc.PublishedServiceComponent.cloudTemplateParameters[0].value = "t2.small"
    $psc.PublishedServiceComponent.cloudTemplateParameters[1].value = "0.0.0.0/0"
    $psc.PublishedServiceComponent.cloudTemplateParameters[2].value = "#{form.inputField['KeyPair Name']}"

    #Create the form with input elements
    $dnsInputFormElement = New-DTOTemplateObject -DTOTagName "RequestFormTextFieldElement"
    $dnsInputFormElement.RequestFormTextFieldElement.label="KeyPair Name"
    $dnsInputFormElement.RequestFormTextFieldElement.mandatory = $true
    $dnsInputFormElement.RequestFormTextFieldElement.maxChars = 20
    $dnsInputFormElement.RequestFormTextFieldElement.numOfLines = 1

    $formElements =@($dnsInputFormElement.RequestFormTextFieldElement)
    Add-Member -InputObject $psc.PublishedServiceComponent.serviceComponentSettings -MemberType NoteProperty -Name "formElements" -Value $formElements -Force

    $psDTO.PublishedService.serviceComponents = @()
    $psDTO.PublishedService.serviceComponents += $psc.PublishedServiceComponent

    $createPS = New-PublishedService -psDto $psDTO
#>

function New-PublishedService {
    [cmdletbinding()]
    Param(
        $psDto = $(Throw "Provide the published service DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/publishedservices"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $psDto
    
    Request-SessionAlive
   
    try{
        $requestResponse  = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
        
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Get a template for a published service component based on a cloud template (ARM template or CloudFormation template)

.DESCRIPTION
This will have pre-populated data for most of the fields, and can often be submitted to the New-PublishedService
without modification. See example in New-PublishedService.

.EXAMPLE
#Create template for ARM based
$descriptor = New-DTOTemplateObject -DTOTagName "CloudTemplateDescriptor"
$descriptor.CloudTemplateDescriptor.cloudTemplateUrl = "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/101-vm-simple-linux/azuredeploy.json"
$descriptor.CloudTemplateDescriptor.cloudTemplateType = "ARM_TEMPLATE"
$psc = Get-CloudTemplateServiceComponentTemplate -descriptor $descriptor

#>

function Get-CloudTemplateServiceComponentTemplate {
    [cmdletbinding()]
    Param(
        $descriptor = $(Throw "Provide the CloudTemplateDescriptor that points to the URL or contains the body.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/cloudtemplate/templates/servicecomponent"
    Write-Debug "URI is: $fullURI"
   
    $descriptor_dto = Convert-ObjectToXml $descriptor
   
    Request-SessionAlive
    
    $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $descriptor_dto -ContentType "application/xml"
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Get the list of parameters in the specified cloud template.

.DESCRIPTION
These can be used to inspect the parameters, or to get a list of them to be inserted into a published service component that is based on a cloudtemplate.

.EXAMPLE
#Create template for ARM based
$descriptor = New-DTOTemplateObject -DTOTagName "CloudTemplateDescriptor"
$descriptor.CloudTemplateDescriptor.cloudTemplateUrl = "https://s3-us-west-2.amazonaws.com/cloudformation-templates-us-west-2/EC2InstanceWithSecurityGroupSample.template"
$descriptor.CloudTemplateDescriptor.cloudTemplateType = "CLOUD_FORMATION"
$params = Get-CloudTemplateParameters -descriptor $descriptor

#>

function Get-CloudTemplateParameters {
    [cmdletbinding()]
    Param(
        $descriptor = $(Throw "Provide the CloudTemplateDescriptor that points to the URL or contains the body.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/cloudtemplate/parameters"
    Write-Debug "URI is: $fullURI"
   
    $descriptor_dto = Convert-ObjectToXml $descriptor
   
    Request-SessionAlive
    
    $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $descriptor_dto -ContentType "application/xml"
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Modify a published service

.DESCRIPTION
Modify a published service

.EXAMPLE

$existingPS = Get-PublishedServiceByName -name "Published Service #1"
$existingPS.PublishedService.name = "New Name"
$updatedPublishedService = Update-PublishedService -psId $existingPS.PublishedService.id -updatePs $existingPS

.EXAMPLE
#Changing instance types

$existingPS = Get-PublishedServiceByName -name "Published Service #1"
$serviceComponent = $existingPS.PublishedService.serviceComponents | Where-Object {$_.name -match "Windows 2008 R2 - webserver"}

#Dealing with instances type
#$allInstanceTypes = Get-VMInstanceTypesByManagementServerType -mstype "AMAZON_AWS"
#$t2microInstanceType = $allInstanceTypes.VMInstanceTypeCollection.VMInstanceTypes | Where-Object {$_.name -match "t2.micro"}
#$m3largeInstanceType = $allInstanceTypes.VMInstanceTypeCollection.VMInstanceTypes | Where-Object {$_.name -match "m3.large"}

#Override with said instance types
#$serviceComponent.instanceTypes =@($xsmallInstanceType.instanceType,$smallInstance.instanceType)

#or add to
#$serviceComponent.instanceTypes +=($xsmallInstanceType.instanceType,$smallInstance.instanceType)

$updatedPublishedService = Update-PublishedService -psId $existingPS.PublishedService.id -updatePs $existingPS

.EXAMPLE
Set service component resources on Update-PublishedService operation

$existingPS = Get-PublishedServiceByName -name "Published Service #1"
$serviceComponent = $existingPS.PublishedService.serviceComponents | Where-Object {$_.name -match "Windows Server 2008"}


#Set new CPU count value
#Get the CPU Resource configuration
$cpu = $serviceComponent.serviceComponentSettings.resources | Where-Object {$_.resourceType -match "CPUResource"}
$cpu.cpuCount = 2

# Add a disk to the existing list
$disk = New-DTOTemplateObject -DTOTagName "DiskResource"
$disk.DiskResource.diskSizeInKB=15971520
$disk.DiskResource.operation = "ADD_DISK"
$disk.DiskResource.storageTierName="Storage Tier 2"


#Get the Storage Resource configuration
$storage = $serviceComponent.serviceComponentSettings.resources | Where-Object {$_.resourceType -match "StorageResource"}
$newDisks = @()
$newDisks += $storage.disks
$newDisks += $disk.DiskResource
$storage.disks = $newDisks


#Update published service
$updatedPublishedService = Update-PublishedService -psId $existingPS.PublishedService.id -updatePs $existingPS

.EXAMPLE
#
############################
# Add Puppet metadata #
############################
#Locate the service
$ps = Get-PublishedServiceByName "MidgetVM3"

#Find all the components
$comp1 = $ps.PublishedService.serviceComponents | Where-Object { $_.name -eq "Comp1" }
$comp2 = $ps.PublishedService.serviceComponents | Where-Object { $_.name -eq "Comp2" }

#Puppet metadata for first component
$puppetClasses1 = @("apt","acl")
$puppetGroups1= @("Microsoft")

#Puppet metadata for second comoponent
$puppetClasses2 = @("apt::backports")
$puppetGroups2 = @("Linux")

#Find the Puppet resource and set properties
$puppetResource1 = $comp1.serviceComponentSettings.resources | Where-Object {$_.resourceType -match "PuppetResource"}
$puppetResource2 = $comp2.serviceComponentSettings.resources | Where-Object {$_.resourceType -match "PuppetResource"}

Add-Member -InputObject $puppetResource1 -MemberType NoteProperty -Name "classes" -Value $puppetClasses -Force
Add-Member -InputObject $puppetResource1 -MemberType NoteProperty -Name "groups" -Value $puppetGroups -Force

Add-Member -InputObject $puppetResource2 -MemberType NoteProperty -Name "classes" -Value $puppetClasses2 -Force
Add-Member -InputObject $puppetResource2 -MemberType NoteProperty -Name "groups" -Value $puppetGroups2 -Force

#Chef follows the same pattern
#Call to update the published service catalog
Update-PublishedService -psId $ps.PublishedService.id -updatePs $ps


.EXAMPLE
#
##################################
# Assign request form to component #
##################################

#Locate the service
$ps = Get-PublishedServiceByName "My Service"

#Find all the components
$comp1 = $ps.PublishedService.serviceComponents[0]

$header = New-DTOTemplateObject -DTOTagName "RequestFormHeaderElement"
$header.RequestFormHeaderElement.label = "Header"
$header.RequestFormHeaderElement.tagName = "H4"

#Create a text input field element
$textFieldInputObject = New-DTOTemplateObject -DTOTagName "RequestFormTextFieldElement"
$textFieldInputObject.RequestFormTextFieldElement.label="My Text input"
$textFieldInputObject.RequestFormTextFieldElement.mandatory = $true
$textFieldInputObject.RequestFormTextFieldElement.maxChars = 200
$textFieldInputObject.RequestFormTextFieldElement.numOfLines = 20

#Create a memory element
$memory = New-DTOTemplateObject -DTOTagName "RequestFormMemoryElement"
$memory.RequestFormMemoryElement.label="Total Memory"
$memory.RequestFormMemoryElement.mandatory = $true
$memory.RequestFormMemoryElement.maxMemorySize = 2048

#Create a disk element
$disk = New-DTOTemplateObject -DTOTagName "RequestFormDiskElement"
$disk.RequestFormDiskElement.label= "Total Disks"
$disk.RequestFormDiskElement.mandatory = $true
$disk.RequestFormDiskElement.allowDiskAddition = $true
$disk.RequestFormDiskElement.allowDiskChange = $true
$disk.RequestFormDiskElement.maxNewDiskCount = 5
$disk.RequestFormDiskElement.maxNewDiskSizeInMB = 50

#Create CPU element
$cpu = New-DTOTemplateObject -DTOTagName "RequestFormCPUElement"
$cpu.RequestFormCPUElement.label = "CPU Count"
$cpu.RequestFormCPUElement.mandatory = $true
$cpu.RequestFormCPUElement.allowableValue = @("1","4","8")

#Create network form element
$devNetworkZone = Get-NetworkZoneByName "Dev"
$dmzNetworkZone = GEt-NetworkZoneByname "DMZ"

$networkElement = New-DTOTemplateObject -DTOTagName "RequestFormNetworkElement"
$networkElement.RequestFormNetworkElement.label="My Network"
$networkElement.RequestFormNetworkElement.maxNewNic = 5
$networkElement.RequestFormNetworkElement.allowNetworkAddition = $true
$networkElement.RequestFormNetworkElement.allowNetworkChange = $true
$networkElement.RequestFormNetworkElement.allowableNetworkZones = @($devNetworkZone.NetworkZone.name ,$dmzNetworkZone.NetworkZone.name )

#Add other elements here

$formElements =@($header.RequestFormHeaderElement, $textFieldInputObject.RequestFormTextFieldElement, $memory.RequestFormMemoryElement, $disk.RequestFormDiskElement, $cpu.RequestFormCPUElement,$networkElement.RequestFormNetworkElement)
Add-Member -InputObject $comp1.serviceComponentSettings -MemberType NoteProperty -Name "formElements" -Value $formElements -Force
   
#Call to update the published service catalog
Update-PublishedService -psId $ps.PublishedService.id -updatePs $ps

.EXAMPLE
#
##################################
# Update the completion workflow for a component #
##################################

#Locate the service
$ps = Get-PublishedServiceByName "My Service"

#Find all the components
$comp1 = $ps.PublishedService.serviceComponents[0]

$workflow = Get-WorkflowDefinition -name "Workflow Name" -type "NEW_VM_POST_DEPLOY"
$wfRef = New-DTOTemplateObject -DTOTagName "ManagedObjectReference"
$wfRef.ManagedObjectReference.id = $workflow.ComponentCompletionWorkflowDefinition.id
$wfRef.ManagedObjectReference.type = $workflow.ComponentCompletionWorkflowDefinition.type
$wfRef.ManagedObjectReference.displayName = $workflow.ComponentCompletionWorkflowDefinition.displayName
$comp1.completionWorkflow = $wfRef.ManagedObjectReference

#Call to update the published service catalog
Update-PublishedService -psId $ps.PublishedService.id -updatePs $ps

.EXAMPLE
#############################
# Apply placement attribute #
#############################

$publishedService = Get-PublishedServiceByName -name "Test"

# Get the specific applied attribute we're adding an allowed value to
$appliedPlacementAttributes = $publishedService.PublishedService.appliedPlacementAttributes
$cityPlacementAttribyte = $appliedPlacementAttributes | Where-Object { $_.placementAttribute.displayName -match "City" }
$cityPlacementAttribyte.allowedValues += "Kanata"

$updatedPublishedService = Update-PublishedService -psId $publishedService.PublishedService.id -updatePs $publishedService
#>

function Update-PublishedService {
    [cmdletbinding()]
    Param(
        [String] $psId = $(Throw "Provide the id of the published service."),
        $updatePs = $(Throw "Provide the updated published service DTO.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/publishedservices/$psId"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $updatePs
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -ContentType "application/xml" -Method PUT -Body $xml_dto
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    
    Convert-RestResponseOutput $requestResponse.RawContent
}



<#
.SYNOPSIS
Remove a published service

.DESCRIPTION
Remove a published service

.EXAMPLE
$ps = Get-PublishedServiceByName -name "Latest Build"
Remove-PublishedService -psId $ps.PublishedService.id

#>

function Remove-PublishedService {
    [cmdletbinding()]
    Param(
        $psId = $(Throw "Provide the ID of the published service.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/publishedservices/$psId"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method DELETE
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    
    $true
}

<#
.SYNOPSIS
Change the publish state of the published service

.DESCRIPTION
Change the publish state of the published service

.EXAMPLE

#Retrieve the published service, assuming a service named "ABC"
$ps = Get-PublishedServiceByName "ABC"
$psId = $ps.PublishedService.id

#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.loginId = "superuser"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null


$catalogConfigObject = New-DTOTemplateObject -DTOTagName "CatalogConfig"

$catalogConfigObject.CatalogConfig.publishState = "PUBLISHED_SPECIFIC_USERS"
$catalogConfigObject.CatalogConfig.ServiceUsers = @()
$catalogConfigObject.CatalogConfig.ServiceUsers += $userDTO.OwnerInfo

#Update
$updatedPS = Publish-Publishedservice -psId $psid -catalogConfig $catalogConfigObject

#>

function Publish-Publishedservice {
    [cmdletbinding()]
    Param(
        [String] $psId = $(Throw "Provide the ID of the published service."),
        $catalogConfig = $(Throw "Provide the catalog config DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/publishedservices/$psId/action/publish"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $catalogConfig
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a collection of management servers

.DESCRIPTION
Retrieve a collection of management servers

.EXAMPLE
#
###################################################################################
# Retrieve all management servers
###################################################################################
$managementServers = Get-ManagementServers

#>

function Get-ManagementServers {
    [cmdletbinding()]
    Param()

    $fullURI = "$Global:BASE_SERVICE_URI/managementservers";
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a management server by ID

.DESCRIPTION
Retrieve a management server by ID

.EXAMPLE
#
###################################################################################
# Retrieve a management server by ID
###################################################################################

$ms = Get-ManagementServerById -id 22424
#>

function Get-ManagementServerById {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the management server.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/managementservers/$id"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Add a new managment server to vCommander.

.DESCRIPTION
Add a new managment server to vCommander. An exception will be thrown if it's slready added to vCommander.

.EXAMPLE
#
########################################################################
#Adding a VMWare management server
########################################################################

#Encrypt your credential
New-EncryptCredential -destFilePath "C:\temp\domain_credential.xml"
$cred = New-DecryptCredential -keyFilePath "C:\temp\domain_credential.xml"

#Create a VMWare management server DTO
$serverInfo = New-DTOTemplateObject -DTOTagName "VMwareServerInfo"
$serverInfo.VMwareServerInfo.autoApproveVms = $true
$serverInfo.VMwareServerInfo.address="vsp5"
$serverInfo.VMwareServerInfo.port = 443
$serverInfo.VMwareServerInfo.username = $cred.GetNetworkCredential().username
$serverInfo.VMwareServerInfo.password = $cred.GetNetworkCredential().password

#Add the management server to vCommander
$taskInfo = New-ManagementServer -serverInfo $serverInfo

#
########################################################################
#Adding an AWS management server
########################################################################

#Encrypt your credential
New-EncryptCredential -destFilePath "C:\temp\domain_credential.xml"
$cred = New-DecryptCredential -keyFilePath "C:\temp\domain_credential.xml"

#Create a VMWare management server DTO
$serverInfo = New-DTOTemplateObject -DTOTagName "AwsServerInfo"
$serverInfo.AwsServerInfo.autoApproveVms = $true
$serverInfo.AwsServerInfo.name="aws"
$serverInfo.AwsServerInfo.accessKeyId = "your key Id goes here"
$serverInfo.AwsServerInfo.secretAccessKey = "your secret access key goes here"

#Add the management server to vCommander
$taskInfo = New-ManagementServer -serverInfo $serverInfo

#
########################################################################
#Adding an Azure ARM management server
########################################################################

$serverInfo = New-DTOTemplateObject -DTOTagName "ArmServerInfo"
$serverInfo.ArmServerInfo.autoApproveVms = $true
$serverInfo.ArmServerInfo.name = "Azure ARM"
$serverInfo.ArmServerInfo.subscriptionId = "subscription ID goes here"
$serverInfo.ArmServerInfo.applicationId = "application ID goes here"
$serverInfo.ArmServerInfo.apiKey = "API key goes here"
$serverInfo.ArmServerInfo.tenantId = "tenant ID or domain name goes here"

#Add the management server to vCommander
$taskInfo = New-ManagementServer -serverInfo $serverInfo

#
########################################################################
#Adding an Azure ASM (classic) management server
########################################################################

#Encrypt your credential
New-EncryptCredential -destFilePath "C:\temp\domain_credential.xml"
$cred = New-DecryptCredential -keyFilePath "C:\temp\domain_credential.xml"

#Create a VMWare management server DTO
#Note: You need to upload your certificate to Microsoft before you can add Azure
$serverInfo = New-DTOTemplateObject -DTOTagName "AzureServerInfo"
$serverInfo.AzureServerInfo.autoApproveVms = $true
$serverInfo.AzureServerInfo.name="azure"
$serverInfo.AzureServerInfo.subscriptionId = "your subscription key goes here"

#Add the management server to vCommander
$taskInfo = New-ManagementServer -serverInfo $serverInfo

#
########################################################################
#Adding a Microsoft SCVMM management server
########################################################################

#Encrypt your credential
New-EncryptCredential -destFilePath "C:\temp\domain_credential.xml"
$cred = New-DecryptCredential -keyFilePath "C:\temp\domain_credential.xml"

#Create a VMWare management server DTO
$serverInfo = New-DTOTemplateObject -DTOTagName "ScvmmServerInfo"
$serverInfo.ScvmmServerInfo.autoApproveVms = $true
$serverInfo.ScvmmServerInfo.address="blaze.pv.embotics.com"
$serverInfo.ScvmmServerInfo.port=8100
$serverInfo.ScvmmServerInfo.username = $cred.GetNetworkCredential().username
$serverInfo.ScvmmServerInfo.password = $cred.GetNetworkCredential().password
    
#Add the management server to vCommander
$taskInfo = New-ManagementServer -serverInfo $serverInfo

#>

function New-ManagementServer {
    [cmdletbinding()]
    param(
        $serverInfo = $(Throw "Provide the management server DTO.")
    )
    $fullURI = "$Global:BASE_SERVICE_URI/managementservers"
    Write-Verbose "URI is: $fullURI"

    $xml_dto = Convert-ObjectToXml $serverInfo
    
    Request-SessionAlive
   
    $requestResponse = Invoke-WebRequest -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    Convert-RestResponseOutput $requestResponse.RawContent

}

<#
.SYNOPSIS
Synchronize inventory of a management server by ID

.DESCRIPTION
Synchronize inventory of a management server by ID

.EXAMPLE
#
###################################################################################
# Synchronize inventory of a management server by ID
###################################################################################
$managementServers = Get-ManagementServers
$msId = $managementServers.ManagementServerCollection.ManagementServers | Where-Object {$_.name -match "manta"} | Select -ExpandProperty "id"
$taskInfo = Start-InventorySynchronization -msId $msId
#>

function Start-InventorySynchronization {
    [cmdletbinding()]
    Param(
        [Long] $msId = $(Throw "Provide the ID of the management server.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/managementservers/$msId/action/synchronizeinventory"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -ContentType "text/plain" -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST 
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a collection of clusters

.DESCRIPTION
Retrieve a collection of clusters

.EXAMPLE
#
###################################################################################
# Retrieve clusters
###################################################################################
$managementServers = Get-ManagementServers
$msId = $managementServers.ManagementServerCollection.ManagementServers | Where-Object {$_.name -match "manta"} | Select -ExpandProperty "id"
$clusters = Get-Clusters -msId $msId
#>

function Get-Clusters {
    [cmdletbinding()]
    Param(
        [Long] $msId = $(Throw "Provide the ID of the management server.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/managementservers/$msId/clusters";
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}


<#
.SYNOPSIS
Retrieve a cluster by name

.DESCRIPTION
Retrieve a cluster by name

.EXAMPLE
#
###################################################################################
# Retrieve a cluster by name
###################################################################################

#Retrieve the management server
$managementServers = Get-ManagementServers
$msId = $managementServers.ManagementServerCollection.ManagementServers | Where-Object {$_.name -match "manta"} | Select -ExpandProperty "id"

#Retrieve the cluster
$cluster = Get-ClusterByName -msId $msId -clusterName "Engineering Cluster"
#>

function Get-ClusterByName {
    [cmdletbinding()]
    Param(
        [Long] $msId = $(Throw "Provide the Id of the management server."),
        [String]$clusterName = $(Throw "Provide the cluster name")
    )
    
    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($clusterName)
    Write-Debug "Before encoding name: $clusterName | After encode: $urlSafeName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/managementservers/$msId/clusters/name/$urlSafeName";
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a collection of hosts

.DESCRIPTION
Retrieve a collection of hosts

.EXAMPLE
$managementServers = Get-ManagementServers
$msId = $managementServers.ManagementServerCollection.ManagementServers | Where-Object {$_.name -match "manta"} | Select -ExpandProperty "id"
$runtimeServers = Get-RuntimeServers -msId $msid

#>

function Get-RuntimeServers {
    [cmdletbinding()]
    Param(
        [Long] $msId = $(Throw "Provide the ID of the management server.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/runtimeservers?msid=$msId"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
   
    try{
        $Global:requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
            
    Convert-RestResponseOutput $requestResponse.RawContent
}


<#
.SYNOPSIS
Retrieve a collection of datacenters

.DESCRIPTION
Retrieve a collection of datacenters

.EXAMPLE
#Retrieves a collection of datacenters for a specific management server
$managementServers = Get-ManagementServers
$msId = $managementServers.ManagementServerCollection.ManagementServers | Where-Object {$_.name -match "manta"} | Select -ExpandProperty "id"
$datacenters = Get-Datacenters -msId $msid

#>

function Get-Datacenters {
    [cmdletbinding()]
    Param(
        [Long] $msId = $(Throw "Provide the ID of the management server.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/datacenters?msid=$msId"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}


<#
.SYNOPSIS
Retrieve a collection of folders

.DESCRIPTION
Retrieve a collection of folders

.PARAMETER max
The maximum number of folders to retrieved

.EXAMPLE
$folders = Get-Folders -max 10
#>

function Get-Folders {
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory = $false)]
        [Int] $max=100 #Maximum number of VMs to return
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/folders?max=$max"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve folder(s) with the specified name

.DESCRIPTION
Retrieve folder(s) with the specified name

.PARAMETER name
The folder name(s)

.EXAMPLE
$folders = Get-FoldersByName -name "Engineering"
#>

function Get-FoldersByName {
    [cmdletbinding()]
    Param(
        [String] $name = $(Throw "Provide the name of the folder(s).")
    )
    
    $urlSafeFolderName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafeFolderName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/folders/name/$urlSafeFolderName"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}


<#
.SYNOPSIS
Retrieve all custom attributes

.DESCRIPTION
Retrieve all custom attributes

.EXAMPLE
$cas = Get-CustomAttributes
#>

function Get-CustomAttributes {
    [cmdletbinding()]
    Param()

    $fullURI = "$Global:BASE_SERVICE_URI/attributes"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a custom attribute by ID

.DESCRIPTION
Retrieve a custom attribute by ID

.EXAMPLE
$ca = Get-CustomAttributeById -id 22424
#>

function Get-CustomAttributeById {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the custom attribute.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/attributes/$id"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a custom attribute by name

.DESCRIPTION
Retrieve a custom attribute by name

.EXAMPLE
$ca = Get-CustomAttributeByName -name "SLA"
#>

function Get-CustomAttributeByName {
    [cmdletbinding()]
    Param(
        [String] $name = $(Throw "Provide the name of the custom attribute.")
    )
    
    $urlSafeCAName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafeCAName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/attributes/name/$urlSafeCAName"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Modify a custom attribute

.DESCRIPTION
Modify a custom attribute

.EXAMPLE
$ca = Get-CustomAttributeByName -name "SLA"
$ca.CustomAttribute.allowedValues += "TestValue"

$updatedCa = Update-CustomAttribute -id $ca.CustomAttribute.id -customAttributeDTo $ca

.EXAMPLE
$ca = Get-CustomAttributeByName -name "SLA"
$ca.CustomAttribute.name = "SLA_Modified"
$ca.CustomAttribute.description = "new description"

$updatedCa = Update-CustomAttribute -id $ca.CustomAttribute.id -customAttributeDTo $ca


.EXAMPLE

$ca = Get-CustomAttributeByName -name "SLA"
$ca.CustomAttribute.portalEditable = $false

$updatedCa = Update-CustomAttribute -id $ca.CustomAttribute.id -customAttributeDTo $ca

.EXAMPLE

#Update a parent list attribute, a child sublist attribute, and grandchild sublist attribute

### List custom attribute that is the parent of the Cities sublist custom attribute

# Get a custom attribute and add a new allowed value
$customAttribute = Get-CustomAttributeByName -name "Provinces"
$allowedValues = $customAttribute.CustomAttribute.allowedValues
$customAttribute.CustomAttribute.allowedValues = @()
$customAttribute.CustomAttribute.allowedValues += $allowedValues
$customAttribute.CustomAttribute.allowedValues += "Nova Scotia"

Update-CustomAttribute -id $customAttribute.CustomAttribute.id -customAttributeDTo $customAttribute

### Sublist custom attribute that is a child of the Provinces list custom attribute and parent of the Mayors sublist custom attribute

# Get a sublist custom attribute and add new allowed values for a specific parent value (allowed values for that parent must be empty before)
$customAttribute = Get-CustomAttributeByName -name "Cities"
$customAttribute.SublistCustomAttribute.allowedValues.Add("Nova Scotia", @("Halifax", "Sydney"))

Update-CustomAttribute -id $customAttribute.SublistCustomAttribute.id -customAttributeDTo $customAttribute

### Sublist custom attribute that is a child of the Provinces list custom attribute and parent of the Mayors sublist custom attribute

# Get a sublist custom attribute and add new additional allowed values for a specific parent value (there might be already allowed values for that parent before)
$customAttribute = Get-CustomAttributeByName -name "Cities"
[System.Collections.ArrayList]$ArrayList = $customAttribute.SublistCustomAttribute.allowedValues.'Nova Scotia'
$ArrayList.Add(@("Cape Breton", "Lunenburg"))
$customAttribute.SublistCustomAttribute.allowedValues.'Nova Scotia' = $ArrayList.ToArray()

Update-CustomAttribute -id $customAttribute.SublistCustomAttribute.id -customAttributeDTo $customAttribute


### Sublist custom attribute that is a child of the Cities sublist custom attribute

# Get a sublist custom attribute and add new allowed values for a specific parent value (allowed values for that parent must be empty before)
$customAttribute = Get-CustomAttributeByName -name "Mayors"
$customAttribute.SublistCustomAttribute.allowedValues.Add("Halifax", "ha")
$customAttribute.SublistCustomAttribute.allowedValues.Add("Sydney", "sy")

Update-CustomAttribute -id $customAttribute.SublistCustomAttribute.id -customAttributeDTo $customAttribute
#>

function Update-CustomAttribute {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the custom attribute."),
        $customAttributeDTo = $(Throw "Provide the custom attribute DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/attributes/$id"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $customAttributeDTo
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method PUT -Body $xml_dto -ContentType "application/xml"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Remove a custom attribute

.DESCRIPTION
Remove a custom attribute

.EXAMPLE
$ca = Get-CustomAttributeByName -name "SLA"
$result = Remove-CustomAttribute -id $ca.CustomAttribute.id
#>

function Remove-CustomAttribute {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the custom attribute.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/attributes/$id"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method DELETE
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Create a custom attribute

.DESCRIPTION
Create a custom attribute

.EXAMPLE
#Create a list-type custom attribute

#Create an attribute DTO
$caObject = New-DTOTemplateObject -DTOTagName "CustomAttribute"

#Specify attribute value
$caObject.CustomAttribute.name="My Test Attribute #10"
$caObject.CustomAttribute.description="Attribute description"
$caObject.CustomAttribute.targetManagedObjectTypes = @("VIRTUALMACHINE")
$caObject.CustomAttribute.id = -1
$caObject.CustomAttribute.allowedValues = @("SampleVal1", "SampleVal2")

$createdCa = New-CustomAttribute -customAttributeDTo $caObject


.EXAMPLE

# Create an attribute DTO
$customAttribute = New-DTOTemplateObject -DTOTagName "SubListCustomAttribute"

#Specify attribute value
$customAttribute.SubListCustomAttribute.name = "Capitals"
$customAttribute.SubListCustomAttribute.description = "Provincial Capitals"
$customAttribute.SubListCustomAttribute.id = -1
$customAttribute.SubListCustomAttribute.sublistOfAttribute = "Provinces"
$customAttribute.SubListCustomAttribute.allowedValues = @{"Alberta" = @("Edmonton"); "Ontario" = @("Toronto"); "Quebec" = @("Quebec City")}

$createdAttribute = New-CustomAttribute -customAttributeDTO $customAttribute


.EXAMPLE

#Create a text-type custom attribute

#Create an attribute DTO
$caObject = New-DTOTemplateObject -DTOTagName "CustomAttribute"

#Specify attribute value
$caObject.CustomAttribute.name="My Test Attribute Text Type"
$caObject.CustomAttribute.description="Attribute description"
$caObject.CustomAttribute.targetManagedObjectTypes = @("VIRTUALMACHINE")
$caObject.CustomAttribute.id = -1
$caObject.CustomAttribute.allowedValues = @()

$createdCa = New-CustomAttribute -customAttributeDTo $caObject


.EXAMPLE

#Create a text-type custom attribute targeting datastore

#Create an attribute DTO
$caObject = New-DTOTemplateObject -DTOTagName "CustomAttribute"

#Specify attribute value
$caObject.CustomAttribute.name="My Test Attribute Datastore"
$caObject.CustomAttribute.description="Attribute description"
$caObject.CustomAttribute.targetManagedObjectTypes = @("DATASTORE")
$caObject.CustomAttribute.id = -1
$caObject.CustomAttribute.allowedValues = @()
$caObject.CustomAttribute.portalEditable = $false

$createdCa = New-CustomAttribute -customAttributeDTo $caObject


.EXAMPLE

#Create a text-type custom attribute of a specific format

$caObject = New-DTOTemplateObject -DTOTagName "CustomAttribute"

#Specify attribute value
$caObject.CustomAttribute.name="My Test Attribute All"
$caObject.CustomAttribute.description="Attribute description"
$caObject.CustomAttribute.targetManagedObjectTypes = @("ALL")
$caObject.CustomAttribute.id = -1
$caObject.CustomAttribute.allowedValues = @()
$caObject.CustomAttribute.portalEditable = $false

Add-Member -InputObject $caObject.CustomAttribute -MemberType NoteProperty -Name "regularExpression" -Value "abc.*def" -Force
Add-Member -InputObject $caObject.CustomAttribute -MemberType NoteProperty -Name "validationMessage" -Value "Put validation error message here" -Force

$createdCa = New-CustomAttribute -customAttributeDTo $caObject


.EXAMPLE

#Create a parent list attribute, a child sublist attribute, and grandchild sublist attribute

### List custom attribute that is the parent of the Cities sublist custom attribute

# Create a custom attribute DTO
$customAttribute = New-DTOTemplateObject -DTOTagName "CustomAttribute"
    
# Specify the values for the list custom attribute properties
$customAttribute.CustomAttribute.name = "Provinces"
$customAttribute.CustomAttribute.description = "Canadian Provinces"
$customAttribute.CustomAttribute.id = -1
$customAttribute.CustomAttribute.targetManagedObjectTypes = @("VIRTUALMACHINE")
$customAttribute.CustomAttribute.allowedValues = @("Ontario", "Quebec","Alberta")
    
$createdAttribute = New-CustomAttribute -customAttributeDTO $customAttribute

### Sublist custom attribute that is a child of the Provinces list custom attribute and parent of the Mayors sublist custom attribute

# Create a sublist custom attribute DTO
$customAttribute = New-DTOTemplateObject -DTOTagName "SubListCustomAttribute"

# Specify the values for the sublist custom attribute properties
$customAttribute.SubListCustomAttribute.name = "Cities"
$customAttribute.SubListCustomAttribute.description = "Canadian Cities by Province"
$customAttribute.SubListCustomAttribute.id = -1
# The targetManagedObjectTypes property does not need to be specified as the value gets inherited from the parent list custom attribute
$customAttribute.SubListCustomAttribute.sublistOfAttribute = "Provinces"
$customAttribute.SubListCustomAttribute.allowedValues = @{"Alberta" = @("Calgary", "Edmonton"); "Ontario" = @("Aylmer", "Toronto", "Ottawa"); "Quebec" = @("Quebec City", "Aylmer", "Montreal")}

$createdAttribute = New-CustomAttribute -customAttributeDTo $customAttribute

### Sublist custom attribute that is a child of the Cities sublist custom attribute

# Create a sublist custom attribute DTO
$customAttribute = New-DTOTemplateObject -DTOTagName "SubListCustomAttribute"

# Specify the values for the sublist custom attribute properties
$customAttribute.SubListCustomAttribute.name = "Mayors"
$customAttribute.SubListCustomAttribute.description = "Mayors of Canadian Cities"
$customAttribute.SubListCustomAttribute.id = -1
# The targetManagedObjectTypes property does not need to be specified as the value gets inherited from the parent list custom attribute
$customAttribute.SubListCustomAttribute.sublistOfAttribute = "Cities"
# Because the city of Aylmer exists in both Ontario and Quebec, the city name needs to be prefixed by the province followed by a semicolon
$customAttribute.SubListCustomAttribute.allowedValues = @{"Calgary" = @("Naheed Nenshi"); "Edmonton" = @("Don Iveson"); "Ontario;Aylmer" = @("Greg Currie"); "Toronto" = @("John Tory"); "Ottawa" = @("Jim Watson"); "Quebec City" = @("Regis Labeaume"); "Montreal" = @("Denis Coderre"); "Quebec;Aylmer" = @("Marc Croteau")}

$createdAttribute = New-CustomAttribute -customAttributeDTo $customAttribute
#>

function New-CustomAttribute { 
    [cmdletbinding()]
    Param(
        $customAttributeDTo = $(Throw "Provide the custom attribute DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/attributes"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $customAttributeDTo
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Create a new workflow definition

.DESCRIPTION
Create a new workflow definition

.EXAMPLE
Create a new request approval workflow

#Create a DTO
$approvalWorkflowDTO = New-DTOTemplateObject -DTOTagName "NewApprovalWorkflowDefinition"

#Set appropriate properties
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.name = "My New Request Approval"
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.global = $false
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.addOwnerAsAdmin = $true
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.autoDeploy = $true

#Create an email step
$emailStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowEmailStepDefinition"
$emailStepDefinitionDTO.WorkflowEmailStepDefinition.name="Send Email To ABC"
$emailStepDefinitionDTO.WorkflowEmailStepDefinition.addresses="bob@example.com;sally@example.com"
$emailStepDefinitionDTO.WorkflowEmailStepDefinition.emailBody = "This is email body"
$emailStepDefinitionDTO.WorkflowEmailStepDefinition.emailSubject = "This is email subject"
$emailStepDefinitionDTO.WorkflowEmailStepDefinition.includeRequestDetails = $true
$emailStepDefinitionDTO.WorkflowEmailStepDefinition.stepNumber = 0

#Create an approver step
$approverStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowApproverStepDefinition"
$approverStepDefinitionDTO.WorkflowApproverStepDefinition.name="To Manager for approval"
$approverStepDefinitionDTO.WorkflowApproverStepDefinition.addresses="managers@example.com"
$approverStepDefinitionDTO.WorkflowApproverStepDefinition.emailBody = "This is the content of the approver email body"
$approverStepDefinitionDTO.WorkflowApproverStepDefinition.emailSubject = "This is the subject of the approver email"
$approverStepDefinitionDTO.WorkflowApproverStepDefinition.stepNumber = 1

#Create a quota approval step
$quotaApprovalStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowQuotaApprovalStepDefinition"
$quotaApprovalStepDefinitionDTO.WorkflowQuotaApprovalStepDefinition.name="Corp Quota Approval"
$quotaApprovalStepDefinitionDTO.WorkflowQuotaApprovalStepDefinition.addresses="allmanagers@example.com"
$quotaApprovalStepDefinitionDTO.WorkflowQuotaApprovalStepDefinition.emailBody = "quota approval body"
$quotaApprovalStepDefinitionDTO.WorkflowQuotaApprovalStepDefinition.emailSubject = "quota approval subject"
$quotaApprovalStepDefinitionDTO.WorkflowQuotaApprovalStepDefinition.condition = "REJECT_REQUEST_USER_QUOTA_EXCEEDED"
$quotaApprovalStepDefinitionDTO.WorkflowQuotaApprovalStepDefinition.stepNumber = 2

#Create an approval script step
$approvalScriptStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowApprovalScriptStepDefinition"
$approvalScriptStepDefinitionDTO.WorkflowApprovalScriptStepDefinition.name="My Approval Script"
$approvalScriptStepDefinitionDTO.WorkflowApprovalScriptStepDefinition.commandLine = "C:\\Windows\\System32\\handle.exe"
$approvalScriptStepDefinitionDTO.WorkflowApprovalScriptStepDefinition.captureOutput = $true
$approvalScriptStepDefinitionDTO.WorkflowApprovalScriptStepDefinition.timeoutInSeconds = 600
$approvalScriptStepDefinitionDTO.WorkflowApprovalScriptStepDefinition.stepNumber = 3

#Create a script step
$scriptStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowScriptStepDefinition"
$scriptStepDefinitionDTO.WorkflowScriptStepDefinition.name="My custom Script"
$scriptStepDefinitionDTO.WorkflowScriptStepDefinition.commandLine = "C:\\Windows\\System32\\whatever.exe"
$scriptStepDefinitionDTO.WorkflowScriptStepDefinition.captureOutput = $true
$scriptStepDefinitionDTO.WorkflowScriptStepDefinition.timeoutInSeconds = 750
$scriptStepDefinitionDTO.WorkflowScriptStepDefinition.proceedToNextStepOnFailure = $true
$scriptStepDefinitionDTO.WorkflowScriptStepDefinition.stepNumber = 4

#Clear the existing step(s); add new ones we just created
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.Steps = @()
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.Steps += $emailStepDefinitionDTO.WorkflowEmailStepDefinition
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.Steps += $approverStepDefinitionDTO.WorkflowApproverStepDefinition
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.Steps += $quotaApprovalStepDefinitionDTO.WorkflowQuotaApprovalStepDefinition
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.Steps += $approvalScriptStepDefinitionDTO.WorkflowApprovalScriptStepDefinition
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.Steps += $scriptStepDefinitionDTO.WorkflowScriptStepDefinition

#Retrieve the organization by name [This will fail if you don't have an organization with the name 'Org 1']
$org = Get-OrganizationByName -name "Org 1"

#Create a reference to this organization. We really just need the ID and type.
$orgReferenceDTO = New-DTOTemplateObject -DTOTagName "ManagedObjectReference"
$orgReferenceDTO.ManagedObjectReference.id = $org.Organization.id
$orgReferenceDTO.ManagedObjectReference.type = "ORGANIZATION"

#Clear out organization(s); add new one we just created
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.Organizations =@()
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.Organizations += $orgReferenceDTO.ManagedObjectReference

#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.id = -1
$userDTO.OwnerInfo.loginId = "superuser"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null

#Clear out user(s); add new one we just created
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.Users = @()
$approvalWorkflowDTO.NewApprovalWorkflowDefinition.Users += $userDTO.OwnerInfo

#Submit the DTO for creation
$createdDefinition = New-WorkflowDefinition -definitionDto $approvalWorkflowDTO

#If you don't assign any users or organizations, then the definition is global

.EXAMPLE
Create a change request approval workflow

#Create a DTO
$changeApprovalWorkflowDTO = New-DTOTemplateObject -DTOTagName "ChangeApprovalWorkflowDefinition"

#Set appropriate properties
$changeApprovalWorkflowDTO.ChangeApprovalWorkflowDefinition.name = "My Change Request Approval"
$changeApprovalWorkflowDTO.ChangeApprovalWorkflowDefinition.global = $false

#Choose NONE, SCHEDULED, or ALWAYS
$changeApprovalWorkflowDTO.ChangeApprovalWorkflowDefinition.autoChangeFulfillmentRule = "SCHEDULED"

#Grab the right request form
$requestForms = Get-RequestFormsOfType "CHANGE_VM"
$targetFormId = $requestForms.RequestFormCollection.RequestForms | Where-Object {$_.displayName -match "Decommissioning"} | Select-Object -ExpandProperty "formId"

#Set up an auto fulfillment option
$autoFulfillOption1 = New-DTOTemplateObject -DTOTagName "AutoChangeFulfillmentFormOption"
$autoFulfillOption1.AutoChangeFulfillmentFormOption.formId = $targetFormId

#Choose NONE, SCHEDULED, or ALWAYS
$autoFulfillOption1.AutoChangeFulfillmentFormOption.fulfillmentRule = "NONE"

#Add fulfillment option(s) to workflow
$formOptions = @($autoFulfillOption1.AutoChangeFulfillmentFormOption)
Add-Member -InputObject $changeApprovalWorkflowDTO.ChangeApprovalWorkflowDefinition -MemberType NoteProperty -Name "autoChangeFulfillmentFormOptions" -Value $formOptions -Force

#Create an approver step
$approverStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowApproverStepDefinition"
$approverStepDefinitionDTO.WorkflowApproverStepDefinition.name="To Manager for approval"
$approverStepDefinitionDTO.WorkflowApproverStepDefinition.addresses="managers@example.com"
$approverStepDefinitionDTO.WorkflowApproverStepDefinition.emailBody = "This is the content of the approver email body"
$approverStepDefinitionDTO.WorkflowApproverStepDefinition.emailSubject = "This is the subject of the approver email"
$approverStepDefinitionDTO.WorkflowApproverStepDefinition.stepNumber = 1

#Create an approval script step
$approvalScriptStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowApprovalScriptStepDefinition"
$approvalScriptStepDefinitionDTO.WorkflowApprovalScriptStepDefinition.name="My Approval Script"
$approvalScriptStepDefinitionDTO.WorkflowApprovalScriptStepDefinition.commandLine = "C:\\Windows\\System32\\handle.exe"
$approvalScriptStepDefinitionDTO.WorkflowApprovalScriptStepDefinition.captureOutput = $true
$approvalScriptStepDefinitionDTO.WorkflowApprovalScriptStepDefinition.timeoutInSeconds = 600
$approvalScriptStepDefinitionDTO.WorkflowApprovalScriptStepDefinition.stepNumber = 2

#Create a wait step
$waitStepDTO = New-DTOTemplateObject -DTOTagName "WorkflowWaitStepDefinition"
$waitStepDTO.WorkflowWaitStepDefinition.name="Wait for Post Processing"
$waitStepDTO.WorkflowWaitStepDefinition.waitFor = "ELAPSED_TIME"
$waitStepDTO.WorkflowWaitStepDefinition.timeoutInSeconds = 900
$waitStepDTO.WorkflowWaitStepDefinition.stepNumber = 3
$waitStepDTO.WorkflowWaitStepDefinition.PSObject.Properties.Remove('condition')

#Clear the existing step(s); add new ones we just created
$changeApprovalWorkflowDTO.ChangeApprovalWorkflowDefinition.Steps = @()
$changeApprovalWorkflowDTO.ChangeApprovalWorkflowDefinition.Steps += $approverStepDefinitionDTO.WorkflowApproverStepDefinition
$changeApprovalWorkflowDTO.ChangeApprovalWorkflowDefinition.Steps += $approvalScriptStepDefinitionDTO.WorkflowApprovalScriptStepDefinition
$changeApprovalWorkflowDTO.ChangeApprovalWorkflowDefinition.Steps += $waitStepDTO.WorkflowWaitStepDefinition

#Retrieve the organization by name [This will fail if you don't have an organization with the name 'Org 1']
$org = Get-OrganizationByName -name "Org 1"

#Create a reference to this organization. We really just need the ID and type.
$orgReferenceDTO = New-DTOTemplateObject -DTOTagName "ManagedObjectReference"
$orgReferenceDTO.ManagedObjectReference.id = $org.Organization.id
$orgReferenceDTO.ManagedObjectReference.type = "ORGANIZATION"

#Clear out organization(s); add new one we just created
$changeApprovalWorkflowDTO.ChangeApprovalWorkflowDefinition.Organizations =@()
$changeApprovalWorkflowDTO.ChangeApprovalWorkflowDefinition.Organizations += $orgReferenceDTO.ManagedObjectReference

#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.loginId = "superuser"

#Clear out user(s); add new one we just created
#If you don't assign any users or organizations, then the definition is global
$changeApprovalWorkflowDTO.ChangeApprovalWorkflowDefinition.Users = @()
$changeApprovalWorkflowDTO.ChangeApprovalWorkflowDefinition.Users += $userDTO.OwnerInfo

#Submit the DTO for creation
$createdDefinition = New-WorkflowDefinition -definitionDto $changeApprovalWorkflowDTO


.EXAMPLE
Create a VM Completion workflow.

#Create a DTO
$vmCompletionWorkflowDTO = New-DTOTemplateObject -DTOTagName "ComponentCompletionWorkflowDefinition"

#Set appropriate properties
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.name = "My VM Completion workflow"
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.global = $false

#Retrieve a published service and get one of its components
#This assumes that you have a published service with the name 'AutomatedTest_SC_VM'
$ps = Get-PublishedServiceByName -name "AutomatedTest_SC_VM"
$psComponentId = $ps.PublishedService.serviceComponents[0].id

#Create a object to represent this component
$objectRepresentationDTO = New-DTOTemplateObject -DTOTagName "ObjectRepresentation"
$objectRepresentationDTO.ObjectRepresentation.originalObjectId = $psComponentId

#Clear out all the component representations; add the one we just created
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.ComponentRepresentations = @()
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.ComponentRepresentations += $objectRepresentationDTO.ObjectRepresentation

#Create a custom attribute DTO
$attributeDTO = New-DTOTemplateObject -DTOTagName "CustomAttribute"
$attributeDTO.CustomAttribute.allowedValues = @() #not important
$attributeDTO.CustomAttribute.description = $null #not important
$attributeDTO.CustomAttribute.targetManagedObjectTypes = @() #not important
$attributeDTO.CustomAttribute.name= "SLA"
$attributeDTO.CustomAttribute.value = "Gold"

#Create a custom attribute step
$caStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowCustomAttributeStepDefinition"
$caStepDefinitionDTO.WorkflowCustomAttributeStepDefinition.name="Set Custom Attribute"
$caStepDefinitionDTO.WorkflowCustomAttributeStepDefinition.CustomAttributes = @()
$caStepDefinitionDTO.WorkflowCustomAttributeStepDefinition.CustomAttributes += $attributeDTO.CustomAttribute
$caStepDefinitionDTO.WorkflowCustomAttributeStepDefinition.stepNumber = 0

#Create an expiry date step definition
$expiryDateStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowExpiryDateStepDefinition"
$expiryDateStepDefinitionDTO.WorkflowExpiryDateStepDefinition.name="My Set Expiry Date Step"
$expiryDateStepDefinitionDTO.WorkflowExpiryDateStepDefinition.expiryDate = "2014/10/28"
$expiryDateStepDefinitionDTO.WorkflowExpiryDateStepDefinition.stepNumber = 1

#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.id = -1
$userDTO.OwnerInfo.loginId = "superuser"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null

#Create ownership step definition
$ownershipStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowOwnershipStepDefinition"
$ownershipStepDefinitionDTO.WorkflowOwnershipStepDefinition.name="My Ownership Step"
$ownershipStepDefinitionDTO.WorkflowOwnershipStepDefinition.Owners = @()
$ownershipStepDefinitionDTO.WorkflowOwnershipStepDefinition.Owners += $userDTO.OwnerInfo
$ownershipStepDefinitionDTO.WorkflowOwnershipStepDefinition.stepNumber = 2

#Create an acknowledgement step definition
$ackStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowAcknowledgementStepDefinition"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.name="To X for acknowledgement"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.addresses="x@example.com"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.emailBody = "This is the content of the ack email body"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.emailSubject = "This is the subject of the ack email"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.stepNumber = 3

#Create a Puppet step definition
$puppetStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowPuppetStepDefinition"
$puppetStepDefinitionDTO.WorkflowPuppetStepDefinition.name="Configure Puppet"
$puppetStepDefinitionDTO.WorkflowPuppetStepDefinition.action="CREATE"
$puppetStepDefinitionDTO.WorkflowPuppetStepDefinition.node="node"
Add-Member -InputObject $puppetStepDefinitionDTO.WorkflowPuppetStepDefinition -MemberType NoteProperty -Name "classes" -Value "class1, class2" -Force
Add-Member -InputObject $puppetStepDefinitionDTO.WorkflowPuppetStepDefinition -MemberType NoteProperty -Name "groups" -Value "group1, group2" -Force

#Create a Customize VM step definition
$customizeStepDefinition = New-DTOTemplateObject -DTOTagName "WorkflowCustomizeVMStepDefinition"
$customizeStepDefinition.WorkflowCustomizeVMStepDefinition.name = "Customize Linux VM"
$customizeStepDefinition.WorkflowCustomizeVMStepDefinition.customizationSpecName = "Linux"

$guestCreds = Get-VCmdrCredentialsByType -type "GUEST"
$guestOSCredId = $guestCreds.CredentialCollection.Credentials[0].id

#Create 'Upload Files' step
$copyUploadedFileDTO = New-DTOTemplateObject -DTOTagName "WorkflowCopyUploadedFilesStepDefinition"
$copyUploadedFileDTO.WorkflowCopyUploadedFilesStepDefinition.guestOSCredential.displayName = $null #doesn't matter
$copyUploadedFileDTO.WorkflowCopyUploadedFilesStepDefinition.guestOSCredential.id = $guestOSCredId
$copyUploadedFileDTO.WorkflowCopyUploadedFilesStepDefinition.destinationDir="C:\\temp3"
$copyUploadedFileDTO.WorkflowCopyUploadedFilesStepDefinition.uploadFormLabel="Answer Files3"
$copyUploadedFileDTO.WorkflowCopyUploadedFilesStepDefinition.overwrite = $false
$copyUploadedFileDTO.WorkflowCopyUploadedFilesStepDefinition.deleteFilesAfterUpload = $false


#Create a Run in Guest OS step definition
$remoteCommandStepDefinition = New-DTOTemplateObject -DTOTagName "WorkflowRemoteCommandStepDefinition"
$remoteCommandStepDefinition.WorkflowRemoteCommandStepDefinition.name = "Run command"
$remoteCommandStepDefinition.WorkflowRemoteCommandStepDefinition.captureOutput = $true
$remoteCommandStepDefinition.WorkflowRemoteCommandStepDefinition.commandLine = "C:\Windows\System32\cmd.exe dir"
$remoteCommandStepDefinition.WorkflowRemoteCommandStepDefinition.guestOSCredential.displayName = $null #doesn't matter
$remoteCommandStepDefinition.WorkflowRemoteCommandStepDefinition.guestOSCredential.id = $guestOSCredId

#Create a Set Virtual Network step definition
$configVirtualNetworkStepDefinition = New-DTOTemplateObject -DTOTagName "WorkflowVirtualNetworkStepDefinition"
Add-Member -InputObject $configVirtualNetworkStepDefinition.WorkflowVirtualNetworkStepDefinition -MemberType NoteProperty -Name "action" -Value "EXISTING" -Force
Add-Member -InputObject $configVirtualNetworkStepDefinition.WorkflowVirtualNetworkStepDefinition -MemberType NoteProperty -Name "adapter" -Value 1 -Force
Add-Member -InputObject $configVirtualNetworkStepDefinition.WorkflowVirtualNetworkStepDefinition -MemberType NoteProperty -Name "source" -Value "ZONE" -Force
Add-Member -InputObject $configVirtualNetworkStepDefinition.WorkflowVirtualNetworkStepDefinition -MemberType NoteProperty -Name "networkZoneName" -Value "DMZ" -Force
Add-Member -InputObject $configVirtualNetworkStepDefinition.WorkflowVirtualNetworkStepDefinition -MemberType NoteProperty -Name "connectAtPowerOn" -Value $true -Force
Add-Member -InputObject $configVirtualNetworkStepDefinition.WorkflowVirtualNetworkStepDefinition -MemberType NoteProperty -Name "connected" -Value $true -Force

#Create a Migrate step definition
$migrateStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowMigrateStepDefinition"
$migrateStepDefinitionDTO.WorkflowMigrateStepDefinition.name="Migrate VM"
$migrateStepDefinitionDTO.WorkflowMigrateStepDefinition.destinationName="Deployment Default #1"

#Create an Run SSH Command step definition
$sshCommandStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowSSHCommandStepDefinition"
$sshCommandStepDefinitionDTO.WorkflowSSHCommandStepDefinition.name="Execute SSH Command"
$sshCommandStepDefinitionDTO.WorkflowSSHCommandStepDefinition.timeoutInSeconds = 300
$sshCommandStepDefinitionDTO.WorkflowSSHCommandStepDefinition.proceedToNextStepOnFailure = $true
$sshCommandStepDefinitionDTO.WorkflowSSHCommandStepDefinition.captureOutput = $true
$sshCommandStepDefinitionDTO.WorkflowSSHCommandStepDefinition.commandLine = "ls"
$sshCommandStepDefinitionDTO.WorkflowSSHCommandStepDefinition.hostname = "example-host"
$sshCommandStepDefinitionDTO.WorkflowSSHCommandStepDefinition.guestOSCredential.displayName = $null #doesn't matter
$sshCommandStepDefinitionDTO.WorkflowSSHCommandStepDefinition.guestOSCredential.id = $guestOSCredId

#Clear the existing step(s); add new ones we just created
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.Steps = @()
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.Steps += $caStepDefinitionDTO.WorkflowCustomAttributeStepDefinition
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.Steps += $expiryDateStepDefinitionDTO.WorkflowExpiryDateStepDefinition
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.Steps += $ownershipStepDefinitionDTO.WorkflowOwnershipStepDefinition
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.Steps += $ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.Steps += $puppetStepDefinitionDTO.WorkflowPuppetStepDefinition
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.Steps += $customizeStepDefinition.WorkflowCustomizeVMStepDefinition
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.Steps += $configVirtualNetworkStepDefinition.WorkflowVirtualNetworkStepDefinition
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.Steps += $remoteCommandStepDefinition.WorkflowRemoteCommandStepDefinition
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.Steps += $migrateStepDefinitionDTO.WorkflowMigrateStepDefinition
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.Steps += $sshCommandStepDefinitionDTO.WorkflowSSHCommandStepDefinition
$vmCompletionWorkflowDTO.ComponentCompletionWorkflowDefinition.Steps += $copyUploadedFileDTO.WorkflowCopyUploadedFilesStepDefinition


#Submit the DTO for creation
$createdDefinition = New-WorkflowDefinition -definitionDto $vmCompletionWorkflowDTO

#If you don't assign any users or organizations, then the definition is global

.EXAMPLE
Create a published service completion workflow

#Create a DTO
$psCompletionWorkflowDTO = New-DTOTemplateObject -DTOTagName "PSServiceCompletionWorkflowDefinition"
$psCompletionWorkflowDTO.PSServiceCompletionWorkflowDefinition.name = "PS Completion workflow"
$psCompletionWorkflowDTO.PSServiceCompletionWorkflowDefinition.global = $false

#Retrieve a published service
#This assumes that you have a published service with the name 'AutomatedTest_SC_VM'
$ps = Get-PublishedServiceByName -name "AutomatedTest_SC_VM"

#Create an object to represent this component
$serviceReferenceDTO = New-DTOTemplateObject -DTOTagName "ManagedObjectReference"
$serviceReferenceDTO.ManagedObjectReference.id = $ps.PublishedService.id
$serviceReferenceDTO.ManagedObjectReference.type = "PUBLISHED_SERVICE"

#Clear out all the services; add the one we just created
$psCompletionWorkflowDTO.PSServiceCompletionWorkflowDefinition.Services = @()
$psCompletionWorkflowDTO.PSServiceCompletionWorkflowDefinition.Services += $serviceReferenceDTO.ManagedObjectReference

#Create an acknowledgement step definition
$ackStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowAcknowledgementStepDefinition"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.name="To Y for acknowledgement"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.addresses="Y@example.com"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.emailBody = "This is the content of the ack email body"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.emailSubject = "This is the subject of the ack email"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.stepNumber = 0

#Clear the existing step(s); add new ones we just created
$psCompletionWorkflowDTO.PSServiceCompletionWorkflowDefinition.Steps = @()
$psCompletionWorkflowDTO.PSServiceCompletionWorkflowDefinition.Steps += $ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition

#Submit the DTO for creation
$createdDefinition = New-WorkflowDefinition -definitionDto $psCompletionWorkflowDTO


.EXAMPLE

Create a command (VM) workflow

#Create a DTO
$vmWorkflowDTO = New-DTOTemplateObject -DTOTagName "ServiceWorkflowDefinition"

#Set appropriate properties
$vmWorkflowDTO.ServiceWorkflowDefinition.name = "MY VM Workflow #2"
$vmWorkflowDTO.ServiceWorkflowDefinition.global = $false
$vmWorkflowDTO.ServiceWorkflowDefinition.promptOnRun = $true
$vmWorkflowDTO.ServiceWorkflowDefinition.workflowAsCommand = $true

#Create an email step
$emailStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowEmailStepDefinition"
$emailStepDefinitionDTO.WorkflowEmailStepDefinition.name="Send Email To ABC"
$emailStepDefinitionDTO.WorkflowEmailStepDefinition.addresses="bob@example.com;sally@example.com"
$emailStepDefinitionDTO.WorkflowEmailStepDefinition.emailBody = "This is email body"
$emailStepDefinitionDTO.WorkflowEmailStepDefinition.emailSubject = "This is email subject"
$emailStepDefinitionDTO.WorkflowEmailStepDefinition.includeRequestDetails = $true
$emailStepDefinitionDTO.WorkflowEmailStepDefinition.stepNumber = 0

#Clear the existing step(s); add new ones we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Steps = @()
$vmWorkflowDTO.ServiceWorkflowDefinition.Steps += $emailStepDefinitionDTO.WorkflowEmailStepDefinition

#Retrieve the organization by name [This will fail if you don't have an organization with the name 'Org 1']
$org = Get-OrganizationByName -name "Org 1"

#Create a reference to this organization. We really just need the ID and type.
$orgReferenceDTO = New-DTOTemplateObject -DTOTagName "ManagedObjectReference"
$orgReferenceDTO.ManagedObjectReference.id = $org.Organization.id
$orgReferenceDTO.ManagedObjectReference.type = "ORGANIZATION"

#Clear out organization(s); add new one we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Organizations =@()
$vmWorkflowDTO.ServiceWorkflowDefinition.Organizations += $orgReferenceDTO.ManagedObjectReference

#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.id = -1
$userDTO.OwnerInfo.loginId = "superuser"
$userDTO.OwnerInfo.itContact = $false
$userDTO.OwnerInfo.primary = $false
$userDTO.OwnerInfo.email = $null
$userDTO.OwnerInfo.displayName = $null

#Clear out user(s); add new one we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Users = @()
$vmWorkflowDTO.ServiceWorkflowDefinition.Users += $userDTO.OwnerInfo

#Submit the DTO for creation
$createdDefinition = New-WorkflowDefinition -definitionDto $vmWorkflowDTO

#If you don't assign any users or organizations, then the definition is global


.EXAMPLE
Create a command workflow with Join Domain & Configure Networking steps (Assign IP from vCommander IP Pool)

#Create a DTO
$vmWorkflowDTO = New-DTOTemplateObject -DTOTagName "ServiceWorkflowDefinition"

#Set appropriate properties
$vmWorkflowDTO.ServiceWorkflowDefinition.name = "Wrkflow with Join Domain and Configure Networking"
$vmWorkflowDTO.ServiceWorkflowDefinition.global = $true
$vmWorkflowDTO.ServiceWorkflowDefinition.promptOnRun = $true
$vmWorkflowDTO.ServiceWorkflowDefinition.workflowAsCommand = $true

#Retrieve credentials
$creds = Get-VCmdrCredentialsByType -type "SYSTEM"
$domainCredId = $creds.CredentialCollection.Credentials[-1].id

$guestCreds = Get-VCmdrCredentialsByType -type "GUEST"
$guestOSCredId = $guestCreds.CredentialCollection.Credentials[-1].id

#Create a Join Domain step
$joinDomainDto = New-DTOTemplateObject -DTOTagName "WorkflowJoinDomainStepDefinition"

$joinDomainDto.WorkflowJoinDomainStepDefinition.domainCredential.displayName = $null #doesn't matter
$joinDomainDto.WorkflowJoinDomainStepDefinition.domainCredential.id = $domainCredId
$joinDomainDto.WorkflowJoinDomainStepDefinition.guestOSCredential.displayName = $null #doesn't matter
$joinDomainDto.WorkflowJoinDomainStepDefinition.guestOSCredential.id = $guestOSCredId
$joinDomainDto.WorkflowJoinDomainStepDefinition.domainName ="example.com"
$joinDomainDto.WorkflowJoinDomainStepDefinition.name = "Join Domain Step"
$joinDomainDto.WorkflowJoinDomainStepDefinition.restartGuestOS = $true

#Create a Configure Networking step
$configureNetworkDto = New-DTOTemplateObject -DTOTagName "WorkflowConfigureNetworkStepDefinition"
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.name = "Configure Network"
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.adapter = "NIC 3"
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.guestOSCredential.displayName = $null #doesn't matter
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.guestOSCredential.id = $guestOSCredId
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.ipSourceType = "VCOMMANDER_IP_POOLS"

#Clear the existing step(s); add new ones we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Steps = @()
$vmWorkflowDTO.ServiceWorkflowDefinition.Steps += $joinDomainDto.WorkflowJoinDomainStepDefinition
$vmWorkflowDTO.ServiceWorkflowDefinition.Steps += $configureNetworkDto.WorkflowConfigureNetworkStepDefinition

#Clear out organization(s); add new one we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Organizations =@()
#Clear out user(s); add new one we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Users = @()

#Submit the DTO for creation
$createdDefinition = New-WorkflowDefinition -definitionDto $vmWorkflowDTO

.EXAMPLE
Create a command workflow with Configure Networking steps (Assign IP from Approver comments)

#Create a DTO
$vmWorkflowDTO = New-DTOTemplateObject -DTOTagName "ServiceWorkflowDefinition"

#Set appropriate properties
$vmWorkflowDTO.ServiceWorkflowDefinition.name = "Workflow with Join Domain and Configure Networking"
$vmWorkflowDTO.ServiceWorkflowDefinition.global = $true
$vmWorkflowDTO.ServiceWorkflowDefinition.promptOnRun = $true
$vmWorkflowDTO.ServiceWorkflowDefinition.workflowAsCommand = $true

$guestCreds = Get-VCmdrCredentialsByType -type "GUEST"
$guestOSCredId = $guestCreds.CredentialCollection.Credentials[-1].id

#Create a Configure Networking step
$configureNetworkDto = New-DTOTemplateObject -DTOTagName "WorkflowConfigureNetworkStepDefinition"
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.name = "Configure Network"
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.guestOSCredential.displayName = $null #doesn't matter
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.guestOSCredential.id = $guestOSCredId
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.ipSourceType = "APPROVER_COMMENTS"

Add-Member -InputObject $configureNetworkDto.WorkflowConfigureNetworkStepDefinition -MemberType NoteProperty -Name "subnetMask" -Value "255.255.255.0" -Force
Add-Member -InputObject $configureNetworkDto.WorkflowConfigureNetworkStepDefinition -MemberType NoteProperty -Name "gateway" -Value "10.10.10.100" -Force
Add-Member -InputObject $configureNetworkDto.WorkflowConfigureNetworkStepDefinition -MemberType NoteProperty -Name "primaryDns" -Value "10.10.10.10" -Force
Add-Member -InputObject $configureNetworkDto.WorkflowConfigureNetworkStepDefinition -MemberType NoteProperty -Name "secondaryDns" -Value "10.10.10.11" -Force

#Clear the existing step(s); add new ones we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Steps = @()
$vmWorkflowDTO.ServiceWorkflowDefinition.Steps += $configureNetworkDto.WorkflowConfigureNetworkStepDefinition

#Clear out organization(s); add new one we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Organizations =@()
#Clear out user(s); add new one we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Users = @()

#Submit the DTO for creation
$createdDefinition = New-WorkflowDefinition -definitionDto $vmWorkflowDTO


.EXAMPLE
Create a command workflow with Configure Networking steps (Assign IP from BlueCat IPAM)

#Create a DTO
$vmWorkflowDTO = New-DTOTemplateObject -DTOTagName "ServiceWorkflowDefinition"

#Set appropriate properties
$vmWorkflowDTO.ServiceWorkflowDefinition.name = "Wrkflow with Join Domain and Configure Networking"
$vmWorkflowDTO.ServiceWorkflowDefinition.global = $true
$vmWorkflowDTO.ServiceWorkflowDefinition.promptOnRun = $true
$vmWorkflowDTO.ServiceWorkflowDefinition.workflowAsCommand = $true

$guestCreds = Get-VCmdrCredentialsByType -type "GUEST"
$guestOSCredId = $guestCreds.CredentialCollection.Credentials[-1].id

#Create a Configure Networking step
$configureNetworkDto = New-DTOTemplateObject -DTOTagName "WorkflowConfigureNetworkStepDefinition"
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.name = "Configure Network"
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.adapter = "NIC 5"
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.guestOSCredential.displayName = $null #doesn't matter
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.guestOSCredential.id = $guestOSCredId
$configureNetworkDto.WorkflowConfigureNetworkStepDefinition.ipSourceType = "BLUE_CAT"

Add-Member -InputObject $configureNetworkDto.WorkflowConfigureNetworkStepDefinition -MemberType NoteProperty -Name "subnetMask" -Value "255.255.255.0" -Force
Add-Member -InputObject $configureNetworkDto.WorkflowConfigureNetworkStepDefinition -MemberType NoteProperty -Name "gateway" -Value "10.10.10.100" -Force
Add-Member -InputObject $configureNetworkDto.WorkflowConfigureNetworkStepDefinition -MemberType NoteProperty -Name "primaryDns" -Value "10.10.10.10" -Force
Add-Member -InputObject $configureNetworkDto.WorkflowConfigureNetworkStepDefinition -MemberType NoteProperty -Name "secondaryDns" -Value "10.10.10.11" -Force
Add-Member -InputObject $configureNetworkDto.WorkflowConfigureNetworkStepDefinition -MemberType NoteProperty -Name "domainName" -Value "embotics" -Force
Add-Member -InputObject $configureNetworkDto.WorkflowConfigureNetworkStepDefinition -MemberType NoteProperty -Name "view" -Value "view" -Force
Add-Member -InputObject $configureNetworkDto.WorkflowConfigureNetworkStepDefinition -MemberType NoteProperty -Name "block" -Value "10.10.10.4/16" -Force
Add-Member -InputObject $configureNetworkDto.WorkflowConfigureNetworkStepDefinition -MemberType NoteProperty -Name "network" -Value "devNetwork" -Force
Add-Member -InputObject $configureNetworkDto.WorkflowConfigureNetworkStepDefinition -MemberType NoteProperty -Name "recordType" -Value "Static DNS" -Force #or DHCP

#Clear the existing step(s); add new ones we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Steps = @()
$vmWorkflowDTO.ServiceWorkflowDefinition.Steps += $configureNetworkDto.WorkflowConfigureNetworkStepDefinition

#Clear out organization(s); add new one we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Organizations =@()
#Clear out user(s); add new one we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Users = @()

#Submit the DTO for creation
$createdDefinition = New-WorkflowDefinition -definitionDto $vmWorkflowDTO

.EXAMPLE
Create a command workflow with a Decommission Networking step

#Create a DTO
$vmWorkflowDTO = New-DTOTemplateObject -DTOTagName "ServiceWorkflowDefinition"

#Set appropriate properties
$vmWorkflowDTO.ServiceWorkflowDefinition.name = "Workflow with Decommission Networking step"
$vmWorkflowDTO.ServiceWorkflowDefinition.global = $true
$vmWorkflowDTO.ServiceWorkflowDefinition.promptOnRun = $true
$vmWorkflowDTO.ServiceWorkflowDefinition.workflowAsCommand = $true

#Create a Decommission Networking step
$decomissionGuestNetworkDto = New-DTOTemplateObject -DTOTagName "WorkflowDecomissionGuestNetworkStepDefinition"
$decomissionGuestNetworkDto.WorkflowDecomissionGuestNetworkStepDefinition.name = "Decommission Networking"
$decomissionGuestNetworkDto.WorkflowDecomissionGuestNetworkStepDefinition.ipSource = "BLUE_CAT"

#Clear the existing step(s); add new ones we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Steps = @()
$vmWorkflowDTO.ServiceWorkflowDefinition.Steps += $decomissionGuestNetworkDto.WorkflowDecomissionGuestNetworkStepDefinition

#Clear out organization(s); add new one we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Organizations =@()
#Clear out user(s); add new one we just created
$vmWorkflowDTO.ServiceWorkflowDefinition.Users = @()

#Submit the DTO for creation
$createdDefinition = New-WorkflowDefinition -definitionDto $vmWorkflowDTO

.EXAMPLE
Create a completion workflow for a virtual service component

#Create a DTO
$vAppWorkflowDTO = New-DTOTemplateObject -DTOTagName "vAppCompletionWorkflowDefinition"

#Set appropriate properties
$vAppWorkflowDTO.vAppCompletionWorkflowDefinition.name = "My vApp Completion workflow"
$vAppWorkflowDTO.vAppCompletionWorkflowDefinition.global = $false

#Retrieve a published service and get one of its components
#This assumes that you have a published service with the name 'AutomatedTest_SC_vApp-001'
$ps = Get-PublishedServiceByName -name "AutomatedTest_SC_vApp-001"
$psComponentId = $ps.PublishedService.serviceComponents[0].id

#Create an object to represent this component
$objectRepresentationDTO = New-DTOTemplateObject -DTOTagName "ObjectRepresentation"
$objectRepresentationDTO.ObjectRepresentation.originalObjectId = $psComponentId

#Clear out all the component representations; add the one we just created
$vAppWorkflowDTO.vAppCompletionWorkflowDefinition.ComponentRepresentations = @()
$vAppWorkflowDTO.vAppCompletionWorkflowDefinition.ComponentRepresentations += $objectRepresentationDTO.ObjectRepresentation

#Create an acknowledgement step definition
$ackStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowAcknowledgementStepDefinition"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.name="To X for acknowledgement"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.addresses="x@example.com"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.emailBody = "This is the content of the ack email body"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.emailSubject = "This is the subject of the ack email"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.stepNumber = 3

#Clear the existing step(s); add new ones we just created
$vAppWorkflowDTO.vAppCompletionWorkflowDefinition.Steps = @()
$vAppWorkflowDTO.vAppCompletionWorkflowDefinition.Steps += $ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition

#Submit the DTO for creation
$createdDefinition = New-WorkflowDefinition -definitionDto $vAppWorkflowDTO

.EXAMPLE
Create a global Shared VM Completion workflow.

#Create a DTO
$sharedVMCompletionWorkflowDTO = New-DTOTemplateObject -DTOTagName "SharedVMCompletionWorkflowDefinition"

#Set appropriate properties
$sharedVMCompletionWorkflowDTO.SharedVMCompletionWorkflowDefinition.name = "My Shared VM Completion workflow"
$sharedVMCompletionWorkflowDTO.SharedVMCompletionWorkflowDefinition.global = $true

#Create an Email step definition
$emailStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowEmailStepDefinition"

#Clear the existing step(s); add new ones we just created
$sharedVMCompletionWorkflowDTO.SharedVMCompletionWorkflowDefinition.Steps = @()
$sharedVMCompletionWorkflowDTO.SharedVMCompletionWorkflowDefinition.Steps += $emailStepDefinitionDTO.WorkflowEmailStepDefinition

#Submit the DTO for creation
$createdDefinition = New-WorkflowDefinition -definitionDto $sharedVMCompletionWorkflowDTO

.EXAMPLE
Create a completion workflow for a custom component

#Create a DTO
$unmanagedWorkflowDTO = New-DTOTemplateObject -DTOTagName "UnmanagedCompletionWorkflowDefinition"

#Set appropriate properties
$unmanagedWorkflowDTO.UnmanagedCompletionWorkflowDefinition.name = "My unmanaged completion workflow"
$unmanagedWorkflowDTO.UnmanagedCompletionWorkflowDefinition.global = $false

#Retrieve a published service and get one of its components
#This assumes that you have a published service with the name 'OfficeEquipment'
$ps = Get-PublishedServiceByName -name "OfficeEquipment"
$psComponentId = $ps.PublishedService.serviceComponents[0].id

#Create an object to represent this component
$objectRepresentationDTO = New-DTOTemplateObject -DTOTagName "ObjectRepresentation"
$objectRepresentationDTO.ObjectRepresentation.originalObjectId = $psComponentId

#Clear out all the component representations; add the one we just created
$unmanagedWorkflowDTO.UnmanagedCompletionWorkflowDefinition.ComponentRepresentations = @()
$unmanagedWorkflowDTO.UnmanagedCompletionWorkflowDefinition.ComponentRepresentations += $objectRepresentationDTO.ObjectRepresentation

#Create an acknowledgement step
$ackStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowAcknowledgementStepDefinition"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.name="To X for acknowledgement"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.addresses="x@example.com"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.emailBody = "This is the content of the ack email body"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.emailSubject = "This is the subject of the ack email"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.stepNumber = 3

#Clear the existing step(s); add new ones we just created
$unmanagedWorkflowDTO.UnmanagedCompletionWorkflowDefinition.Steps = @()
$unmanagedWorkflowDTO.UnmanagedCompletionWorkflowDefinition.Steps += $ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition

#Submit the DTO for creation
$createdDefinition = New-WorkflowDefinition -definitionDto $unmanagedWorkflowDTO


.EXAMPLE
Create a completion workflow for change requests

#Create a DTO
$changeRequestCompletionDTO = New-DTOTemplateObject -DTOTagName "ChangeCompletionWorkflowDefinition"

#Set appropriate properties
$changeRequestCompletionDTO.ChangeCompletionWorkflowDefinition.name = "My change completion workflow"
$changeRequestCompletionDTO.ChangeCompletionWorkflowDefinition.global = $false

#Retrieve a published service and get one of its components
#This assumes that you have a published service with the name 'OfficeEquipment'
$ps = Get-PublishedServiceByName -name "OfficeEquipment"
$psComponentId = $ps.PublishedService.serviceComponents[0].id

$requestForms = Get-RequestFormsOfType -formType "CHANGE_VM"
$formIds = $requestForms.RequestFormCollection.RequestForms | select -ExpandProperty "formId"

#Clear out all the form IDs; add the one we just retrieved
$changeRequestCompletionDTO.ChangeCompletionWorkflowDefinition.FormIds = @($formIds[0])

#Create an acknowledgement step
$ackStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowAcknowledgementStepDefinition"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.name="To X for acknowledgement"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.addresses="x@example.com"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.emailBody = "This is the content of the ack email body"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.emailSubject = "This is the subject of the ack email"
$ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition.stepNumber = 3

#Clear the existing step(s); add new ones we just created
$changeRequestCompletionDTO.ChangeCompletionWorkflowDefinition.Steps = @()
$changeRequestCompletionDTO.ChangeCompletionWorkflowDefinition.Steps += $ackStepDefinitionDTO.WorkflowAcknowledgementStepDefinition

#Submit the DTO for creation
$createdDefinition = New-WorkflowDefinition -definitionDto $changeRequestCompletionDTO
#>

function New-WorkflowDefinition {
    [cmdletbinding()]
    Param(
        $definitionDto = $(Throw "Provide the definition DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/workflows"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $definitionDto
    
    Request-SessionAlive
   
    try{
        $requestResponse  = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
        
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Modify a workflow

.DESCRIPTION
Modify a workflow

.EXAMPLE
Modify an approval workflow for new requests

#Retrieve existing workflows
$definitions = Get-WorkflowDefinitionsByType -workflowType "NEW_VM_APPROVAL"
$defId = $definitions.WorkflowDefinitionCollection.WorkflowDefinitions[0].id

$def = New-DTOTemplateObject -DTOTagName "NewApprovalWorkflowDefinition"
$def.NewApprovalWorkflowDefinition.id = $defId
$def.NewApprovalWorkflowDefinition.name = "Modified Name"
$def.NewApprovalWorkflowDefinition.Users = @()
$def.NewApprovalWorkflowDefinition.Organizations = @()
$def.NewApprovalWorkflowDefinition.global = $true
$def.NewApprovalWorkflowDefinition.addOwnerAsAdmin = $false
$def.NewApprovalWorkflowDefinition.autoDeploy = $false

#Create an acknowledgement step definition
$ackStepDefinitionDTO = New-DTOTemplateObject -DTOTagName "WorkflowApproverStepDefinition"
$ackStepDefinitionDTO.WorkflowApproverStepDefinition.name="Modified step name"
$ackStepDefinitionDTO.WorkflowApproverStepDefinition.addresses="modified@example.com"
$ackStepDefinitionDTO.WorkflowApproverStepDefinition.emailBody = "modified: This is the content of the ack email body"
$ackStepDefinitionDTO.WorkflowApproverStepDefinition.emailSubject = "modified: This is the subject of the ack email"
$ackStepDefinitionDTO.WorkflowApproverStepDefinition.stepNumber = 0

#Clear the existing step(s); add new ones we just created
$def.NewApprovalWorkflowDefinition.Steps = @()
$def.NewApprovalWorkflowDefinition.Steps += $ackStepDefinitionDTO.WorkflowApproverStepDefinition

$updatedDefinition = Update-WorkflowDefinition -definitionId $defId -definitionDto $def
#>

function Update-WorkflowDefinition {
    [cmdletbinding()]
    Param(
        [Long] $definitionId = $(Throw "Provide the workflow definition ID"),
        $definitionDto = $(Throw "Provide the definition DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/workflows/$definitionId"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $definitionDto
    
    Request-SessionAlive
   
    try{
        $requestResponse  = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method PUT -Body $xml_dto -ContentType "application/xml"
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
        
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Links a workflow definition to a published service component

.DESCRIPTION
Links a workflow definition to a published service component

.PARAMETER definitionId
The ID of the workflow definition to link

.PARAMETER componentId
The ID of the published service component to link

.EXAMPLE
Join-WorkflowDefinitionToServiceComponent -definitionId 123 -componentId 456

#>

function Join-WorkflowDefinitionToServiceComponent {
    [cmdletbinding()]
    Param(
        [Long] $definitionId = $(Throw "Provide the workflow definition ID"),
        [Long] $componentId = $(Throw "Provide the service component ID")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/workflows/id/$definitionId/action/link/$componentId"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse  = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -ContentType "application/xml"
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
        
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Export workflow definition(s) to XML

.DESCRIPTION
Export workflow definition(s) to XML

.EXAMPLE
Export-WorkflowDefinitions -outputFilePath "C:\\workflow-definitions.xml"

.EXAMPLE
Export-WorkflowDefinitions -outputFilePath "C:\\workflow-definitions.xml" -type "NEW_VM_POST_DEPLOY" -name "Workflow Definition #1"

#>

function Export-WorkflowDefinitions {
    [cmdletbinding()]
    Param(
        [String] $outputFilePath = $(Throw "Specify the location to export to including the file name (ie. C:\workflow-definitions.xml)"),
        [String] $type,
        [String] $name
    )

    if ($type) {
    
    }
    
    if(  (($type) -and (!$name)) -or ((!$type) -and ($name)) ) {
        throw "Specify both the type and name of workflow definition or leave them out to export all workflow definitions."
    }

    if( ($type) -and ($name) ) {    
        $urlSafeName = [System.Web.HttpUtility]::UrlEncode($name)
        Write-Debug "Before encoding workflow name: $name | After encode: $urlSafeName"
        $fullURI = "$Global:BASE_SERVICE_URI/configurations/workflows/type/$($type)/name/$($urlSafeName)/action/export"
    } else {
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/workflows/action/export"
    }

    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
    
    try{
        $requestResponse  = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -ContentType "application/xml"
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
        
    $xml = $requestResponse.Content
    $xml | Out-File $outputFilePath
    Write-Debug "Workflow definitions exported to $outputFilePath"    
}

<#
.SYNOPSIS
Import workflow definitions from XML

.DESCRIPTION
Import workflow definitions from XML

.EXAMPLE
Import-WorkflowDefinitions -filePath "C:\\workflow-definitions.xml"
#>

function Import-WorkflowDefinitions {
    [cmdletbinding()]
    Param(
        [String] $filePath = $(Throw "Specify the full path to the workflow definitions xml. (ie. C:\workflow-definitions.xml)")
    )

    Write-Debug "Searching for workflow definitions XML at file path: $($filePath)"
    $xml_dto  = Get-Content -Raw $filePath
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/workflows/action/import"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
    
    Write-Debug "Importing workflow definitions..."
    try{
        $requestResponse  = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "text/xml"
    } 
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
        
    Convert-RestResponseOutput $requestResponse.RawContent | Out-Null
    
    Write-Debug "Workflow definitions imported"    
    $true
}

<#
.SYNOPSIS
Retrieve a list of workflow definitions

.DESCRIPTION
Retrieve a list of workflow definitions

.EXAMPLE
$definition = Get-WorkflowDefinitions
#>

function Get-WorkflowDefinitions {
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/workflows"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET -TimeoutSec 2147483647
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Delete a workflow definition

.DESCRIPTION
Delete a workflow definition

.EXAMPLE
#Get the workflow by name and remove it
$wfId = (Get-WorkflowDefinition -name "Example Approval Workflow" -type "NEW_VM_APPROVAL").NewApprovalWorkflowDefinition.id
$result = Remove-WorkflowDefinition -definitionId $wfId
#>

function Remove-WorkflowDefinition {
    [cmdletbinding()]
    Param(
        [Long]$definitionId = $(Throw "Provide the definition iD.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/configurations/workflows/$definitionId";
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method DELETE
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    
    if ($requestResponse.StatusCode -eq 200){
        $true
        return
    }
    
    $false
}

<#
.SYNOPSIS
Retrieve a list of running workflows

.DESCRIPTION
Retrieve a list of running workflows

.EXAMPLE
$workflows = Get-RunningWorkflows -max 30 -offset 30
#>

function Get-RunningWorkflows {
    [cmdletbinding()]
    Param(        
        [Parameter(Mandatory = $false)]
        [Int] $max=20, #Maximum number of workflows to return
        [Int] $offset=0 #First workflow to retrieve in ordered list - use in combination with the max to batch retrieval of running workflows
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/workflows?max=$max&offset=$offset"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a list of running workflows of the specified type

.DESCRIPTION
Retrieve a list of running workflows of the specified type

.EXAMPLE
$workflows = Get-RunningWorkflowsByType -workflowType "NEW_VM_APPROVAL"

# With a maximum of results to return
$workflows = Get-RunningWorkflowsByType -workflowType "NEW_VM_APPROVAL" -max 10 -offset 10
#>

function Get-RunningWorkflowsByType {
    [cmdletbinding()]
    Param(        
        $workflowType = $(Throw "Provide the type of the running workflow."),
        [Int] $max = 20, #Maximum number of workflows to return
        [Int] $offset=0 #First workflow to retrieve in ordered list - use in combination with the max to batch retrieval of running workflows
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/workflows/type/$($workflowType)?max=$max&offset=$offset"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a list of running workflows with the specified status

.DESCRIPTION
Retrieve a list of running workflows with the specified status

.EXAMPLE
$workflows = Get-RunningWorkflowsByStatus -workflowStatus "RUNNING"
$workflows = Get-RunningWorkflowsByStatus -workflowStatus "RUNNING" -max 10 -offset 10
#>

function Get-RunningWorkflowsByStatus {
    [cmdletbinding()]
    Param(        
        $workflowStatus = $(Throw "Provide the status of the running workflow."),
        [Int] $max = 20, #Maximum number of workflows to return
        [Int] $offset=0 #First workflow to retrieve in ordered list - use in combination with the max to batch retrieval of running workflows
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/workflows/status/$($workflowStatus)?max=$max&offset=$offset"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a running workflow by ID

.DESCRIPTION
Retrieve a running workflow by ID

.EXAMPLE
$workflow = Get-RunningWorkflowById -workflowId $workflowId
#>

function Get-RunningWorkflowById {
    [cmdletbinding()]
    Param(        
        [Long] $workflowId = $(Throw "Provide the ID of the running workflow.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/workflows/$workflowId"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve running workflows pending approval from specified approver

.DESCRIPTION
Retrieve running workflows pending approval from specified approver

.EXAMPLE
$workflows = Get-RunningWorkflowsPendingApprovalFrom -emailAddress bob@example.com -max 10 -offset 10
#>

function Get-RunningWorkflowsPendingApprovalFrom {
    [cmdletbinding()]
    Param(        
        [String] $emailAddress = $(Throw "Provide the email address of one approver."),
        [Int]$max = 20, #Maximum number of workflows to return
        [Int] $offset=0 #First workflow to retrieve in ordered list - use in combination with the max to batch retrieval of running workflows
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/workflows/metadata/approvers/$($emailAddress)?max=$max&offset=$offset"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve running workflows assigned to an organization

.DESCRIPTION
Retrieve running workflows assigned to an organization

.EXAMPLE
#Get the running workflows for a specific organization
$org = Get-Organization -name "Default Organization"
$workflows = Get-RunningWorkflowsByOrg -orgId $org.Organization.id -max 10 -offset 10
#>

function Get-RunningWorkflowsByOrg {
    [cmdletbinding()]
    Param(        
        [Long] $orgId = $(Throw "Provide the id of the organization."),
        [Int]$max = 20, #Maximum number of workflows to return
        [Int] $offset=0 #First workflow to retrieve in ordered list - use in combination with the max to batch retrieval of running workflows
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/workflows/metadata/organization/$($orgId)?max=$max&offset=$offset"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve running workflows initiated by specified initiator

.DESCRIPTION
Retrieve running workflows initiated by specified initiator

.EXAMPLE
$workflows = Get-RunningWorkflowsByInitiator -initiatorLoginid bob@example.com -max 10 -offset 10

.EXAMPLE
$workflows = Get-RunningWorkflowsByInitiator -initiatorLoginid "Service Request 151" -max 10 -offset 10

#>

function Get-RunningWorkflowsByInitiator {
    [cmdletbinding()]
    Param(        
        [String] $initiatorLoginid = $(Throw "Provide the login id of the initiator."),
        [Int]$max = 20, #Maximum number of workflows to return
        [Int] $offset=0 #First workflow to retrieve in ordered list - use in combination with the max to batch retrieval of running workflows
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/workflows/metadata/initiators/$($initiatorLoginid)?max=$max&offset=$offset"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieves the steps of a running workflow

.DESCRIPTION
Retrieves the steps of a running workflow

.EXAMPLE
$steps = Get-RunningWorkflowSteps -workflowId 133
#>

function Get-RunningWorkflowSteps {
    [cmdletbinding()]
    Param(        
        [Long] $workflowId = $(Throw "Provide the ID of the running workflow.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/workflows/$workflowId/steps"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieves a specific step of a running workflow

.DESCRIPTION
Retrieves a specific step of a running workflow

.EXAMPLE
$step = Get-RunningWorkflowSteps -workflowId 133 -stepId 44
#>

function Get-RunningWorkflowStepById {
    [cmdletbinding()]
    Param(        
        [Long] $workflowId = $(Throw "Provide the ID of the running workflow."),
        [Long] $stepId = $(Throw "Provide the ID of the running workflow step.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/workflows/$workflowId/steps/$stepId"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieves VM billing records

.DESCRIPTION
Retrieves VM billing records

Specifying time in your local timezone:
$dateTime = Get-Date -Date "2013-10-27"

or

Specifying time in UTC
$dateTime = Get-Date -Date "2013-10-27Z"

.EXAMPLE
$records = Get-BillingRecords -max 50

.EXAMPLE

#Specifying time in UTC
$startDate = Get-Date -Date "2013-09-27Z"
$endDate = Get-Date -Date "2013-10-27Z"

$records = Get-BillingRecords -max 50 -startDate $startDate -endDate $endDate

.EXAMPLE
Get all billing records for VMs with custom attribute "Customer" having a value of "Embotics Inc"

#Specifying time in UTC
$startDate = Get-Date -Date "2013-09-27Z"
$endDate = Get-Date -Date "2013-10-27Z"

$records = Get-BillingRecords -startDate $startDate -endDate $endDate -attributeName "Customer" -attributeValue "Embotics Inc"

.EXAMPLE
Retrive the first billing record having cost allocations

$startDate = Get-Date -Date "2013-09-27Z"
$endDate = Get-Date -Date "2013-10-27Z"
$records = Get-BillingRecords -max 50 -startDate $startDate -endDate $endDate

foreach ($record in $records.BillingRecordCollection.BillingRecords) {
    if ($record.CostAllocationBillingRecords -ne $null) {
        $alloc = $record.CostAllocationBillingRecords
    break
    }
}

$alloc

#>

function Get-BillingRecords {
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory = $false)]
        [Int] $max=100, #Maximum number of records to return
        $startDate = $null,
        $endDate = $null,
        $attributeName = $null,
        $attributeValue = $null,
        $owner_login_id = $null,
        $organizationName = $null
    )
    
    #First, we convert time into UTC, then we make it suitable for URL
    $urlSafeStartDate = "";
    $urlSafeEndDate = "";
    $urlSafeAttributeName = "";
    $urlSafeAttributeValue = "";
    $urlSafeOwnerLoginId = "";
    $urlSafeOrgName = "";
    
    if ($startDate -ne $null) {
        $urlSafeStartDate = Get-Date -Date $startDate.ToUniversalTime() -Format $Global:DATE_FORMAT
    }
    
    if ($endDate -ne $null) {
        $urlSafeEndDate = Get-Date -Date $endDate.ToUniversalTime() -Format $Global:DATE_FORMAT
    }
    
    if ($attributeName -ne $null) {
        $urlSafeAttributeName = [System.Web.HttpUtility]::UrlEncode($attributeName)
        Write-Debug "Before encoding custom attribute: $attributeName | After encode: $urlSafeAttributeName"
    }
    
    if ($attributeValue -ne $null) {
        $urlSafeAttributeValue = [System.Web.HttpUtility]::UrlEncode($attributeValue)
        Write-Debug "Before encoding custom attribute: $attributeValue | After encode: $urlSafeAttributeValue"
    }
    
    if ($owner_login_id -ne $null) {
        $urlSafeOwnerLoginId = [System.Web.HttpUtility]::UrlEncode($owner_login_id)
        Write-Debug "Before encoding owner login id: $owner_login_id | After encode: $urlSafeOwnerLoginId"
    }
    
    if ($organizationName -ne $null) {
        $urlSafeOrgName = [System.Web.HttpUtility]::UrlEncode($organizationName)
        Write-Debug "Before encoding org name: $organizationName | After encode: $urlSafeOrgName"
    }
    
    #It's ok to send in blank string to the server; we will handle it
    $fullURI = "$Global:BASE_SERVICE_URI/billingrecords?max=$max&startdate=$urlSafeStartDate&enddate=$urlSafeEndDate&attribute_name=$urlSafeAttributeName&attribute_value=$urlSafeAttributeValue&owner_login_id=$urlSafeOwnerLoginId&org_name=$urlSafeOrgName"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Remove a VM using its ID

.DESCRIPTION
Remove a VM using its ID

.EXAMPLE
$vm = Get-VMs -vmName "VM 001X001"
$vmId = $vm.VirtualMachineCollection.VirtualMachines | select -ExpandProperty "id"
$result = Remove-VM -Id $vmId
#>

function Remove-VM {
    [cmdletbinding()]
    Param(
        [Long]$Id = $(Throw "Provide the ID of the VM.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$Id";
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method DELETE
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent | Out-Null
}

<#
.SYNOPSIS
Retrieve a VM using its ID

.DESCRIPTION
Retrieve a VM using its ID

.PARAMETER Id
The ID of the VM

.EXAMPLE
$vm = Get-VM -Id 123
#>

function Get-VM {
    [cmdletbinding()]
    Param(
        [Long]$Id = $(Throw "Provide the ID of the VM.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$Id";
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a collection of VMs

.DESCRIPTION
Retrieve a collection of VMs

.EXAMPLE
$vms = Get-VMs -max 10

.EXAMPLE
$vms = Get-VMs -vmName 'VM 0001'

.EXAMPLE
$vms = Get-VMs -orgName 'Default Organization'

.PARAMETER msId
The ID of the management server

.PARAMETER datacenterId
The ID of the datacenter

.PARAMETER hostId
The ID of the host

.PARAMETER vmName
The name of the VM

.PARAMETER orgName
The name of the organization that owns the VM

.PARAMETER primaryOwnerLogin
The login of the primary owner of this VM

.PARAMETER parentId
The parent ID of the VM (that is, the folder ID)

.PARAMETER max
The maximum number of VMs to retrieve

#>

#For remoteid, use the command Get-VMByRemoteId
function Get-VMs {
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory = $false)]
        [Long] $msId=-1, #Provide the ID of the management server
        
        [Parameter(Mandatory = $false)]
        [Long] $datacenterId=-1, # Provide the ID of the datacenter
        
        [Parameter(Mandatory = $false)]
        [Long] $hostId=-1, #Provide the ID of the host
        
        [Parameter(Mandatory = $false)]
        [String] $vmName, #Provide the name of the VM

        [Parameter(Mandatory = $false)]
        [String] $orgName, #Provide the name of the organization that owns the VM

        [Parameter(Mandatory = $false)]
        [String] $primaryOwnerLogin, #Login of the primary owner, will be exact match
        
        [Parameter(Mandatory = $false)]
        [String] $parentId=-1, #Provide the parent ID
        
        [Parameter(Mandatory = $false)]
        [Int] $max=100, #Maximum number of VMs to return

            [Parameter(Mandatory = $false)]
            [Int] $offset=0 #Index of the first element in the collection to return. Use in combination with max to do batched object retrieval
    )
    
    $urlSafeVmName = $vmName
    if ($vmName -ne $null) {
        $urlSafeVmName = [System.Web.HttpUtility]::UrlEncode($vmName)
        Write-Debug "Before encoding vm name: $vmName | After encode: $urlSafeVmName"
    }
    
    $urlSafeOrgName = $orgName
    if ($orgName -ne $null) {
        $urlSafeOrgName = [System.Web.HttpUtility]::UrlEncode($orgName)
        Write-Debug "Before encoding organization name: $orgName | After encode: $urlSafeOrgName"
    }
    $urlSafePrimaryOwnerLogin = $primaryOwnerLogin
    if ($primaryOwnerLogin -ne $null) {
        $urlSafePrimaryOwnerLogin = [System.Web.HttpUtility]::UrlEncode($primaryOwnerLogin)
        Write-Debug "Before encoding organization name: $primaryOwnerLogin | After encode: $urlSafePrimaryOwnerLogin"
    }
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms?msid=$msId&dcid=$datacenterId&hostid=$hostId&vmname=$urlSafeVmName&parentid=$parentId&max=$max&offset=$offset&orgName=$urlSafeOrgName&primaryOwnerLogin=$urlSafePrimaryOwnerLogin"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a collection of VM references

.DESCRIPTION
Retrieve a collection of VM references

.EXAMPLE
$vms = Get-VMReferences -max 10

.EXAMPLE
$vms = Get-VMReferences -vmName 'VM 0001'

.EXAMPLE
$vms = Get-VMReferences -orgName 'Default Organization'

.PARAMETER msId
The ID of the management server

.PARAMETER datacenterId
The ID of the datacenter

.PARAMETER hostId
The ID of the host

.PARAMETER vmName
The name of the VM

.PARAMETER orgName
The name of the organization that owns the VM

.PARAMETER primaryOwnerLogin
The login of the primary owner of this VM

.PARAMETER parentId
The parent ID of the VM (that is, the folder ID)

.PARAMETER max
The maximum number of VMs to retrieve

#>

#For remoteid, use the command Get-VMByRemoteId
function Get-VMReferences {
    param(
        [Parameter(Mandatory = $false)]
        [Long] $msId=-1, #Provide the Id of the management server
        
        [Parameter(Mandatory = $false)]
        [Long] $datacenterId=-1, # Provide the Id of the datacenter
        
        [Parameter(Mandatory = $false)]
        [Long] $hostId=-1, #Provide the Id of the host
        
        [Parameter(Mandatory = $false)]
        [String] $vmName, #Provide the name of the VM
        
        [Parameter(Mandatory = $false)]
        [String] $orgName, #Provide the name of the organization that owns the VM
        
        [Parameter(Mandatory = $false)]
        [String] $primaryOwnerLogin, #Login of the primary owner, will be exact match

        [Parameter(Mandatory = $false)]
        [String] $parentId=-1, #Provide the parent Id
        
        [Parameter(Mandatory = $false)]
        [Int] $max=100, #Maximum number of VMs to return

            [Parameter(Mandatory = $false)]
        [Int] $offset=0 #Index of the first element in the collection to return. Use in combination with max to do batched object retrieval
    )
    
    $urlSafeVmName = $vmName
    if ($vmName -ne $null) {
        $urlSafeVmName = [System.Web.HttpUtility]::UrlEncode($vmName)
        Write-Debug "Before encoding vm name: $vmName | After encode: $urlSafeVmName"
    }
    
    $urlSafeOrgName = $orgName
    if ($orgName -ne $null) {
        $urlSafeOrgName = [System.Web.HttpUtility]::UrlEncode($orgName)
        Write-Debug "Before encoding organization name: $orgName | After encode: $urlSafeOrgName"
    }

    $urlSafePrimaryOwnerLogin = $primaryOwnerLogin
    if ($primaryOwnerLogin -ne $null) {
        $urlSafePrimaryOwnerLogin = [System.Web.HttpUtility]::UrlEncode($primaryOwnerLogin)
        Write-Debug "Before encoding organization name: $primaryOwnerLogin | After encode: $urlSafePrimaryOwnerLogin"
    }
    
    $fullURI = "$Global:BASE_SERVICE_URI/references/vms?msid=$msId&dcid=$datacenterId&hostid=$hostId&vmname=$urlSafeVmName&parentid=$parentId&max=$max&offset=$offset&orgName=$urlSafeOrgName&primaryOwnerLogin=$urlSafePrimaryOwnerLogin"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a VM by remote ID

.DESCRIPTION
Retrieve a VM by remote ID

.EXAMPLE
$vm = Get-VMByRemoteId -vmRemoteId vm-2566
#>

function Get-VMByRemoteId {
    [cmdletbinding()]
    Param(
        [String] $vmRemoteId = $(Throw "Provide the remote ID of the VM.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms?remoteid=$vmRemoteId";
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive

    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a VM by its object handle ID

.DESCRIPTION
Retrieve a VM by its object handle ID

.EXAMPLE
$vm = Get-VMByObjecthandleId -objectHandleId 23424
#>

function Get-VMByObjecthandleId {
    [cmdletbinding()]
    Param(
        [Long] $objectHandleId = $(Throw "Provide the object handle ID of the VM.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/objecthandle/$objectHandleId";
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}


<#
.SYNOPSIS
Remove a Stack using its ID

.DESCRIPTION
Remove a Stack using its ID

.EXAMPLE
$result = Remove-Stack -Id 434
#>

function Remove-Stack {
    [cmdletbinding()]
    Param(
        [Long]$Id = $(Throw "Provide the ID of the Stack.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/stacks/$Id";
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method DELETE
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent | Out-Null
}

<#
.SYNOPSIS
Retrieve a Stack using its ID

.DESCRIPTION
Retrieve a Stack using its ID

.PARAMETER Id
The ID of the Stack

.EXAMPLE
$stack = Get-Stack -Id 123

.EXAMPLE
$stack = Get-Stack -objectHandleId 456

#>

function Get-Stack {
    [cmdletbinding()]
    param(
        [Parameter(Mandatory = $false)]
        [Long] $Id=-1, #Provide the ID of the Stack
        
        [Parameter(Mandatory = $false)]
        [Int] $objectHandleId=0 #Provide the object handle ID of the Stack.
    )
    
    
    if ($Id -ne -1) {
        $fullURI = "$Global:BASE_SERVICE_URI/stacks/$Id";
    } else {    
        $fullURI = "$Global:BASE_SERVICE_URI/stacks/objecthandle/$objectHandleId";
    }

    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a collection of Stack references

.DESCRIPTION
Retrieve a collection of Stack references

.EXAMPLE
$stacks = Get-StackReferences -max 10

.EXAMPLE
$stacks = Get-StackReferences -name 'Stack 001'

.EXAMPLE
$stacks = Get-StackReferences -orgName 'Default Organization'

.PARAMETER msId
The ID of the management server

.PARAMETER regionId
The ID of the region

.PARAMETER name
The name of the Stack

.PARAMETER remoteId
The remote Id (resource Id) of the Stack

.PARAMETER orgName
The name of the organization that owns the Stack

.PARAMETER primaryOwnerLogin
The login Id of the primary owner of the Stack

.PARAMETER max
The maximum number of Stacks to retrieve

.PARAMETER offset
The pagination offset

#>

function Get-StackReferences {
    param(
        [Parameter(Mandatory = $false)]
        [Long] $msId=-1, #Provide the ID of the management server
        
        [Parameter(Mandatory = $false)]
        [Long] $regionId=-1, # The ID of the region
        
        [Parameter(Mandatory = $false)]
        [String] $remoteId, #The remote Id (resource Id) of the Stack
        
        [Parameter(Mandatory = $false)]
        [String] $name, #Provide the name of the Stack
        
        [Parameter(Mandatory = $false)]
        [String] $orgName, #Provide the name of the organization that owns the VM
        
        [Parameter(Mandatory = $false)]
        [String] $primaryOwnerLogin, #Login of the primary owner, will be exact match
        
        [Parameter(Mandatory = $false)]
        [Int] $max=100, #Maximum number of Stacks to return

            [Parameter(Mandatory = $false)]
        [Int] $offset=0 #Index of the first element in the collection to return. Use in combination with max to do batched object retrieval
    )
    
    $urlSafeVmName = $name
    if ($name -ne $null) {
        $urlSafeVmName = [System.Web.HttpUtility]::UrlEncode($name)
        Write-Debug "Before encoding vm name: $name | After encode: $urlSafeVmName"
    }
    
    $urlSafeOrgName = $orgName
    if ($orgName -ne $null) {
        $urlSafeOrgName = [System.Web.HttpUtility]::UrlEncode($orgName)
        Write-Debug "Before encoding organization name: $orgName | After encode: $urlSafeOrgName"
    }

    $urlSafePrimaryOwnerLogin = $primaryOwnerLogin
    if ($primaryOwnerLogin -ne $null) {
        $urlSafePrimaryOwnerLogin = [System.Web.HttpUtility]::UrlEncode($primaryOwnerLogin)
        Write-Debug "Before encoding organization name: $primaryOwnerLogin | After encode: $urlSafePrimaryOwnerLogin"
    }
    
    $urlSafeRemoteId = $remoteId
    if ($remoteId -ne $null) {
        $urlSafeRemoteId = [System.Web.HttpUtility]::UrlEncode($remoteId)
        Write-Debug "Before encoding remote Id: $remoteId | After encode: $urlSafeRemoteId"
    }
    
    $fullURI = "$Global:BASE_SERVICE_URI/references/stacks?msid=$msId&regionId=$regionId&remoteId=$urlSafeRemoteId&stackName=$urlSafeVmName&max=$max&offset=$offset&orgName=$urlSafeOrgName&primaryOwnerLogin=$urlSafePrimaryOwnerLogin"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Apply a custom attribute to a resource

.DESCRIPTION
Apply a custom attribute to a resource

.EXAMPLE

$vm = Get-VMs -vmName "XVM 001"
$Id = $vm.VirtualMachineCollection.VirtualMachines | select -ExpandProperty "Id"

#Create a custom attribute DTO
$attributeDTO = New-DTOTemplateObject -DTOTagName "CustomAttribute"
$attributeDTO.CustomAttribute.allowedValues = @() #not important
$attributeDTO.CustomAttribute.description = $null #not important
$attributeDTO.CustomAttribute.targetManagedObjectTypes = @() #not important
$attributeDTO.CustomAttribute.name= "SLA"

# For previous releases, you may need to add the value property to the custom attribute
#Add-Member -InputObject $sla.CustomAttribute -MemberType NoteProperty -Name "value" -Value "Gold" -Force

$attributeDTO.CustomAttribute.value = "Gold"

$result = Set-Attribute -vmId $Id -customAttributeDTo $attributeDTO



.EXAMPLE

#Create a custom attribute DTO
$attributeDTO = New-DTOTemplateObject -DTOTagName "CustomAttribute"
$attributeDTO.CustomAttribute.allowedValues = @() #not important
$attributeDTO.CustomAttribute.description = $null #not important
$attributeDTO.CustomAttribute.targetManagedObjectTypes = @() #not important
$attributeDTO.CustomAttribute.name= "SLA"

# For previous releases, you may need to add the value property to the custom attribute
#Add-Member -InputObject $sla.CustomAttribute -MemberType NoteProperty -Name "value" -Value "Gold" -Force

$attributeDTO.CustomAttribute.value = "Gold"

$result = Set-Attribute -stackId 213 -customAttributeDTo $attributeDTO

#>

function Set-Attribute {
    [cmdletbinding()]
    Param(
        [Long] $vmId=-1, #Provide the ID of the VM
        [Long] $stackId=-1, #Provide the Id of the Stack
        $customAttributeDTo = $(Throw "Provide the custom attribute DTO.")
    )
    
    if ($vmId -ne -1) {
        $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/applyattribute"
    } else {
        $fullURI = "$Global:BASE_SERVICE_URI/stacks/$stackId/action/applyattribute"
    }

    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $customAttributeDTo
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent | Out-null
    
    $true
}

<#
.SYNOPSIS
Apply compliance data to a VM

.DESCRIPTION
Apply compliance data to a VM

.EXAMPLE

$vm = Get-VMs -vmName "VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

#Create a custom attribute DTO - only name-value pair is needed
$attribute1DTO = New-DTOTemplateObject -DTOTagName "CustomAttribute"
$attribute1DTO.CustomAttribute.allowedValues = @() #not important
$attribute1DTO.CustomAttribute.description = $null #not important
$attribute1DTO.CustomAttribute.targetManagedObjectTypes = @() #not important
$attribute1DTO.CustomAttribute.name= "SLA"
$attribute1DTO.CustomAttribute.value = "Gold"

#Create a custom attribute DTO - only name-value pair is needed
$attribute2DTO = New-DTOTemplateObject -DTOTagName "CustomAttribute"
$attribute2DTO.CustomAttribute.allowedValues = @() #not important
$attribute2DTO.CustomAttribute.description = $null #not important
$attribute2DTO.CustomAttribute.targetManagedObjectTypes = @() #not important
$attribute2DTO.CustomAttribute.name= "Cost Center"
$attribute2DTO.CustomAttribute.value = "Cost Center #1"

$complianceData = New-DTOTemplateObject -DTOTagName "ComplianceData"
$complianceData.ComplianceData.expiryDate = "2014/04/18"
$complianceData.ComplianceData.primaryOwner.loginId = "superuser" #only the loginId is relevant

#Add the attribute
$complianceData.ComplianceData.Attributes = @()
$complianceData.ComplianceData.Attributes += $attribute1DTO.CustomAttribute
$complianceData.ComplianceData.Attributes += $attribute2DTO.CustomAttribute

Set-ComplianceData -vmId $vmId -data $complianceData
#>

function Set-ComplianceData {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM."),
        $data = $(Throw "Provide the compliance data.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/applycompliancedata"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $data
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Apply ownership to a resource

.DESCRIPTION
Apply ownership to a resource

.EXAMPLE
#Apply organization and owners

#Retrieve the VM
$vm = Get-VMs -vmName "VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

#Grab the organization
$org = Get-OrganizationByName -name "Default Organization"

#Create an ownership DTO
$ownershipDto = New-DTOTemplateObject -DTOTagName "Ownership"
$ownershipDto.Ownership.organization.displayName = $org.Organization.name
$ownershipDto.Ownership.organization.id = $org.Organization.id

#Create a user
$user1DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user1DTO.OwnerInfo.id = -1
$user1DTO.OwnerInfo.loginId = "superuser"
$user1DTO.OwnerInfo.itContact = $true
$user1DTO.OwnerInfo.primary = $false
$user1DTO.OwnerInfo.email = $null
$user1DTO.OwnerInfo.displayName = $null

#Create a user
$user2DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user2DTO.OwnerInfo.id = -1
$user2DTO.OwnerInfo.loginId = "manager"
$user2DTO.OwnerInfo.itContact = $false
$user2DTO.OwnerInfo.primary = $true
$user2DTO.OwnerInfo.email = $null
$user2DTO.OwnerInfo.displayName = $null

#Add the user to ownership structure
$ownershipDto.Ownership.Owners = @()
$ownershipDto.Ownership.Owners += $user1DTO.OwnerInfo
$ownershipDto.Ownership.Owners += $user2DTO.OwnerInfo

Set-Ownership -vmId $vmId -dto $ownershipDto


.EXAMPLE

#Apply owners

#Retrieve the VM
$vm = Get-VMs -vmName "VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

#Create an ownership DTO
$org = Get-Organization -name "Default Organization"
$ownershipDto = New-DTOTemplateObject -DTOTagName "Ownership"
$ownershipDto.Ownership.organization.displayName = $org.Organization.name
$ownershipDto.Ownership.organization.id = $org.Organization.id

#Create a user
$user1DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user1DTO.OwnerInfo.id = -1
$user1DTO.OwnerInfo.loginId = "superuser"
$user1DTO.OwnerInfo.itContact = $true
$user1DTO.OwnerInfo.primary = $false
$user1DTO.OwnerInfo.email = $null
$user1DTO.OwnerInfo.displayName = $null

#Create a user
$user2DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user2DTO.OwnerInfo.id = -1
$user2DTO.OwnerInfo.loginId = "manager"
$user2DTO.OwnerInfo.itContact = $false
$user2DTO.OwnerInfo.primary = $true
$user2DTO.OwnerInfo.email = $null
$user2DTO.OwnerInfo.displayName = $null

#Add the user to the ownership structure
$ownershipDto.Ownership.PSObject.Properties.Remove("organization")
$ownershipDto.Ownership.Owners = @()
$ownershipDto.Ownership.Owners += $user1DTO.OwnerInfo
$ownershipDto.Ownership.Owners += $user2DTO.OwnerInfo

Set-Ownership -vmId $vmId -dto $ownershipDto


.EXAMPLE

#Apply owners to Stack

#Create an ownership DTO
$org = Get-Organization -name "Default Organization"
$ownershipDto = New-DTOTemplateObject -DTOTagName "Ownership"
$ownershipDto.Ownership.organization.displayName = $org.Organization.name
$ownershipDto.Ownership.organization.id = $org.Organization.id

#Create a user
$user1DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user1DTO.OwnerInfo.id = -1
$user1DTO.OwnerInfo.loginId = "superuser"
$user1DTO.OwnerInfo.itContact = $true
$user1DTO.OwnerInfo.primary = $false
$user1DTO.OwnerInfo.email = $null
$user1DTO.OwnerInfo.displayName = $null

#Create a user
$user2DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user2DTO.OwnerInfo.id = -1
$user2DTO.OwnerInfo.loginId = "manager"
$user2DTO.OwnerInfo.itContact = $false
$user2DTO.OwnerInfo.primary = $true
$user2DTO.OwnerInfo.email = $null
$user2DTO.OwnerInfo.displayName = $null

#Add the user to the ownership structure
$ownershipDto.Ownership.PSObject.Properties.Remove("organization")
$ownershipDto.Ownership.Owners = @()
$ownershipDto.Ownership.Owners += $user1DTO.OwnerInfo
$ownershipDto.Ownership.Owners += $user2DTO.OwnerInfo

Set-Ownership -stackId 213 -dto $ownershipDto
#>

function Set-Ownership {
    [cmdletbinding()]
    Param(
        [Long] $vmId=-1, #Provide the ID of the VM
        [Long] $stackId=-1, #Provide the Id of the Stack
        $dto = $(Throw "Provide the ownership dto.")
    )

    if ($vmId -ne -1) {
        $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/applyownership"
    } else {
        $fullURI = "$Global:BASE_SERVICE_URI/stacks/$stackId/action/applyownership"
    }

    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $dto
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Apply ownership to a virtual service

.DESCRIPTION
Apply ownership to a virtual service

.EXAMPLE

#Apply organization and owners

$vApp = Get-vApps -vAppName "VirtualService001"
$vAppId = $vApp.VirtualAppCollection.VirtualApps | Select-Object -ExpandProperty "id"
    
#Grab the organization
$org = Get-OrganizationByName -name "Default Organization"
    
#Create an ownership DTO
$ownershipDto = New-DTOTemplateObject -DTOTagName "Ownership"
$ownershipDto.Ownership.organization.displayName = $org.Organization.name
$ownershipDto.Ownership.organization.id = $org.Organization.id
    
#Create a user
$user1DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user1DTO.OwnerInfo.loginId = "bob@example.com"
$user1DTO.OwnerInfo.itContact = $true
$user1DTO.OwnerInfo.primary = $false

    
#Create a user
$user2DTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$user2DTO.OwnerInfo.loginId = "sally@embotics.com"
$user2DTO.OwnerInfo.itContact = $false
$user2DTO.OwnerInfo.primary = $true
    
#Add the user to ownership structure
$ownershipDto.Ownership.Owners = @()
$ownershipDto.Ownership.Owners += $user1DTO.OwnerInfo
$ownershipDto.Ownership.Owners += $user2DTO.OwnerInfo
   
$result = Set-vAppOwnership -vAppId $vAppId -dto $ownershipDto
#>

function Set-vAppOwnership {
    [cmdletbinding()]
    Param(
        [Long] $vAppId = $(Throw "Provide the ID of the vApp."),
        $dto = $(Throw "Provide the ownership dto.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/vapps/$vAppId/action/applyownership"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $dto
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Apply an expiry date to a VM

.DESCRIPTION
Apply an expiry date to a VM

.PARAMETER date
Empty string = No Expiry, 0 = Never Expires, and > 0 = expiry date set (Format is yyyy/mm/dd)

.EXAMPLE
#Set expiry date

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

$date = "2016/02/13"
Set-ExpiryDate -vmId $vmId -date $date

.EXAMPLE
#Expiry date set to Never Expires

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

$date = "0"
Set-ExpiryDate -vmId $vmId -date $date

.EXAMPLE
#Expiry date not set
$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

$date = ""
Set-ExpiryDate -vmId $vmId -date $date
#>

function Set-ExpiryDate {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM."),
        [String] $date = $(Throw "Provide the expiry date string.")
    )
    
    if ($date -eq $null) {
        $date = ""
    }
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/applyexpirydate"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $date -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Reset the number of times that a VM has had its expiry date extended.

.DESCRIPTION
Reset the expiry date extension count for a VM.

.EXAMPLE
#Reset the expiry date extension count for a VM.

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

Reset-ExpiryExtensionCount -vmId $vmId
#>

function Reset-ExpiryExtensionCount {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/resetexpiryextensionsperformed"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $date -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Apply the End of Life state to a VM

.DESCRIPTION
Apply the End of Life state to a VM

.EXAMPLE
#Set end of life: true

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

Set-EndOfLife -vmId $vmId -endOfLIfe $true

.EXAMPLE
#Set end of life: false

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

Set-EndOfLife -vmId $vmId -endOfLIfe $false
#>

function Set-EndOfLife {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM."),
        [Bool]$endOfLife = $(Throw "Provide a boolean value of true or false")
    )
    
    if ($endOfLife -eq $true) {
        $endOfLifeAsString = "true"
    } else {
        $endOfLifeAsString = "false"
    }
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/applyeof"
    Write-Debug "URI is: $fullURI"
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $endOfLifeAsString -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Apply the Suspect state to a VM

.DESCRIPTION
Apply the Suspect state to a VM

.EXAMPLE
#Set suspect: true

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

Set-Suspect -vmId $vmId -suspect $true

.EXAMPLE
#Set suspect: false

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

Set-Suspect -vmId $vmId -suspect $false
#>

function Set-Suspect {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM."),
        [Bool]$suspect = $(Throw "Provide a boolean value of true or false")
    )
    
    if ($suspect -eq $true) {
        $suspectAsString = "true"
    } else {
        $suspectAsString = "false"
    }
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/applysuspect"
    Write-Debug "URI is: $fullURI"
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $suspectAsString -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Apply an approval state to a VM

.DESCRIPTION
Apply an approval state to a VM

.EXAMPLE
#Set approval: true

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

Set-Approval -vmId $vmId -approve $true

.EXAMPLE
#Set approval: false

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

Set-Approval -vmId $vmId -approve $false
#>

function Set-Approval {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM."),
        [Bool]$approve = $(Throw "Provide a boolean value of true or false")
    )
    
    if ($approve -eq $true) {
        $approveAsString = "true"
    } else {
        $approveAsString = "false"
    }
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/applyapproval"
    Write-Debug "URI is: $fullURI"
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $approveAsString -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}


<#
.SYNOPSIS
Apply an expiry group to a resource

.DESCRIPTION
Apply an expiry group to a resource

.EXAMPLE

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

$taskInfo = Set-ExpiryGroup -vmId $vmId -group "Default Expiry Group"


.EXAMPLE

$taskInfo = Set-ExpiryGroup -stackId 213 -group "Default Expiry Group"

#>

function Set-ExpiryGroup {
    [cmdletbinding()]
    Param(
        [Long] $vmId=-1, #Provide the ID of the VM
        [Long] $stackId=-1, #Provide the Id of the Stack
        [String]$group = $(Throw "Provide the group name")
    )
    
    if ($vmId -ne -1) {
        $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/applyexpirygroup"
    } else {
        $fullURI = "$Global:BASE_SERVICE_URI/stacks/$stackId/action/applyexpirygroup"
    }
    Write-Debug "URI is: $fullURI"
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $group -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Apply a guest OS scan group to a VM

.DESCRIPTION
Apply a guest OS scan group to a VM

.EXAMPLE

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

$taskInfo = Set-ScanGroup -vmId $vmId -group "Default Guest OS Scan Group"
#>

function Set-ScanGroup {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM."),
        [String]$group = $(Throw "Provide the group name")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/applyscangroup"
    Write-Debug "URI is: $fullURI"
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $group -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Apply a power schedule group to a VM

.DESCRIPTION
Apply a power schedule group to a VM

.EXAMPLE

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

$taskInfo = Set-PowerScheduleGroup -vmId $vmId -group "Default Power Schedule Group"
#>

function Set-PowerScheduleGroup {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM."),
        [String]$group = $(Throw "Provide the group name")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/applypowergroup"
    Write-Debug "URI is: $fullURI"
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $group -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Apply a maintenance group to a VM

.DESCRIPTION
Apply a maintenance group to a VM

.EXAMPLE

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

$taskInfo = Set-MaintenanceGroup -vmId $vmId -group "Default Maintenance Group"
#>

function Set-MaintenanceGroup {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM."),
        [String]$group = $(Throw "Provide the group name")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/applymaintenancegroup"
    Write-Debug "URI is: $fullURI"
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $group -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Apply a rightsizing group to a VM

.DESCRIPTION
Apply a rightsizing group to a VM

.EXAMPLE

$vm = Get-VMs -vmName "Test- VM 001" -max 1
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

$taskInfo = Set-RightsizingGroup -vmId $vmId -group "Default Rightsizing Group"
#>

function Set-RightsizingGroup {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM."),
        [String]$group = $(Throw "Provide the group name")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/applyrightsizinggroup"
    Write-Debug "URI is: $fullURI"
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $group -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Use the vmshare command to add a VM to the Service Catalog

.DESCRIPTION
Use the vmshare command to add a VM to the Service Catalog for specified users and groups

.PARAMETER vmId
The ID of the VM that is being shared

.PARAMETER shareOptions
A structure for all share options

.EXAMPLE

#Retrieve VM
$vm = Get-VMs -vmName "DevVM - 001"
$vmId = $vm.VirtualMachineCollection.VirtualMachines.id

#Retrieve organization
$org = Get-OrganizationByName "Default Organization"

#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.loginId = "manager"

#Create a ShareOptions DTO
$shareOptions = New-DTOTemplateObject -DTOTagName "ShareOptions"
$shareOptions.ShareOptions.daysUntilExpired = 10
$shareOptions.ShareOptions.emailComments = "Please investigate automated tests failure."
$shareOptions.ShareOptions.emailSubject = "Automated tests failure"
$shareOptions.ShareOptions.keepSourceOwnership = $false
$shareOptions.ShareOptions.notifyRecipients = $true
$shareOptions.ShareOptions.serviceDescription = "VM containing installer and tests."
$shareOptions.ShareOptions.serviceName = "W2K8 Service"

#Use the specified organization
$shareOptions.ShareOptions.sharedOrganization = $org.Organization

#To remove an organization, uncomment this line
#$shareOptions.ShareOptions.PSObject.Properties.Remove("sharedOrganization")

#Use the specified users
$shareOptions.ShareOptions.sharedUsers = @($userDTO.OwnerInfo)

#To remove users, uncomment this line
#$shareOptions.ShareOptions.PSObject.Properties.Remove("sharedUsers")

$shareResults = New-VMShare -vmId $vmId -shareOptions $shareOptions

#>

function New-VMShare {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM."),
        $shareOptions = $(Throw "Provide a share options DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/share"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $shareOptions
   
    Request-SessionAlive
    
    #When sharing a VM with the 'Save memory state' flag, the call could take a long time to finish; we set the timeout to indefinite.
    #According to TechNet documention, the TimeoutSec parameter was supposed to default to indefinite but it's actually defaulted to 100 seconds.
    #Setting TimeoutSec to zero will timeout at 100 seconds so setting to 2147483647.
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $xml_dto -ContentType "application/xml" -TimeoutSec 2147483647
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}