CitrixSdwanFunctions.psm1

<#
.SYNOPSIS
    This module file contains SD-WAN functions.
.DESCRIPTION
    Version 2.
.NOTES
    Copyright (c) Citrix Systems, Inc. All rights reserved.
#>


<#Tested against Powershell Version 5.1.14393.2312 - run $PSVersionTable.PSVersion to identify on your system #>

#Define default URL protocol to https, which can be changed by calling Set-Protocol function
$Script:SDWURLProtocol = "https"

#Workaround for SelfSigned Cert and force TLS 1.2
#without this snippet of code, there is a certicate trust relationship error which prevents successful connection to the SDWAN
add-type @
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
�@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
[System.Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

function Set-SDWMgmtProtocol {
    <#
    .SYNOPSIS
        Set $Script:SDWURLProtocol, this will be used for all subsequent invocation of NITRO APIs
    .DESCRIPTION
        Set $Script:SDWURLProtocol
    .PARAMETER Protocol
        Protocol, acceptable values are "http" and "https"
    .EXAMPLE
        Set-SDWMgmtProtocol -Protocol https
    .NOTES
        Copyright (c) Citrix Systems, Inc. All rights reserved.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)] [ValidateSet("http","https")] [string]$Protocol
    )

    Write-Verbose "$($MyInvocation.MyCommand): Enter"

    $Script:SDWURLProtocol = $Protocol

    Write-Verbose "$($MyInvocation.MyCommand): Exit"
}

function Get-SDWMgmtProtocol {
    <#
    .SYNOPSIS
        Get the value of $Script:SDWURLProtocol
    .DESCRIPTION
        Set $Script:SDWURLProtocol
    .EXAMPLE
        $protocol = Get-Protocol
    .NOTES
        Copyright (c) Citrix Systems, Inc. All rights reserved.
    #>

    [CmdletBinding()]
    param()

    return $Script:SDWURLProtocol
}

function Connect-SDWAppliance {
     <#
    .SYNOPSIS
        Connect to SD-WAN device (physical or virtual appliance)
    .DESCRIPTION
        A custom web request session object will be returned.
    .PARAMETER SDWAddress
        Management IP address
    .PARAMETER SDWName
        DNS name or FQDN
    .PARAMETER SDWUserName
        UserName to access the SD-WAN device
    .PARAMETER SDWPassword
        Password to access the SD-WAN device
    .PARAMETER Timeout
        Timeout in seconds to for the token of the connection to the SD-WAN device. ### is the default admin configured value.
    .EXAMPLE
         $Session = Connect-SDWAppliance -SDWAddress 192.168.100.1
    .EXAMPLE
         $Session = Connect-SDWAppliance - SDWName sdwan.mydomain.com
    .OUTPUTS
        CustomPSObject
    .NOTES
        Copyright (c) Citrix Systems, Inc. All rights reserved.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true,ParameterSetName='Address')] [string]$SDWAddress,
        [Parameter(Mandatory=$true,ParameterSetName='Name')] [string]$SDWName,
        [Parameter(Mandatory=$false)] [string]$SDWUserName="admin",
        [Parameter(Mandatory=$false)] [string]$SDWPassword="password",
        [Parameter(Mandatory=$false)] [int]$Timeout=900
    )
    Write-Verbose "$($MyInvocation.MyCommand): Enter"

    if ($PSCmdlet.ParameterSetName -eq 'Address') {
        Write-Verbose "Validating IP Address"
        $IPAddressObj = New-Object -TypeName System.Net.IPAddress -ArgumentList 0
        if (-not [System.Net.IPAddress]::TryParse($SDWAddress,[ref]$IPAddressObj)) {
            throw "'$SDWAddress' is an invalid IP address"
        }
        $sdwEndpoint = $SDWAddress
    } elseif ($PSCmdlet.ParameterSetName -eq 'Name') {
        $sdwEndpoint = $SDWName
    }


    $login = @{"login" = @{"username"=$SDWUserName;"password"=$SDWPassword;"timeout"=$Timeout}}
    $loginJson = ConvertTo-Json $login

    try {
        Write-Verbose "Calling Invoke-RestMethod for login"
        $response = Invoke-RestMethod -Uri "$($Script:SDWURLProtocol)://$sdwEndpoint/sdwan/nitro/v1/config/login" -Body $loginJson -Method POST -SessionVariable saveSession -ContentType application/json

        if ($response.severity -eq "ERROR") {
            throw "Error. See response: `n$($response | fl * | Out-String)"
        } else {
            Write-Verbose "Response:`n$(ConvertTo-Json $response | Out-String)"
        }
    }
    catch [Exception] {
        throw $_
    }

    $sdwSession = New-Object -TypeName PSObject
    $sdwSession | Add-Member -NotePropertyName Endpoint -NotePropertyValue $sdwEndpoint -TypeName String
    $sdwSession | Add-Member -NotePropertyName WebSession  -NotePropertyValue $saveSession -TypeName Microsoft.PowerShell.Commands.WebRequestSession

    Write-Verbose "$($MyInvocation.MyCommand): Exit"

    return $sdwSession
}

