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
}

<#
.SYNOPSIS
Retrieve a collection of virtual services

.DESCRIPTION
Retrieve a collection of virtual services

.EXAMPLE
$vApps = Get-vApps -max 10

.EXAMPLE
$vApps = Get-vApps -vAppName "vApp 001"

.PARAMETER msId
The ID of the management server

.PARAMETER datacenterId
The ID of the datacenter

.PARAMETER hostId
The ID of the host

.PARAMETER vAppName
The name of the vApp

.PARAMETER max
The maximum number of VMs to retrieve
#>

function Get-vApps() {
    [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] $vAppName, #Provide the name of the vApp
        
        [Parameter(Mandatory = $false)]
        [Int] $max=100 #Maximum number of VMs to return
    )

    $urlSafeVappName = $vAppName
    if ($vAppName -ne $null) {
        $urlSafeVappName = [System.Web.HttpUtility]::UrlEncode($vAppName)
        Write-Debug "Before encoding vapp name: $vAppName | After encode: $urlSafeVappName"
    }
    
    $fullURI = "$Global:BASE_SERVICE_URI/vapps?msid=$msId&dcid=$datacenterId&hostid=$hostId&vappname=$urlSafeVappName&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 virtual service by remote ID

.DESCRIPTION
Retrieve a virtual service by remote ID

.EXAMPLE
$vApp = Get-vAppByRemoteId -vAppRemoteId res-2566
#>