function Disconnect-SDWAppliance {
    <#
    .SYNOPSIS
        Disconnect SDWAN Appliance session
    .DESCRIPTION
        Disconnect SDWAN Appliance session
    .PARAMETER SDWSession
        An existing custom SDWAN Web Request Session object returned by Connect-SDWAppliance
    .EXAMPLE
        Disconnect-SDWAppliance -SDWSession $Session
    .NOTES
        Copyright (c) Citrix Systems, Inc. All rights reserved.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)] [PSObject]$SDWSession
    )

    Write-Verbose "$($MyInvocation.MyCommand): Enter"
    
    try {
        Write-Verbose "Calling Invoke-RestMethod for logout"
        $response = Invoke-RestMethod -Uri "$($Script:SDWURLProtocol)://$($SDWSession.Endpoint)/sdwan/nitro/v1/config/login" -Method DELETE -ContentType application/json -WebSession $SDWSession.WebSession
    }
    catch [Exception] {
        throw $_
    }

    Write-Verbose "$($MyInvocation.MyCommand): Exit"
}

function Invoke-SDWNitroRestApi {
    <#
    .SYNOPSIS
        Invoke SDWAN NITRO REST API
    .DESCRIPTION
        Invoke SDWAN NITRO REST API
    .PARAMETER SDWSession
        An existing custom SDWAN Web Request Session object returned by Connect-SDWAppliance
    .PARAMETER OperationMethod
        Specifies the method used for the web request (e.g. POST, PUT, GET, ADD, MODIFY, DELETE)
    .PARAMETER ResourceType
        Type of the SDWAN appliance resource (e.g. config, monitor, config_editor, diag, download, mobile_broadband)
    .PARAMETER ResourceName
        Name of the SDWAN appliance resource, optional (e.g. mangement_ip, config_packages, remote_licnese, interface_groups, etc)
    .PARAMETER Action
        Name of the action to perform on the SDWAN appliance resource (e.g. compile, enable_remote_server, export_to_cm)
    .PARAMETER Payload
        Payload of the web request, in hashtable format
    .PARAMETER GetWarning
        Switch parameter, when turned on, warning message will be sent in 'message' field and 'WARNING' value is set in severity field of the response in case there is a warning.
        Turned off by default
    .PARAMETER OnErrorAction
        Use this parameter to set the onerror status for nitro request. Applicable only for bulk requests.
        Acceptable values: "EXIT", "CONTINUE", "ROLLBACK", default to "EXIT"
    .EXAMPLE
        #Invoke NITRO REST API to manaually set the management IP address.
        @{gateway="172.30.200.1";netmask="255.255.255.0";ip_address="172.30.200.150"}
        Invoke-SDWNitroRestApi -SDWSession $mySDWSession -OperationMethod PUT -ResourceType config -ResourceName management_ip -Payload $ReqPayload
    .OUTPUTS
        Only when the OperationMethod is GET:
        PSCustomObject that represents the JSON response content. This object can be manipulated using the ConvertTo-Json Cmdlet.
    .NOTES
        Copyright (c) Citrix Systems, Inc. All rights reserved.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)] [PSObject]$SDWSession,
        [Parameter(Mandatory=$true)] [ValidateSet("DELETE","GET","POST","PUT")] [string]$OperationMethod,
        [Parameter(Mandatory=$true)] [string]$ResourceType,
        [Parameter(Mandatory=$true)] [string]$ResourceName,
        [Parameter(Mandatory=$false)] [string]$SubResource,
        [Parameter(Mandatory=$false)] [string]$Action,
        [Parameter(Mandatory=$false)] [ValidateScript({$OperationMethod -in @("GET", "DELETE")})] [hashtable]$Arguments=@{},
        [Parameter(Mandatory=$false)] [ValidateScript({$OperationMethod -notin @("GET", "DELETE")})] [hashtable]$Payload=@{},
        [Parameter(Mandatory=$false)] [switch]$GetWarning=$false,
        [Parameter(Mandatory=$false)] [ValidateSet("EXIT", "CONTINUE", "ROLLBACK")] [string]$OnErrorAction="EXIT"
    )
        
    Write-Verbose "$($MyInvocation.MyCommand): Enter"

    #To do:test for healthy session, exit and return user instruction

    Write-Verbose "Building URI"
    $uri = "$($Script:SDWURLProtocol)://$($SDWSession.Endpoint)/sdwan/nitro/v1/$ResourceType/$ResourceName"
    if (-not [string]::IsNullOrEmpty($SubResource)) {
        $uri += "/$SubResource"
    }
    if ($OperationMethod -notin @("GET", "DELETE", "PUT")) {
        if (-not [string]::IsNullOrEmpty($Action)) {
            $uri += "?action=$Action"
        }
    } else {
        if ($Arguments.Count -gt 0) {
            $uri += "?args="
            $argsList = @()
            foreach ($arg in $Arguments.GetEnumerator()) {
                $argsList += "$($arg.Name):$([System.Uri]::EscapeDataString($arg.Value))"
            }
            $uri += $argsList -join ','
        }
        #To do: Add filter, view, and pagesize
    }
    Write-Verbose "URI: $uri"

    if ($OperationMethod -notin @("GET", "DELETE")) {
        Write-Verbose "Building Payload"
        #$warning = if ($GetWarning) { "YES" } else { "NO" }
        $hashtablePayload = @{}
        #$hashtablePayload."params" = @{"warning"=$warning;"onerror"=$OnErrorAction;<#"action"=$Action#>}
        $hashtablePayload.$ResourceName = $Payload
        $jsonPayload = ConvertTo-Json $hashtablePayload -Depth 100
        Write-Verbose "JSON Payload:`n$jsonPayload"
    }

    try {
        Write-Verbose "Calling Invoke-RestMethod"
        $restParams = @{
            Uri = $uri
            ContentType = "application/json"
            Method = $OperationMethod
            WebSession = $SDWSession.WebSession
            ErrorVariable = "restError"
        }

        if ($OperationMethod -notin @("GET", "DELETE")) {
            $restParams.Add("Body",$jsonPayload)
        }

        
        if ($ResourceName -eq "get_package_file"){
            Invoke-RestMethod @restParams > activate_package.zip
            return "package_downloaded"
        }

        $response = Invoke-RestMethod @restParams
       
        if ($response) {
            if ($response.severity -eq "ERROR") {
                throw "Error. See response: `n$($response | fl * | Out-String)"
                #to do: writeout .status and .message to help root cuase 404 errors
            } else {
                Write-Verbose "Response:`n$(ConvertTo-Json $response | Out-String)"
            }
        }
    }
    catch [Exception] {
        if ($ResourceType -eq "reboot" -and $restError[0].Message -eq "The underlying connection was closed: The connection was closed unexpectedly.") {
            Write-Verbose "Connection closed due to reboot"
        } else {
            throw $_
        }
    }

    Write-Verbose "$($MyInvocation.MyCommand): Exit"

    if (($OperationMethod -eq "GET") -or ($Action -eq "compile")) {
        return $response
    }
}