function Get-vAppByRemoteId {
    [cmdletbinding()]
    Param(
        [String] $vAppRemoteId = $(Throw "Provide the remote ID of the vApp.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vapps?remoteid=$vAppRemoteId";
    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 service by object handle ID

.DESCRIPTION
Retrieve a virtual service by object handle ID

.EXAMPLE
$vApp = Get-vAppByObjecthandleId -objectHandleId 23424
#>

function Get-vAppByObjecthandleId {
    [cmdletbinding()]
    Param(
        [Long] $objectHandleId = $(Throw "Provide the object handle ID of the vApp")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/vapps/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 virtual service

.DESCRIPTION
Remove a virtual service using its ID

.EXAMPLE
$vapps = Get-vApps -vAppName "ABC100000"
$vappId = $vapps.VirtualAppCollection.VirtualApps | select -ExpandProperty "id"
$result = Remove-vApp -Id $vappId
#>

function Remove-vApp {
    [cmdletbinding()]
    Param(
        [Long]$Id = $(Throw "Provide the ID of the vApp.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vapps/$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
Create a custom component type

.DESCRIPTION
Create a custom component type

.EXAMPLE

#Create custom component type DTO
$customComponentObject = New-DTOTemplateObject -DTOTagName "CustomComponentType"

#Set its properties
$customComponentObject.CustomComponentType.name = "Desk"
$customComponentObject.CustomComponentType.description = "Office Equipment"
$customComponentObject.CustomComponentType.cost = 50.0

$cc = New-CustomComponentType -customComponentTypeDTO $customComponentObject
#>

function New-CustomComponentType() {
    [cmdletbinding()]
    Param (
        $customComponentTypeDTO = $(Throw "Provide the custom component type DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/customcomponenttypes";
    Write-Debug "URI is: $fullURI"

    $xml_dto = Convert-ObjectToXml $customComponentTypeDTO

    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
Remove a custom component type

.DESCRIPTION
Remove a custom component type

.EXAMPLE
#Remove Custom Component by name
$cct = Get-CustomComponentTypes
$ccId = $cct.CustomComponentTypeCollection.CustomComponentTypes | Where-Object {$_.name} | Select -ExpandProperty "id"
Remove-CustomComponentType -customComponentTypeId $ccId

#>

function Remove-CustomComponentType() {
    [cmdletbinding()]
    Param(
        $customComponentTypeId = $(Throw "Provide the ID of the custom component type.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/customcomponenttypes/$customComponentTypeId";
    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
Retrieve all custom component types

.DESCRIPTION
Retrieve all custom component types

.EXAMPLE
$cctypes = Get-CustomComponentTypes
#>

function Get-CustomComponentTypes {
    [cmdletbinding()]
    Param()
    
    $fullURI = "$Global:BASE_SERVICE_URI/customcomponenttypes";
    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 component type by ID

.DESCRIPTION
Retrieve a custom component type by ID

.EXAMPLE
$cctype = Get-CustomcomponentType -customComponentTypeId 3949294
#>

function Get-CustomcomponentType {
    [cmdletbinding()]
    Param(
        [String] $customComponentTypeId = $(Throw "Provide the ID of the custom component type.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/customcomponenttypes/$customComponentTypeId"
    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 service categories

.DESCRIPTION
Retrieve a collection of service categories

.EXAMPLE
$categories = Get-ServiceCategories
#>

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

    $fullURI = "$Global:BASE_SERVICE_URI/servicecategories"
    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 service category by ID

.DESCRIPTION
Retrieve a service category by ID

.EXAMPLE
$category = Get-ServiceCategory -scId 324242
#>

function Get-ServiceCategory {
    [cmdletbinding()]
    Param(
        [Long] $scId = $(Throw "Provide the ID of the service category.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/servicecategories/$scId";
    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 request form

.DESCRIPTION
Create a new request form

.EXAMPLE
Create a VM component form

#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 required by element
$requiredByObject = New-DTOTemplateObject -DTOTagName "RequestFormRequiredByElement"
$requiredByObject.RequestFormRequiredByElement.defaultLeadTime = 30
$requiredByObject.RequestFormRequiredByElement.mandatory = $true
$requiredByObject.RequestFormRequiredByElement.label="Require Date"

#Create an expiry date element
$expiryDate = New-DTOTemplateObject -DTOTagName "RequestFormExpiryDateElement"
$expiryDate.RequestFormExpiryDateElement.label="Expiry"
$expiryDate.RequestFormExpiryDateElement.mandatory = $true
$expiryDate.RequestFormExpiryDateElement.maximumLifeSpanInDays = 100
$expiryDate.RequestFormExpiryDateElement.defaultLifeSpanInDays = 99
$expiryDate.RequestFormExpiryDateElement.allowNeverExpires = $true

#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 a custom attribute
$customAttribute = New-DTOTemplateObject -DTOTagName "RequestFormElement"
$customAttribute.RequestFormElement.label = "SLA"
$customAttribute.RequestFormElement.mandatory = $true
$customAttribute.RequestFormElement.formType = "CUSTOM_ATTRIBUTE"

#Create a destination element
$dest = New-DTOTemplateObject -DTOTagName "RequestFormDestinationElement"
$dest.RequestFormDestinationElement.label = "Dest"
$dest.RequestFormDestinationElement.mandatory = $false
$dest.RequestFormDestinationElement.formType = "DESTINATION"

#Create an instanceType element
$instanceType = New-DTOTemplateObject -DTOTagName "RequestFormElement"
$instanceType.RequestFormElement.label = "Inst Type"
$instanceType.RequestFormElement.mandatory = $false
$instanceType.RequestFormElement.formType = "INSTANCE_TYPE"

#Create a key pair
$keyPair = New-DTOTemplateObject -DTOTagName "RequestFormElement"
$keyPair.RequestFormElement.label = "Key-pair"
$keyPair.RequestFormElement.mandatory = $false
$keyPair.RequestFormElement.formType = "KEY_PAIR"

#Create a text element
$text = New-DTOTemplateObject -DTOTagName "RequestFormElement"
$text.RequestFormElement.label = "My Text"
$text.RequestFormElement.mandatory = $false
$text.RequestFormElement.formType = "TEXT"

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

#Create a VM Name element
$vmName = New-DTOTemplateObject -DTOTagName "RequestFormVMNameElement"
$vmName.RequestFormVMNameElement.mandatory = $true
$vmName.RequestFormVMNameElement.maxChars = 79

#Create a primary owner
$primaryOwner = New-DTOTemplateObject -DTOTagName "RequestFormPrimaryOwnerElement"
$primaryOwner.RequestFormPrimaryOwnerElement.label = "Primary Owner"
$primaryOwner.RequestFormPrimaryOwnerElement.mandatory = $true
$primaryOwner.RequestFormPrimaryOwnerElement.readOnly = $false

#Create a header
$header = New-DTOTemplateObject -DTOTagName "RequestFormHeaderElement"
$header.RequestFormHeaderElement.label = "Hello"
$header.RequestFormHeaderElement.tagName = "H4"

#Create a cost
$cost = New-DTOTemplateObject -DTOTagName "RequestFormEstimatedCostElement"
$cost.RequestFormEstimatedCostElement.label = "Cost"
$cost.RequestFormEstimatedCostElement.costInterval = "MONTHLY"

#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 )

$attributePickList = New-DTOTemplateObject -DTOTagName "RequestFormAttributePickListElement"
$attributePickList.RequestFormAttributePickListElement.label = "Pick your SLA"
$attributePickList.RequestFormAttributePickListElement.attributeName = "SLA"
$attributePickList.RequestFormAttributePickListElement.allowMultiple = $false
$attributePickList.RequestFormAttributePickListElement.mandatory = $true

#Create a request form
$requestFormObject = New-DTOTemplateObject -DTOTagName "RequestForm"
$requestFormObject.RequestForm.menuText="Form #10"
$requestFormObject.RequestForm.requestType="NEW_VM"
$requestFormObject.RequestForm.formComponentType = "VM"

#Add form elements to form
$requestFormObject.RequestForm.RequestFormElements = @()
$requestFormObject.RequestForm.RequestFormElements += $textFieldInputObject.RequestFormTextFieldElement
$requestFormObject.RequestForm.RequestFormElements += $requiredByObject.RequestFormRequiredByElement
$requestFormObject.RequestForm.RequestFormElements += $expiryDate.RequestFormExpiryDateElement
$requestFormObject.RequestForm.RequestFormElements += $memory.RequestFormMemoryElement
$requestFormObject.RequestForm.RequestFormElements += $disk.RequestFormDiskElement
$requestFormObject.RequestForm.RequestFormElements += $customAttribute.RequestFormElement
$requestFormObject.RequestForm.RequestFormElements += $cpu.RequestFormCPUElement
$requestFormObject.RequestForm.RequestFormElements += $vmName.RequestFormVMNameElement
$requestFormObject.RequestForm.RequestFormElements += $primaryOwner.RequestFormPrimaryOwnerElement
$requestFormObject.RequestForm.RequestFormElements += $header.RequestFormHeaderElement
$requestFormObject.RequestForm.RequestFormElements += $cost.RequestFormEstimatedCostElement
$requestFormObject.RequestForm.RequestFormElements += $dest.RequestFormDestinationElement
$requestFormObject.RequestForm.RequestFormElements += $instanceType.RequestFormElement
$requestFormObject.RequestForm.RequestFormElements += $keyPair.RequestFormElement
$requestFormObject.RequestForm.RequestFormElements += $text.RequestFormElement
$requestFormObject.RequestForm.RequestFormElements += $networkElement.RequestFormNetworkElement
$requestFormObject.RequestForm.RequestFormElements += $attributePickList.RequestFormAttributePickListElement

#Create a user; the only information we need is the loginId
$userDTO = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$userDTO.OwnerInfo.loginId = "superuser"

#Add the user to the request form
$requestFormObject.RequestForm.Users = @()
$requestFormObject.RequestForm.Users += $userDTO.OwnerInfo

#Create the form
$createdForm = New-RequestForm -requestFormDto $requestFormObject

.EXAMPLE
Create a virtual service component form

#Create a header
$header = New-DTOTemplateObject -DTOTagName "RequestFormHeaderElement"
$header.RequestFormHeaderElement.label = "Hello"
$header.RequestFormHeaderElement.tagName = "H4"

#Create a request form
$requestFormObject = New-DTOTemplateObject -DTOTagName "RequestForm"
$requestFormObject.RequestForm.menuText="Form #15"
$requestFormObject.RequestForm.requestType="NEW_VM"
$requestFormObject.RequestForm.formComponentType = "VAPP"

#Add elements to form
$requestFormObject.RequestForm.RequestFormElements = @()
$requestFormObject.RequestForm.RequestFormElements += $header.RequestFormHeaderElement

#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

#Add the user to the request form
$requestFormObject.RequestForm.Users = @()
$requestFormObject.RequestForm.Users += $userDTO.OwnerInfo

#Create the form
$createdForm = New-RequestForm -requestFormDto $requestFormObject

.EXAMPLE

Create a form for a custom component

#Create a header
$header = New-DTOTemplateObject -DTOTagName "RequestFormHeaderElement"
$header.RequestFormHeaderElement.label = "Hello"
$header.RequestFormHeaderElement.tagName = "H4"

#Get custom component types
#This assume you have at least one custom component type
$customComponentTypes = Get-CustomComponentTypes
$customComponentType = $customComponentTypes.CustomComponentTypeCollection.CustomComponentTypes[0]

#Create a request form
$requestFormObject = New-DTOTemplateObject -DTOTagName "RequestForm"
$requestFormObject.RequestForm.menuText="Custom component form"
$requestFormObject.RequestForm.requestType="NEW_VM"
$requestFormObject.RequestForm.formComponentType = "UNMANAGED"

Add-Member -InputObject $requestFormObject.RequestForm -MemberType NoteProperty -Name "customComponentType" -Value $customComponentType -Force

#Add form elements to form
$requestFormObject.RequestForm.RequestFormElements = @()
$requestFormObject.RequestForm.RequestFormElements += $header.RequestFormHeaderElement

#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

#Add the user to the request form
$requestFormObject.RequestForm.Users = @()
$requestFormObject.RequestForm.Users += $userDTO.OwnerInfo

#Create the form
$createdForm = New-RequestForm -requestFormDto $requestFormObject

.EXAMPLE
Create a request form for a service

#Create an instance count element
$instanceCount = New-DTOTemplateObject -DTOTagName "RequestFormServiceInstanceCountElement"
$instanceCount.RequestFormServiceInstanceCountElement.label = "Service Instance"
$instanceCount.RequestFormServiceInstanceCountElement.maxCount = 4

#Create a request form
$requestFormObject = New-DTOTemplateObject -DTOTagName "RequestForm"
$requestFormObject.RequestForm.menuText="Form #11"
$requestFormObject.RequestForm.formComponentType = "SERVICE"

#Add elements to form
$requestFormObject.RequestForm.RequestFormElements = @()
$requestFormObject.RequestForm.RequestFormElements += $instanceCount.RequestFormServiceInstanceCountElement

#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

#Add the user to the request form
$requestFormObject.RequestForm.Users = @()
$requestFormObject.RequestForm.Users += $userDTO.OwnerInfo

#Create the form
$createdForm = New-RequestForm -requestFormDto $requestFormObject


.Example
Create a global change request form

#Create a header
$header = New-DTOTemplateObject -DTOTagName "RequestFormHeaderElement"
$header.RequestFormHeaderElement.label = "Hello"
$header.RequestFormHeaderElement.tagName = "H4"

#Create a request form
$requestFormObject = New-DTOTemplateObject -DTOTagName "RequestForm"
$requestFormObject.RequestForm.menuText="Form #13"
$requestFormObject.RequestForm.requestType = "CHANGE_VM"

#Add form elements to form
$requestFormObject.RequestForm.RequestFormElements = @()
$requestFormObject.RequestForm.RequestFormElements += $header.RequestFormHeaderElement

#No user
$requestFormObject.RequestForm.Users = @()

#Create the form
$createdForm = New-RequestForm -requestFormDto $requestFormObject


.Example
Create a change request form for a specific user

#Create a header
$header = New-DTOTemplateObject -DTOTagName "RequestFormHeaderElement"
$header.RequestFormHeaderElement.label = "Hello"
$header.RequestFormHeaderElement.tagName = "H4"

#Create a request form
$requestFormObject = New-DTOTemplateObject -DTOTagName "RequestForm"
$requestFormObject.RequestForm.menuText="Form #14"
$requestFormObject.RequestForm.requestType = "CHANGE_VM"

#Add form elements to form
$requestFormObject.RequestForm.RequestFormElements = @()
$requestFormObject.RequestForm.RequestFormElements += $header.RequestFormHeaderElement

#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

#Add the user to the request form
$requestFormObject.RequestForm.Users = @()
$requestFormObject.RequestForm.Users += $userDTO.OwnerInfo

#Create the form
$createdForm = New-RequestForm -requestFormDto $requestFormObject

#>

function New-RequestForm {
    [cmdletbinding()]
    Param(
        $requestFormDto = $(Throw "Provide the request form DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/forms"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $requestFormDto
    
    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
Remove a request form

.DESCRIPTION
Remove a request form

.EXAMPLE
# Get a form id for the Decommissioning Request and remove it
$forms = Get-RequestForms
$formId = $forms.RequestFormCollection.RequestForms | Where-Object {$_.displayName -eq "Decommissioning Request"} | Select -ExpandProperty "formId"
Removes-RequestForm -formId $formId
#>

function Remove-RequestForm {
    [cmdletbinding()]
    Param(
        $formId = $(Throw "Provide the ID the service form.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/forms/$formId";
    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
Modify a request form

.DESCRIPTION
Modify a request form

.EXAMPLE
#Update a request Form
$rfs = Get-RequestForms
$formId = $rfs.RequestFormCollection.RequestForms | Where {$_.displayName -eq "Default VM"} | Select -ExpandProperty "formId"
$dto = $dto = Get-RequestFormById -formId $formId
$dto.RequestForm.menuText = "Default Service2
$updatedForm = Update-RequestForm -formId $formId -requestFormDto $dto
#>

function Update-RequestForm {
    [cmdletbinding()]
    Param(
        [Long] $formId = $(Throw "Provide the ID the service form."),
        $requestFormDto = $(Throw "Provide the updated request form DTO.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/forms/$formId"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $requestFormDto
    
    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
Export request form(s) to XML

.DESCRIPTION
Export request form(s) to XML

.EXAMPLE
Export-RequestForms -outputFilePath "C:\\request-forms.xml"

.EXAMPLE
Export-RequestForms -outputFilePath "C:\\request-forms.xml" -name "Default Service"

#>

function Export-RequestForms {
    [cmdletbinding()]
    Param(
        [String] $outputFilePath = $(Throw "Specify the location to export to including the file name (ie. C:\request-forms.xml)"),
        [String] $name
    )

    if($name) {
        $urlSafeName = [System.Web.HttpUtility]::UrlEncode($name)
        Write-Debug "Before encoding request form name: $name | After encode: $urlSafeName"
        $fullURI = "$Global:BASE_SERVICE_URI/forms/name/$($urlSafeName)/action/export"
    } else {
    $fullURI = "$Global:BASE_SERVICE_URI/forms/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 "Request forms exported to $outputFilePath"
}

<#
.SYNOPSIS
Import request forms from XML

.DESCRIPTION
Import request forms from XML

.EXAMPLE
Import-RequestForms -filePath "C:\\request-forms.xml"
#>

function Import-RequestForms {
    [cmdletbinding()]
    Param(
        [String] $filePath = $(Throw "Specify the full path to the request forms xml. (ie. C:\request-forms.xml)")
    )

    Write-Debug "Searching for request forms XML at file path: filePath"
    $xml_dto  = Get-Content -Raw $filePath
    
    $fullURI = "$Global:BASE_SERVICE_URI/forms/action/import"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
    
    Write-Debug "Importing request forms..."
    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 "Request forms imported"    
    $true
}

<#
.SYNOPSIS
Retrieve all request forms

.DESCRIPTION
Retrieve all request forms

.EXAMPLE
$forms = Get-RequestForms
#>

function Get-RequestForms {

    $fullURI = "$Global:BASE_SERVICE_URI/forms"
    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 request forms for the specified type

.DESCRIPTION
Retrieve all request forms for the specified type

.EXAMPLE
    
$new_forms = Get-RequestFormsOfType NEW_VM
$change_forms = Get-RequestFormsOfType CHANGE_VM
#>

function Get-RequestFormsOfType {
    [cmdletbinding()]
    Param(
        [String] $formType = $(Throw "Provide the type of service form.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/forms/type/$formType"
    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 request form by ID

.DESCRIPTION
Retrieve a request form by ID

.EXAMPLE
    
$form = Get-RequestFormById 2424
#>

function Get-RequestFormById {
    [cmdletbinding()]
    Param(
        [Long] $formId = $(Throw "Provide the ID of a service form.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/forms/$formId"
    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 request form elements for the specified form

.DESCRIPTION
Retrieve request form elements for the specified form

.EXAMPLE
    
$form_elements = Get-RequestFormElements -formId 942994
#>

function Get-RequestFormElements {
    [cmdletbinding()]
    Param(
        [Long] $formId = $(Throw "Provide the ID of a service form.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/forms/$formId/formelements"
    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
Submit a new service request

.DESCRIPTION
Submit a new service request

.PARAMETER psId
The ID of the requested published service

.PARAMETER requestParams
The deployment parameters

.EXAMPLE
Request a new service

#Locate an appropriate service
$ps = Get-PublishedServiceByName -name "Service #1"
$requestParams = Get-PSRequestParams -psId $ps.PublishedService.id

#Pull out service form elements
$projectCodeElement = $requestParams.PSDeployParamCollection.serviceformElements.RequestFormElements | Where-Object {$_.label -eq "Project Code"}
$applicationElement = $requestParams.PSDeployParamCollection.serviceformElements.RequestFormElements | Where-Object {$_.label -eq "Primary Application"}

#Configure correct form values
$projectCodeElement.value = "EF-1022"
$applicationElement.value = "Tomcat Server 7.0"

#Repeat for all components
$component1DeployParam = $requestParams.PSDeployParamCollection.componentParams | Where-Object { $_.componentName -match "TemplateVM1" }

#Pull out all the component form elements so we can pull out the data (repeat for all components)
$primaryOwnerElement = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Primary Owner"}
$textInput1Element = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Text Input #1"}
$expiryDateElement = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Expiry Date"}
$requiredByElement = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Required By"}
$memorySizeElement = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Memory Size"}
$cpuCountElement = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "CPU Count"}
$storageElement = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Storage"}
$componentNameElement = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Component Name"}
$customAttributeElement = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "SLA"}
$puppetClasses = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Puppet Classes"}
$puppetGroups = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Puppet Groups"}
#Recipes and roles fields have been deprecated. Please use runList field instead
#$chefRecipes = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Chef Recipes"}
#$chefRoles = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Chef Roles"}#Set chef run list as an ordered list of roles and recipes
$chefRunList = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Chef Run List"}

#Configure correct form values
$primaryOwnerElement.value = "bob@example.com"
$textInput1Element.value = "This is text input #1 entry"
$expiryDateElement.value = "0" #never expires
$requiredByElement.value = "2020/11/09"
$memorySizeElement.value = 1024
$cpuCountElement.value = 2
$componentNameElement.value = "VM 001X001"
$customAttributeElement.value = "Gold"

#Set up the existing disks
$disk1 = $storageElement.RequestedDisks | Where-Object {$_.label -eq "Hard disk 1" }
$disk1.storageTierLabel = "Storage Tier 5"
$disk2 = $storageElement.RequestedDisks | Where-Object {$_.label -eq "Hard disk 2" }
$disk2.storageTierLabel = "Storage Tier 5"

#Create a new disk
$thirdRequestedDisk = New-DTOTemplateObject "VMRequestDisk"
$thirdRequestedDisk.VMRequestDisk.diskSizeInKB = 102400
$thirdRequestedDisk.VMRequestDisk.storageTierLabel = "Storage Tier 5"
$storageElement.RequestedDisks += $thirdRequestedDisk.VMRequestDisk

#Add the Puppet and Chef form elements
$puppetClasses.values=@("acl","apt::update")
$puppetGroups.values=@("Microsoft","MySQL")

#Recipes and Roles fields have been deprecated. Please use runList field instead
$chefRecipes.values=@("apache2::god_monitor")
$chefRoles.values=@("windows")

#Set Chef Run-list as an ordered list of roles and recipes
$chefRunList.values=@("role[linux]", "recipe[7-zip]","recipe[apache2::god_monitor]")

#Submit the request
$requestDTO = New-ServiceRequest -psId $ps.PublishedService.id -requestParams $requestParams

.EXAMPLE
Request a new service and assign a network zone to a VM's adapter

$ps = Get-PublishedServiceByName -name "Service #1"
$requestParams = Get-PSRequestParams -psId $ps.PublishedService.id

$component1DeployParam = $requestParams.PSDeployParamCollection.componentParams | Where-Object { $_.componentName -match "TemplateVM1" }

$devZone = Get-NetworkZoneByName -name "Dev"

$network = New-DTOTemplateObject "VMRequestNetworking"
$network.VMRequestNetworking.id = 0
$network.VMRequestNetworking.networkZoneId = $devZone.NetworkZone.id
$network.VMRequestNetworking.networkZoneName = $devZone.NetworkZone.name

$networkElement = $component1DeployParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Network"}
$networkElement.selectedVMRequestNetworkings+=$network.VMRequestNetworking

$requestDTO = New-ServiceRequest -psId $ps.PublishedService.id -requestParams $requestParams

#>

function New-ServiceRequest {
    [cmdletbinding()]
    Param(
        [Long] $psId = $(Throw "Provide the ID of the published service."),
        $requestParams = $(Throw "Provide the DTO of the request (deploy) parameters.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/publishedservices/$psId/action/submit"
    Write-Debug "URI is: $fullURI"
   
    $xml_dto = Convert-ObjectToXml $requestParams
   
    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
Submit a new multi-service request

.DESCRIPTION
Submit a new multi-service request

.EXAMPLE

#Retrieve published services
$ps1 = Get-PublishedServiceByName -name "CalculusWin8.1"
$ps2 = Get-PublishedServiceByName -name "CalculusWin8.1_2"

#Query vCommander for the request parameters
$requestParams = Get-MultiplePSRequestParams $ps1.PublishedService.id $ps2.PublishedService.id

#Find request parameters for each published service request
$ps1RequestParams = $requestParams.MultiplePSDeployParamCollection.psParamCollection.entry | Where-Object {$_.key -eq $ps1.PublishedService.id} | select -ExpandProperty "value"
$ps2RequestParams = $requestParams.MultiplePSDeployParamCollection.psParamCollection.entry | Where-Object {$_.key -eq $ps2.PublishedService.id} | select -ExpandProperty "value"

#First published service
#Pull out all the component form elements so we can pull out the data (repeat for all components)
$ps1_primaryOwnerElement = $ps1RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Primary Owner"}
$ps1_textInput1Element = $ps1RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Text Input #1"}
$ps1_expiryDateElement = $ps1RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Expiry Date"}
$ps1_requiredByElement = $ps1RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Required By"}
$ps1_memorySizeElement = $ps1RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Memory Size"}
$ps1_cpuCountElement = $ps1RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "CPU Count"}
$ps1_storageElement = $ps1RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Storage"}
$ps1_componentNameElement = $ps1RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Component Name"}
$ps1_customAttributeElement = $ps1RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "SLA"}

#Configure correct form values
$ps1_primaryOwnerElement.value = "bob@example.com"
$ps1_textInput1Element.value = "This is text input #1 entry"
$ps1_expiryDateElement.value = "0" #never expires
$ps1_requiredByElement.value = "2019/11/09"
$ps1_memorySizeElement.value = 1024
$ps1_cpuCountElement.value = 2
$ps1_componentNameElement.value = "VM 001X001"
$ps1_customAttributeElement.value = "Gold"

#Set up the existing disks
$ps1_disk1 = $ps1_storageElement.RequestedDisks | Where-Object {$_.label -eq "Hard disk 1" }
$ps1_disk1.storageTierLabel = "Storage Tier 5"
$ps1_disk2 = $ps1_storageElement.RequestedDisks | Where-Object {$_.label -eq "Hard disk 2" }
$ps1_disk2.storageTierLabel = "Storage Tier 5"

#Create a new disk
$ps1_thirdRequestedDisk = New-DTOTemplateObject "VMRequestDisk"
$ps1_thirdRequestedDisk.VMRequestDisk.diskSizeInKB = 102400
$ps1_thirdRequestedDisk.VMRequestDisk.storageTierLabel = "Storage Tier 5"
$ps1_storageElement.RequestedDisks += $ps1_thirdRequestedDisk.VMRequestDisk

#Pull out service form elements
$ps1_projectCodeElement = $ps1RequestParams.serviceformElements.RequestFormElements | Where-Object {$_.label -eq "Project Code"}
$ps1_applicationElement = $ps1RequestParams.serviceformElements.RequestFormElements | Where-Object {$_.label -eq "Primary Application"}

#Configure correct form values
$ps1_projectCodeElement.value = "EF-1022-A"
$ps1_applicationElement.value = "Tomcat Server 7.0A"

#Second published service
#Pull out all the component form elements so we can pull out the data (repeat for all components)
$ps2_primaryOwnerElement = $ps2RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Primary Owner"}
$ps2_textInput1Element = $ps2RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Text Input #1"}
$ps2_expiryDateElement = $ps2RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Expiry Date"}
$ps2_requiredByElement = $ps2RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Required By"}
$ps2_memorySizeElement = $ps2RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Memory Size"}
$ps2_cpuCountElement = $ps2RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "CPU Count"}
$ps2_storageElement = $ps2RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Storage"}
$ps2_componentNameElement = $ps2RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "Component Name"}
$ps2_customAttributeElement = $ps2RequestParams.componentParams.formElements.RequestFormElements | Where-Object {$_.label -eq "SLA"}

#Configure correct form values
$ps2_primaryOwnerElement.value = "superuser"
$ps2_textInput1Element.value = "This is text input #1 entry"
$ps2_expiryDateElement.value = "0" #never expires
$ps2_requiredByElement.value = "2020/11/09"
$ps2_memorySizeElement.value = 2048
$ps2_cpuCountElement.value = 1
$ps2_componentNameElement.value = "VM 001X002"
$ps2_customAttributeElement.value = "Silver"

#Set up the existing disks
$ps2_disk1 = $ps2_storageElement.RequestedDisks | Where-Object {$_.label -eq "Hard disk 1" }
$ps2_disk1.storageTierLabel = "Storage Tier 5"
$ps2_disk2 = $ps2_storageElement.RequestedDisks | Where-Object {$_.label -eq "Hard disk 2" }
$ps2_disk2.storageTierLabel = "Storage Tier 5"

#Create a new disk
$ps2_thirdRequestedDisk = New-DTOTemplateObject "VMRequestDisk"
$ps2_thirdRequestedDisk.VMRequestDisk.diskSizeInKB = 102400
$ps2_thirdRequestedDisk.VMRequestDisk.storageTierLabel = "Storage Tier 5"
$ps2_storageElement.RequestedDisks += $ps2_thirdRequestedDisk.VMRequestDisk

#Pull out service form elements
$ps2_projectCodeElement = $ps2RequestParams.serviceformElements.RequestFormElements | Where-Object {$_.label -eq "Project Code"}
$ps2_applicationElement = $ps2RequestParams.serviceformElements.RequestFormElements | Where-Object {$_.label -eq "Primary Application"}

#Configure correct form values
$ps2_projectCodeElement.value = "EF-1022-B"
$ps2_applicationElement.value = "Tomcat Server 7.0B"

#See New-ServiceRequest API for more examples
    
$requestDTO = New-MultiServiceRequest -requestParams $requestParams
#>

function New-MultiServiceRequest {
    [cmdletbinding()]
    Param(
        $requestParams = $(Throw "Provide the DTO of the request (deploy) parameters.")
    )
   
    $fullURI = "$Global:BASE_SERVICE_URI/publishedservices/action/submit"
    Write-Debug "URI is: $fullURI"
   
    $xml_dto = Convert-ObjectToXml $requestParams
   
    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 the parameters required to deploy the specified published service

.DESCRIPTION
Retrieve the parameters required to deploy the specified published service

.PARAMETER psId
The published service ID

.EXAMPLE
$ps = Get-PublishedServiceByName -name "Service #1"
$requestParams = Get-PSRequestParams -psId $ps.PublishedService.id
#>

function Get-PSRequestParams {
    [cmdletbinding()]
    Param(
        [Long] $psId = $(Throw "Provide the ID of the published service.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/publishedservices/$psId/metadata/newrequestparams"
    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 parameters required to deploy the specified published services

.DESCRIPTION
Retrieve the parameters required to deploy the specified published services

.EXAMPLE
#
$ps1 = Get-PublishedServiceByName -name "Service #1"
$ps2 = Get-PublishedServiceByName -name "Service #2"

$requestParams = Get-MultiplePSRequestParams $ps1.PublishedService.id $ps2.PublishedService.id
#>

function Get-MultiplePSRequestParams {
    $queryArgs=''
    for ( $i = 0; $i -lt $args.count; $i++ ) {
        $queryParameter = "psid=" + $args[$i] + "&"
        $queryArgs = $queryArgs + $queryParameter
    }
    
    if ($args.count -gt 0 ) {
        $queryArgs = $queryArgs.Substring(0,$queryArgs.Length-1)
    }
    
    $fullURI = "$Global:BASE_SERVICE_URI/publishedservices/metadata/newrequestparams?$queryArgs"
    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
Submit a change request for the specified VM

.DESCRIPTION
Submit a change request for the specified VM

.EXAMPLE

#Retrieve the VM, find its ID
$vm = Get-VMs -vmName "VM 001" -max 1
$vmid = $vm.VirtualMachineCollection.VirtualMachines[0].id

#Find the formId of a change request form with name that contains "Resource Change Request"
$forms = Get-RequestFormsOfType -formType "CHANGE_VM"
$formdId = $forms.RequestFormCollection.RequestForms | Where-Object {$_.menuText -match "Resource Change Request"} | select -ExpandProperty "formId"

#Find all the form elements required to fulfill the change request
$vmChangeParams = Get-VMChangeRequestParams -vmId $vmid -formid $formdId

#Set the number of CPUs to 2
$cpuFormElement = $vmChangeParams.ChangeRequestParam.formElements.RequestFormElements | Where-Object {$_.formType -eq "CPU"}
$cpuFormElement.value = 2

#Set memory to 2048
$memoryFormElement = $vmChangeParams.ChangeRequestParam.formElements.RequestFormElements | Where-Object {$_.formType -eq "MEMORY"}
$memoryFormElement.value = 2048

$customAttributeElement = $vmChangeParams.ChangeRequestParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Cost Center"}
$customAttributeElement.value = "Cost Center 1"

$requestDTO = New-VMChange -vmId $vmid -requestParams $vmChangeParams

.EXAMPLE
Add and change the disks for a VM

#Retrieve the VM, find its ID
$vm = Get-VMs -vmName "VM 001" -max 1
$vmid = $vm.VirtualMachineCollection.VirtualMachines[0].id
    
#Find the formId of a change request form with name that contains "Resource Change Request"
$forms = Get-RequestFormsOfType -formType "CHANGE_VM"
$formdId = $forms.RequestFormCollection.RequestForms | Where-Object {$_.menuText -match "Resource Change Request"} | select -ExpandProperty "formId"
    
#Find all the form elements required to fulfill the change request
$vmChangeParams = Get-VMChangeRequestParams -vmId $vmid -formid $formdId
    
#Set memory to 2048
$memoryFormElement = $vmChangeParams.ChangeRequestParam.formElements.RequestFormElements | Where-Object {$_.formType -eq "MEMORY"}
$memoryFormElement.value = 2048
    
#Set any other elements here
    
#Create a new disk
$newDiskObject = New-DTOTemplateObject -DTOTagName "VMRequestDisk"
$newDiskObject.VMRequestDisk.diskSizeInKB = 20480
$newDiskObject.VMRequestDisk.storageTierLabel = "Storage Tier 1"
    
#Resize an existing disk
$diskFormElement = $vmChangeParams.ChangeRequestParam.formElements.RequestFormElements | Where-Object {$_.formType -eq "DISK"}
$existingDisk = $diskFormElement.RequestedDisks[0]
$existingDisk.diskSizeInKB = 41943040
    
#Add a new disk
$diskFormElement.RequestedDisks = @($existingDisk, $newDiskObject.VMRequestDisk)
    
$requestDTo = New-VMChange -vmId $vmId -requestParams $vmChangeParams
#>

function New-VMChange {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM."),
        $requestParams = $(Throw "Provide the DTO of the request (change) parameters.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/submitchange"
    Write-Debug "URI is: $fullURI"
   
    $xml_dto = Convert-ObjectToXml $requestParams
   
    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
Submit a change request for the specified virtual service

.DESCRIPTION
Submit a change request for the specified virtual service

.EXAMPLE

#Retrieve the virtual service
$vApp = Get-vApps -vAppName "vApp-003"
$vAppId = $vapp.VirtualAppCollection.VirtualApps.id

#Find the formId of a change request form with name that contains "Resource Change Request"
$forms = Get-RequestFormsOfType -formType "CHANGE_VM"
$formdId = $forms.RequestFormCollection.RequestForms | Where-Object {$_.menuText -match "Resource Change Request"} | select -ExpandProperty "formId"

$requestParams = Get-vAppChangeRequestParams -vAppId $vAppId -formid $formdId
$primaryOwnerElement = $requestParams.ChangeRequestParam.formElements.RequestFormElements | Where-Object {$_.label -eq "Primary Owner"}
$primaryOwnerElement.value = "bob@example.com"

New-vAppChange -vAppId $vapp.VirtualAppCollection.VirtualApps.id -requestParams $requestParams

#>

function New-vAppChange {
    [cmdletbinding()]
    Param(
        [Long]$vAppId = $(Throw "Provide the ID of the vApp"),
        $requestParams = $(Throw "Provide the DTO of the request (change) parameters.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vapps/$vAppId/action/submitchange"
    Write-Debug "URI is: $fullURI"
   
    $xml_dto = Convert-ObjectToXml $requestParams
   
    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 the parameters required to change this VM

.DESCRIPTION
Retrieve the parameters required to change this VM

.EXAMPLE
$vm = Get-VMs -vmName "BuildVM-001" -max 1 #Get the VM id
$vmId = $vm.VirtualMachineCollection.VirtualMachines.id

$cc = Get-RequestForms #Get the formId of the RequestForm
$formId = $cc.RequestFormCollection.RequestForms | Where-Object {$_.displayName -eq "Decommissioning Request"} | Select -ExpandProperty "formId"
$requestParams = Get-VMChangeRequestParams -vmId $vmId -formid $formId
#>

function Get-VMChangeRequestParams {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM."),
        [Long] $formid = $(Throw "Provide the ID of the service form.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/metadata/changerequestparams/$formid"
    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 parameters required to change this virtual service

.DESCRIPTION
Retrieve the parameters required to change this virtual service

.EXAMPLE
$requestParams = Get-vAppChangeRequestParams -vAppId 4242424 -formid 12313
#>

function Get-vAppChangeRequestParams {
    [cmdletbinding()]
    Param(
        [Long] $vAppId = $(Throw "Provide the ID of the vApp."),
        [Long] $formid = $(Throw "Provide the ID of the service form.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vapps/$vAppId/metadata/changerequestparams/$formid"
    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 roles

.DESCRIPTION
Retrieve all roles and access rights. You can retrieve Service Portal roles, vCommander roles, and vCommander access rights.

.EXAMPLE
$roles = Get-Roles
#>

function Get-Roles {
    [cmdletbinding()]
    Param()
    
    $fullURI = "$Global:BASE_SERVICE_URI/roles"
    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 role by ID

.DESCRIPTION
Retrieve all roles and access rights by ID. You can retrieve Service Portal roles, vCommander roles, and vCommander access rights.

.EXAMPLE
$role = Get-Role -Id 104
#>

function Get-Role {
    [cmdletbinding()]
    Param(
        [Long] $Id = $(Throw "Provide the role ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/roles/$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 role by name

.DESCRIPTION
Retrieve all roles and access rights by name. You can retrieve Service Portal roles, vCommander roles, and vCommander access rights. If you have customized Service Portal role names, specify the customized name.

.EXAMPLE
$role = Get-RoleByName -name "Customer Admin"
#>

function Get-RoleByName {
    [cmdletbinding()]
    Param(
        [String] $name = $(Throw "Provide the role name.")
    )

    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafeName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/roles/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 Service Portal role

.DESCRIPTION
Create a new role. Only Service Portal roles can be created.

.EXAMPLE
Create a Service Portal role with a set of permissions.

$roleTemplateObject = New-DTOTemplateObject -DTOTagName "Role"
$roleTemplateObject.Role.name = "Admin #2";
$roleTemplateObject.Role.permissions = @("POWER_CONTROLS","SHOW_EXTERNAL_PAGE", "RUN_WORKFLOW")

$createdRole = New-Role -dto $roleTemplateObject

#>

function New-Role {
    [cmdletbinding()]
    Param(
        $dto = $(Throw "Provide the role DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/roles"
    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
Modify a Service Portal role

.DESCRIPTION
Modify role properties such as name and permissions. Only Service Portal roles can be updated.

.EXAMPLE
Add a new permission to a Service Portal role as well as changing the role name

$role = Get-RoleByName "Admin #2"
$role.Role.name = "New Admin #2"
$role.Role.permissions +="VIEW_ANNUAL_COST"
$updatedRole = update-Role -Id $role.Role.id -dto $role

#>

function Update-Role {
    [cmdletbinding()]
    Param(
        [Long] $Id = $(Throw "Provide the role ID"),
        $dto = $(Throw "Provide the role DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/roles/$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
Delete a role

.DESCRIPTION
Delete a role. Only Service Portal roles can be deleted.

.EXAMPLE

$role = Get-RoleByName "New Admin #2"
$result = Remove-Role -Id $role.Role.id
#>

function Remove-Role {
    [cmdletbinding()]
    Param(
        [Long] $Id = $(Throw "Provide the role ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/roles/$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
Assign a role to a user account

.DESCRIPTION
Assign a Service Portal role, vCommander role, or vCommander access rights to an account.

You cannot switch a user's role from a Service Portal role to a vCommander role or vice versa.
Access rights can be assigned only to vCommander users, not Service Portal users.

.EXAMPLE
Assign role and access rights to vCommander user

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

#Retrieve the second management server
$msObject2 = Get-ManagedObjectByName -name "azure" -type "MANAGEMENTSERVER"
$ms2 = $msObject2.ManagedObjectCollection.managedObjects[0]

#Create access rights assignment objects
$contextRoleObject1 = New-DTOTemplateObject -DTOTagName "ContextRoleAssignment"
$contextRoleObject1.ContextRoleAssignment.context = $ms1
$contextRoleObject1.ContextRoleAssignment.roleName = "CONTEXT_APPROVER"

$contextRoleObject2 = New-DTOTemplateObject -DTOTagName "ContextRoleAssignment"
$contextRoleObject2.ContextRoleAssignment.context = $ms2
$contextRoleObject2.ContextRoleAssignment.roleName = "CONTEXT_AUDITOR"

#Assign access rights
$roleTemplateObject = New-DTOTemplateObject -DTOTagName "RoleAssignments"
$roleTemplateObject.RoleAssignments.contextRoleAssignments = @($contextRoleObject1.ContextRoleAssignment, $contextRoleObject2.ContextRoleAssignment)

#Set up user role
$roleTemplateObject.RoleAssignments.roleName = "COM_ENTERPRISE"

$roleAssignments = New-RoleAssignments -loginId "vcmdrUser1" -dto $roleTemplateObject

.EXAMPLE
Assign role to Service Portal user


$roleTemplateObject = New-DTOTemplateObject -DTOTagName "RoleAssignments"
$roleTemplateObject.RoleAssignments.roleName = "Manager"
$roleAssignments = New-RoleAssignments -loginId "portalUser1" -dto $roleTemplateObject

#>

function New-RoleAssignments {
    [cmdletbinding()]
    Param(
        [String] $loginId = $(Throw "Provide the account login ID."),
        $dto = $(Throw "Provide the role assignment DTO.")
    )
    
    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($loginId)
    Write-Debug "Before encoding loginId: $loginId | After encode: $urlSafeName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/accounts/$urlSafeName/roleassignments"
    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
Modify an account's role and access rights

.DESCRIPTION
Modify an account's role and access rights. The new role and access rights replace the existing role and access rights (if access rights were previously assigned).

You cannot switch a user's role from a Service Portal role to a vCommander role or vice versa.
Access rights can be assigned only to vCommander users, not Service Portal users.

.EXAMPLE
Update vCommander user role assignments

#Retrieve the management server
$msObject = Get-ManagedObjectByName -name "azure" -type "MANAGEMENTSERVER"
$ms1 = $msObject.ManagedObjectCollection.managedObjects[0]

#Create access rights assignment objects
$contextRoleObject1 = New-DTOTemplateObject -DTOTagName "ContextRoleAssignment"
$contextRoleObject1.ContextRoleAssignment.context = $ms1
$contextRoleObject1.ContextRoleAssignment.roleName = "CONTEXT_APPROVER"

#Assign access rights
$roleTemplateObject = New-DTOTemplateObject -DTOTagName "RoleAssignments"
$roleTemplateObject.RoleAssignments.contextRoleAssignments = @($contextRoleObject1.ContextRoleAssignment)

#Set up user role
$roleTemplateObject.RoleAssignments.roleName = "COM_USER"

$roleAssignments = Update-RoleAssignments -loginId "vcmdrUser1" -dto $roleTemplateObject

.EXAMPLE
Modify a Service Portal role assignment

$roleTemplateObject = New-DTOTemplateObject -DTOTagName "RoleAssignments"
$roleTemplateObject.RoleAssignments.roleName = "Customer"
$roleAssignments = Update-RoleAssignments -loginId "portalUser1" -dto $roleTemplateObject
#>

function Update-RoleAssignments {
    [cmdletbinding()]
    Param(
        [String] $loginId = $(Throw "Provide the account login Id"),
        $dto = $(Throw "Provide the role DTO.")
    )

    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($loginId)
    Write-Debug "Before encoding loginId: $loginId | After encode: $urlSafeName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/accounts/$urlSafeName/roleassignments"
    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 all access rights for the specified vCommander account

.DESCRIPTION
Remove all access rights for the specified vCommander account. Service Portal users do not have access rights.

.EXAMPLE

$result = Remove-ContextRoleAssignments -loginId "portalUser1"
#>

function Remove-ContextRoleAssignments {
    [cmdletbinding()]
    Param(
        [String] $loginId = $(Throw "Provide the account login ID.")
    )

    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($loginId)
    Write-Debug "Before encoding loginId: $loginId | After encode: $urlSafeName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/accounts/$urlSafeName/roleassignments/contextroles"
    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
Retrieve all accounts in vCommander

.DESCRIPTION
Retrieve all accounts in vCommander

.EXAMPLE
$account = Get-Accounts
#>

function Get-Accounts {
    [cmdletbinding()]
    Param()
    
    $fullURI = "$Global:BASE_SERVICE_URI/accounts"
    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 account by login ID

.DESCRIPTION
Retrieve an account by login ID

.EXAMPLE
$account = Get-Account -loginId "bob@example.com"
#>

function Get-Account {
    [cmdletbinding()]
    Param(
        [String] $loginId = $(Throw "Provide the login ID.")
    )
    
    $urlSafeLoginId = [System.Web.HttpUtility]::UrlEncode($loginId)
    Write-Debug "Before encoding loginId: $loginId | After encode: $urlSafeLoginId"
    
    $fullURI = "$Global:BASE_SERVICE_URI/accounts/$urlSafeLoginId"
    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
Delete an account

.DESCRIPTION
Delete an account

.EXAMPLE
$roles = Remove-Account -loginId "bob@example.com"
#>

function Remove-Account {
    [cmdletbinding()]
    Param(
        [String] $loginId = $(Throw "Provide the login ID.")
    )
    
    $urlSafeLoginId = [System.Web.HttpUtility]::UrlEncode($loginId)
    Write-Debug "Before encoding loginId: $loginId | After encode: $urlSafeLoginId"
    
    $fullURI = "$Global:BASE_SERVICE_URI/accounts/$urlSafeLoginId"
    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 new account

.DESCRIPTION
Create a new account

.EXAMPLE

$accountDto = New-DTOTemplateObject -DTOTagName "Account"
$accountDto.Account.emailAddress = "bob@example.com"
$accountDto.Account.password = "secret"
$accountDto.Account.userid = "bob"
$accountDto.Account.enabled = $true
$accountDto.Account.roleName = "COM_USER"
$accountDto.Account.firstName = "Bob"
$accountDto.Account.lastName = "Smith"
$accountDto.Account.primaryPhoneNo = "613-111-3333"
$accountDto.Account.secondaryPhoneNo = "613-111-2222"
$accountDto.Account.securitySourceType = "LOCAL"
        
$account= New-Account -accountDto $accountDto

.EXAMPLE

$accountDto = New-DTOTemplateObject -DTOTagName "Account"
$accountDto.Account.emailAddress = ""
$accountDto.Account.password = ""
$accountDto.Account.userid = "bclow@example.com"
$accountDto.Account.enabled = $true
$accountDto.Account.roleName = "COM_ENTERPRISE"
$accountDto.Account.firstName = ""
$accountDto.Account.lastName = ""
$accountDto.Account.primaryPhoneNo = ""
$accountDto.Account.secondaryPhoneNo = ""
$accountDto.Account.securitySourceType = "USER_DIRECTORY_USER"
        
$account= New-Account -accountDto $accountDto

#>

function New-Account {
    [cmdletbinding()]
    Param(
        $accountDto = $(Throw "Provide the account DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/accounts"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $accountDto
    
    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 account

.DESCRIPTION
Modify an account

.EXAMPLE

$accountDto = Get-Account -loginId "bob@example.com"
$accountDto.Account.enabled = $false
$accountDto.Account.primaryPhoneNo = "613-111-2222"
    
$account = Update-Account -loginId $accountDto.userId -accountDto $accountDto

#>

function Update-Account {
    [cmdletbinding()]
    Param(
        [String] $loginId = $(Throw "Provide the account ID."),
        $accountDto = $(Throw "Provide the account DTO.")
    )
    
    $urlSafeLoginId = [System.Web.HttpUtility]::UrlEncode($loginId)
    Write-Debug "Before encoding loginId: $loginId | After encode: $urlSafeLoginId"
    
    $fullURI = "$Global:BASE_SERVICE_URI/accounts/$urlSafeLoginId"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $accountDto
    
    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
Retrieve a collection of service requests

.DESCRIPTION
Retrieve a collection of service requests

.EXAMPLE
$requests = Get-ServiceRequests -size 40

.EXAMPLE
$q = 'state -eq "Pending Completion"'
$requests = Get-ServiceRequests -size 400 -offset 0 -query $q

.PARAMETER size
The number of entries to retrieve

.PARAMETER offset
The pagination offset

.PARAMETER query
The query string to filter the requests

#>

function Get-ServiceRequests {
    [cmdletbinding()]
    Param(
        [Int]$size=100,
        [int]$offset=0,
        [String]$query=$null
    )

    $fullURI = "$Global:BASE_SERVICE_URI/requests?size=$size&offset=$offset"
    
    if (-not ([string]::IsNullOrEmpty($query))) {
        
        $urlSafeQuery = [System.Web.HttpUtility]::UrlEncode($query)
        Write-Debug "Before encoding query: $query | After encode: $urlSafeQuery"

        $fullURI = $fullURI + "&q="+ $urlSafeQuery
    }

    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 count of service requests

.DESCRIPTION
Retrieve the count of service requests

.EXAMPLE
$count = Get-ServiceRequestCount

.EXAMPLE
$q = 'state -eq "Pending Completion"'
$count = Get-ServiceRequestCount -query $q

.PARAMETER query
The query string to filter the requests

#>

function Get-ServiceRequestCount {
    [cmdletbinding()]
    Param(
        [String]$query=$null
    )

    $fullURI = "$Global:BASE_SERVICE_URI/requests/count"
    
    if (-not ([string]::IsNullOrEmpty($query))) {
        
        $urlSafeQuery = [System.Web.HttpUtility]::UrlEncode($query)
        Write-Debug "Before encoding query: $query | After encode: $urlSafeQuery"

        $fullURI = $fullURI + "?q="+ $urlSafeQuery
    }
    
    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
    }
    $Global:xo = $requestResponse;
    
    Get-ResponseBodyAsInt -response $requestResponse
}

<#
.SYNOPSIS
Retrieve a collection of service requests by state

.DESCRIPTION
Retrieve a collection of service requests by state

.EXAMPLE
$requests = Get-ServiceRequestsByState -requestState "APPROVED" -max 40
#>

function Get-ServiceRequestsByState {
    [cmdletbinding()]
    Param(
        [String] $requestState = $(Throw "Provide the request state."),
        $max =100
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/state/$($requestState)?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 service requests by type

.DESCRIPTION
Retrieve a collection of service requests by type

.EXAMPLE
$requests = Get-ServiceRequestsByType -requestType "NEW_VM" -max 40
#>

function Get-ServiceRequestsByType {
    [cmdletbinding()]
    Param(
        [String] $requestType = $(Throw "Provide the request type."),
        $max =100
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/type/$($requestType)?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 service request by ID

.DESCRIPTION
Retrieve a service request by ID

.EXAMPLE
$request = Get-ServiceRequest -Id 2313
#>

function Get-ServiceRequest {
    [cmdletbinding()]
    Param(
        [Long] $Id = $(Throw "Provide the request ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$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 all services for the specified request

.DESCRIPTION
Retrieve all services for the specified request

.EXAMPLE
$requestedServices = Get-RequestedServices -requestId 42
#>

function Get-RequestedServices {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/rservices"
    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 requested service by ID

.DESCRIPTION
Retrieve the requested service by ID

.EXAMPLE
$requestedService = Get-RequestedServiceById -requestId 42 -requestedServiceId 3244
#>

function Get-RequestedServiceById {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        [Long] $requestedServiceId = $(Throw "Provide the requested service ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/rservices/$requestedServiceId"
    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 requested components for the specified request

.DESCRIPTION
Retrieve all requested components for the specified request

.EXAMPLE
$requestedComponents = Get-RequestedComponents -requestId 42 -requestedServiceId 3244
#>

function Get-RequestedComponents {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        [Long] $requestedServiceId = $(Throw "Provide the requested service ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/rservices/$requestedServiceId/components"
    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 requested component by ID

.DESCRIPTION
Retrieve the requested component by ID

.EXAMPLE
$requestedComponent = Get-RequestedComponentById -requestId 42 -requestedServiceId 3244
#>

function Get-RequestedComponentById {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        [Long] $requestedServiceId = $(Throw "Provide the requested service ID."),
        [Long] $requestedComponentId = $(Throw "Provide the requested component ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/rservices/$requestedServiceId/components/$requestedComponentId"
    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 form details for the specified requested service

.DESCRIPTION
Retrieve form details for the specified requested service

.EXAMPLE
$formElements = Get-RequestedServiceFormDetails -requestId 42 -requestedServiceId 3244
#>

function Get-RequestedServiceFormDetails {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        [Long] $requestedServiceId = $(Throw "Provide the requested service ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/rservices/$requestedServiceId/metadata/form"
    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 form details for the specified requested component

.DESCRIPTION
Retrieve form details for the specified requested component

.EXAMPLE
$formElements = Get-RequestedComponentFormDetails -requestId 42 -requestedServiceId 3244 -requestedComponentId 535
#>

function Get-RequestedComponentFormDetails{
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        [Long] $requestedServiceId = $(Throw "Provide the requested service ID."),
        [Long] $requestedComponentId = $(Throw "Provide the requested component ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/rservices/$requestedServiceId/components/$requestedComponentId/metadata/form"
    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 form details for the specified change request

.DESCRIPTION
Retrieve form details for the specified change request

.EXAMPLE
$formElementCollections = Get-ChangeRequestFormDetails -requestId 42
#>

function Get-ChangeRequestFormDetails {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/metadata/form"
    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 active workflow associated with a specific request at this moment in time,
not including those that have been completed or rejected

.DESCRIPTION
Retrieve the active workflow associated with a specific request at this moment in time,
not including those that have been completed or rejected

For new requests, only an approval workflow can be retrieved.
For change requests, either an approval or a completion workflow can be retrieved.
     

.EXAMPLE
$workflow = Get-RunningWorkflowsForRequest -requestId 1233
#>

function Get-RunningWorkflowsForRequest {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/workflow"
    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 workflow associated with a requested service, not including those that have been completed or rejected

.DESCRIPTION
Retrieve the workflow associated with a requested service, not including those that have been completed or rejected

.EXAMPLE
$workflow = Get-RunningWorkflowForRequestedService -requestId 1233 -requestedServiceId 221
#>

function Get-RunningWorkflowForRequestedService {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        [Long] $requestedServiceId = $(Throw "Provide the requested service ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/rservices/$requestedServiceId/metadata/workflow"
    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 workflow associated with a requested component, not including those that have been completed or rejected

.DESCRIPTION
Retrieve the workflow associated with a requested component, not including those that have been completed or rejected

.EXAMPLE
$workflow = Get-RunningWorkflowForRequestedComponent -requestId 1233 -requestedServiceId 221 -requestedComponentId 444
#>

function Get-RunningWorkflowForRequestedComponent {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        [Long] $requestedServiceId = $(Throw "Provide the requested service ID."),
        [Long] $requestedComponentId = $(Throw "Provide the requested component ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/rservices/$requestedServiceId/components/$requestedComponentId/metadata/workflow"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Skips the specified running workflow.

.DESCRIPTION
Skips the specified running workflow.

.PARAMETER workflowId
ID of the workflow to skip all steps for

.PARAMETER comment
Comment explaining the reason - mandatory.

.EXAMPLE
Skip all steps of a workflow

Invoke-SkipRunningWorkflow -workflowId $wfId -comment "Workflow failed"
#>

function Invoke-SkipRunningWorkflow {
    [cmdletbinding()]
    Param(
        [Long] $workflowId = $(Throw "Provide the ID of the running workflow."),
        [String] $comment = $(Throw "A comment must be provided when skipping all steps of a running workflow")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/workflows/$workflowId/action/skip"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $comment -ContentType "text/plain"
    Convert-RestResponseOutput $requestResponse.RawContent | Out-null
}

<#
.SYNOPSIS
Skip the current step of a workflow

.DESCRIPTION
Skip the current step in the workflow.

.PARAMETER workflowId
ID of the workflow to skip steps for

.PARAMETER comment
Comment explaining the reason - mandatory.

.EXAMPLE
Skip current step of a workflow

Invoke-SkipRunningWorkflowStep -workflowId $wfId -comment "Workflow step failed"
#>

function Invoke-SkipRunningWorkflowStep {
    [cmdletbinding()]
    Param(
        [Long] $workflowId = $(Throw "Provide the ID of the running workflow."),
        [String] $comment = $(Throw "A comment must be provided when skipping current step of a running workflow")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/workflows/$workflowId/steps/action/skip"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $comment -ContentType "text/plain"
    Convert-RestResponseOutput $requestResponse.RawContent | Out-null
}

<#
.SYNOPSIS
Repeat the current step of a workflow

.DESCRIPTION
Repeat the current step in the workflow.

.PARAMETER workflowId
ID of the workflow to repeat current step for

.PARAMETER comment
Comment explaining the reason - mandatory.

.EXAMPLE
Repeat current steps of a workflow

Invoke-RepeatRunningWorkflowStep -workflowId $wfId -comment "Retry workflow step"
#>

function Invoke-RepeatRunningWorkflowStep {
    [cmdletbinding()]
    Param(
        [Long] $workflowId = $(Throw "Provide the ID of the running workflow."),
        [String] $comment = $(Throw "A comment must be provided when repeating current step of a running workflow")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/workflows/$workflowId/steps/action/repeat"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -Body $comment -ContentType "text/plain"
    Convert-RestResponseOutput $requestResponse.RawContent | Out-null
}


<#
.SYNOPSIS
Retrieve a workflow definition by ID

.DESCRIPTION
Retrieve a workflow definition by ID

.EXAMPLE
$workflowDefinition = Get-WorkflowDefinitionById -workflowDefinitionId 423434
#>

function Get-WorkflowDefinitionById {
    [cmdletbinding()]
    Param(
        [Long] $workflowDefinitionId = $(Throw "Please workflow definition ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/workflows/$workflowDefinitionId"
    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 workflow definition by type

.DESCRIPTION
Retrieve a workflow definition by type

.EXAMPLE
$workflowDefinitions = Get-WorkflowDefinitionsByType -workflowType "NEW_VM_APPROVAL"
#>

function Get-WorkflowDefinitionsByType {
    [cmdletbinding()]
    Param(
        [String] $workflowType = $(Throw "Please workflow definition type.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/workflows/type/$workflowType"
    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 workflow definition

.DESCRIPTION
Retrieve a workflow definition

.PARAMETER type
The type of the workflow

.PARAMETER name
The name of the workflow

.EXAMPLE
$workflow = Get-WorkflowDefinition -type "NEW_VM_POST_DEPLOY" -name "Configure Applications"
#>

function Get-WorkflowDefinition() {
    [cmdletbinding()]
    Param(
        [String] $type = $(Throw "Provide the type of the workflow."),
        [String] $name = $(Throw "Provide the name of the workflow.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/configurations/workflows/type/$type/$name"
    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 workflow step definitions for the specified workflow definition

.DESCRIPTION
Retrieve all workflow step definitions for the specified workflow definition

.EXAMPLE
$workflowStepDefinitions = Get-WorkflowStepDefinitions -workflowDefinitionId 223
#>

function Get-WorkflowStepDefinitions {
    [cmdletbinding()]
    Param(
        [Long] $workflowDefinitionId = $(Throw "Please workflow definition ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/workflows/$workflowDefinitionId/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
Retrieve a workflow step definition by ID

.DESCRIPTION
Retrieve a workflow step definition by ID

.EXAMPLE
$stepDefinition = Get-WorkflowStepDefinitionById -workflowDefinitionId 342 -stepDefinitionId 21212
#>

function Get-WorkflowStepDefinitionById {
    [cmdletbinding()]
    Param(
        [Long] $workflowDefinitionId = $(Throw "Please workflow definition ID."),
        [Long] $stepDefinitionId = $(Throw "Please workflow step definition ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/workflows/$workflowDefinitionId/steps/$stepDefinitionId"
    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
Run a command workflow

.DESCRIPTION
Run a command workflow. This API grants access to all command workflows regardless of workflow visiblity.
The API returns a TaskInfo object to help track task progress. Task completion does not indicate that the workflow has executed to completion.
Once the task completes successfully, the TaskInfo:relatedObjects property holds a managed object reference to a running workflow. Using this reference, other APIs could be used to query the running status of the workflow. See example(s) for details.

.EXAMPLE

$vm = Get-VMs -vmName "DevVM - 001"
$vmId = $vm.VirtualMachineCollection.VirtualMachines[0].id

$workflows = Get-WorkflowDefinitionsByType -workflowType "VM"
$wfId = $workflows.WorkflowDefinitionCollection.WorkflowDefinitions | Where-Object {$_.displayName -eq "serviceWorkflow"} | select -ExpandProperty "Id"

$result = Start-CommandWorkflow -vmId $vmId -workflowDefinitionId $wfId
$result = Wait-ForTaskFinalState -taskId $result.TaskInfo.id -waitTimeInMillis 10000

if ($result.TaskInfo.state -ne "COMPLETE") {
    Write-Error "Task failed"
}

#Once the workflow has started, we find the ID of the running workflow
#Using this ID, we can query the status of this running workflow
$runningWorkflowId = $result.TaskInfo.relatedObjects | Where-Object {$_.type -eq "WORKFLOW"} | Select -ExpandProperty "id"
$runningWorkflowId

#>

function Start-CommandWorkflow {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the target VM."),
        [Long] $workflowDefinitionId  = $(Throw "Provide the ID of the command workflow definition.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/workflows/$workflowDefinitionId/action/execute"

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

    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Wait for a task to enter a final state

.DESCRIPTION
Wait for a task to enter a final state. The call will return when the waitTime expires or when the task enters one of these states, whichever comes first:

QUEUED
COMPLETE
CANCELLED
FAILED

.PARAMETER taskId
The ID of the task to wait on

.PARAMETER waitTimeInMillis
The maximum amount of time to wait for the task to enter the final state, in milliseconds

.EXAMPLE
#
$taskInfo = Wait-ForTaskFinalState -taskId 2131 -waitTimeInMillis 10000

.EXAMPLE
#Get a task by its name

#Grab the Latest Current Tasks Infos
$currentTasks = Get-CurrentTaskInfos

#Get the Latest Task Id with the Name "Create Cost Model"
$taskId = $currentTasks.TaskInfoCollection.TasksInfos | Where-Object {$_.taskName -eq "Create Cost Model"} | Select-Object -ExpandProperty id

#Wait For the Task to Complete
$taskInfo = Wait-ForTaskFinalState -taskId $taskId -waitTimeInMillis 10000

#>

function Wait-ForTaskFinalState {
    [cmdletbinding()]
    Param(
        [Long] $taskId = $(Throw "Provide the ID of the task to wait on."),
        [Long] $waitTimeInMillis = $(Throw "Provide the maximum amount of time to wait for the task to enter the final state, in milliseconds.")
    )

    #Exception will be thrown if not found
    $taskInfo = Get-TaskInfo -Id $taskId
    if ($taskInfo.TaskInfo.finalState -eq "true") {
        $taskInfo
        return
    }

    $initialSleepTimeInternal = $Script:SLEEP_TIME_MILLIS
    if ($waitTimeInMillis -lt $Script:SLEEP_TIME_MILLIS) {
        $initialSleepTimeInternal = $waitTimeInMillis
    }

    Write-Debug "Sleeping for $($initialSleepTimeInternal) milliseconds."

    Start-Sleep -Milliseconds $initialSleepTimeInternal

    $elapsedTimeInMillis = $initialSleepTimeInternal

    while( ($taskInfo.TaskInfo.finalState -ne "true") -and ($elapsedTimeInMillis -lt $waitTimeInMillis)) {
        $taskInfo = Get-TaskInfo -Id $taskId
        if ($taskInfo.TaskInfo.finalState -eq "true") {
            $taskInfo;
            return
        }

        Start-Sleep -Milliseconds $Script:SLEEP_TIME_MILLIS
        Write-Debug "Sleeping for $($Script:SLEEP_TIME_MILLIS) milliseconds."
          
        $elapsedTimeInMillis += $Script:SLEEP_TIME_MILLIS
    }

    Write-Debug "Total elapsed time: $($elapsedTimeInMillis) milliseconds."

    Get-TaskInfo -Id $taskId
    return    
}

<#
.SYNOPSIS
Retrieve a collection of organization references

.DESCRIPTION
Retrieve a collection of organization references

.EXAMPLE
$orgs = Get-OrganizationReferences -max 10

.EXAMPLE
$orgs = Get-OrganizationReferences -name 'Default Organization'

.EXAMPLE
#Get org references with the first element to return
$orgs = Get-OrganizationReferences -max 10 -offset 1

.PARAMETER name
The name of the organization

.PARAMETER max
The maximum number of organizations to retrieve

#>

function Get-OrganizationReferences {
    param(
        [Parameter(Mandatory = $false)]
        [String] $name="", #Provide the name of the organization
            
        [Parameter(Mandatory = $false)]
        [Int] $max=100, #Maximum number of organizations 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
    )
    
    $urlSafeName = $name
    if ($name -ne $null) {
        $urlSafeName = [System.Web.HttpUtility]::UrlEncode($name)
        Write-Debug "Before encoding organization name: $name | After encode: $urlSafeName"
    }
    
    $fullURI = "$Global:BASE_SERVICE_URI/references/organizations?name=$urlSafeName&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 an organization by name

.DESCRIPTION
Retrieve an organization by name

.EXAMPLE
$org = Get-OrganizationByName -name "Finance Department"
#>

function Get-OrganizationByName {
    [cmdletbinding()]
    Param(
        [String] $name = $(Throw "Provide the name of the organization.")
    )
    
    $urlSafeOrgName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafeOrgName"

    $fullURI = "$Global:BASE_SERVICE_URI/configurations/organizations/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
Retrieve an organization by name or ID

.DESCRIPTION
Retrieve an organization by name or ID

.PARAMETER name
The name of the organization (optional)

.PARAMETER orgId
The ID of the organization (optional)

.EXAMPLE
$org = Get-Organization -name "Finance Department"

.EXAMPLE
$org = Get-Organization -orgId 123
#>

function Get-Organization {
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory = $false)]
        [String] $name = "",

        [Parameter(Mandatory = $false)]
        [Long] $orgId = $null
    )

    if ($name -ne "") {
       Get-OrganizationByName -name $name
    } elseif ($orgId -ne $null) {
        $fullURI = "$Global:BASE_SERVICE_URI/configurations/organizations/$($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
    } else {
        throw "One of name or orgId is required"
    }
}

<#
.SYNOPSIS
Remove an organization by name

.DESCRIPTION
Remove an organization by name

.EXAMPLE
$result = Remove-OrganizationByName -name "Finance Department"
#>

function Remove-OrganizationByName {
    [cmdletbinding()]
    Param(
        [String] $name = $(Throw "Please name of the organization.")
    )
    
    $urlSafeOrgName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafeOrgName"

    $fullURI = "$Global:BASE_SERVICE_URI/configurations/organizations/name/$urlSafeOrgName"
    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 an organization

.DESCRIPTION
Create an organization

.EXAMPLE
Create an organization with no quota

#Create an organization
$orgDto = New-DTOTemplateObject -DTOTagName "Organization"

#Specify organization details
$orgDto.Organization.name="Customer - No Quota"

#Remove resource quota
$orgDto.Organization.PSObject.Properties.Remove("resourceQuota")

#Create organization users
$user1DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user1DTO.OrganizationUser.userId = "manager"
$user1DTO.OrganizationUser.manager = $true
$user1DTO.OrganizationUser.portalRole = "Customer"

$user2DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user2DTO.OrganizationUser.userId = "superuser"
$user2DTO.OrganizationUser.manager = $false
$user2DTO.OrganizationUser.portalRole = "Customer"

#Add organization user(s)
$orgDto.Organization.Members = @()
$orgDto.Organization.Members += $user1DTO.OrganizationUser
$orgDto.Organization.Members += $user2DTO.OrganizationUser

$taskInfo = New-Organization -orgDto $orgDto

.EXAMPLE
Create an organization with resource quota

#Create a resource quota
$resourceQuota = New-DTOTemplateObject -DTOTagName "ResourceQuota"
$resourceQuota.ResourceQuota.CPUCount = 1000
$resourceQuota.ResourceQuota.memoryInGB = 2000
$resourceQuota.ResourceQuota.stoppedVmsAllowPowerOn = $true
$resourceQuota.ResourceQuota.stoppedVmsCalculateOnlyStorage = $true
$resourceQuota.ResourceQuota.storageInGB = 3000

#Create an organization
$orgDto = New-DTOTemplateObject -DTOTagName "Organization"

#Specify organization details
$orgDto.Organization.name="Customer #1"

#Add resource quota to organization
$orgDto.Organization.resourceQuota = $resourceQuota.ResourceQuota

#Create organization users
$user1DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user1DTO.OrganizationUser.userId = "manager"
$user1DTO.OrganizationUser.manager = $true
$user1DTO.OrganizationUser.portalRole = "Customer"

$user2DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user2DTO.OrganizationUser.userId = "superuser"
$user2DTO.OrganizationUser.manager = $false
$user2DTO.OrganizationUser.portalRole = "Customer"

#Add organization user(s)
$orgDto.Organization.Members = @()
$orgDto.Organization.Members += $user1DTO.OrganizationUser
$orgDto.Organization.Members += $user2DTO.OrganizationUser

$taskInfo = New-Organization -orgDto $orgDto


.EXAMPLE
Create an organization with organizational and member resource quota

#Create a resource quota
$resourceQuota = New-DTOTemplateObject -DTOTagName "ResourceQuota"
$resourceQuota.ResourceQuota.CPUCount = 1000
$resourceQuota.ResourceQuota.memoryInGB = 2000
$resourceQuota.ResourceQuota.stoppedVmsAllowPowerOn = $true
$resourceQuota.ResourceQuota.stoppedVmsCalculateOnlyStorage = $true
$resourceQuota.ResourceQuota.storageInGB = 3000

$user1ResourceQuota = New-DTOTemplateObject -DTOTagName "ResourceQuota"
$user1ResourceQuota.ResourceQuota.CPUCount = 100
$user1ResourceQuota.ResourceQuota.memoryInGB = 200
$user1ResourceQuota.ResourceQuota.storageInGB = 300

#Create an organization
$orgDto = New-DTOTemplateObject -DTOTagName "Organization"

#Specify organization details
$orgDto.Organization.name="Customer #4"

#Add resource quota to organization
$orgDto.Organization.resourceQuota = $resourceQuota.ResourceQuota

#Create organization users
$user1DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user1DTO.OrganizationUser.userId = "manager"
$user1DTO.OrganizationUser.manager = $true
$user1DTO.OrganizationUser.portalRole = "Customer"
Add-Member -InputObject $user1DTO.OrganizationUser -MemberType NoteProperty -Name "resourceQuota" -Value $user1ResourceQuota.ResourceQuota -Force

$user2DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user2DTO.OrganizationUser.userId = "superuser"
$user2DTO.OrganizationUser.manager = $false
$user2DTO.OrganizationUser.portalRole = "Customer"

#Add organization user(s)
$orgDto.Organization.Members = @()
$orgDto.Organization.Members += $user1DTO.OrganizationUser
$orgDto.Organization.Members += $user2DTO.OrganizationUser

$taskInfo = New-Organization -orgDto $orgDto


.EXAMPLE
Create an organization with cost quota

#Create a cost quota
$costQuota = New-DTOTemplateObject -DTOTagName "CostQuota"
$costQuota.CostQuota.dailyCost = 10000

#Create an organization
$orgDto = New-DTOTemplateObject -DTOTagName "Organization"

#Specify organization details
$orgDto.Organization.name="Customer #2"

#Add cost quota to organization
$orgDto.Organization.PSObject.Properties.Remove("resourceQuota")
Add-Member -InputObject $orgDto.Organization -MemberType NoteProperty -Name "costQuota" -Value $costQuota.CostQuota -Force
    
#Create organization users
$user1DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user1DTO.OrganizationUser.userId = "manager"
$user1DTO.OrganizationUser.manager = $true
$user1DTO.OrganizationUser.portalRole = "Customer"

$user2DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user2DTO.OrganizationUser.userId = "superuser"
$user2DTO.OrganizationUser.manager = $false
$user2DTO.OrganizationUser.portalRole = "Customer"

#Add organization user(s)
$orgDto.Organization.Members = @()
$orgDto.Organization.Members += $user1DTO.OrganizationUser
$orgDto.Organization.Members += $user2DTO.OrganizationUser

$taskInfo = New-Organization -orgDto $orgDto

.EXAMPLE
Create an organization with organizational and member cost quota

#Create a cost quota
$costQuota = New-DTOTemplateObject -DTOTagName "CostQuota"
$costQuota.CostQuota.dailyCost = 10000

$user1Quota = New-DTOTemplateObject -DTOTagName "CostQuota"
$user1Quota.CostQuota.dailyCost = 1000

#Create an organization
$orgDto = New-DTOTemplateObject -DTOTagName "Organization"

#Specify organization details
$orgDto.Organization.name="Customer #3"

#Add cost quota to organization
$orgDto.Organization.PSObject.Properties.Remove("resourceQuota")
Add-Member -InputObject $orgDto.Organization -MemberType NoteProperty -Name "costQuota" -Value $costQuota.CostQuota -Force
    
#Create organization users
$user1DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user1DTO.OrganizationUser.userId = "manager"
$user1DTO.OrganizationUser.manager = $true
$user1DTO.OrganizationUser.portalRole = "Customer"
Add-Member -InputObject $user1DTO.OrganizationUser -MemberType NoteProperty -Name "costQuota" -Value $user1Quota.CostQuota -Force

#This user has no member quota
$user2DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user2DTO.OrganizationUser.userId = "superuser"
$user2DTO.OrganizationUser.manager = $false
$user2DTO.OrganizationUser.portalRole = "Customer"

#This AD group has no member quota
$user3DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user3DTO.OrganizationUser.userId = "pv@example.com"
$user3DTO.OrganizationUser.manager = $false
$user3DTO.OrganizationUser.portalRole = "Customer"

#This AD user has no member quota
$user4DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user4DTO.OrganizationUser.userId = "bob@example.com"
$user4DTO.OrganizationUser.manager = $false
$user4DTO.OrganizationUser.portalRole = "Delegated Admin"

#Add organization user(s)
$orgDto.Organization.Members = @()
$orgDto.Organization.Members += $user1DTO.OrganizationUser
$orgDto.Organization.Members += $user2DTO.OrganizationUser
$orgDto.Organization.Members += $user3DTO.OrganizationUser
$orgDto.Organization.Members += $user4DTO.OrganizationUser

$taskInfo = New-Organization -orgDto $orgDto
.EXAMPLE
Create an organization with storage tier resource quota

#Create a resource quota
$resourceQuota = New-DTOTemplateObject -DTOTagName "ResourceQuota"
$resourceQuota.ResourceQuota.CPUCount = 1000
$resourceQuota.ResourceQuota.memoryInGB = 2000
$resourceQuota.ResourceQuota.stoppedVmsAllowPowerOn = $true
$resourceQuota.ResourceQuota.stoppedVmsCalculateOnlyStorage = $true
$resourceQuota.ResourceQuota.storageInGB = 3000

#Set the Use Per Storage Tier Quotas to true
$resourceQuota.ResourceQuota.perStorageTierQuotas = $true

#Create storage tier quotas
$tier1 = New-DTOTemplateObject -DTOTagName "StorageTierQuota"
$tier1.StorageTierQuota.name = "Storage Tier 1"
$tier1.StorageTierQuota.quota = 100.00

$tier2 = New-DTOTemplateObject -DTOTagName "StorageTierQuota"
$tier2.StorageTierQuota.name = "Storage Tier 2"
$tier2.StorageTierQuota.quota = 80.00

$tier3 = New-DTOTemplateObject -DTOTagName "StorageTierQuota"
$tier3.StorageTierQuota.name = "Storage Tier 3"
$tier3.StorageTierQuota.quota = 60.00

$storageTierQuotas = @( $tier1.StorageTierQuota, $tier2.StorageTierQuota, $tier3.StorageTierQuota )
Add-Member -InputObject $resourceQuota.ResourceQuota -MemberType NoteProperty -Name "storageTierQuotas" -Value $storageTierQuotas -Force

#Create an organization
$orgDto = New-DTOTemplateObject -DTOTagName "Organization"

#Specify organization details
$orgDto.Organization.name="Customer #1"

#Add resource quota to organization
$orgDto.Organization.resourceQuota = $resourceQuota.ResourceQuota

#Create organization users
$user1DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user1DTO.OrganizationUser.userId = "manager"
$user1DTO.OrganizationUser.manager = $true
$user1DTO.OrganizationUser.portalRole = "Customer"

$user2DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user2DTO.OrganizationUser.userId = "superuser"
$user2DTO.OrganizationUser.manager = $false
$user2DTO.OrganizationUser.portalRole = "Customer"

#Add organization user(s)
$orgDto.Organization.Members = @()
$orgDto.Organization.Members += $user1DTO.OrganizationUser
$orgDto.Organization.Members += $user2DTO.OrganizationUser

$taskInfo = New-Organization -orgDto $orgDto

.EXAMPLE
Create an organization with member storage tier resource quota

#Create a resource quota
$managerResourceQuota = New-DTOTemplateObject -DTOTagName "ResourceQuota"
$managerResourceQuota.ResourceQuota.CPUCount = 1000
$managerResourceQuota.ResourceQuota.memoryInGB = 2000
$managerResourceQuota.ResourceQuota.stoppedVmsAllowPowerOn = $true
$managerResourceQuota.ResourceQuota.stoppedVmsCalculateOnlyStorage = $true
$managerResourceQuota.ResourceQuota.storageInGB = 3000

#Set the Use Per Storage Tier Quotas to true, otherwise the storage tier quotas will not be set
$managerResourceQuota.ResourceQuota.perStorageTierQuotas = $true

#Create storage tier quotas and assign them to the resource quota
$tier1 = New-DTOTemplateObject -DTOTagName "StorageTierQuota"
$tier1.StorageTierQuota.name = "Storage Tier 1"
$tier1.StorageTierQuota.quota = 100.00

$tier2 = New-DTOTemplateObject -DTOTagName "StorageTierQuota"
$tier2.StorageTierQuota.name = "Storage Tier 2"
$tier2.StorageTierQuota.quota = 80.00

$tier3 = New-DTOTemplateObject -DTOTagName "StorageTierQuota"
$tier3.StorageTierQuota.name = "Storage Tier 3"
$tier3.StorageTierQuota.quota = 60.00

$storageTierQuotas = @( $tier1.StorageTierQuota, $tier2.StorageTierQuota, $tier3.StorageTierQuota )
Add-Member -InputObject $managerResourceQuota.ResourceQuota -MemberType NoteProperty -Name "storageTierQuotas" -Value $storageTierQuotas -Force

#Create an organization
$orgDto = New-DTOTemplateObject -DTOTagName "Organization"

#Specify organization details
$orgDto.Organization.name="Customer #1"

#Set the Use Per Storage Tier Quotas to true at organization level
$orgDto.Organization.resourceQuota.perStorageTierQuotas = $true

#Create organization users
$user1DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user1DTO.OrganizationUser.userId = "manager"
$user1DTO.OrganizationUser.manager = $true
$user1DTO.OrganizationUser.portalRole = "Customer"

#Assign the resource quota to the user
Add-Member -InputObject $user1DTO.OrganizationUser -MemberType NoteProperty -Name "resourceQuota" -Value $managerResourceQuota.ResourceQuota -Force

$user2DTO = New-DTOTemplateObject -DTOTagName "OrganizationUser"
$user2DTO.OrganizationUser.userId = "superuser"
$user2DTO.OrganizationUser.manager = $false
$user2DTO.OrganizationUser.portalRole = "Customer"

#Add organization user(s)
$orgDto.Organization.Members = @()
$orgDto.Organization.Members += $user1DTO.OrganizationUser
$orgDto.Organization.Members += $user2DTO.OrganizationUser

$taskInfo = New-Organization -orgDto $orgDto
#>

function New-Organization {
    [cmdletbinding()]
    Param(
        $orgDto = $(Throw "Provide the organization DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/async/configurations/organizations"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $orgDto
    
    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 organization

.DESCRIPTION
Modify an organization

.EXAMPLE
$org = Get-OrganizationByName -name "Customer #4"
$org.Organization.resourceQuota.CPUCount = 500
$taskInfo = Update-Organization -Id $org.Organization.id -dto $org
#>

function Update-Organization {
    [cmdletbinding()]
    Param(
        [Long] $Id = $(Throw "Provide the organization ID"),
        $dto = $(Throw "Provide the organization DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/async/configurations/organizations/$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
Retrieve all organizations

.DESCRIPTION
Retrieve all organizations

.EXAMPLE
$orgs = Get-Organizations
#>

function Get-Organizations {
    [cmdletbinding()]
    Param(
           [Parameter(Mandatory = $false)]
        [Int] $max=100, #Maximum number of organizations 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
    )

    $fullURI = "$Global:BASE_SERVICE_URI/configurations/organizations?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 quota statistics for an organization or organization member

.DESCRIPTION
Retrieve quota statistics for an organization or organization member

.PARAMETER orgId
The ID of the organization

.PARAMETER member
The name of an organization mamber (optional)

.EXAMPLE
$stats = Get-QuotaStatistics -orgId 123 -member "user@example.com"
#>

function Get-QuotaStatistics {
    [cmdletbinding()]
    Param(
        [Int] $orgId = $(Throw "Provide the organization ID."),

        [Parameter(Mandatory = $false)]
        [String] $member="" 
    )

    $fullURI = "$Global:BASE_SERVICE_URI/configurations/organizations/$orgId/quotastats"
    if ($member -ne "") {
        $fullURI += "?member=$member"
    }
    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 all quota reservations currently open in the system

.DESCRIPTION
Quota reservations are made when a request is submitted. Quota reservation guarantees that an
organization may not submit multiple concurrent requests that would exceed quota in aggregate,
but not individually. This API allows you to view current reservations.

.EXAMPLE
$stats = Get-QuotaReservations
#>

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

    $fullURI = "$Global:BASE_SERVICE_URI/configurations/organizations/quota/reservations"
    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
Clear a quota reservation

.DESCRIPTION
Clear a quota reservation by its ID, useful for a reservation that is judged to be in error.
You can obtain the reservation ID with the method Get-QuotaReservations.

.PARAMETER reservationId
The ID of the reservation

.EXAMPLE
$stats = Clear-QuotaReservation -reservationId 123
#>

function Clear-QuotaReservation {
    [cmdletbinding()]
    Param(
        [Int] $reservationId = $(Throw "Provide the reservation ID.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/configurations/organizations/quota/reservations/$reservationId"
    
    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
Reject a request

.DESCRIPTION
Reject a request

.PARAMETER requestId
The ID of the request to reject

.PARAMETER comment
The reason for the rejection

.PARAMETER workflowId
The ID of an optional command workflow to execute on all deployed targets (to clean up)

.EXAMPLE
Submit-RejectRequest -requestId 42 -comment "Not at the moment"

.EXAMPLE
Submit-RejectRequest -requestId 42 -comment "Not at the moment" -workflowId 13213
#>

function Submit-RejectRequest {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        [String] $comment,
        [Long] $workflowId = -1 #Optional
    )
    
    #Comment length will be validated in backend
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/action/reject"
    if ($workflowId -ne -1) {
        $fullURI = $fullURI + "?wfid="+ $workflowId
    }

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

<#
.SYNOPSIS
Add a comment to a request

.DESCRIPTION
Add a comment to a request

.EXAMPLE
Add-Comment -requestId 42 -comment "This is a comment"
#>

function Add-Comment {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        [String] $comment
    )
    
    #Comment length will be validated in backend
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/action/addcomment"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $comment -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    $true
}
        
<#
.SYNOPSIS
Approve a request

.DESCRIPTION
Approve a request

.EXAMPLE
Approve-Request -requestId 42 -comment "Approved!"
#>


function Approve-Request {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        [String] $comment
    )
    
    #Comment length will be validated in backend
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/action/approve"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $comment -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    $true
}


<#
.SYNOPSIS
Copy a request

.DESCRIPTION
Copy a request

.EXAMPLE
#Get the Latest Service Request and Copy it
$sr = Get-ServiceRequest
$lastId = $sr.RequestCollection.Requests[0].id
Copy-ServiceRequest -sourceRequestId $lastId
#>

function Copy-ServiceRequest {
    [cmdletbinding()]
    Param(
        [Long] $sourceRequestId = $(Throw "Provide the source request ID.")
    )
    
    #Comment length will be validated in backend
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$sourceRequestId/action/copy"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    $true
}

<#
.SYNOPSIS
Assign a user to a request

.DESCRIPTION
Assign a user to a request

.EXAMPLE

$assignmentDto = New-DTOTemplateObject -DTOTagName "RequestAssignment"
$assignmentDto.RequestAssignment.userLoginId = "bob@example.com"
$assignmentDto.RequestAssignment.comment = "New admin"

$result = New-ServiceRequestUserAssignment -requestId 42 -assignmentDto $assignmentDto
#>

function New-ServiceRequestUserAssignment {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the source request ID."),
        $assignmentDto = $(Throw "Provide the user assignment DTO.")
    )
    
    #Comment length will be validated in backend
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/action/assign"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $assignmentDto
    
    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
Link a VM to a request

.DESCRIPTION
Link a VM to a request

.EXAMPLE

$vm = Get-VMs -vmName "VM -DevTest-10002"
$vmId = $vm.VirtualMachineCollection.VirtualMachines | select -ExpandProperty "Id"

$linkObject = New-DTOTemplateObject -DTOTagName "ServiceRequestLink"
$linkObject.ServiceRequestLink.vmId = $vmId
$linkObject.ServiceRequestLink.comment = "Release!"
$linkObject.ServiceRequestLink.releaseVM = $true

$result = New-VMLinkToServiceRequest -requestId 83 -srlinkDto $linkObject

#>

function New-VMLinkToServiceRequest {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the source request ID."),
        $srlinkDto = $(Throw "Provide the vm link DTO.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/action/linkvm"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $srlinkDto
    
    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
Subscribe to request notification emails

.DESCRIPTION
Subscribe to request notification emails

.EXAMPLE
New-ServiceRequestSubscription -accountLoginId "bob@example.com"
#>

function New-ServiceRequestSubscription {
    [cmdletbinding()]
    Param(
        [String] $accountLoginId = $(Throw "Provide the account login ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/action/subscribenotification"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $accountLoginId -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    $true
}


<#
.SYNOPSIS
Unsubscribe from request notification emails

.DESCRIPTION
Unsubscribe from request notification emails

.EXAMPLE
Remove-ServiceRequestSubscription -accountLoginId "bob@example.com"
#>

function Remove-ServiceRequestSubscription {
    [cmdletbinding()]
    Param(
        [String] $accountLoginId = $(Throw "Provide the account login ID.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/action/unsubscribenotification"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $accountLoginId -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    $true
}

<#
.SYNOPSIS
Release a requested component

.DESCRIPTION
Release a requested component

.EXAMPLE
Submit-RequestedComponentRelease -requestId 42 -requestedServiceId 424 -requestedComponentId 34 -comment "Done."
#>

function Submit-RequestedComponentRelease {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        [Long] $requestedServiceId = $(Throw "Provide the request ID."),
        [Long] $requestedComponentId = $(Throw "Provide the request ID."),
        [String] $comment
    )
    
    #Comment length will be validated in backend
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/rservices/$requestedServiceId/components/$requestedComponentId/action/release"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $comment -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    $true
}

<#
.
Fulfill a change request

.DESCRIPTION
Fulfill a change request

.EXAMPLE
Fulfill a change request using requested values with memory override

#Retrieve the list of form elements containing the requested values
$formElementCollection = Get-ChangeRequestFormDetails -requestId 16

#Set memory to 2048
$memoryFormElement = $formElementCollection.RequestFormElementCollection.RequestFormElements | Where-Object {$_.formType -eq "MEMORY"}
$memoryFormElement.value = 2048

#Submit the change request
Submit-ChangeRequestFulfillment -requestId 16 -formElementCollection $formElementCollection

.EXAMPLE
Fulfill a change request using requested values

Submit-ChangeRequestFulfillment -requestId 1 -formElementCollection $null

#>

function Submit-ChangeRequestFulfillment {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        $formElementCollection =  $null
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/action/fulfillchanges"
    Write-Debug "URI is: $fullURI"
    
    if ($formElementCollection -eq $null) {
        $formElementCollection =  New-BaseObject -objectName "RequestFormElementCollection" -objectContent $null
    }
    
    $xml_dto = Convert-ObjectToXml $formElementCollection
    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
Schedule a change request

.DESCRIPTION
Schedule a change request

.EXAMPLE
Specifying time in your local timezone:
$dateTime = Get-Date -Date "2013-09-27 20:00:00"

or

Specifying time in UTC:
$dateTime = Get-Date -Date "2013-09-27 20:00:00Z"

#See the command Submit-ChangeRequestFulfillment for details on how to create form element collections
Submit-ChangeRequestFulfillmentSchedule -requestId 213 -dateTime $dateTime -formElementCollection $formElementCollection
#>

function Submit-ChangeRequestFulfillmentSchedule {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        $dateTime =  $(Throw "Provide a date-time object using the Get-Date function."),
        $formElementCollection =  $null
    )
    
    #First, we convert time into UTC, then we make it suitable for URL
    $urlSafeTimestamp = Get-Date -Date $dateTime.ToUniversalTime() -Format $GLOBAL:DATE_TIME_FORMAT
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/action/schedulechanges/$urlSafeTimestamp"
    Write-Debug "URI is: $fullURI"
    
    if ($formElementCollection -eq $null) {
        $formElementCollection =  New-BaseObject -objectName "RequestFormElementCollection" -objectContent $null
    }
    
    $xml_dto = Convert-ObjectToXml $formElementCollection
    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
Release a request (mark it as completed)

.DESCRIPTION
Release a request (mark it as completed)

.EXAMPLE
Submit-ReleaseRequest -requestId 133 -comment "Done"
#>

function Submit-ReleaseRequest {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        $comment =  $null
    )
    
    #Comment length will be validated in backend
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/action/release"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Body $comment -ContentType text/plain -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    $true
}

<#
.SYNOPSIS
Deploy a requested component

.DESCRIPTION
Deploy a requested component

.EXAMPLE

#Get the request
$request = Get-ServiceRequest -Id 293
$requestId = $request.Request.id

#Get the requested service
$requestService = Get-RequestedServices -requestId $requestId
$requestedServiceId = $requestService.RequestedServiceCollection.RequestedServices[0].id

#Get the requested component
$requestedComponent = Get-RequestedComponents -requestId $requestId -requestedServiceId $requestedServiceId
$requestedComponentId = $requestedComponent.RequestedComponentCollection.RequestedComponents.id

#Create the DTO
$deployParams = New-DTOTemplateObject -DTOTagName "DeploymentParams"

#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"

#Add the attribute
$deployParams.DeploymentParams.CustomAttributes = @()
$deployParams.DeploymentParams.CustomAttributes += $attributeDTO.CustomAttribute

#Create ownership DTO
$ownership = New-DTOTemplateObject -DTOTagName "Ownership"

#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"

#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

#Add the owner
$ownership.Ownership.Owners = @()
$ownership.Ownership.Owners += $userDTO.OwnerInfo

#Add the organization
$ownership.Ownership.organization = $orgReferenceDTO.ManagedObjectReference

#Set ownership
$deployParams.DeploymentParams.ownership = $ownership.Ownership

#Set specific properties for deployment
$deployParams.DeploymentParams.datastoreName = "SandboxVMFS"
$deployParams.DeploymentParams.targetHostName = $null
$deployParams.DeploymentParams.resourcePoolName = "ResourcePool 1"
$deployParams.DeploymentParams.folderName = "Folder 1"
$deployParams.DeploymentParams.clusterName = "Dev Cluster"
$deployParams.DeploymentParams.managementServerName = "devvc2"
$deployParams.DeploymentParams.memoryInMB = 2048
$deployParams.DeploymentParams.numCPU = 2
$deployParams.DeploymentParams.powerOn = $false
$deployParams.DeploymentParams.releaseTarget = $true
$deployParams.DeploymentParams.releaseTargetComment = "It's ready"
$deployParams.DeploymentParams.expiryDate = "2014/10/07"
$deployParams.DeploymentParams.createLinkedClone = $false
$deployParams.DeploymentParams.highAvailability = $false
$deployParams.DeploymentParams.approved = $true
$deployParams.DeploymentParams.suspect = $false
$deployParams.DeploymentParams.endOfLife = $false
    
#Submit component deployment
$result = New-RequestedComponentDeployment -requestId $requestId -requestedServiceId $requestedServiceId -requestedComponentId $requestedComponentId -deploymentParams $deployParams
    
#>

function New-RequestedComponentDeployment {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        [Long] $requestedServiceId  =  $(Throw "Provide the ID of the requested service,"),
        [Long] $requestedComponentId  =  $(Throw "Provide the ID of the requested component."),
        $deploymentParams  =  $(Throw "Provide a deployment parameters DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/rservices/$requestedServiceId/components/$requestedComponentId/action/deploy"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $deploymentParams
    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
Deploy a requested service

.DESCRIPTION
Deploy a requested service

.EXAMPLE

#Get the request
$request = Get-ServiceRequest -Id 293
$requestId = $request.Request.id

#Get the requested service
$requestService = Get-RequestedServices -requestId $requestId
$requestedServiceId = $requestService.RequestedServiceCollection.RequestedServices[0].id

#Get the requested component
$requestedComponent = Get-RequestedComponents -requestId $requestId -requestedServiceId $requestedServiceId
$requestedComponentId = $requestedComponent.RequestedComponentCollection.RequestedComponents.id

#Create the DTO
$deployParams = New-DTOTemplateObject -DTOTagName "DeploymentParams"

#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"

#Add the attribute
$deployParams.DeploymentParams.CustomAttributes = @()
$deployParams.DeploymentParams.CustomAttributes += $attributeDTO.CustomAttribute

#Create ownership DTO
$ownership = New-DTOTemplateObject -DTOTagName "Ownership"

#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"

#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

#Add the owner
$ownership.Ownership.Owners = @()
$ownership.Ownership.Owners += $userDTO.OwnerInfo

#Add the organization
$ownership.Ownership.organization = $orgReferenceDTO.ManagedObjectReference

#Set ownership
$deployParams.DeploymentParams.ownership = $ownership.Ownership

#Set specific properties for deployment
$deployParams.DeploymentParams.datastoreName = "SandboxVMFS"
$deployParams.DeploymentParams.targetHostName = $null
$deployParams.DeploymentParams.resourcePoolName = "ResourcePool 1"
$deployParams.DeploymentParams.folderName = "Folder 1"
$deployParams.DeploymentParams.clusterName = "Dev Cluster"
$deployParams.DeploymentParams.managementServerName = "devvc2"
$deployParams.DeploymentParams.powerOn = $false
$deployParams.DeploymentParams.releaseTarget = $true
$deployParams.DeploymentParams.releaseTargetComment = "It's ready"
$deployParams.DeploymentParams.expiryDate = "2014/10/07"
$deployParams.DeploymentParams.createLinkedClone = $false
$deployParams.DeploymentParams.highAvailability = $false
$deployParams.DeploymentParams.approved = $true
$deployParams.DeploymentParams.suspect = $false
$deployParams.DeploymentParams.endOfLife = $false
    
#Submit component deployment
$result = New-RequestedServiceDeployment -requestId $requestId -requestedServiceId $requestedServiceId -deploymentParams $deployParams

#>

function New-RequestedServiceDeployment {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID."),
        [Long] $requestedServiceId  =  $(Throw "Provide the ID of the requested service,"),
        $deploymentParams  =  $(Throw "Provide a deployment parameters DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/rservices/$requestedServiceId/action/deploy"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $deploymentParams
    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
Create a Published Service Component DTO from an inventory item with dynamic settings initialized to default values

.DESCRIPTION
Create a Published Service Component DTO from an inventory item with dynamic settings initialized to default values

.EXAMPLE

#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 DTO, backed by a VM inventory
$psc = New-DTOTemplateServiceComponent -refId $vm.id -reftype $vm.type

#>

function New-DTOTemplateServiceComponent {
    [cmdletbinding()]
    Param(
        [Long] $refId = $(Throw "Provide the reference ID."),
        [String] $refType = $(Throw "Provide the reference Type.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/custom/templates/servicecomponent?refid=$refId&reftype=$refType"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    $requestResponse  = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method GET 
    Convert-RestResponseOutput $requestResponse.RawContent
}

#########################################################
# Commments
#########################################################
<#
.SYNOPSIS
Retrieve comments for the specified request

.DESCRIPTION
Retrieve comments for the specified request

.EXAMPLE
$comments= Get-CommentForRequest -requestId
#>

function Get-CommentsForRequest {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/requests/$requestId/metadata/comments"
    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 datastore to all deployment destinations

.DESCRIPTION
Add a datastore to all deployment destinations

.PARAMETER datastore
The name of the datastore. If the name contains spaces, surround it with double quotation marks.

.PARAMETER matchexistingtier
If true, only add the datastore to deployment destinations with a datastore tier that matches the tier of the specified datastore.
If false, add to all applicable deployment destinations.
The default value is $false

.EXAMPLE
Add-DatastoreToDeploymentDestinations -datastore "DS 200"

.EXAMPLE
Add-DatastoreToDeploymentDestinations -datastore "DS 200" -matchexistingtier $true

#>

function Add-DatastoreToDeploymentDestinations {
    [cmdletbinding()]
    Param(
        [String] $datastore = $(Throw "Provide the datastore name."),
        [bool] $matchexistingtier = $false
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/deploymentdestinations/metadata/datastores?matchexistingtier=$matchexistingtier"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method PUT -Body $datastore  -ContentType "text/plain"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    
    $true
}

<#
.SYNOPSIS
Remove a datastore from deployment destinations

.DESCRIPTION
Remove a datastore from deployment destinations with a datastore tier that matches the tier of the specified datastore.
If the specified datastore is the only datastore configured for a deployment destination, that destination will be skipped.

.PARAMETER datastore
The name of the datastore. If the name contains spaces, surround it with double quotation marks.

.EXAMPLE
Remove-DatastoreFromDeploymentDestinations -datastore "DS 200"

#>

function Remove-DatastoreFromDeploymentDestinations {
    [cmdletbinding()]
    Param(
        [String] $datastore = $(Throw "Provide the datastore name.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/deploymentdestinations/metadata/datastores"
    Write-Debug "URI is: $fullURI"

    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method DELETE -Body $datastore -ContentType "text/plain" 
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent | out-null
    
    $true
}

<#
.SYNOPSIS
Retrieve all current tasks

.DESCRIPTION
Retrieve all current tasks

.EXAMPLE
$tasksInfos = Get-CurrentTaskInfos

#>

function Get-CurrentTaskInfos {
    [cmdletbinding()]
    Param()
    
    $fullURI = "$Global:BASE_SERVICE_URI/tasks/metadata/current"
    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 task info

.DESCRIPTION
Retrieve task info

.EXAMPLE
$taskInfo = Get-TaskInfo -Id 22424
#>

function Get-TaskInfo {
    [cmdletbinding()]
    Param(
        [Long] $Id = $(Throw "Provide the ID of the task info.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/tasks/$id/info"
    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 service groups

.DESCRIPTION
Retrieve service groups

.EXAMPLE
$groups = Get-Groups

#>

function Get-Groups {
    [cmdletbinding()]
    Param()
    
    $fullURI = "$Global:BASE_SERVICE_URI/groups"
    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 service groups by type

.DESCRIPTION
Retrieve service groups by type

.EXAMPLE
$groups = Get-GroupsByType -groupType "POWER_SCHEDULE_GROUP"
#>

function Get-GroupsByType {
    [cmdletbinding()]
    Param(
        [String] $groupType = $(Throw "Provide the group type.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/groups/type/$groupType"
    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 service group by name

.DESCRIPTION
Retrieve a service group by name

.EXAMPLE
$group = Get-GroupByName -groupType "EXPIRY_GROUP" -name "Default Expiry Group"
#>

function Get-GroupByName {
    [cmdletbinding()]
    Param(
        [String] $groupType = $(Throw "Provide the group type."),
        [String] $name = $(Throw "Provide the group name.")
    )
    
    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafeName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/groups/type/$groupType/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 vCommander credentials

.DESCRIPTION
Retrieves vCommander credentials

.EXAMPLE
$creds = Get-VCmdrCredentials

#>

function Get-VCmdrCredentials {
    [cmdletbinding()]
    Param()
    
    $fullURI = "$Global:BASE_SERVICE_URI/credentials"
    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 vCommander credentials by type

.DESCRIPTION
Retrieve vCommander credentials by type

.EXAMPLE
$creds = Get-VCmdrCredentialsByType -type "GUEST"
#>

function Get-VCmdrCredentialsByType {
    [cmdletbinding()]
    Param(
        [String] $type = $(Throw "Provide the credential type.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/credentials/type/$type"
    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 instance types

.DESCRIPTION
Retrieve all instance types

.EXAMPLE
$instanceTypes = Get-VMInstanceTypes
$specificInstanceType = $instanceTypes.VMInstanceTypeCollection.VMInstanceTypes | Where-Object {$_.name -match "standard.8xlarge"}

#>

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

    $fullURI = "$Global:BASE_SERVICE_URI/vminstancetypes"
    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 instance types for the specified managed system type

.DESCRIPTION
Retrieve all instance types for the specified managed system type

.EXAMPLE
$instanceTypes = Get-VMInstanceTypesByManagementServerType -mstype "AMAZON_AWS"
$specificInstanceType = $instanceTypes.VMInstanceTypeCollection.VMInstanceTypes | Where-Object {$_.name -match "m3.medium"}

#>

function Get-VMInstanceTypesByManagementServerType {
    [cmdletbinding()]
    Param(
        [String] $mstype = $(Throw "Provide the managed system type.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vminstancetypes/mstype/$mstype"
    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
}

#########################################################
# Request the REST service to keep the session alive
#########################################################
function Request-SessionAlive {
    [cmdletbinding()]
    Param()
    Set-PSDebug -Strict
    Set-StrictMode -version Latest
    
    if ( ($Global:Embotics.REQUEST_HEADERS.Count -eq 0) -OR ($Global:Embotics.REQUEST_HEADERS.securityToken -eq $null)) {
        Throw "Login has not been performed. Please initialize the client (Initialize-Configuration) and then connect the client (Connect-Client)."
    }
    
    if ($Global:CREDENTIAL -eq $null) {
        Throw "Unable to keep session alive because login has not been performed."
    }
    
    Write-Debug "Requesting session keepalive for $($Global:Embotics.REQUEST_HEADERS.securityToken)"
    
    $fullURI = "$Global:BASE_SERVICE_URI/sessions/$($Global:Embotics.REQUEST_HEADERS.securityToken)/action/keepalive"
    
    Write-Debug "URI is: $fullURI"
    try {
        Invoke-WebRequest -UseBasicParsing -Uri $fullURI -ContentType text/plain -Headers $Global:Embotics.REQUEST_HEADERS -Method POST | Out-Null
    } catch {
        Write-Debug "Security token $($Global:Embotics.REQUEST_HEADERS.securityToken) already expired. Attempt to re-login.";
        Get-SecurityToken $Global:CREDENTIAL
    }
}

#########################################################
# Function to remove some tags not needed for conversion
#########################################################
function Convert-RestResponseOutput {
    [cmdletbinding()]
    Param(
        [string]$rawXML
    )    
    # Do not set strict as we are aware that some properties can be null during conversion from XML to PS Object
    #Set-PSDebug -Strict
    #Set-StrictMode -version Latest
    
    Write-Debug "Raw response: `n`r$rawXML"
    
    if ($Script:XML_OUTPUT -eq $false) {
        Write-Verbose "Converting output to powershell object."
        $sanitize_xml_string = Remove-TagsForXMLConversionToObject $rawXML
        $psObject = Convert-XmlToObject $sanitize_xml_string
        $psObject
        return;
    }

    #otherwise just output the raw xml
    $rawXML
}

#########################################################
# Function to remove some tags not needed for conversion
#########################################################
function Remove-TagsForXMLConversionToObject($str) {
   $updated = $str.Replace(' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"',"")
   $updated = $updated.Replace('xmlns:xs="http://www.w3.org/2001/XMLSchema"',"")
   $updated = $updated.Replace(' xsitype=',' xsi:type=')
   $updated = $updated.Replace(' xsi:type="xs:int"',"")
   $updated = $updated.Replace(' xsi:type="xs:long"',"")
   $updated = $updated.Replace(' xsi:type="xs:string"',"")
   $updated = $updated.Replace(' xsi:type="arrayList"',"")
   $updated = $updated.Replace('<value >',"<value>")
   $updated = $updated.Replace('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',"")
   
   #Do not decode yet. It might produce tags that results in us failing to parse XML
   #$updated = [System.Web.HttpUtility]::HtmlDecode($updated)
   return $updated
}

############################################
# Function to help convert XML to PS object
############################################
function Convert-XmlToObject {
    [cmdletbinding()]
    Param(
        [String]$xml = $(Throw "Provide the XML string."),
        [String]$objectType =''
    )
    
    return (HelpConvertXmlTo-Object $xml $objectType)
}

############################################
# Function to help convert XML to PS object
############################################
function HelpConvertXmlTo-Object {
    [cmdletbinding()]
    Param(
        [string]$xml,
        [string]$passedType
    )

    Write-Verbose "Converting this XML to object with type ($passedType): $xml"
        
    $newPSObject = New-Object System.Object
    $buffer = 0

    # BASE CASE
    if ($xml.IndexOf("<") -eq -1) {
        return $xml
    }
    
    #TODO re-write this. Don't like the way we navigate open-end tags to create objects. Better to finish parsing first, and then construct objects.
    # RECURSIVE LOOP
    # Iterate across all directly adjacent (and same level) tags
    while ($xml.IndexOf("<", $buffer) -ne -1) {
        Write-Verbose "Looking for a xml open tag."        
        # Find the index and name of an open tag
        $start = $xml.IndexOf("<", $buffer) + 1
        $endIndexOfOpenTag = $xml.IndexOf(">", $start)
        
        # Split by spacing in case the tag contains any elements. ex. <RequestFormElement xsi:type="wsRequestFormCPUElement">
        $tag = $xml.Substring($start, $endIndexOfOpenTag - $start).Split(" ")
        $openTagName = $tag[0]
        
        Write-Verbose "Open tag found: $openTagName at ($start)"
        
        Write-Verbose "Looking at type attribute of tag $openTagName"
        $type = ""
        for ($i = 1; $i -lt $tag.Length; $i++) {
            # If the tag has elements, look for the type element and store it. It needs to be passed down a recursive level
            # to be stored in the correct ps object.
            if ($tag[$i].StartsWith("xsi:type=")) {
                $type = $tag[$i].Substring(10, $tag[$i].Length - 11)
                Write-Verbose "Extracted object type: $type"
            }
        }        
        Write-Verbose "Tag attribute type for $openTagName is: $type"
        
        Write-Verbose "Looking for the associated close tag for $openTagName"                    
        if ($openTagName.EndsWith("/")) {            
            # Handle tags with no close tag ex. <tag/>
            $openTagName = $openTagName.Replace("/", "")
            Write-Verbose "Open tag $openTagName ended with /, removing forward slash"
            
            # Add a $null to
            if ($newPSObject.$openTagName -isnot [system.array]) {
                Write-Verbose "Open tag $openTagName is not an array, adding it as a property to parent object with null value."
                Add-Member -InputObject $newPSObject -MemberType NoteProperty -Name $openTagName -Value $null
            } else {
                Write-Verbose "Open tag $openTagName is an array, assigning null to it."
                $newPSObject += $null
            }

            # Move the buffer index past what has already been analyzed.
            $buffer = $endIndexOfOpenTag + 1
            Write-Verbose "Open tag analyzed. Advancing buffer to $buffer"
            
        } else {
            # Find the close tag
            $endTagName = "</" + $openTagName + ">"
            $foundCorrectClosingTag = $false
            
            $temp_index = $endIndexOfOpenTag + 1
            do {
                Write-Verbose "Looking at end tag starting at index ($temp_index)"
                $endTagIndex = $xml.IndexOf($endTagName, $temp_index)
                $endTagLength = $endTagIndex - ($endIndexOfOpenTag + 1)
            
                Write-Verbose "Close tag found at index ($endTagIndex) | total content length : $endTagLength "
            
                if ($endTagLength -le 0) {
                    # Handle empty tags (ex. <tag></tag>) that we store as ""
                    Write-Verbose "XML tag has no value so assigning empty string as value"
                    $object = ""
                    $foundCorrectClosingTag = $true
                } else {                            
                    $xml_subString = $xml.Substring($endIndexOfOpenTag + 1, $endTagLength)
                    Write-Verbose "Sub XML string at this point: $xml_subString"
                    $openTagCount = ($xml_subString | select-string ("<" + $openTagName + ">") -AllMatches).matches.count
                    $closingTagCount = ($xml_subString | select-string $endTagName -AllMatches).matches.count
                    
                    Write-Verbose "Open tag count: $openTagCount | Closing tag count: $closingTagCount"
                    
                    if ($openTagCount -eq $closingTagCount) {
                        Write-Verbose "Same number of open and closing tags within sub XML string-> found the correct closing tag."
                        $foundCorrectClosingTag = $true;
                    } else {
                        $temp_index = $endTagIndex + 1
                        Write-Verbose "Number of open and closing tags don't match; advancing lookup index to $temp_index"
                    }
                }
            } while ($foundCorrectClosingTag -eq $false)
            
            if ($endTagLength -gt 0) {
                Write-Verbose "Converting everything in between the open and close tag to a PS object. Object class type is: $type"
                $object = (Convert-XmlToObject -xml $xml.Substring($endIndexOfOpenTag + 1, $endTagLength) -objectType $type)
                
                #Dump out info on the converted object
                Write-Verbose "The converted object: $object"
                if ($object -ne $null) {
                    Write-Verbose "The converted object type: $($object.getType().FullName)"

                    #If this is a primative object (string), we perform an HTML decode
                    if( ($object.getType().FullName -eq "System.String") -and ($object.IndexOf("<") -eq -1)) {
                        $object = [System.Web.HttpUtility]::HtmlDecode($object)
                        Write-Debug "HTML decoded: $object"
                    }
                }
            }

            Write-Verbose "Checking if object has the property $openTagName"
            if ($newPSObject.PSObject.Properties.Match($openTagName).Count) {
                Write-Verbose "Property $openTagName exists on $newPSObject"
            
                # This property name already has information stored at it.
                if ($newPSObject.$openTagName -isnot [system.array]) {
                    # Make an array with the existing property and the new property under the same property name.
                    $newArray = @()
                    $newArray += $newPSObject.$openTagName
                    $newArray += $object
                    
                    Write-Verbose "Make an array with the existing property and the new property under the same property name: $($newPSObject.$openTagName)"
                    Add-Member -InputObject $newPSObject -MemberType NoteProperty -Name $openTagName -Value $newArray -Force
                } else {
                    # Add to the existing array at that name
                    Write-Verbose "Add to the existing array: $($newPSObject.$openTagName)"
                    $newPSObject.$openTagName += $object
                }
            } else {
                Write-Verbose "Initializing property $openTagName"
                $rightObject = $object;
                if ("hashtable" -like $type){
                    Write-Verbose "Converting a HashTable $object"
                    $rightObject = New-Object Hashtable
                    if ($object -ne $null -and  
                        [bool]($object.PSobject.Properties.name -match "entry") -and
                        $object.entry -ne $null){
                        if ($object.entry -isnot [system.array]){
                            Write-Verbose "Converting a HashTable was an string creating a list $object"
                            $rightObject.Add($object.entry.key, @($object.entry.value)) 
                        } else {
                            Write-Verbose "Converting a HashTable was a list $object"
                            $object.entry.GetEnumerator() | ForEach-Object {
                                if ($_.value -ne $null) {
                                    if ($_.value -isnot [system.array]){
                                        Write-Verbose "Converting the value $_.value to a list and adding it to the map"
                                        $rightObject.Add($_.key, @($_.value)) 
                                     } else {
                                        Write-Verbose "Adding the value $_.value to the map"
                                        $rightObject.Add($_.key, $_.value) 
                                     }
                                }
                            }
                        }
                
                    }
            
                }
                # This property name has not been used yet. Initialize it.
                Add-Member -InputObject $newPSObject -MemberType NoteProperty -Name $openTagName -Value $rightObject
            }

            # Move the buffer index past what has already been analyzed.
            $buffer = $endTagIndex + $endTagName.Length + 1
            Write-Verbose "Close tag analyzed. Advancing buffer to $buffer"
        }

    }

    Write-Verbose "Done inspecting xml tag"
    
    if ($passedType -ne "") {
        Write-Verbose "Object class type: $passedType"
        # Store the passed type. Will overwrite any preexisting "class" property.
        Add-Member -InputObject $newPSObject -MemberType NoteProperty -Name "class" -Value $passedType -Force
    }

    return $newPSObject
}

function Convert-ObjectToXml() {
    [cmdletbinding()]
    Param(
        $psObject
    )

    return (HelpConvertObjectTo-Xml $psObject "" -1 $false)
}

<#
Get the response body as Int
#>

function Get-ResponseBodyAsInt {
    [cmdletbinding()]
    Param(
        $response
    )
     
    #vCommander will throw 400 or 500 on error
    if ($response.StatusCode -eq 200) {
        [int]$response.Content;
        return
    }
    
    throw "Unable to get response body as int : $($response.RawContent)"
}

#####################################
# Function to help PS object to XML
#####################################
function HelpConvertObjectTo-Xml {
    [cmdletbinding()]
    Param(
        $psObject, 
        [string]$name, 
        [int]$tabs, 
        [boolean]$generateXml
    )

    Write-Verbose "Converting the specified object to xml: $psObject"
    
    $xml = ""

    # BASE CASE 1
    if (($psObject -eq $null) -and $generateXml) {
        $xml += (Return-Tabs $tabs)
        $xml += "<$name/>`n"
        
        Write-Verbose "Converted XML: $xml"
        return $xml
    }

    # Converting to CSV provides the easiest way to get the necessary info
    # about the PS object and its properties.
    Write-Verbose "Convert PS object to csv"
    $csv = ConvertTo-Csv $psObject
    if ($csv -isnot [system.array]) {
        $csv = ,"$csv"
    }
    $type = $csv[0]    
    Write-Verbose "Object type: $type"

    # BASE CASE 2
    if ((-not $type.StartsWith("#TYPE System.Object")) -and $generateXml) {
        $xml += (Return-Tabs $tabs)
    
        $xml += "<$name>$([System.Web.HttpUtility]::HtmlEncode($psObject))</$name>`n"
        
        Write-Verbose "Converted XML: $xml"
        return $xml
    }
    
    $generateTags = (($psObject -isnot [system.array]) -and $generateXml)
    # Generate the open tag.
    if ($generateTags) {
        $xml += (Return-Tabs $tabs)
        Write-Verbose "Generating open tag: <$name>"
        $xml += "<$name>`n"
    }

    # RECURSIVE LOOPS
    if ($psObject -isnot [system.array]) {
        Write-Verbose "Object is not an array: $psObject"
        # The object is not an array. Get the names of all the object's properties.
        $names = $csv[1].Split(",").Trim("`"")
        
        Write-Verbose "Object properties: $names"
        
        # Iterate across its properties.
        for ($i = 0; $i -lt $names.Count; $i++) {
            if ($names -isnot [system.array]) {
                # Handle objects with one property.
                $objName = $names
            } else {
                $objName = $names[$i]
            }
            
            Write-Verbose "Object name: $objname"
            if ($objname -eq "class") {
                # Special case to handle the "class" tag for RequestFormElement.
                $objValue = $psObject.$objname
                $xml = $xml.Insert($xml.IndexOf($name) + $name.Length, " xmlns:xsi=`"http://www.w3.org/2001/XMLSchema-instance`" xsi:type=`"$objValue`"")
            } else {
                Write-Verbose "Converting $psObject.$objname"
                if ($psObject.$objname -is [System.Collections.Hashtable]){
                    $xml += "<$objname>`n"
                        $psObject.$objname.Keys.GetEnumerator() | ForEach-Object { 
                            $xml += "<entry>`n<key>$_</key>`n" 
                            $xml += (HelpConvertObjectTo-Xml $psObject.$objname.Item($_)  'value' ($tabs + 1) $true)
                            $xml += "</entry>`n"
                        }
                    $xml += "</$objname>`n" 
                } else {
                    $xml += (HelpConvertObjectTo-Xml $psObject.$objname $objname ($tabs + 1) $true)            
                }
            }
        }
    } else {
        Write-Verbose "Object is an array: $psObject"
        # The object is an array. Iterate across its elements.
        for ($i = 0; $i -lt $psObject.Count; $i++) {
            Write-Verbose "Converting $psObject[$i]"
            $xml += (HelpConvertObjectTo-Xml $psObject[$i] $name $tabs $true)
        }
    }

    # Generate the close tag.
    if ($generateTags) {
        $xml += (Return-Tabs $tabs)
        Write-Verbose "Generating close tag: </$name>"
        $xml += "</$name>`n"
    }

    return $xml
}

#####################################
# Generate tab in XML string
#####################################
function Return-Tabs([int]$tabs) {
    $xml = ""
    for ($i = 0; $i -lt $tabs; $i++) {
        $xml += " "
    }
    return $xml
}

#####################################################################################################
#
# DTO instantiation
#
#####################################################################################################

<#
.SYNOPSIS
Create a managed object reference

.DESCRIPTION
Create a managed object reference


.EXAMPLE

$mor = New-ManagedObjectReference -id 2424 -type "VIRTUALMACHINE" -displayName

#>

function New-ManagedObjectReference {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the managed object reference."),
        $type = $(Throw "Provide the type of this managed object reference."),
        $displayName = $null
    )
    
    Write-Warning 'Deprecated as of release 2.3, replaced by New-DTOTemplateObject -DTOTagName "ManagedObjectReference"'

    $refObject = New-Object System.Object
    Add-Member -InputObject $refObject -MemberType NoteProperty -Name "id" -Value $id
    Add-Member -InputObject $refObject -MemberType NoteProperty -Name "type" -Value $type
    Add-Member -InputObject $refObject -MemberType NoteProperty -Name "displayName" -Value $displayName
    
    $refObject;
    return;
}

<#
.SYNOPSIS
Create a catalog config DTO

.DESCRIPTION
Create a catalog config DTO

.EXAMPLE

#Create the service users DTO
$superUserDTo = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$superUserDTo.OwnerInfo.loginId = "superuser"

$portalDTo = New-DTOTemplateObject -DTOTagName "OwnerInfo"
$portalDTo.OwnerInfo.loginId = "portal"

#Construct a catalog config
$catalog_config = New-CatalogConfig -publishState "PUBLISHED_SPECIFIC_USERS" -serviceUsers @($portalDTo.OwnerInfo,$superUserDTo.OwnerInfo)

#>

function New-CatalogConfig {
    [cmdletbinding()]
    Param(
        [String] $publishState = $(Throw "Provide the published state."),
        $serviceUsers = $null #See New-OwnerInfoDTO
    )
    
    Write-Warning 'Deprecated as of release 2.3, replaced by New-DTOTemplateObject -DTOTagName "CatalogConfig"'

    $catalogConfig = New-Object System.Object
    Add-Member -InputObject $catalogConfig -MemberType NoteProperty -Name "publishState" -Value $publishState
    
    if ($serviceUsers -ne $null) {
        Add-Member -InputObject $catalogConfig -MemberType NoteProperty -Name "serviceUsers" -Value $serviceUsers -Force
    }
    
    $catalogConfig;
    return;
}

<#
.SYNOPSIS
Create a custom attribute DTO

.DESCRIPTION
Create a custom attribute DTO

.EXAMPLE

$ca = New-CustomAttributeDTO -name "My Custom Attributes" -description "My test CA" -allowableValues yes, no -targetManagedObjectTypes VIRTUALMACHINE, DATASTORE
#>

function New-CustomAttributeDTO {
    [cmdletbinding()]
    Param(
        [String] $name= $(Throw "Provide the custom attribute name."),
        [String] $description= $null, #Provide the custom attribute description
        [String] $value = $null, #Provide the custom attribute value
        [String[]]$allowableValues = $null, #Provide the allowable values for the custom attribute
        $targetManagedObjectTypes= $null #Provide the target managed object type
    )
    
    Write-Warning 'Deprecated as of release 2.3, replaced by New-DTOTemplateObject -DTOTagName "CustomAttribute"'
        
    #Create the custom attribute DTO
    $ca = New-Object System.Object
    Add-Member -InputObject $ca -MemberType NoteProperty -Name "name" -Value $name
    Add-Member -InputObject $ca -MemberType NoteProperty -Name "description" -Value $description
    Add-Member -InputObject $ca -MemberType NoteProperty -Name "value" -Value $value
    Add-Member -InputObject $ca -MemberType NoteProperty -Name "allowedValues" -Value $allowableValues
    if ($targetManagedObjectTypes -ne $null) {
        Add-Member -InputObject $ca -MemberType NoteProperty -Name "targetManagedObjectTypes" -Value $targetManagedObjectTypes
    }

    $ca;
}


<#
.SYNOPSIS
Create an OwnerInfo DTO

.DESCRIPTION
Create an OwnerInfo DTO

.EXAMPLE

$user = New-OwnerInfoDTO -loginId bob@example.com
$primaryOwner = New-OwnerInfoDTO -loginId sally@example.com -primary $true
$itContactOwner = New-OwnerInfoDTO -loginId marry@example.com -itContact $true
#>

function New-OwnerInfoDTO {
    [cmdletbinding()]
    Param(
        [String] $loginId = $(Throw "Provide the login ID of the user/group."),
        [String] $email = $null,
        [bool] $primary = $false,
        [bool] $itContact = $false
    )
    
    Write-Warning 'Deprecated as of release 2.3, replaced by New-DTOTemplateObject -DTOTagName "OwnerInfo"'
    
    #has to be a toggle
    if ($primary -eq $true) {    
        $itContact  = $false    
    }
    
    $ownerInfo = New-Object System.Object
    Add-Member -InputObject $ownerInfo -MemberType NoteProperty -Name "id" -Value '-1'
    Add-Member -InputObject $ownerInfo -MemberType NoteProperty -Name "loginId" -Value $loginId
    Add-Member -InputObject $ownerInfo -MemberType NoteProperty -Name "email" -Value $email
    Add-Member -InputObject $ownerInfo -MemberType NoteProperty -Name "displayName" -Value $null
    Add-Member -InputObject $ownerInfo -MemberType NoteProperty -Name "primary" -Value $primary
    Add-Member -InputObject $ownerInfo -MemberType NoteProperty -Name "itContact" -Value $itContact
    
    $ownerInfo;
}


<#
.SYNOPSIS
Create a user assignment DTO

.DESCRIPTION
Create a user assignment DTO

.EXAMPLE

$userAssignment = New-UserAssignmentDTO -userLoginId "bob@example.com" -comment "This is the new admin."
#>

function New-UserAssignmentDTO {
    [cmdletbinding()]
    Param(
        [String] $userLoginId= $(Throw "Provide the user login ID."),
        [String] $comment = $null
    )
    
    Write-Warning 'Deprecated as of release 2.3, replaced by New-DTOTemplateObject -DTOTagName "RequestAssignment"'
    
    $assignment = New-Object System.Object
    Add-Member -InputObject $assignment -MemberType NoteProperty -Name "userLoginId" -Value $userLoginId
    Add-Member -InputObject $assignment -MemberType NoteProperty -Name "comment" -Value $comment
    
    $assignment
}


<#
.SYNOPSIS
Create an ownership DTO

.DESCRIPTION
Create an ownership DTO

.EXAMPLE

$ownershipDTO = New-OwnershipDTO -organizationName "Org1" -userLoginIds bob@example.com, sally@example.com
#>

function New-OwnershipDTO {
    [cmdletbinding()]
    Param(
        [String] $organizationName= $null, #Provide the organization name
        [String[]]$userLoginIds
    )

    Write-Warning 'Deprecated as of release 2.3, replaced by New-DTOTemplateObject -DTOTagName "Ownership"'
    
    #Create the ownership DTO
    $ownershipDTO = New-Object System.Object
    
    #Organization Reference
    $warningpreference = "SilentlyContinue"
    $orgReference = New-ManagedObjectReference -id -1 -type "ORGANIZATION" -displayName $organizationName
    
    #Create a collection of owners
    $ownerInfoCollection = New-Object System.Object
    
    #Create the owner
    $userArray = @()
    Foreach($eachUserLoginId in $userLoginIds) {
        Write-Verbose "Create ownerinfo structure for $eachUserLoginId"
        $user = New-OwnerInfoDTO -loginId $eachUserLoginId
        $userArray += $user
    }
    
    Write-Verbose "# of users added: $($userArray.length)"
    
    #Set the owner to the owner collection
    Add-Member -InputObject $ownerInfoCollection -MemberType NoteProperty -Name "Owners" -Value $userArray -Force
    
    #Add the org reference
    Add-Member -InputObject $ownershipDTO -MemberType NoteProperty -Name "organization" -Value $orgReference -Force
    
    #Add the owner collection reference
    Add-Member -InputObject $ownershipDTO -MemberType NoteProperty -Name "owners" -Value $ownerInfoCollection -Force
    
    $ownershipDTO    
}


<#
.SYNOPSIS
Create a VM link DTO

.DESCRIPTION
Create a VM link DTO

.EXAMPLE

$srLinkDTO = New-VMLinkToServiceRequestDTO -vmId 42424 -comment "Linking VM" -releaseVM $true
#>

function New-VMLinkToServiceRequestDTO {
    [cmdletbinding()]
    Param(
        [String] $vmId= $(Throw "Provide the VM ID."),
        [String] $comment = $null,
        [bool] $releaseVM = $true
    )

    Write-Warning 'Deprecated as of release 2.3, replaced by New-DTOTemplateObject -DTOTagName "ServiceRequestLink"'

    $srLink = New-Object System.Object
    Add-Member -InputObject $srLink -MemberType NoteProperty -Name "vmId" -Value $vmId
    Add-Member -InputObject $srLink -MemberType NoteProperty -Name "comment" -Value $comment
    Add-Member -InputObject $srLink -MemberType NoteProperty -Name "releaseVM" -Value $releaseVM
    
    $srLink
}

<#
.SYNOPSIS
Create a new custom component type DTO

.DESCRIPTION
Create a new custom component type DTO

.EXAMPLE

$cc = New-CustomComponentTypeDTO -name "Phone" -description "Office Equipment" -cost 10.0
#>

function New-CustomComponentTypeDTO {
    [cmdletbinding()]
    Param(
        [String] $name= $(Throw "Provide a name."),
        [String] $description = $(Throw "Provide a description."),
        [double] $cost= 0
    )
    
    Write-Warning 'Deprecated as of release 2.3, replaced by New-DTOTemplateObject -DTOTagName "CustomComponentType"'

    $customComponentObject = New-Object System.Object
    
    Add-Member -InputObject $customComponentObject -MemberType NoteProperty -Name "id" -Value -1
    Add-Member -InputObject $customComponentObject -MemberType NoteProperty -Name "name" -Value $name
    Add-Member -InputObject $customComponentObject -MemberType NoteProperty -Name "description" -Value $description
    Add-Member -InputObject $customComponentObject -MemberType NoteProperty -Name "cost" -Value $cost
    
    $customComponentObject
}

<#
.SYNOPSIS
Create a base object. All calls through the REST service requires a base object as the container

.DESCRIPTION
Create a base object. All calls through the REST service requires a base object as the container

.EXAMPLE

$object = New-BaseObject "Account" -objectContent $accountDto
#>

function New-BaseObject {
    [cmdletbinding()]
    Param(
        [String] $objectName= $(Throw "The object name."),
        $objectContent = $(Throw "Provide the DTO of the object content.")
    )
    
    $baseObject =  New-Object System.Object
    Add-Member -InputObject $baseObject -MemberType NoteProperty -Name $objectName -Value $objectContent -Force
    
    $baseObject
}

<#
.SYNOPSIS
Clone an object (shallow copy)

.DESCRIPTION
Clone an object (shallow copy)

.EXAMPLE

$clone = Copy-Object -sourceObject $sourceObject
#>

function Copy-Object {
    [cmdletbinding()]
    Param(
        $sourceObject= $(Throw "Provide the target object to copy.")
    )
    
    $cloneObject = New-Object PsObject
    $sourceObject.psobject.properties | % {
        $cloneObject | Add-Member -MemberType $_.MemberType -Name $_.Name -Value $_.Value
    }
    
    $cloneObject
}

<#
.SYNOPSIS
Clone an object (deep copy)

.DESCRIPTION
Clone an object (deep copy)

.EXAMPLE

$clone = Copy-ObjectDeep -sourceObject $sourceObject
#>

function Copy-ObjectDeep {
    [cmdletbinding()]
    Param(
        $sourceObject= $(Throw "Provide the target object to copy.")
    )
    
    #Serialize the object to get everything
    $tempXML = Convert-ObjectToXml $sourceObject 
    
    #Create another object from this serialized data
    $cloneObject = Convert-XmlToObject $tempXML
    
    $cloneObject
}

<#
.SYNOPSIS
Create a new DTO object

.DESCRIPTION
Create a new DTO object

.EXAMPLE

$account = New-DTOObject "Account"

If there are properties you are not interested in, remove them:
    $obj.PSObject.Properties.Remove('Foo')
#>

function New-DTOTemplateObject {
    [cmdletbinding()]
    Param(
        $DTOTagName= $(Throw "Provide the tag name of the DTO.")
    )
    
    if ($DTOTagName -eq $null) {
        throw "XML tag name is empty"
    }

    Write-Debug "Looking for DTO template object inside cache for $DTOTagName"
    $templateDTOObject = $DTOTemplateMap.Get_Item($DTOTagName)
    if ($templateDTOObject -eq $null) {
        Write-Debug "Template object for $DTOTagName not found in cache. Looking up through service"
        
        #Either exception is throw or null
        $templateDTOObject = Get-DTOTemplateObject -DTOTagName $DTOTagName
        if ($templateDTOObject -eq $null) {
            throw "Unable to retrieve DTO template for $DTOTagName (might yet to be supported); manual creation is required."
        }
    
        Write-Debug "Caching $DTOTagName"
        $DTOTemplateMap.Add($DTOTagName, $templateDTOObject)
    } else {
        Write-Debug "$DTOTagName found in cache, using"
    }

    Write-Debug "Creating template DTO object for $DTOTagName"
    
    #We make a clone instead of returning the object itself because we want to use this object as a template
    #So we are avoding references back to this object
    $clone = Copy-ObjectDeep -sourceObject $templateDTOObject

    #return the clone
    $clone
}

<#
.SYNOPSIS
Retrieve a new DTO object template from service

.DESCRIPTION
Retrieve a new DTO object template from service
#>

#Not really used by the user but exposed for debugging purposes
function Get-DTOTemplateObject {
    [cmdletbinding()]
    Param(
        $DTOTagName= $(Throw "Provide the XML tag name of the DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/templates/$DTOTagName"
    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
Clear the cache

.DESCRIPTION
Clear the cache
#>

function Clear-Cache {
    $DTOTemplateMap.Clear()
}

#####################################################################################################
#
# Deprecated Functions
#
#####################################################################################################

<#
.SYNOPSIS
Retrieve task info by ID

.DESCRIPTION
Retrieve task info by ID

.EXAMPLE
$taskInfo = Get-TaskInfoById -id 22424
#>

function Get-TaskInfoById {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the task info.")
    )
    
    Write-Warning "Deprecated as of release 2.4, replaced by Get-TaskInfo."
    Get-TaskInfo -Id $id
}

<#
.SYNOPSIS
Retrieve a service request by ID

.DESCRIPTION
Retrieve a service request by ID

.EXAMPLE
$requests = Get-ServiceRequestById -requestId
#>

function Get-ServiceRequestById {
    [cmdletbinding()]
    Param(
        [Long] $requestId = $(Throw "Provide the request ID.")
    )
    
    Write-Warning "Deprecated as of release 2.3, replaced by Get-ServiceRequest."
    Get-ServiceRequest -Id $requestId
}

function Remove-VMById {
    [cmdletbinding()]
    Param(
        [Long]$Id = $(Throw "Provide the ID of the VM.")
    )
    
    Write-Warning "Deprecated as of release 2.3, replaced by Remove-VM."
    Remove-VM -Id $Id
}

function New-RoleDTO {
    [cmdletbinding()]
    Param(
        [Long] $roleId= $(Throw "Provide the ID of the role."),
        [String] $key = $(Throw "Provide the role key.")
    )
    
    Write-Warning 'Deprecated as of release 2.3, replaced by New-DTOTemplateObject -DTOTagName "Role"'
    
    $role = New-DTOTemplateObject -DTOTagName "Role"
    $role.Role.id = $roleId
    $role.Role.key = $key
    
    $role
}

<#
.SYNOPSIS
Create an empty account DTO

.DESCRIPTION
Create an empty account DTO

.EXAMPLE

$role = New-RoleDTO -roleId 104 -key "COM_USER"
$accountDTO = New-AccountDTO -userid "bob" -password "Secret1" -roleDto $role -emailAddress "bob@example.com" -securitySourceType "LOCAL" -firstName "Bob" -lastName "Smith" -primaryPhoneNo "613-111-2222" -secondaryPhoneNo "613-222-5555" -enabled $true

.EXAMPLE

#AD user

$role = New-RoleDTO -roleId 104 -key "COM_USER"
$accountDTO = New-AccountDTO -userid "bob@example.com" -password "" -emailAddress "" -roleDto $role -securitySourceType "USER_DIRECTORY_USER" -enabled $true -firstName "" -lastName ""
#>

function New-AccountDTO {
    [cmdletbinding()]
    Param(
        [String] $userid= $(Throw "Provide the user ID."),
        [String] $password = $(Throw "Provide the password."),
        [String] $roleDto= $(Throw "Provide role dto."),
        [String] $emailAddress= $(Throw "Provide the email address."),
        [String] $securitySourceType= $(Throw "Provide the security type."),
        [String] $firstName= $(Throw "Provide the first name."),
        [String] $lastName= $(Throw "Provide the last name."),
        [String] $primaryPhoneNo = $null,
        [String] $secondaryPhoneNo =$null,
        [bool] $enabled = $true
    )

    Write-Warning 'Deprecated as of release 2.3, replaced by New-DTOTemplateObject -DTOTagName "Account"'
    
    $accountDto = New-DTOTemplateObject -DTOTagName "Account"
    $accountDto.Account.emailAddress = $emailAddress
    $accountDto.Account.password = $password
    $accountDto.Account.userid = $userid
    $accountDto.Account.enabled = $enabled
    $accountDto.Account.role.id = $role.id
    $accountDto.Account.role.key = $role.key

    $accountDto.Account.firstName = $firstName
    $accountDto.Account.lastName = $lastName
    $accountDto.Account.primaryPhoneNo = $primaryPhoneNo
    $accountDto.Account.secondaryPhoneNo = $secondaryPhoneNo
    $accountDto.Account.securitySourceType = $securitySourceType
    
    $accountDTO
}

<#
.SYNOPSIS
Request deployment of a published service

.DESCRIPTION
Request deployment of a published service

#>

function Request-PublishedService {
    [cmdletbinding()]
    Param(
        [Long] $psId = $(Throw "Provide the ID of the published service."),
        $requestParams = $(Throw "Provide the DTO of the request (deploy) parameters.")
    )
    
    Write-Warning 'Deprecated as of release 2.3, replaced by New-ServiceRequest'
    
    $fullURI = "$Global:BASE_SERVICE_URI/publishedservices/$psId/action/request"
    Write-Debug "URI is: $fullURI"
   
    $xml_dto = Convert-ObjectToXml $requestParams
   
    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
Request deployment of multiple published services

.DESCRIPTION
Request deployment of multiple published services

.EXAMPLE
$result = Request-MultiplePublishedServices -requestParams $requestParams
#>

function Request-MultiplePublishedServices {
    [cmdletbinding()]
    Param(
        $requestParams = $(Throw "Provide the DTO of the request (deploy) parameters.")
    )
   
    Write-Warning 'Deprecated as of release 2.3, replaced by New-MultiServiceRequest'
   
    $fullURI = "$Global:BASE_SERVICE_URI/publishedservices/action/request"
    Write-Debug "URI is: $fullURI"
   
    $xml_dto = Convert-ObjectToXml $requestParams
   
    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
Submit a change request for the specified VM

.DESCRIPTION
Submit a change request for the specified VM
#>

function Request-VMChange {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the VM."),
        $requestParams = $(Throw "Provide the DTO of the request (change) parameters.")
    )

    Write-Warning 'Deprecated as of release 2.3, replaced by New-VMChange'
        
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/requestchange"
    Write-Debug "URI is: $fullURI"
   
    $xml_dto = Convert-ObjectToXml $requestParams
   
    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
Submit a change request for the specified vApp

.DESCRIPTION
Submit a change request for the specified vApp

.EXAMPLE
Request-vAppChange -vAppId 4242424 -requestParams $requestParams
#>

function Request-vAppChange {
    [cmdletbinding()]
    Param(
        [Long]$vAppId = $(Throw "Provide the ID of the vApp"),
        $requestParams = $(Throw "Provide the DTO of the request (change) parameters.")
    )
    
    Write-Warning 'Deprecated as of release 2.3, replaced by New-vAppChange'
        
    $fullURI = "$Global:BASE_SERVICE_URI/vapps/$vAppId/action/requestchange"
    Write-Debug "URI is: $fullURI"
   
    $xml_dto = Convert-ObjectToXml $requestParams
   
    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
Retrieve an account by login ID

.DESCRIPTION
Retrieve an account by login ID

.EXAMPLE
$account = Get-AccountById -loginId "bob@example.com"
#>

function Get-AccountById {
    [cmdletbinding()]
    Param(
        [String] $loginId = $(Throw "Provide the login ID.")
    )
    
    Write-Warning 'Deprecated as of release 2.4, replaced by Get-Account'
    
    $fullURI = "$Global:BASE_SERVICE_URI/accounts/$loginId"
    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 cost models

.DESCRIPTION
Retrieve a collection of cost models

.EXAMPLE
$costModels = Get-CostModels

#>

function Get-CostModels {
    [cmdletbinding()]
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/costmodels"
    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 cost model by name

.DESCRIPTION
Retrieve a cost model by name

.EXAMPLE
$costModel = Get-CostModelByName -name "Cost Model #1"

#>

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

    $urlSafeName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafeName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/costmodels/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 cost model by ID

.DESCRIPTION
Retrieve a cost model by ID

.EXAMPLE
$costModel = Get-CostModelById -id 12584

#>

function Get-CostModelById {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the cost model.")
    )
        
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/costmodels/$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
Remove a cost model

.DESCRIPTION
Remove a cost model

.EXAMPLE

$costModel = Get-CostModelByName -name "Cost Model #1"
$taskInfo = Remove-CostModel -id $costModel.CostModel.id

#>

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

    $fullURI = "$Global:BASE_SERVICE_URI/configurations/costmodels/$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
Modify a cost model

.DESCRIPTION
Modify a cost model

.EXAMPLE
$costModel = Get-CostModelByName -name "Cost Model #1"
$costModel.CostModel.name= "New Name"
#All other properties follow the same pattern as New-CostModel command.

$taskInfo = Update-CostModel -id $costModel.CostModel.id -updatedCostModel $costModel

#>
 

function Update-CostModel { 
    [cmdletbinding()]
    Param( 
        [Long] $id = $(Throw "Provide the ID of the cost model."), 
        $updatedCostModel = $(Throw "Provide the updated cost model DTO.") 
    )

    $fullURI = "$Global:BASE_SERVICE_URI/configurations/costmodels"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $updatedCostModel
    
    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
Create a cost model

.DESCRIPTION
Create a cost model

.EXAMPLE
Create a cost model and set resource and storage costs

$costModel = New-DTOTemplateObject -DTOTagName "CostModel"
$costModel.CostModel.name = "Cost Model #1"

# Retrieve the target
Add-Member -InputObject $costModel.CostModel -MemberType NoteProperty -Name "targets" -Value "PowerShell Rest client" -Force
$mobjectCollection = Get-ManagedObjectByName -name "manta" -type "MANAGEMENTSERVER"
$managementServer = $mobjectCollection.ManagedObjectCollection.ManagedObjectReferences[0]
$costModel.CostModel.targets = @( $managementServer )

# Valid values for view: OPERATIONAL | VMS_AND_TEMPLATES
$costModel.CostModel.view = "OPERATIONAL"

# Resource Costs
$costModel.CostModel.reservedMemoryCost = 500.00
$costModel.CostModel.allocatedMemoryCost = 400.00
$costModel.CostModel.reservedCpuCost = 300.00
$costModel.CostModel.allocatedCpuCost = 200.00

# There is another property called pricingPlan which is only meaningful for cost models with an Amazon EC2 target
# Valid values are "NOT_APPLICABLE", "ON_DEMAND" and "YEARLY"

# Storage Costs
$tier1 = New-DTOTemplateObject -DTOTagName "StorageTierCost"
$tier1.StorageTierCost.name = "Storage Tier 1"
$tier1.StorageTierCost.cost = 100.00

$tier2 = New-DTOTemplateObject -DTOTagName "StorageTierCost"
$tier2.StorageTierCost.name = "Storage Tier 2"
$tier2.StorageTierCost.cost = 80.00

$tier3 = New-DTOTemplateObject -DTOTagName "StorageTierCost"
$tier3.StorageTierCost.name = "Storage Tier 3"
$tier3.StorageTierCost.cost = 60.00

$costModel.CostModel.defaultStorageTier = "Storage Tier 2"

$costModel.CostModel.storageTierCosts = @( $tier1.StorageTierCost, $tier2.StorageTierCost, $tier3.StorageTierCost )

# Valid values for storageCostCalculation: ACTUAL_SIZE | PROVISIONED_SIZE
$costModel.CostModel.storageCostCalculation = "ACTUAL_SIZE"

# Create the Cost Model
$taskId = New-CostModel -costModelDto $costModel


.EXAMPLE
Create a cost model and include operating system and support costs

$costModel = New-DTOTemplateObject -DTOTagName "CostModel"
$costModel.CostModel.name = "Cost Model #2"

# Retrieve the target
Add-Member -InputObject $costModel.CostModel -MemberType NoteProperty -Name "targets" -Value "PowerShell Rest client" -Force
$mobjectCollection = Get-ManagedObjectByName -type "DATACENTER" -name "Engineering"
$dataCenter = $mobjectCollection.ManagedObjectCollection.ManagedObjectReferences[0]
$costModel.CostModel.targets = @( $dataCenter )

# Valid values for view: OPERATIONAL | VMS_AND_TEMPLATES
$costModel.CostModel.view = "VMS_AND_TEMPLATES"

# Operating System Costs
$osCost1 = New-DTOTemplateObject -DTOTagName "OperatingSystemCost"
$osCost1.OperatingSystemCost.os = "Global Default"
$osCost1.OperatingSystemCost.cost = 800

$osCost2 = New-DTOTemplateObject -DTOTagName "OperatingSystemCost"
$osCost2.OperatingSystemCost.os = "Windows Default"
$osCost2.OperatingSystemCost.cost = 900

$costModel.CostModel.operatingSystemCosts = @( $osCost1.OperatingSystemCost, $osCost2.OperatingSystemCost )

# Support Costs
$supportCost1 = New-DTOTemplateObject -DTOTagName "SupportCost"
$supportCost1.SupportCost.os = "Global Default"
$supportCost1.SupportCost.cost = 1000

$supportCost2 = New-DTOTemplateObject -DTOTagName "SupportCost"
$supportCost2.SupportCost.os = "Windows Default"
$supportCost2.SupportCost.cost = 1100

$costModel.CostModel.supportCosts = @( $supportCost1.SupportCost, $supportCost2.SupportCost )
    
# Create the Cost Model
$taskId = New-CostModel -costModelDto $costModel

.EXAMPLE
Create a cost model and include custom attribute costs and set the uptime calculation only when running

$costModel = New-DTOTemplateObject -DTOTagName "CostModel"
$costModel.CostModel.name = "Cost Model #3"

# Retrieve the target
Add-Member -InputObject $costModel.CostModel -MemberType NoteProperty -Name "targets" -Value "PowerShell Rest client" -Force
$mobjectCollection = Get-ManagedObjectByName -type "DATACENTER" -name "Engineering"
$dataCenter = $mobjectCollection.ManagedObjectCollection.ManagedObjectReferences[0]
$costModel.CostModel.targets = @( $dataCenter )

# Valid values for view: OPERATIONAL | VMS_AND_TEMPLATES
$costModel.CostModel.view = "VMS_AND_TEMPLATES"

# Retrieve SLA custom attributes and assign costs to each item
$slaAttribute = Get-CustomAttributeByName -name "SLA"

$gold = New-DTOTemplateObject -DTOTagName "AttributeCostItem"
$gold.AttributeCostItem.name = "GOLD"
$gold.AttributeCostItem.cost = 505.00

$silver= New-DTOTemplateObject -DTOTagName "AttributeCostItem"
$silver.AttributeCostItem.name = "SILVER"
$silver.AttributeCostItem.cost = 400.00

$bronze= New-DTOTemplateObject -DTOTagName "AttributeCostItem"
$bronze.AttributeCostItem.name = "BRONZE"
$bronze.AttributeCostItem.cost = 200.00

# Cost formulas are also supported, for example $osCost2.OperatingSystemCost.costFormula = "100 * 55"
# When a cost formula is specified, the OperatingSystemCost.cost property is ignored
# All costs for an attribute must have the same type of cost, either numeric or formulas

$costItems = @( $gold.AttributeCostItem, $silver.AttributeCostItem, $bronze.AttributeCostItem )
Add-Member -InputObject $slaAttribute.CustomAttribute -MemberType NoteProperty -Name "costItems" -Value $costItems -Force

# Retrieve the Project Code custom attribute
# This is a text-type attribute and does not require further initialization
# since the cost is assigned to the actual attribute on the VMs
$projectCodeAttribute = Get-CustomAttributeByName -name "Project Code"

$costAttributes = @( $slaAttribute.CustomAttribute, $projectCodeAttribute.CustomAttribute )
Add-Member -InputObject $costModel.CostModel -MemberType NoteProperty -Name "costAttributes" -Value $costAttributes -Force

# Uptime Costs
$cpuUptime = New-DTOTemplateObject -DTOTagName "UptimeCalc"
$cpuUptime.UptimeCalc.name = "CPU"

# Valid values for calcUptime: CONTINUOUS | WHILE_RUNNING
$cpuUptime.UptimeCalc.calcUptime = "CONTINUOUS"
        
$slaUptime = New-DTOTemplateObject -DTOTagName "UptimeCalc"
$slaUptime.UptimeCalc.name = "SLA"
$slaUptime.UptimeCalc.calcUptime = "WHILE_RUNNING"

$costModel.CostModel.uptimeCalcs = @( $cpuUptime.UptimeCalc, $slaUptime.UptimeCalc )

# Create the Cost Model
$taskId = New-CostModel -costModelDto $costModel

#>

function New-CostModel {
    [cmdletbinding()]
    Param(
        $costModelDto = $(Throw "Provide the cost model DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/costmodels"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $costModelDto
    
    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
Enable debug mode

.DESCRIPTION
Enable debug mode

.EXAMPLE

#>

function Enable-DebugMode {
    Write-Warning 'Deprecated as of release 2.4, use the -debug switch on any function'
    $Global:DebugPreference = "Continue"
}

<#
.SYNOPSIS
Disable debug mode

.DESCRIPTION
Disable debug mode

.EXAMPLE
#>

function Disable-DebugMode {
    Write-Warning 'Deprecated as of release 2.4, use DebugPreference = "SilentlyContinue" in your script.'
    $Global:DebugPreference = "SilentlyContinue"
}

<#
.SYNOPSIS
Mount a media file for the specified VM's disk

.DESCRIPTION
Mount a media file for the specified VM's disk

.PARAMETER vmid
The VM ID

.PARAMETER diskkey
The VM's disk key

.PARAMETER mediaFileDto
The media file mount paramter DTO

.EXAMPLE
Mount a media file for a VM's CD-ROM

#Get the VM
$vms = Get-VMByRemoteId -vmRemoteId 'vm-2566'
$vm = $vms.VirtualMachineCollection.VirtualMachines[0]

# Find the CD-ROM
foreach ($disk in $vm.disks) {
    if ($disk.diskType -eq "CDROM") {
        $diskkey = $disk.key
        $mediaFileMountDto = New-DTOTemplateObject -DTOTagName "MediaFileMountSpec"
        $mediaFileMountDto.MediaFileMountSpec.mediaFilePath = "[DataStoreName] Folder/SubFolder/mediaFile.iso"

        $taskInfo = Mount-MediaFile -vmid $vm.id -diskkey $diskkey -mediaFileDto $mediaFileMountDto
        break
    }
}
#>

function Mount-MediaFile {
    [cmdletbinding()]
    Param(
        [Long] $vmid = $(Throw "Provide the ID of the VM."),
        [Long] $diskkey = $(Throw "Provide the key of the VM's disk."),
        $mediaFileDto = $(Throw "Provide the DTO of the media file mount paramter")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmid/disks/$diskkey/mount"
    Write-Debug "URI is: $fullURI"
   
    $xml_dto = Convert-ObjectToXml $mediaFileDto
   
    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
Unmount a media file from the specified VM's disk

.DESCRIPTION
Unmount a media file from the specified VM's disk

.PARAMETER vmid
The virtual machine ID

.PARAMETER diskkey
The VM's disk key

.EXAMPLE
Unmount a media file from a VM's CD-ROM

#Get the VM
$vms = Get-VMByRemoteId -vmRemoteId 'vm-2566'
$vm = $vms.VirtualMachineCollection.VirtualMachines[0]

# Find the CD-ROM
foreach ($disk in $vm.disks) {
    if ($disk.diskType -eq "CDROM") {
        $diskkey = $disk.key

        $taskInfo = Dismount-MediaFile -vmid $vm.id -diskkey $diskkey
        break
    }
}
#>

function Dismount-MediaFile {
    [cmdletbinding()]
    Param(
        [Long] $vmid = $(Throw "Provide the ID of the VM."),
        [Long] $diskkey = $(Throw "Provide the key of the VM's disk.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmid/disks/$diskkey/unmount"
    Write-Debug "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -UseBasicParsing -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -ContentType "application/xml" -Body "" -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

<#
.SYNOPSIS
Retrieve a collection of media folders

.DESCRIPTION
Retrieve a collection of media folders

.PARAMETER name
The name of the media folder

.PARAMETER orgid
The ID of the organization assigned to the media folder

.PARAMETER max
The maximum number of media folders to retrieve

.EXAMPLE
$mfs = Get-MediaFolders -max 10

.EXAMPLE
$mfs = Get-MediaFolders -name 'MyFolder'

.EXAMPLE
$mfs = Get-MediaFolders -orgid 456987

#>

function Get-MediaFolders {
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory = $false)]
        [String] $name, #Provide the name of the media folder
        
        [Parameter(Mandatory = $false)]
        [String] $orgid=-1, #Provide the organization ID
        
        [Parameter(Mandatory = $false)]
        [Int] $max=100 #Maximum number of media folders to return
    )
    
    $urlSafeName = $name
    if ($name -ne $null) {
        $urlSafeName = [System.Web.HttpUtility]::UrlEncode($name)
        Write-Debug "Before encoding media folder name: $name | After encode: $urlSafeName"
    }
    
    $fullURI = "$Global:BASE_SERVICE_URI/mediafolders?&name=$urlSafeName&orgid=$orgid&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 media folder by ID.

.DESCRIPTION
Retrieve a media folder by ID.

.PARAMETER id
The media folder ID

.EXAMPLE
$mediaFolders = Get-MediaFolder -id 456987
#>

function Get-MediaFolder {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the media folder.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/mediafolders/$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 a media folder

.DESCRIPTION
Create a media folder

.PARAMETER mfDto
A media folder DTO

.EXAMPLE
Create a global media folder

#Create a media folder
$mfDto = New-DTOTemplateObject -DTOTagName "MediaFolder"

#Specify name and description
$mfDto.MediaFolder.name="GlobalMediaFolder"
$mfDto.MediaFolder.description="description example"

#Specify datastore and path
$mobjectCollection = Get-ManagedObjectByName -name "myDatastore" -type "DATASTORE"
$datastoreDto = $mobjectCollection.ManagedObjectCollection.managedObjects[0]
$mfDto.MediaFolder.datastore = $datastoreDto
$mfDto.MediaFolder.path = "ISOFolder"

#Specify that it's global
$mfDto.MediaFolder.global = $true
#Clear organizations
$mfDto.MediaFolder.organizations = @()

$taskInfo = New-MediaFolder -mfDto $mfDto

.EXAMPLE
Create an organization-specific media folder

#Create a media folder
$mfDto = New-DTOTemplateObject -DTOTagName "MediaFolder"

#Specify name and description
$mfDto.MediaFolder.name="Org1MediaFolder"
$mfDto.MediaFolder.description="description example"

#Specify datastore and path
$mobjectCollection = Get-ManagedObjectByName -name "myDatastore" -type "DATASTORE"
$datastoreDto = $mobjectCollection.ManagedObjectCollection.managedObjects[0]
$mfDto.MediaFolder.datastore = $datastoreDto
$mfDto.MediaFolder.path = "ISOFolderOrg1"

#Specify organization
$org1 = Get-OrganizationByName -name "Org1"
#Create a reference to this organization. We really just need the id and type.
$mfDto.MediaFolder.global = $false
$mfDto.MediaFolder.organizations = @()
$mfDto.MediaFolder.organizations += $org1.Organization

$taskInfo = New-MediaFolder -mfDto $mfDto
#>

function New-MediaFolder {
    [cmdletbinding()]
    Param(
        $mfDto = $(Throw "Provide the media folder DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/mediafolders"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $mfDto
    
    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 media folder

.DESCRIPTION
Modify a media folder

.PARAMETER id
The media folder ID

.PARAMETER mfDto
The updated media folder DTO

.EXAMPLE
$mfDto = Get-MediaFolder -id 456987
$id = $mfDto.MediaFolder.id

#Change any other properties as needed
$mfDto.MediaFolder.name="UpdatedMediaFolder"

$taskInfo = Update-MediaFolder -id $id -mfDto $mfDto
#>

function Update-MediaFolder {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the media folder."),
        $mfDto = $(Throw "Provide the updated media folder DTO.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/mediafolders/$id"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $mfDto
    
    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 media folder

.DESCRIPTION
Remove a media folder

.PARAMETER id
The media folder ID

.EXAMPLE
$taskInfo = Remove-MediaFolder -id 456987
#>

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

    $fullURI = "$Global:BASE_SERVICE_URI/mediafolders/$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
Creates a new VM snapshot

.DESCRIPTION
Create a new VM snapshot

.PARAMETER vmId
The ID of the VM

.PARAMETER name
The name of the snapshot

.PARAMETER quiesce
True to quiesce the VM before taking the snapshot

.PARAMETER memory
True to capture memory with the snapshot

.EXAMPLE
$taskInfo = New-VMSnapshot -vmId 456987 -name "New Snapshot" -memory $true
#>

function New-VMSnapshot {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the virtual machine."),
        
        [String] $name = $(Throw "Provide the name of the snapshot."),
        
        [Parameter(Mandatory = $false)]
        [String] $description = "", #Provide a description
        
        [Parameter(Mandatory = $false)]
        [bool] $quiesce = $false, #True to quiesce the VM before taking the snapshot
        
        [Parameter(Mandatory = $false)]
        [bool] $memory = $false #True to capture memory
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/createsnapshot"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    $postParams = @{}
    $postParams.Add("name", $name)
    $postParams.Add("description", $description)
    $postParams.Add("quiesce", $quiesce)
    $postParams.Add("memory", $memory)

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

<#
.SYNOPSIS
Delete a VM snapshot

.DESCRIPTION
Delete a VM snapshot

.PARAMETER vmId
The ID of the VM

.PARAMETER name
The name of the snapshot to delete (optional)

.PARAMETER remoteId
The remote ID of the snapshot to delete (optional)

.PARAMETER snapshotId
The ID of the snapshot to delete (optional)

.EXAMPLE
$taskInfo = Remove-VMSnapshot -vmId 456987 -name "New Snapshot"
#>

function Remove-VMSnapshot {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the virtual machine."),
        
        [Parameter(Mandatory = $false)]
        [String] $name = $null,
        
        [Parameter(Mandatory = $false)]
        [String] $remoteId = $null,
        
        [Parameter(Mandatory = $false)]
        [Long] $snapshotId = $null
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/deletesnapshot"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    $postParams = @{}
    if ($name -ne $null) {
        $postParams.Add("name", $name)
    }
    if ($remoteId -ne $null) {
        $postParams.Add("remoteId", $remoteId)
    }
    if ($snapshotId -ne $null) {
        $postParams.Add("snapshotId", $snapshotId)
    }

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

<#
.SYNOPSIS
Reset a VM snapshot

.DESCRIPTION
Reset a VM snapshot

.PARAMETER vmId
The ID of the VM

.PARAMETER name
The name of the snapshot to reset (optional)

.PARAMETER remoteId
The remote ID of the snapshot to reset (optional)

.PARAMETER snapshotId
The ID of the snapshot to reset (optional)

.EXAMPLE
$vmCollection = Get-VMs -vmName 'QA vC6.5/ESXi5.5 2008 R2'
$vmId = $vmCollection.VirtualMachineCollection.VirtualMachines[0].id

$snapshotCollection = Get-VMSnapshots -vmId $vmId
$snapshots = $snapshotCollection.VirtualMachineSnapshotCollection.items

$taskInfo = Reset-VMSnapshot -vmId $vmId -name $snapshots[1].name

.EXAMPLE
$vmCollection = Get-VMs -vmName 'QA vC6.5/ESXi5.5 2008 R2'
$vmId = $vmCollection.VirtualMachineCollection.VirtualMachines[0].id

$snapshotCollection = Get-VMSnapshots -vmId $vmId
$snapshots = $snapshotCollection.VirtualMachineSnapshotCollection.items

$taskInfo = Reset-VMSnapshot -vmId $vmId -remoteId $snapshots[1].remoteId

.EXAMPLE
$vmCollection = Get-VMs -vmName 'QA vC6.5/ESXi5.5 2008 R2'
$vmId = $vmCollection.VirtualMachineCollection.VirtualMachines[0].id

$snapshotCollection = Get-VMSnapshots -vmId $vmId
$snapshots = $snapshotCollection.VirtualMachineSnapshotCollection.items

$taskInfo = Reset-VMSnapshot -vmId $vmId -snapshotId $snapshots[1].id
#>

function Reset-VMSnapshot {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the virtual machine."),
        
        [Parameter(Mandatory = $false)]
        [String] $name = $null,
        
        [Parameter(Mandatory = $false)]
        [String] $remoteId = $null,
        
        [Parameter(Mandatory = $false)]
        [Long] $snapshotId = $null
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/action/revertsnapshot"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    $postParams = @{}
    if ($name -ne $null) {
        $postParams.Add("name", $name)
    }
    if ($remoteId -ne $null) {
        $postParams.Add("remoteId", $remoteId)
    }
    if ($snapshotId -ne $null) {
        $postParams.Add("snapshotId", $snapshotId)
    }

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

<#
.SYNOPSIS
Retrieve the list of snapshots for a VM

.DESCRIPTION
Retrieve the list of snapshots for a VM

.PARAMETER vmId
The ID of the Virtual Machine.

.EXAMPLE
$snapshots = Get-VMSnapshots -vmId 456987
#>

function Get-VMSnapshots {
    [cmdletbinding()]
    Param(
        [Long] $vmId = $(Throw "Provide the ID of the virtual machine.")
    )

    $fullURI = "$Global:BASE_SERVICE_URI/vms/$vmId/snapshots"

    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
Clear the deployment destination cache

.DESCRIPTION
Clear the deployment destination cache

.EXAMPLE
#
$result = Clear-DeploymentDestinationCache
#>

function Clear-DeploymentDestinationCache {
    [cmdletbinding()]
    Param()

    $fullURI = "$Global:BASE_SERVICE_URI/deploymentdestinations/cache/action/clear"
    Write-Verbose "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -ContentType "text/plain"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    
    if ($requestResponse.StatusCode -eq 200){
        $true
        return
    }
    
    $false
}

<#
.SYNOPSIS
Revalidate deployment destinations

.DESCRIPTION
Revalidate deployment destinations

.EXAMPLE
#
$result = Invoke-DeploymentDestinationValidations
#>

function Invoke-DeploymentDestinationValidations {
    [cmdletbinding()]
    Param()

    $fullURI = "$Global:BASE_SERVICE_URI/deploymentdestinations/action/revalidate"
    Write-Verbose "URI is: $fullURI"
   
    Request-SessionAlive
    
    try{
        $requestResponse = Invoke-WebRequest -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST -ContentType "text/plain"
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    
    if ($requestResponse.StatusCode -eq 200){
        $true
        return
    }
    
    $false
}


<#
.SYNOPSIS
Remove a storage reservation

.DESCRIPTION
Remove a storage reservation

.EXAMPLE
Remove-StorageReservation -reservationId 243240

#>

function Remove-StorageReservation() {
    [cmdletbinding()]
    Param(
        $reservationId = $(Throw "Provide the ID of the storage reservation.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/storagereservations/$reservationId";
    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
Retrieve a collection of storage reservations

.DESCRIPTION
Retrieve a collection of storage reservations

.EXAMPLE
#
$reservations = Get-StorageReservations

#>

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

    $fullURI = "$Global:BASE_SERVICE_URI/storagereservations"
    
    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
Helper method for retreiving an Embotics error message

.DESCRIPTION
Helper method for retreiving an Embotics error message

.EXAMPLE
#
Convert-ErrorForUser $exception

#>

function Convert-ErrorForUser {
    Param(
        [System.Object] $errorRecord
    )
    
    if ($errorRecord -eq $null) {
        return;
    }
    
    if ($errorRecord.getType().name -eq "ErrorRecord") {
        
        # vCommander error record
        if ($errorRecord.ErrorDetails -ne $null) {
            $out = $errorRecord.ErrorDetails.Message
        
        # Method call exception - eg. operation timed out
        } else {
            $out = $errorRecord.Exception
        }
    } else {
        $result = $errorRecord.Exception.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($result)
        $responseBody = [xml]$reader.ReadToEnd()
        if($responseBody.APICallErrorResult){
            $out = $responseBody.APICallErrorResult.Error.errorMessage
        }else {
            $out = $errorRecord.Exception
        }
    }

    $out
}

<#
.SYNOPSIS
Retrieve all placement attributes

.DESCRIPTION
Retrieve all placement attributes

.EXAMPLE
$placementAttributes = Get-PlacementAttributes
#>

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

    $fullURI = "$Global:BASE_SERVICE_URI/placementattributes"
    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 placement attribute by ID

.DESCRIPTION
Retrieve a placement attribute by ID

.EXAMPLE
$ca = Get-PlacementAttributeById -id 22424
#>

function Get-PlacementAttributeById {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the placement attribute.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/placementattributes/$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 placement attribute by name

.DESCRIPTION
Retrieve a placement attribute by name

.EXAMPLE
$placementAttribute = Get-PlacementAttributeByName -name "SLA"
#>

function Get-PlacementAttributeByName {
    [cmdletbinding()]
    Param(
        [String] $name = $(Throw "Provide the name of the placement attribute.")
    )
    
    $urlSafePAName = [System.Web.HttpUtility]::UrlEncode($name)
    Write-Debug "Before encoding name: $name | After encode: $urlSafePAName"
    
    $fullURI = "$Global:BASE_SERVICE_URI/placementattributes/name/$urlSafePAName"
    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 placement attribute by name or ID

.DESCRIPTION
Retrieve a placement attribute by name or ID

.PARAMETER name
The name of the placement attribute (optional, one of name or ID must be specified)

.PARAMETER id
The ID of the placement attribute (optional, one of name or ID must be specified,)

.EXAMPLE
$placementAttribute = Get-PlacementAttribute -name "SLA"

.EXAMPLE
$placementAttribute = Get-PlacementAttribute -id 73485
#>

function Get-PlacementAttribute {
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory = $false)]
        [String] $name = "",

        [Parameter(Mandatory = $false)]
        [Long] $id = $null
    )

    if ($id -ne 0) {
        return Get-PlacementAttributeById -id $id
    } elseif ($name -ne "") {
        return Get-PlacementAttributeByName -name $name
    } else {
        throw "One of name or ID is required"
    }
}

<#
.SYNOPSIS
Modify a placement attribute

.DESCRIPTION
Modify a placement attribute

.EXAMPLE
# Update the description of a placement attribute
$placementAttribute = Get-PlacementAttributeByName -name "ToUpdate"
$placementAttribute.PlacementAttribute.description = "Updated to a new description"

$updatedPlacementAttribute = Update-PlacementAttribute -id $placementAttribute.PlacementAttribute.id $placementAttribute

.EXAMPLE
# Add an allowed value to a list-type placement attribute

# Get the placement attribute to update
$placementAttribute = Get-PlacementAttributeByName -name "ToUpdate"

# Add a new value (if there's only a single value it will need to be converted to an array first)
$placementAttribute.PlacementAttribute.allowedValues += "new value"

$updatedPlacementAttribute = Update-PlacementAttribute -id $placementAttribute.PlacementAttribute.id $placementAttribute

.EXAMPLE
# Add a child allowed value whose parent already exists in the sublist-type placement attribute
$placementAttribute = Get-PlacementAttributeByName -name "ToUpdate"

$placementAttribute.SublistPlacementAttribute.allowedValues['existing parent value'] += "new child value"

$updatedPlacementAttribute = Update-PlacementAttribute -id $placementAttribute.PlacementAttribute.id $placementAttribute

.EXAMPLE
# Add a child allowed value whose parent is new to the sublist-type placement attribute
$placementAttribute = Get-PlacementAttributeByName -name "ToUpdate"

# Note that this needs to explicitly be an array
$placementAttribute.SublistPlacementAttribute.allowedValues['new parent value'] += @("new child value")

$updatedPlacementAttribute = Update-PlacementAttribute -id $placementAttribute.PlacementAttribute.id $placementAttribute

#>

function Update-PlacementAttribute {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the placement attribute."),
        $placementAttributeDTo = $(Throw "Provide the placement attribute DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/placementattributes/$id"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $placementAttributeDTo
    
    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 placement attribute

.DESCRIPTION
Remove a placement attribute

.EXAMPLE
$placementAttribute = Get-PlacementAttributeByName -name "SLA"
$result = Remove-PlacementAttribute -id $placementAttribute.PlacementAttribute.id
#>

function Remove-PlacementAttribute {
    [cmdletbinding()]
    Param(
        [Long] $id = $(Throw "Provide the ID of the placement attribute.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/placementattributes/$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 placement attribute

.DESCRIPTION
Create a placement attribute

.EXAMPLE
# Create a new list-type placement attribute
$attribute = New-DTOTemplateObject -DTOTagName "PlacementAttribute"
$attribute.PlacementAttribute

$attribute.PlacementAttribute.name = "Province"
$attribute.PlacementAttribute.description = "The principal administrative division of Canada"
$attribute.PlacementAttribute.isEditableInPortal = $true
$attribute.PlacementAttribute.allowedValues = @("Ontario", "Quebec")

New-PlacementAttribute -placementAttributeDTo $attribute

.EXAMPLE
# Create a new sublist-type placement attribute

# First, get the attribute this is going to be nested under
$parentAttribute = Get-PlacementAttributeByName -name "Province"

# Get the appropriate template
$childAttribute = New-DTOTemplateObject -DTOTagName "SublistPlacementAttribute"
$childAttribute.SublistPlacementAttribute

# Populate the template
$childAttribute.SublistPlacementAttribute.name = "City"
$childAttribute.SublistPlacementAttribute.description = "The urbanized area"
$childAttribute.SublistPlacementAttribute.isEditableInPortal = $false
$childAttribute.SublistPlacementAttribute.parentPlacementAttributeId = $parentAttribute.PlacementAttribute.id

# Build and assign the allowed values map (note the keys must match $parentAttribute.PlacementAttribute.allowedValues )
$allowedValues = @{}
$allowedValues.add("Ontario", @("Ottawa", "Kanata"))
$allowedValues.add("Quebec", @("Quebec City", "Montreal"))
$childAttribute.SublistPlacementAttribute.allowedValues = $allowedValues

New-PlacementAttribute -placementAttributeDTo $childAttribute
#>

function New-PlacementAttribute { 
    [cmdletbinding()]
    Param(
        $placementAttributeDTo = $(Throw "Provide the placement attribute DTO.")
    )
    
    $fullURI = "$Global:BASE_SERVICE_URI/placementattributes"
    Write-Debug "URI is: $fullURI"
    
    $xml_dto = Convert-ObjectToXml $placementAttributeDTo
    
    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
Trigger cost reload.

.DESCRIPTION
Ths will create a task for vCommander to reload all costs.

.EXAMPLE
Invoke-ReloadCosts
#>

function Invoke-ReloadCosts {
    [cmdletbinding()]
    
    $fullURI = "$Global:BASE_SERVICE_URI/configurations/costmodels/action/reload"
    Write-Debug "URI is: $fullURI"
    
    Request-SessionAlive
   
    try{
        $requestResponse = Invoke-WebRequest -Uri $fullURI -Headers $Global:Embotics.REQUEST_HEADERS -Method POST
    }
    catch{
        Write-Error (Convert-ErrorForUser $_)
        Throw $_.Exception
    }
    Convert-RestResponseOutput $requestResponse.RawContent
}

#####################################################################################################
#
# Functions export
#
#####################################################################################################

#TODO Make visible to this and internal rest client module only
Export-ModuleMember -function *Request-SessionAlive*
Export-ModuleMember -function *Convert-RestResponseOutput*

Export-ModuleMember -function *Get-SecurityToken*

Export-ModuleMember -function *Get-TaskInfo*
Export-ModuleMember -function *Get-CurrentTaskInfos*

Export-ModuleMember -function *New-VMShare*

Export-ModuleMember -function *Get-NetworkZoneById*
Export-ModuleMember -function *Get-NetworkZoneByName*
Export-ModuleMember -function *Get-NetworkZones*
Export-ModuleMember -function *New-NetworkZone*
Export-ModuleMember -function *Update-NetworkZone*
Export-ModuleMember -function *Remove-NetworkZone*

Export-ModuleMember -function *Set-NetworkZoneToNetwork*
Export-ModuleMember -function *Set-NetworkZoneToSubnet*

Export-ModuleMember -function *Get-ManagementServers*
Export-ModuleMember -function *Get-ManagementServerById*
Export-ModuleMember -function *New-ManagementServer*
Export-ModuleMember -function *Get-RuntimeServers*
Export-ModuleMember -function *Get-Datacenters*
Export-ModuleMember -function *Get-Folders*
Export-ModuleMember -function *Get-FoldersByName*

Export-ModuleMember -function *Get-Clusters*
Export-ModuleMember -function *Get-ClusterByName*

Export-ModuleMember -function *Initialize-Configuration*
Export-ModuleMember -function *Connect-Client*
Export-ModuleMember -function *Connect-Client2*
Export-ModuleMember -function *Disconnect-Client*
Export-ModuleMember -function *Switch-Organization*
Export-ModuleMember -function *Get-CustomAttributes*
Export-ModuleMember -function *Get-CustomAttributeById*
Export-ModuleMember -function *Get-CustomAttributeByName*
Export-ModuleMember -function *Update-CustomAttribute*
Export-ModuleMember -function *Remove-CustomAttribute*
Export-ModuleMember -function *New-CustomAttribute*

Export-ModuleMember -function *New-VMSnapshot*
Export-ModuleMember -function *Remove-VMSnapshot*
Export-ModuleMember -function *Reset-VMSnapshot*
Export-ModuleMember -function *Get-VMSnapshots*

Export-ModuleMember -function *Get-RunningWorkflows*
Export-ModuleMember -function *Get-RunningWorkflowsByType*
Export-ModuleMember -function *Get-RunningWorkflowsByStatus*
Export-ModuleMember -function *Get-RunningWorkflowById*
Export-ModuleMember -function *Get-WorkflowDefinitions*
Export-ModuleMember -function *Get-WorkflowDefinition*
Export-ModuleMember -function *Remove-WorkflowDefinition*
Export-ModuleMember -function *Get-RunningWorkflowsPendingApprovalFrom*
Export-ModuleMember -function *Get-RunningWorkflowsByOrg*
Export-ModuleMember -function *Get-RunningWorkflowsByInitiator*
Export-ModuleMember -function *Get-RunningWorkflowSteps*
Export-ModuleMember -function *Get-RunningWorkflowStepById*

Export-ModuleMember -function *Invoke-SkipRunningWorkflow*
Export-ModuleMember -function *Invoke-SkipRunningWorkflowStep*
Export-ModuleMember -function *Invoke-RepeatRunningWorkflowStep*

Export-ModuleMember -function *Start-CommandWorkflow*

Export-ModuleMember -function *Get-BillingRecords*

Export-ModuleMember -function *Remove-VMById*
Export-ModuleMember -function *Remove-VM*
Export-ModuleMember -function *Get-VMs*
Export-ModuleMember -function *Get-VM*
Export-ModuleMember -function *Get-VMByRemoteId*
Export-ModuleMember -function *Get-VMByObjecthandleId*
Export-ModuleMember -function *Get-VMReferences*

Export-ModuleMember -function *Remove-Stack*
Export-ModuleMember -function *Get-Stack*
Export-ModuleMember -function *Get-StackReferences*

Export-ModuleMember -function *Set-Attribute*
Export-ModuleMember -function *Set-ComplianceData*
Export-ModuleMember -function *Set-Ownership*
Export-ModuleMember -function *Set-vAppOwnership*
Export-ModuleMember -function *Set-ExpiryDate*
Export-ModuleMember -function *Reset-ExpiryExtensionCount*
Export-ModuleMember -function *Set-EndOfLife*
Export-ModuleMember -function *Set-Suspect*
Export-ModuleMember -function *Set-Approval*
Export-ModuleMember -function *Set-ExpiryGroup*
Export-ModuleMember -function *Set-ScanGroup*
Export-ModuleMember -function *Set-PowerScheduleGroup*
Export-ModuleMember -function *Set-MaintenanceGroup*
Export-ModuleMember -function *Set-RightsizingGroup*

Export-ModuleMember -function *Get-vApps*
Export-ModuleMember -function *Get-vAppByRemoteId*
Export-ModuleMember -function *Get-vAppByObjecthandleId*
Export-ModuleMember -function *Remove-vApp*


Export-ModuleMember -function *New-CustomComponentType*
Export-ModuleMember -function *Remove-CustomComponentType*
Export-ModuleMember -function *Get-CustomComponentTypes*
Export-ModuleMember -function *Get-CustomcomponentType*
Export-ModuleMember -function *New-PublishedService*
Export-ModuleMember -function *Get-CloudTemplateServiceComponentTemplate
Export-ModuleMember -function *Get-CloudTemplateParameters
Export-ModuleMember -function *Update-PublishedService*
Export-ModuleMember -function *Remove-PublishedService*
Export-ModuleMember -function *Publish-PublishedService*
Export-ModuleMember -function *Get-PublishedServices*
Export-ModuleMember -function *Get-PublishedServiceReferences*
Export-ModuleMember -function *New-DTOTemplateServiceComponent

Export-ModuleMember -function *Get-PublishedServiceById*
Export-ModuleMember -function *Get-PublishedServiceByName*
Export-ModuleMember -function *Get-ServiceCategories*
Export-ModuleMember -function *Get-ServiceCategory*
Export-ModuleMember -function *New-RequestForm*
Export-ModuleMember -function *Remove-RequestForm*
Export-ModuleMember -function *Update-RequestForm*
Export-ModuleMember -function *Export-RequestForms*
Export-ModuleMember -function *Import-RequestForms*
Export-ModuleMember -function *Get-RequestForms*
Export-ModuleMember -function *Get-RequestFormsOfType*
Export-ModuleMember -function *Get-RequestFormById*
Export-ModuleMember -function *Get-RequestFormElements*
Export-ModuleMember -function *Get-PSRequestParams*
Export-ModuleMember -function *Get-MultiplePSRequestParams*
Export-ModuleMember -function *Get-VMChangeRequestParams*
Export-ModuleMember -function *Get-vAppChangeRequestParams*
Export-ModuleMember -function *New-VMChange*
Export-ModuleMember -function *New-vAppChange*

Export-ModuleMember -function *Get-Roles*
Export-ModuleMember -function *Get-Role*
Export-ModuleMember -function *New-Role*
Export-ModuleMember -function *Update-Role*
Export-ModuleMember -function *Remove-Role*

Export-ModuleMember -function *Update-RoleAssignments*
Export-ModuleMember -function *New-RoleAssignments*
Export-ModuleMember -function *Remove-ContextRoleAssignments*

Export-ModuleMember -function *New-Account*
Export-ModuleMember -function *Remove-Account*
Export-ModuleMember -function *Get-Accounts*
Export-ModuleMember -function *Get-Account*
Export-ModuleMember -function *Get-AccountById*
Export-ModuleMember -function *Update-Account*

Export-ModuleMember -function *New-ServiceRequest*
Export-ModuleMember -function *New-MultiServiceRequest*

Export-ModuleMember -function *Get-ServiceRequests*
Export-ModuleMember -function *Get-ServiceRequestsByType*
Export-ModuleMember -function *Get-ServiceRequestsByState*
Export-ModuleMember -function *Get-ServiceRequest*
Export-ModuleMember -function *Get-CommentsForRequest*
Export-ModuleMember -function *Get-RequestedServices*
Export-ModuleMember -function *Get-RequestedServiceById*
Export-ModuleMember -function *Get-RequestedComponents*
Export-ModuleMember -function *Get-RequestedComponentById*
Export-ModuleMember -function *Get-RequestedServiceFormDetails*
Export-ModuleMember -function *Get-RequestedComponentFormDetails*
Export-ModuleMember -function *Get-ChangeRequestFormDetails*

Export-ModuleMember -function *Get-RunningWorkflowForRequestedService*
Export-ModuleMember -function *Get-RunningWorkflowForRequestedComponent*
Export-ModuleMember -function *Get-RunningWorkflowsForRequest*

Export-ModuleMember -function *Export-WorkflowDefinitions*
Export-ModuleMember -function *Import-WorkflowDefinitions*
Export-ModuleMember -function *Update-WorkflowDefinition*
Export-ModuleMember -function *New-WorkflowDefinition*
Export-ModuleMember -function *Get-WorkflowDefinitionById*
Export-ModuleMember -function *Get-WorkflowDefinitionsByType*
Export-ModuleMember -function *Get-WorkflowStepDefinitions*
Export-ModuleMember -function *Get-WorkflowStepDefinitionById*
Export-ModuleMember -function *Join-WorkflowDefinitionToServiceComponent*

Export-ModuleMember -function *Get-Groups*
Export-ModuleMember -function *Get-GroupsByType*
Export-ModuleMember -function *Get-GroupByName*


Export-ModuleMember -function *Get-VCmdrCredentials*
Export-ModuleMember -function *Get-VCmdrCredentialsByType*

Export-ModuleMember -function *Get-VMInstanceTypes*
Export-ModuleMember -function *Get-VMInstanceTypesByManagementServerType*

Export-ModuleMember -function *Get-OrganizationReferences*
Export-ModuleMember -function *Get-OrganizationByName*
Export-ModuleMember -function *Get-Organization*
Export-ModuleMember -function *Get-Organizations*
Export-ModuleMember -function *Get-QuotaStatistics*
Export-ModuleMember -function *Get-QuotaReservations*
Export-ModuleMember -function *Clear-QuotaReservation*
Export-ModuleMember -function *Remove-OrganizationByName*
Export-ModuleMember -function *New-Organization*
Export-ModuleMember -function *Update-Organization*
Export-ModuleMember -function *Submit-RejectRequest*
Export-ModuleMember -function *Add-Comment*
Export-ModuleMember -function *Approve-Request*
Export-ModuleMember -function *Copy-ServiceRequest*
Export-ModuleMember -function *New-ServiceRequestUserAssignment*
Export-ModuleMember -function *New-VMLinkToServiceRequest*
Export-ModuleMember -function *New-ServiceRequestSubscription*
Export-ModuleMember -function *Remove-ServiceRequestSubscription*
Export-ModuleMember -function *Submit-RequestedComponentRelease*
Export-ModuleMember -function *Submit-ChangeRequestFulfillment*
Export-ModuleMember -function *Submit-ChangeRequestFulfillmentSchedule*
Export-ModuleMember -function *Submit-ReleaseRequest*
Export-ModuleMember -function *New-RequestedComponentDeployment*
Export-ModuleMember -function *New-RequestedServiceDeployment*
Export-ModuleMember -function *Add-DatastoreToDeploymentDestinations*
Export-ModuleMember -function *Remove-DatastoreFromDeploymentDestinations*

Export-ModuleMember -function *Get-IpPools*
Export-ModuleMember -function *Get-IpPoolByName*
Export-ModuleMember -function *Get-IpPoolById*
Export-ModuleMember -function *Remove-IpPool*
Export-ModuleMember -function *Update-IpPool*
Export-ModuleMember -function *New-IpPool*

Export-ModuleMember -function *Get-Projects*
Export-ModuleMember -function *Get-Regions*
Export-ModuleMember -function *Get-RegionById*
Export-ModuleMember -function *Get-RegionAvailabilityZones*
Export-ModuleMember -function *Get-RegionSubnets*
Export-ModuleMember -function *Get-VirtualCloudSubnets*
Export-ModuleMember -function *Get-VirtualCloudSecurityGroups*

Export-ModuleMember -function *Get-HostStandardSwitches*
Export-ModuleMember -function *Get-DistributedSwitches*

Export-ModuleMember -function *Get-DeploymentDestinations*
Export-ModuleMember -function *Get-DeploymentDestinationById*
Export-ModuleMember -function *Get-DeploymentDestinationsByOrg*
Export-ModuleMember -function *Get-DeploymentDestinationByName*
Export-ModuleMember -function *New-DeploymentDestination*
Export-ModuleMember -function *New-VMWareDeploymentDestination*
Export-ModuleMember -function *New-SCVMMDeploymentDestination*
Export-ModuleMember -function *New-AWSDeploymentDestination*
Export-ModuleMember -function *New-ARMDeploymentDestination*

Export-ModuleMember -function *Get-Policies*
Export-ModuleMember -function *Get-PoliciesByType*
Export-ModuleMember -function *Get-PolicyById*
Export-ModuleMember -function *Get-PolicyByName*
Export-ModuleMember -function *New-Policy*
Export-ModuleMember -function *Update-Policy*
Export-ModuleMember -function *Remove-Policy*
Export-ModuleMember -function *New-CompliancePolicy*
Export-ModuleMember -function *New-AttributesPolicy*
Export-ModuleMember -function *New-OwnershipPolicy*
Export-ModuleMember -function *New-ExpiryPolicy*
Export-ModuleMember -function *New-UnapprovedPolicy*
Export-ModuleMember -function *New-SuspectPolicy*
Export-ModuleMember -function *New-EndOfLifePolicy*

Export-ModuleMember -function *Update-DeploymentDestination*
Export-ModuleMember -function *Remove-DeploymentDestination*

Export-ModuleMember -function *Get-AvailableNetworks*

Export-ModuleMember -function *Get-ManagedObject*
Export-ModuleMember -function *Get-ManagedObjectByName*
Export-ModuleMember -function *Get-ManagedObjectById*

Export-ModuleMember -function *New-CustomComponentTypeDTO*
Export-ModuleMember -function *New-BaseObject*
Export-ModuleMember -function *Copy-Object*
Export-ModuleMember -function *Copy-ObjectDeep*
Export-ModuleMember -function *New-DTOTemplateObject*
Export-ModuleMember -function *Get-DTOTemplateObject*
Export-ModuleMember -function *Clear-Cache*
Export-ModuleMember -function *Set-BaseServiceURI*
Export-ModuleMember -function *Get-BaseServiceURI*
Export-ModuleMember -function *Invoke-Activate*
Export-ModuleMember -function *Get-SystemState*

Export-ModuleMember -function *Set-LicenseKey*
Export-ModuleMember -function *Wait-ForTaskFinalState*
Export-ModuleMember -function *Start-InventorySynchronization*

Export-ModuleMember -function *New-ManagementServer*


#Cost model
Export-ModuleMember -function *Get-CostModels*
Export-ModuleMember -function *Get-CostModelById*
Export-ModuleMember -function *Get-CostModelByName*
Export-ModuleMember -function *Remove-CostModel*
Export-ModuleMember -function *New-CostModel*
Export-ModuleMember -function *Update-CostModel*
#Helper - exposed for used in debugging
Export-ModuleMember -function *Convert-ObjectToXml*
Export-ModuleMember -function *Convert-XmlToObject*

#Media
Export-ModuleMember -function *Mount-MediaFile*
Export-ModuleMember -function *Dismount-MediaFile*
Export-ModuleMember -function *Get-MediaFolders*
Export-ModuleMember -function *Get-MediaFolder*
Export-ModuleMember -function *New-MediaFolder*
Export-ModuleMember -function *Update-MediaFolder*
Export-ModuleMember -function *Remove-MediaFolder*
Export-ModuleMember -function *Clear-DeploymentDestinationCache*
Export-ModuleMember -function *Invoke-DeploymentDestinationValidations*
Export-ModuleMember -function *Get-StorageReservations*
Export-ModuleMember -function *Remove-StorageReservation*

Export-ModuleMember -function *Get-ServiceAPIInfo*

Export-ModuleMember -function *Get-PlacementAttributes*
Export-ModuleMember -function *Get-PlacementAttributeById*
Export-ModuleMember -function *Get-PlacementAttributeByName*
Export-ModuleMember -function *Get-PlacementAttribute*
Export-ModuleMember -function *Update-PlacementAttribute*
Export-ModuleMember -function *Remove-PlacementAttribute*
Export-ModuleMember -function *New-PlacementAttribute*

Export-ModuleMember -function *Invoke-ReloadCosts*

########################### DEPRECATED ###########################
Export-ModuleMember -function *New-RoleDTO*
Export-ModuleMember -function *Remove-VMById*
Export-ModuleMember -function *New-AccountDTO*
Export-ModuleMember -function *Request-PublishedService*
Export-ModuleMember -function *Request-MultiplePublishedServices*
Export-ModuleMember -function *Get-ServiceRequestById*
Export-ModuleMember -function *Request-VMChange*
Export-ModuleMember -function *Request-vAppChange*
Export-ModuleMember -function *New-CustomAttributeDTO*
Export-ModuleMember -function *New-OwnershipDTO*
Export-ModuleMember -function *New-VMLinkToServiceRequestDTO*
Export-ModuleMember -function *New-ManagedObjectReference*
Export-ModuleMember -function *New-CatalogConfig*
Export-ModuleMember -function *New-OwnerInfoDTO*
Export-ModuleMember -function *New-UserAssignmentDTO*
Export-ModuleMember -function *Get-TaskInfoById*
Export-ModuleMember -function *Enable-DebugMode*
Export-ModuleMember -function *Disable-DebugMode*
Export-ModuleMember -function *New-AzureDeploymentDestination*