Nectar10.psm1
<#
These series of PowerShell functions allow administrators to automate many functions in Nectar 10 and the Endpoint Client Controller that are otherwise difficult or time-consuming to perform in the UI. To install, follow the instructions at https://support.nectarcorp.com/docs/powershell If you want help with a particular command, type Get-Help <commandname> -Full (ie. Get-Help Connect-NectarCloud -Full). Full documentation is available at https://support.nectarcorp.com/docs/powershell #> ################################################################################################################################################# ################################################################################################################################################# ## ## ## Nectar 10 Functions ## ## ## ################################################################################################################################################# ################################################################################################################################################# ################################################################################################################################################# # # # Tenant Connection Functions # # # ################################################################################################################################################# Function Connect-NectarCloud { <# .SYNOPSIS Connects to Nectar 10 cloud and store the credentials for later use. .DESCRIPTION Connects to Nectar 10 cloud and store the credentials for later use. .PARAMETER CloudFQDN The FQDN of the Nectar 10 cloud. .PARAMETER TenantName The name of a Nectar 10 cloud tenant to connect to and use for subsequent commands. Only useful for multi-tenant deployments .PARAMETER Credential The credentials used to access the Nectar 10 UI. Normally in username@domain.com format .PARAMETER StoredCredentialTarget Use stored credentials saved via New-StoredCredential. Requires prior installation of CredentialManager module via Install-Module CredentialManager, and running: Get-Credential | New-StoredCredential -Target MyN10Creds -Persist LocalMachine .PARAMETER EnvFromFile Use a CSV file called N10EnvList.csv located in the user's default Documents folder to show a list of environments to select from. Run [Environment]::GetFolderPath("MyDocuments") to find your default document folder. This parameter is only available if N10EnvList.csv is found in the user's default Documents folder (ie: C:\Users\username\Documents) Also sets the default stored credential target to use for the selected environment. Requires prior installation and configuration of CredentialManager PS add-in. N10EnvList.csv must have a header with three columns defined as "Environment, DefaultTenant, StoredCredentialTarget". Each environment and StoredCredentialTarget (if used) should be on their own separate lines .EXAMPLE $Cred = Get-Credential Connect-NectarCloud -Credential $cred -CloudFQDN contoso.nectar.services Connects to the contoso.nectar.services Nectar 10 cloud using the credentials supplied to the Get-Credential command .EXAMPLE Connect-NectarCloud -CloudFQDN contoso.nectar.services -StoredCredentialTarget MyN10Creds Connects to contoso.nectar.services Nectar 10 cloud using previously stored credentials called MyN10Creds .NOTES Version 1.4 #> [Alias("cnc")] Param ( [Parameter(ValueFromPipeline, Mandatory=$False)] [ValidateScript ({ If ($_ -Match "^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$") { $True } Else { Throw "ERROR: Nectar 10 cloud name must be in FQDN format." } })] [string] $CloudFQDN, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.Credential()] [PSCredential] $Credential, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$StoredCredentialTarget ) DynamicParam { $DefaultDocPath = [Environment]::GetFolderPath("MyDocuments") $EnvPath = "$DefaultDocPath\N10EnvList.csv" If (Test-Path $EnvPath -PathType Leaf) { # Set the dynamic parameters' name $ParameterName = 'EnvFromFile' # Create the dictionary $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary # Create the collection of attributes $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] # Create and set the parameters' attributes $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute $ParameterAttribute.Mandatory = $False $ParameterAttribute.Position = 1 # Add the attributes to the attributes collection $AttributeCollection.Add($ParameterAttribute) # Generate and set the ValidateSet $EnvSet = Import-Csv -Path $EnvPath $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($EnvSet.Environment) # Add the ValidateSet to the attributes collection $AttributeCollection.Add($ValidateSetAttribute) # Create and return the dynamic parameter $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection) $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter) Return $RuntimeParameterDictionary } } Begin { # Bind the dynamic parameter to a friendly variable If (Test-Path $EnvPath -PathType Leaf) { If ($PsBoundParameters[$ParameterName]) { $CloudFQDN = $PsBoundParameters[$ParameterName] Write-Verbose "CloudFQDN: $CloudFQDN" # Get the array position of the selected environment $EnvPos = $EnvSet.Environment.IndexOf($CloudFQDN) # Check for default tenant in N10EnvList.csv and use if available, but don't override if user explicitly set the TenantName If (!$PsBoundParameters['TenantName']) { $TenantName = $EnvSet[$EnvPos].DefaultTenant Write-Verbose "DefaultTenant: $TenantName" } # Check for stored credential target in N10EnvList.csv and use if available $StoredCredentialTarget = $EnvSet[$EnvPos].StoredCredentialTarget Write-Verbose "StoredCredentialTarget: $StoredCredentialTarget" } } } Process { # Need to force TLS 1.2, if not already set If ([Net.ServicePointManager]::SecurityProtocol -ne 'Tls12') { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } # Ask for the tenant name if global Nectar tenant variable not available and not entered on command line If ((-not $Global:NectarCloud) -And (-not $CloudFQDN)) { $CloudFQDN = Read-Host "Enter the Nectar 10 cloud FQDN" } ElseIf (($Global:NectarCloud) -And (-not $CloudFQDN)) { $CloudFQDN = $Global:NectarCloud } $RegEx = "^(?:http(s)?:\/\/)?([\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+)$" $FQDNMatch = Select-String -Pattern $Regex -InputObject $CloudFQDN $CloudFQDN = $FQDNMatch.Matches.Groups[2].Value # Ask for credentials if global Nectar creds aren't available If (((-not $Global:NectarCred) -And (-not $Credential)) -Or (($Global:NectarCloud -ne $CloudFQDN) -And (-Not $Credential)) -And (-Not $StoredCredentialTarget)) { $Credential = Get-Credential } ElseIf ($Global:NectarCred -And (-not $Credential)) { $Credential = $Global:NectarCred } # Pull stored credentials if specified If ($StoredCredentialTarget) { Try { $Credential = Get-StoredCredential -Target $StoredCredentialTarget } Catch { Write-Error "Cannot find stored credential for target: $StoredCredentialTarget" } } If ((-not $Global:NectarCred) -Or (-not $Global:NectarCloud) -Or ($Global:NectarCloud -ne $CloudFQDN)) { # First check and notify if updated N10 PS module available [decimal]$InstalledN10Ver = (Get-InstalledModule -Name Nectar10 -ErrorAction SilentlyContinue).Version If ($InstalledN10Ver -gt 0) { [decimal]$LatestN10Ver = (Find-Module Nectar10).Version If ($LatestN10Ver -gt $InstalledN10Ver) { Write-Host "=============== Nectar 10 PowerShell module version $LatestN10Ver available ===============" -ForegroundColor Yellow Write-Host "You are running version $InstalledN10Ver. Type " -ForegroundColor Yellow -NoNewLine Write-Host 'Update-Module Nectar10' -ForegroundColor Green -NoNewLine Write-Host ' to update.' -ForegroundColor Yellow } } # Attempt connection to tenant $WebRequest = Invoke-WebRequest -Uri "https://$CloudFQDN/dapi/info/network/types" -Method GET -Credential $Credential -UseBasicParsing -SessionVariable NectarSession If ($WebRequest.StatusCode -ne 200) { Write-Error "Could not connect to $CloudFQDN using $($Credential.UserName)" } Else { Write-Host -ForegroundColor Green "Successful connection to " -NoNewLine Write-Host -ForegroundColor Yellow "https://$CloudFQDN" -NoNewLine Write-Host -ForegroundColor Green " using " -NoNewLine Write-Host -ForegroundColor Yellow ($Credential).UserName $Global:NectarCloud = $CloudFQDN $Global:NectarCred = $Credential $Global:NectarSession = $NectarSession # If there is only one available tenant, assign that to the NectarTenantName global variable $TenantList = $WebRequest | ConvertFrom-Json If ($TenantList.Count -eq 1) { $Global:NectarTenantName = $TenantList } } } # Check to see if tenant name was entered and set global variable, if valid. If ($TenantName) { $TenantList = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/aapi/tenant" Try { If ($TenantList -Contains $TenantName) { $Global:NectarTenantName = $TenantName Write-Host -ForegroundColor Green "Successsfully set the tenant name to " -NoNewLine Write-Host -ForegroundColor Yellow "$TenantName" -NoNewLine Write-Host -ForegroundColor Green ". This name will be used in all subsequent commands." } Else { $TenantList | %{$TList += ($(if($TList){", "}) + $_)} Write-Error "Could not find a tenant with the name $TenantName on https://$Global:NectarCloud. Select one of $TList" } } Catch { Write-Error "Invalid tenant name on https://$Global:NectarCloud" } } ElseIf ($PSBoundParameters.ContainsKey('TenantName')) { # Remove the NectarTenantName global variable only if TenantName is explicitly set to NULL Remove-Variable NectarTenantName -Scope Global -ErrorAction:SilentlyContinue } } } Function Connect-NectarCloudSSO { <# .SYNOPSIS Connects to Nectar 10 cloud via SSO and store the crendentials for later use. .DESCRIPTION Connects to Nectar 10 cloud via SSO and store the crendentials for later use. .PARAMETER CloudFQDN The FQDN of the Nectar 10 cloud. .PARAMETER DomainName The name of the SSO domain to use for connecting to N10 .NOTES Version 0.1 (not working yet) #> [Alias("cncs")] param ( [Parameter(ValueFromPipeline, Mandatory=$False)] [ValidateScript ({ If ($_ -Match "(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}$)") { $True } Else { Throw "ERROR: Nectar 10 cloud name must be in FQDN format." } })] [string] $CloudFQDN, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$DomainName ) # Need to force TLS 1.2, if not already set If ([Net.ServicePointManager]::SecurityProtocol -ne 'Tls12') { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } # Ask for the tenant name if global Nectar tenant variable not available and not entered on command line If ((-not $Global:NectarCloud) -And (-not $CloudFQDN)) { $CloudFQDN = Read-Host "Enter the Nectar 10 cloud FQDN" } ElseIf (($Global:NectarCloud) -And (-not $CloudFQDN)) { $CloudFQDN = $Global:NectarCloud } # Check that the entered domain is valid $DomainCheck = Invoke-WebRequest -Uri "https://$CloudFQDN/adminapi/user/sso/domain/exists?domain=$DomainName" -Method GET If ($DomainCheck.content -eq 'True') { $WebRedirect = Invoke-WebRequest -Uri "https://$CloudFQDN/saml/login?domain=$DomainName" -Method POST -MaximumRedirection 0 -SessionVariable RequestCookie -UseDefaultCredentials $SAMLRedirectURL = $WebRedirect.Forms[0].action $SAMLRequest = $WebRedirect.Forms[0].fields.Values[0] $SAML = Invoke-WebRequest -Uri $SAMLRedirectURL -Method POST -MaximumRedirection 0 -ContentType 'application/x-www-form-urlencoded' -WebSession $RequestCookie } Else { Write-Error "The domain name $DomainName is not valid for $CloudFQDN." } # This opens the auth provider portal in a web browser and progresses from there to the N10 web UI. # Need to figure out how to intercept the auth token for PS usage. } Function Disconnect-NectarCloud { <# .SYNOPSIS Disconnects from any active Nectar 10 connection .DESCRIPTION Essentially deletes any stored credentials and FQDN from global variables .EXAMPLE Disconnect-NectarCloud Disconnects from all active connections to Nectar 10 tenants .NOTES Version 1.1 #> [Alias("dnc")] [cmdletbinding()] param () Remove-Variable NectarCred -Scope Global -ErrorAction:SilentlyContinue Remove-Variable NectarCloud -Scope Global -ErrorAction:SilentlyContinue Remove-Variable NectarSession -Scope Global -ErrorAction:SilentlyContinue Remove-Variable NectarTenantName -Scope Global -ErrorAction:SilentlyContinue } Function Get-NectarCloudInfo { <# .SYNOPSIS Shows information about the active Nectar 10 connection .DESCRIPTION Shows information about the active Nectar 10 connection .EXAMPLE Get-NectarCloud .NOTES Version 1.1 #> [Alias("gnci")] [cmdletbinding()] param () $CloudInfo = "" | Select-Object -Property CloudFQDN, Credential $CloudInfo.CloudFQDN = $Global:NectarCloud $CloudInfo.Credential = ($Global:NectarCred).UserName $CloudInfo | Add-Member -TypeName 'Nectar.CloudInfo' Try { $TenantCount = Get-NectarTenantNames If ($TenantCount.Count -gt 1) { If ($Global:NectarTenantName) { $CloudInfo | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $Global:NectarTenantName } Else { $CloudInfo | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue '<Not Set>' } } } Catch { } Return $CloudInfo } Function Get-NectarTenantNames { <# .SYNOPSIS Shows all the available Nectar tenants on the cloud host. .DESCRIPTION Shows all the available Nectar tenants on the cloud host. Only available for multi-tenant deployments. .EXAMPLE Get-NectarTenantNames .NOTES Version 1.0 #> [Alias("gntn")] [cmdletbinding()] param () Begin { Connect-NectarCloud } Process { Try { $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri "https://$Global:NectarCloud/aapi/tenant" $TenantList = @() Foreach ($Item in $JSON) { $PSObject = New-Object PSObject -Property @{ TenantName = $Item } $TenantList += $PSObject } $TenantList } Catch { Write-Error 'No tenants found, or insufficient permissions.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarTenantDatasources { <# .SYNOPSIS Shows all the datasources available on a given tenant. .DESCRIPTION Shows all the datasources available on a given tenant. .EXAMPLE Get-NectarTenantDatasources .NOTES Version 1.0 #> [Alias("gntdc")] [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { If (!$TenantName) { $TenantName = Get-NectarDefaultTenantName } $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri "https://$Global:NectarCloud/aapi/tenant/datasources?tenant=$TenantName" Return $JSON.data } Catch { Write-Error 'No tenant datasources found, or insufficient permissions.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarTenantPlatforms { <# .SYNOPSIS Shows all the platforms available on a given tenant. .DESCRIPTION Shows all the platforms available on a given tenant. .EXAMPLE Get-NectarTenantPlatforms .NOTES Version 1.0 #> [Alias("gntp")] [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { If (!$TenantName) { $TenantName = Get-NectarDefaultTenantName } $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri "https://$Global:NectarCloud/aapi/tenant/platforms?tenant=$TenantName" ForEach ($Item in $JSON) { $PlatformList = [pscustomobject][ordered]@{ TenantName = $TenantName Platform = $Item.platform Supported = $Item.supported } $PlatformList } } Catch { Write-Error 'No tenant platforms found, or insufficient permissions.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarTenantSSOConfig { <# .SYNOPSIS Shows the SSO config for a given tenant. .DESCRIPTION Shows the SSO config for a given tenant. .EXAMPLE Get-NectarTenantSSOConfig .NOTES Version 1.0 #> [Alias("gntsc")] [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { If (!$TenantName) { $TenantName = Get-NectarDefaultTenantName } $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri "https://$Global:NectarCloud/aapi/client/sso-config?tenant=$TenantName" Return $JSON ForEach ($Item in $JSON) { $PlatformList = [pscustomobject][ordered]@{ TenantName = $TenantName Platform = $Item.platform Supported = $Item.supported } $PlatformList } } Catch { Write-Error 'No tenant platforms found, or insufficient permissions.' Get-JSONErrorStream -JSONResponse $_ } } } ################################################################################################################################################# # # # Tenant Email Domain Functions # # # ################################################################################################################################################# Function Get-NectarEmailDomain { <# .SYNOPSIS Returns a list of Nectar 10 allowed email domains that can be used for login IDs. .DESCRIPTION Returns a list of Nectar 10 allowed email domains that can be used for login IDs. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-NectarEmailDomain Returns all the allowed email domains for the logged in tenant. .NOTES Version 1.1 #> [Alias("gne")] Param ( [Parameter(Mandatory=$False)] [string]$SearchQuery, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize = 1000 ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri "https://$Global:NectarCloud/aapi/client/domains?searchQuery=$SearchQuery&tenant=$TenantName&pageSize=$ResultSize" If ($JSON.domainNames) { Return $JSON.domainNames } If ($JSON.elements) { Return $JSON.elements } } Catch { Write-Error 'No email domains found, or insufficient permissions.' Get-JSONErrorStream -JSONResponse $_ } } } Function New-NectarEmailDomain { <# .SYNOPSIS Add a new allowed email domain that can be used for login IDs. .DESCRIPTION Add a new allowed email domain that can be used for login IDs. .PARAMETER EmailDomain The email domain to add to the tenant. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE New-NectarEmailDomain -EmailDomain contoso.com Adds the contoso.com email domain to the logged in Nectar 10 tenant. .NOTES Version 1.1 #> [Alias("nne")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$EmailDomain, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $URI = "https://$Global:NectarCloud/aapi/client/domains?tenant=$TenantName" $JSONBody = $EmailDomain Try { $JSON = Invoke-RestMethod -Method POST -WebSession $Global:NectarSession -uri $URI -Body $JSONBody -ContentType 'application/json; charset=utf-8' Write-Verbose "Successfully added $EmailDomain as an allowed email domain." } Catch { Write-Error "Unable to add $EmailDomain to list of allowed email domains." Get-JSONErrorStream -JSONResponse $_ } } } Function Remove-NectarEmailDomain { <# .SYNOPSIS Remove an allowed email domain that can be used for login IDs. .DESCRIPTION Remove an allowed email domain that can be used for login IDs. .PARAMETER EmailDomain The email domain to remove from the tenant. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Remove-NectarEmailDomain -EmailDomain contoso.com Removes the contoso.com email domain from the list of allowed domains on the logged in Nectar 10 tenant. .NOTES Version 1.1 #> [Alias("rne")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("domainName")] [string]$EmailDomain, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("id")] [int]$Identity ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($EmailDomain -and !$Identity) { $Identity = (Get-NectarEmailDomain -TenantName $TenantName -SearchQuery $EmailDomain -ResultSize 1 -ErrorVariable GetDomainError).ID } If (!$GetDomainError) { $URI = "https://$Global:NectarCloud/aapi/client/domains/$Identity/?tenant=$TenantName" Try { $JSON = Invoke-RestMethod -Method DELETE -WebSession $Global:NectarSession -uri $URI Write-Verbose "Successfully deleted $EmailDomain from list of allowed email account domains." } Catch { Write-Error "Unable to delete email domain $EmailDomain. Ensure you typed the name of the email domain correctly." Get-JSONErrorStream -JSONResponse $_ } } } } ################################################################################################################################################# # # # Tenant Admin Functions # # # ################################################################################################################################################# Function Get-NectarAdmin { <# .SYNOPSIS Get information about 1 or more Nectar 10 users. .DESCRIPTION Get information about 1 or more Nectar 10 users. .PARAMETER SearchQuery A full or partial match of the user's first or last name or email address .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-NectarAdmin -SearchQuery tferguson@contoso.com Returns information about the user tferguson@contoso.com .NOTES Version 1.1 #> [Alias("gna")] Param ( [Parameter(Mandatory=$False)] [string]$SearchQuery, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize = 1000 ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $URI = "https://$Global:NectarCloud/aapi/users?searchQuery=$SearchQuery&tenant=$TenantName&pageSize=$ResultSize" Try { $JSON = Invoke-RestMethod -Method POST -WebSession $Global:NectarSession -uri $URI If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} # Add the tenant name to the output which helps pipelining $JSON.elements | Add-Member -TypeName 'Nectar.AdminList' Return $JSON.elements } Catch { Write-Error "Unable to get user details." Get-JSONErrorStream -JSONResponse $_ } } } Function Set-NectarAdmin { <# .SYNOPSIS Update 1 or more Nectar 10 admin accounts. .DESCRIPTION Update 1 or more Nectar 10 admin accounts. .PARAMETER FirstName The first name of the user .PARAMETER LastName The last name of the user .PARAMETER EmailAddress The email address of the user .PARAMETER AdminStatus True if Admin, False if not. Used when importing many admin accounts via CSV .PARAMETER IsAdmin Include if user is to be an admin. If not present, then user will be read-onl .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER Identity The numerical identity of the user .EXAMPLE Set-NectarAdmin Identity 233 .NOTES Version 1.1 #> [Alias("sna")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("name")] [string]$FirstName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$LastName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("email")] [string]$EmailAddress, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("admin")] [string]$AdminStatus, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [switch]$IsAdmin, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Role, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("id")] [String]$Identity ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } # If ($IsAdmin -and !$AdminStatus) { # $AdminStatus = "true" # } # ElseIf (!$IsAdmin -and !$AdminStatus) { # $AdminStatus = "false" # } If ($EmailAddress -And !$Identity) { $UserInfo = Get-NectarAdmin -SearchQuery $EmailAddress -Tenant $TenantName -ResultSize 1 $Identity = $UserInfo.id } If (-not $FirstName) {$FirstName = $UserInfo.firstName} If (-not $LastName) {$LastName = $UserInfo.lastName} If (-not $EmailAddress) {$EmailAddress = $UserInfo.email} If (-not $IsAdmin -and !$AdminStatus) {$AdminStatus = $UserInfo.admin} $URI = "https://$Global:NectarCloud/aapi/user/$Identity/?tenant=$TenantName" $Body = @{ id = $Identity email = $EmailAddress firstName = $FirstName lastName = $LastName # isAdmin = $AdminStatus userRoleName = $Role # userStatus = "ACTIVE" } $JSONBody = $Body | ConvertTo-Json Try { Write-Verbose $JSONBody $JSON = Invoke-RestMethod -Method PUT -WebSession $Global:NectarSession -uri $URI -Body $JSONBody -ContentType 'application/json; charset=utf-8' } Catch { Write-Error "Unable to apply changes for user $EmailAddress." Get-JSONErrorStream -JSONResponse $_ } } } Function New-NectarAdmin { <# .SYNOPSIS Create a new Nectar 10 admin account. .DESCRIPTION Create a new Nectar 10 admin account. .PARAMETER FirstName The user's first name .PARAMETER LastName The user's last name .PARAMETER EmailAddress The user's email address .PARAMETER Password The password to assign to the new account .PARAMETER AdminStatus True if Admin, False if not. Used when importing many admin accounts via CSV .PARAMETER IsAdmin Include if user is to be an admin. If not present, then user will be read-only .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE New-NectarAdmin -FirstName Turd -LastName Ferguson -Email tferguson@contoso.com -Password VeryStrongPassword -IsAdmin Creates a new admin user called Turd Ferguson .EXAMPLE Import-Csv .\Users.csv | New-NectarAdmin Creates admin accounts using a CSV file as input. CSV file must have the following headers: FirstName,LastName,Password,Email,AdminStatus (Use True/False for AdminStatus) . .NOTES Version 1.2 #> [Alias("nna")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$FirstName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$LastName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("email")] [string]$EmailAddress, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Password, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AdminStatus, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [switch]$IsAdmin, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Role, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { If ($IsAdmin -and !$AdminStatus) { $AdminStatus = "true" } ElseIf (!$IsAdmin -and !$AdminStatus) { $AdminStatus = "false" } # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $URI = "https://$Global:NectarCloud/aapi/user?tenant=$TenantName" $Body = @{ email = $EmailAddress firstName = $FirstName lastName = $LastName isAdmin = $AdminStatus userRoleName = $Role userStatus = "ACTIVE" } If ($Password) { $Body.Add('password',$Password) } $JSONBody = $Body | ConvertTo-Json Try { Write-Verbose $JSONBody $JSON = Invoke-RestMethod -Method POST -WebSession $Global:NectarSession -uri $URI -Body $JSONBody -ContentType 'application/json; charset=utf-8' } Catch { Write-Error "Unable to create admin account $EmailAddress." Get-JSONErrorStream -JSONResponse $_ } } } Function Remove-NectarAdmin { <# .SYNOPSIS Removes one or more Nectar 10 admin account. .DESCRIPTION Removes one or more Nectar 10 admin account. .PARAMETER EmailAddress The email address of the admin account to remove. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER Identity The numerical ID of the admin account to remove. Can be obtained via Get-NectarAdmin and pipelined to Remove-NectarAdmin .EXAMPLE Remove-NectarAdmin tferguson@nectarcorp.com Removes the admin account tferguson@nectarcorp.com .NOTES Version 1.1 #> [Alias("rna")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("email")] [string]$EmailAddress, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("id")] [string]$Identity ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($EmailAddress -and !$Identity) { $Identity = (Get-NectarAdmin -SearchQuery $EmailAddress -Tenant $TenantName -ResultSize 1 -ErrorVariable GetUserError).ID } If (!$GetUserError) { $URI = "https://$Global:NectarCloud/aapi/user/$Identity/?tenant=$TenantName" Try { $JSON = Invoke-RestMethod -Method DELETE -WebSession $Global:NectarSession -uri $URI Write-Verbose "Successfully deleted $EmailAddress from admin account list." } Catch { Write-Error "Unable to delete user $EmailAddress. Ensure you typed the name of the user correctly." Get-JSONErrorStream -JSONResponse $_ } } } } ################################################################################################################################################# # # # Tenant Location Functions # # # ################################################################################################################################################# Function Get-NectarLocation { <# .SYNOPSIS Returns a list of Nectar 10 locations .DESCRIPTION Returns a list of Nectar 10 locations .PARAMETER SearchQuery The name of the location to get information on based on either network, networkName, City, StreetAddress, State, SiteName or SiteCode. Can be a partial match, and may return more than one entry. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-NectarLocation Returns the first 10 locations .EXAMPLE Get-NectarLocation -ResultSize 100 Returns the first 100 locations .EXAMPLE Get-NectarLocation -LocationName Location2 Returns up to 10 locations that contains "location2" anywhere in the name. The search is not case-sensitive. This example would return Location2, Location20, Location214, MyLocation299 etc .EXAMPLE Get-NectarLocation -LocationName ^Location2 Returns up to 10 locations that starts with "location2" in the name. The search is not case-sensitive. This example would return Location2, Location20, Location214 etc, but NOT MyLocation299 .EXAMPLE Get-NectarLocation -LocationName ^Location2$ Returns a location explicitly named "Location2". The search is not case-sensitive. .NOTES Version 1.1 #> [Alias("gnl")] Param ( [Parameter(Mandatory=$False)] [string]$SearchQuery, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize = 5000 ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $URI = "https://$Global:NectarCloud/aapi/config/locations?pageNumber=1&tenant=$TenantName&pageSize=$ResultSize&searchQuery=$SearchQuery" $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri $URI If (!$JSON.elements) { Write-Error "Location $SearchQuery not found." } Else { If ($TenantName) { $JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty } # Add the tenant name to the output which helps pipelining $JSON.elements | Add-Member -TypeName 'Nectar.LocationList' Return $JSON.elements } } Catch { Write-Error "Unable to get location details." Get-JSONErrorStream -JSONResponse $_ } } } Function Set-NectarLocation { <# .SYNOPSIS Update a Nectar 10 location in the location database .DESCRIPTION Update a Nectar 10 location in the location database. This command can use the Google Geocode API to automatically populate the latitude/longitude for each location. You can register for an API key and save it as persistent environment variable called GoogleGeocode_API_Key on this machine. This command will prompt for the GeoCode API key and will save it in the appropriate location. Follow this link to get an API Key - https://developers.google.com/maps/documentation/geocoding/get-api-key. If this is not an option, then use the -SkipGeoLocate switch .PARAMETER SearchQuery A string to search for. Will search in Network, NetworkName, City, Street Address, Region etc. .PARAMETER Network The IP subnet of the network .PARAMETER NetworkMask The subnet mask of the network .PARAMETER ExtNetwork The IP subnet of the external/public network. Optional. Used to help differentiate calls from corporate locations that use common home subnets (192.168.x.x) .PARAMETER ExtNetworkMask The subnet mask of the external/public network. Optional. Used to help differentiate calls from corporate locations that use common home subnets (192.168.x.x) .PARAMETER NetworkName The name to give to the network .PARAMETER SiteName The name to give to the siteCode .PARAMETER SiteCode A site code to assign to the site .PARAMETER Region The name of the region. Typically is set to country name or whatever is appropriate for the company .PARAMETER StreetAddress The street address of the location .PARAMETER City The city of the location .PARAMETER State The state/province of the location .PARAMETER PostCode The postal/zip code of the location .PARAMETER Country The 2-letter ISO country code of the location .PARAMETER Description A description to apply to the location .PARAMETER IsWireless True or false if the network is strictly wireless .PARAMETER IsExternal True or false if the network is outside the corporate network .PARAMETER IsVPN True or false if the network is a VPN .PARAMETER Latitude The geographical latitude of the location. If not specified, will attempt automatic geolocation. .PARAMETER Longitude The geographical longitude of the location. If not specified, will attempt automatic geolocation. .PARAMETER SkipGeoLocate Don't attempt geolocation. Do this if you don't have a valid Google Maps API key. .PARAMETER Identity The numerical ID of the location to update. Can be obtained via Get-NectarLocation and pipelined to Set-NectarLocation .EXAMPLE Set-NectarLocation HeadOffice -Region WestUS Changes the region for HeadOffice to WestUS .EXAMPLE Get-NectarLocation | Set-NectarLocation Will go through each location and update the latitude/longitude. Useful if a Google Geocode API key was obtained after initial location loading .NOTES Version 1.11 #> [Alias("snl")] Param ( [Parameter(Mandatory=$False)] [string]$SearchQuery, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("subnet")] [string]$Network, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("networkRange", "subnetMask")] [ValidateRange(1,32)] [string]$NetworkMask, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$ExtNetwork, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateRange(0,32)] [string]$ExtNetworkMask, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateLength(1,99)] [Alias("Network Name")] [string]$NetworkName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$SiteName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("Site Code")] [string]$SiteCode, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Region, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("address")] [string]$StreetAddress, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$City, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("province")] [string]$State, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("zipcode")] [string]$PostCode, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateLength(0,2)] [string]$Country, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Description, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateSet("True","False","Yes","No",0,1, IgnoreCase=$True)] [Alias("isWirelessNetwork","Wireless(Yes/No)","Wireless","Wifi")] [string]$IsWireless, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateSet("True","False","Yes","No",0,1, IgnoreCase=$True)] [Alias("External(Yes/No)","External")] [string]$IsExternal, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateSet("True","False","Yes","No",0,1, IgnoreCase=$True)] [Alias("VPN","VPN(Yes/No)")] [string]$IsVPN, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateRange(-90,90)] [Alias("CoordinatesLatitude")] [double]$Latitude, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateRange(-180,180)] [Alias("CoordinatesLongitude")] [double]$Longitude, [parameter(Mandatory=$False)] [switch]$ForceGeoLocate, [parameter(Mandatory=$False)] [switch]$SkipGeoLocate, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("id")] [String]$Identity ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } Try { If ($SearchQuery) { $LocationInfo = Get-NectarLocation -SearchQuery $SearchQuery -Tenant $TenantName -ResultSize 1 $Identity = $LocationInfo.id.ToString() } If (-not $Network) {$Network = $LocationInfo.network} If (-not $NetworkMask) {$NetworkMask = $LocationInfo.networkRange} If (-not $ExtNetwork) {$ExtNetwork = $LocationInfo.externalNetwork} If (-not $ExtNetworkMask) {$ExtNetworkMask = $LocationInfo.externalNetworkRange} If (-not $NetworkName) {$NetworkName = $LocationInfo.networkName} If (-not $SiteName) {$SiteName = $LocationInfo.siteName} If (-not $SiteCode) {$SiteCode = $LocationInfo.siteCode} If (-not $Region) {$Region = $LocationInfo.region} If (-not $StreetAddress) {$StreetAddress = $LocationInfo.streetAddress} If (-not $City) {$City = $LocationInfo.city} If (-not $State) {$State = $LocationInfo.state} If (-not $PostCode) {$PostCode = $LocationInfo.zipCode} If (-not $Country) {$Country = $LocationInfo.country} If (-not $Description) {$Description = $LocationInfo.description} If (-not $IsWireless) {$IsWireless = $LocationInfo.isWirelessNetwork} If (-not $IsExternal) {$IsExternal = $LocationInfo.isExternal} If (-not $IsVPN) {$IsVPN = $LocationInfo.vpn} If ($Latitude -eq $NULL -or $Latitude -eq 0) {$Latitude = $LocationInfo.latitude} If ($Longitude -eq $NULL -or $Longitude -eq 0) {$Longitude = $LocationInfo.longitude} If (-not $IsVPN) {$IsVPN = $LocationInfo.vpn} If ((($Latitude -eq $NULL -Or $Longitude -eq $NULL) -Or ($Latitude -eq 0 -And $Longitude -eq 0)) -Or $ForceGeoLocate -And !$SkipGeoLocate) { Write-Verbose "Lat/Long missing. Getting Lat/Long." $LatLong = Get-LatLong "$StreetAddress, $City, $State, $PostCode, $Region" $Latitude = $LatLong.Latitude $Longitude = $LatLong.Longitude } $URI = "https://$Global:NectarCloud/aapi/config/location/$Identity/?tenant=$TenantName" $Body = @{ city = $City description = $Description id = $Identity isExternal = ParseBool $IsExternal isWirelessNetwork = ParseBool $IsWireless latitude = $Latitude longitude = $Longitude network = $Network networkRange = $NetworkMask externalNetwork = $ExtNetwork externalNetworkRange = $ExtNetworkMask networkName = $NetworkName region = $Region siteCode = $SiteCode siteName = $SiteName state = $State streetAddress = $StreetAddress country = $Country vpn = ParseBool $IsVPN zipCode = $PostCode } Write-Verbose $Body $JSONBody = $Body | ConvertTo-Json Try { Write-Verbose $JSONBody $JSON = Invoke-RestMethod -Method PUT -WebSession $Global:NectarSession -uri $URI -Body $JSONBody -ContentType 'application/json; charset=utf-8' } Catch { If ($NetworkName) { $IDText = $NetworkName } Else { $IDText = "with ID $Identity" } Write-Error "Unable to apply changes for location $IDText." Get-JSONErrorStream -JSONResponse $_ } } Catch { Write-Error $_ } } } Function New-NectarLocation { <# .SYNOPSIS Creates a Nectar 10 location in the location database .DESCRIPTION Creates a Nectar 10 location in the location database. This command can use the Google Geocode API to automatically populate the latitude/longitude for each location. You can register for an API key and save it as persistent environment variable called GoogleGeocode_API_Key on this machine. This command will prompt for the GeoCode API key and will save it in the appropriate location. Follow this link to get an API Key - https://developers.google.com/maps/documentation/geocoding/get-api-key. If this is not an option, then use the -SkipGeoLocate switch .PARAMETER Network The IP subnet of the network .PARAMETER NetworkMask The subnet mask of the network .PARAMETER ExtNetwork The IP subnet of the external/public network. Optional. Used to help differentiate calls from corporate locations that use common home subnets (192.168.x.x) .PARAMETER ExtNetworkMask The subnet mask of the external/public network. Optional. Used to help differentiate calls from corporate locations that use common home subnets (192.168.x.x) .PARAMETER NetworkName The name to give to the network .PARAMETER SiteName The name to give to the site .PARAMETER SiteCode A site code to assign to the site .PARAMETER Region The name of the region. Typically is set to country name or whatever is appropriate for the company .PARAMETER StreetAddress The street address of the location .PARAMETER City The city of the location .PARAMETER State The state/province of the location .PARAMETER PostCode The postal/zip code of the location .PARAMETER Country The 2-letter ISO country code of the location .PARAMETER Description A description to apply to the location .PARAMETER IsWireless True or false if the network is strictly wireless .PARAMETER IsExternal True or false if the network is outside the corporate network .PARAMETER IsVPN True or false if the network is a VPN .PARAMETER SkipGeoLocate Don't attempt geolocation. Do this if you don't have a valid Google Maps API key. .PARAMETER Latitude The geographical latitude of the location. If not specified, will attempt automatic geolocation. .PARAMETER Longitude The geographical longitude of the location. If not specified, will attempt automatic geolocation. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE New-NectarLocation -Network 10.14.3.0 -NetworkMask 24 -NetworkName Corp5thFloor -SiteName 'Head Office' Creates a new location using the minimum required information .EXAMPLE New-NectarLocation -Network 10.15.1.0 -NetworkMask 24 -ExtNetwork 79.23.155.71 -ExtNetworkMask 28 -NetworkName Corp3rdFloor -SiteName 'Head Office' -SiteCode HO3 -IsWireless True -IsVPN False -Region EastUS -StreetAddress '366 North Broadway' -City Jericho -State 'New York' -Country US -PostCode 11753 -Description 'Head office 3rd floor' -Latitude 40.7818283 -Longitude -73.5351438 Creates a new location using all available fields .EXAMPLE Import-Csv LocationData.csv | New-NectarLocation Imports a CSV file called LocationData.csv and creates new locations .EXAMPLE Import-Csv LocationData.csv | New-NectarLocation -SkipGeolocate Imports a CSV file called LocationData.csv and creates new locations but will not attempt geolocation .NOTES Version 1.1 #> [Alias("nnl")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [ValidatePattern('^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$')] [Alias('subnet','searchquery')] [string]$Network, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias('Network Range','networkRange', 'subnetMask')] [ValidateRange(1,32)] [string]$NetworkMask, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$ExtNetwork, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateRange(0,32)] [string]$ExtNetworkMask, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [ValidateLength(1,99)] [Alias('Network Name')] [string]$NetworkName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias('Site Name')] [string]$SiteName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias('Site Code')] [string]$SiteCode, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Region, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias('Street Address', 'address')] [string]$StreetAddress, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$City, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias('province')] [string]$State, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias('Zip Code', 'zipcode')] [string]$PostCode, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateLength(0,2)] [string]$Country, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Description, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateSet('True','False','Yes','No',0,1, IgnoreCase=$True)] [Alias('isWirelessNetwork','Wireless(Yes/No)','Wireless(True/False)','Wireless','Wifi')] [string]$IsWireless, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateSet('True','False','Yes','No',0,1, IgnoreCase=$True)] [Alias('External(Yes/No)','External(True/False)','External','Internal/External')] [string]$IsExternal, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateSet('True','False','Yes','No',0,1, IgnoreCase=$True)] [Alias('VPN','VPN(Yes/No)','VPN(True/False)')] [string]$IsVPN, [parameter(Mandatory=$False)] [switch]$SkipGeoLocate, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateRange(-90,90)] [Alias('Coordinates Latitude','CoordinatesLatitude')] [double]$Latitude, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateRange(-180,180)] [Alias('Coordinates Longitude','CoordinatesLongitude')] [double]$Longitude, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $URI = "https://$Global:NectarCloud/aapi/config/location?tenant=$TenantName" If (-not $Latitude -Or -not $Longitude -And !$SkipGeoLocate) { $LatLong = Get-LatLong "$StreetAddress, $City, $State, $PostCode, $Country" [double]$Latitude = $LatLong.Latitude [double]$Longitude = $LatLong.Longitude } $Body = @{ city = $City description = $Description isExternal = ParseBool $IsExternal isWirelessNetwork = ParseBool $IsWireless latitude = $Latitude longitude = $Longitude network = $Network networkName = $NetworkName networkRange = $NetworkMask externalNetwork = $ExtNetworkName externalNetworkRange = $ExtNetworkMask region = $Region siteCode = $SiteCode siteName = $SiteName state = $State streetAddress = $StreetAddress country = $Country vpn = ParseBool $IsVPN zipCode = $PostCode } $JSONBody = $Body | ConvertTo-Json Try { Write-Verbose $JSONBody $JSON = Invoke-RestMethod -Method POST -WebSession $Global:NectarSession -uri $URI -Body $JSONBody -ContentType 'application/json; charset=utf-8' } Catch { Write-Error "Unable to create location $NetworkName with network $Network/$NetworkMask" Get-JSONErrorStream -JSONResponse $_ } } } Function Remove-NectarLocation { <# .SYNOPSIS Removes a Nectar 10 location from the location database .DESCRIPTION Removes a Nectar 10 location from the location database .PARAMETER SearchQuery The name of the location to remove. Can be a partial match. To return an exact match and to avoid ambiguity, enclose location name with ^ at the beginning and $ at the end. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER Identity The numerical ID of the location to remove. Can be obtained via Get-NectarLocation and pipelined to Remove-NectarLocation .NOTES Version 1.1 #> [Alias("rnl")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("networkName")] [string]$SearchQuery, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("id")] [string]$Identity ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($SearchQuery -And !$Identity) { $LocationInfo = Get-NectarLocation -SearchQuery $SearchQuery -Tenant $TenantName -ResultSize 1 -ErrorVariable GetLocationError $Identity = $LocationInfo.id $NetworkName = $LocationInfo.networkName } If (!$GetLocationError) { $URI = "https://$Global:NectarCloud/aapi/config/location/$Identity/?tenant=$TenantName" Try { $JSON = Invoke-RestMethod -Method DELETE -WebSession $Global:NectarSession -uri $URI Write-Verbose "Successfully deleted $LocationName." } Catch { Write-Error "Unable to delete location $NetworkName. Ensure you typed the name of the location correctly." Get-JSONErrorStream -JSONResponse $_ } } } } Function Import-NectarLocations { <# .SYNOPSIS Imports a CSV list of locations into Nectar 10 .DESCRIPTION Import a CSV list of locations into Nectar 10. This will overwrite any existing locations with the same network ID. Useful for making wholesale changes without wiping and replacing everything. Assumes you are working from an export from the existing Nectar 10 location list. .PARAMETER Path The path to the CSV file to import into Nectar 10. The CSV file must use the standard column heading template used by Nectar 10 exports. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER SkipGeoLocate Don't attempt geolocation. Do this if you don't have a valid Google Maps API key or the lat/long is already included in the CSV. .NOTES Version 1.1 #> Param ( [Parameter(Mandatory=$True)] [string]$Path, [Parameter(Mandatory=$False)] [string]$TenantName, [parameter(Mandatory=$False)] [switch]$SkipGeoLocate ) $LocTable = ((Get-Content -Path $Path -Raw) -replace '\(Yes/No\)','') $LocTable = $LocTable -replace '\"?Network\"?\,',"""SearchQuery""," $LocationList = ConvertFrom-Csv $LocTable ForEach ($Location in $LocationList) { $LocationHashTable = @{} $Location.psobject.properties | ForEach { $LocationHashTable[$_.Name] = $_.Value } If ($TenantName) { $LocationHashTable += @{TenantName = $TenantName } }# Add the tenant name to the hashtable If ($SkipGeoLocate) { $LocationHashTable += @{SkipGeoLocate = $TRUE} } Try { Write-Host "Updating location with subnet $($Location.SearchQuery)" Write-Verbose $LocationHashTable Set-NectarLocation @LocationHashTable -ErrorAction:Stop } Catch { Write-Host "Location does not exist. Creating location $($Location.SearchQuery)" New-NectarLocation @LocationHashTable } } } Function Import-MSTeamsLocations { <# .SYNOPSIS Imports a CSV list of locations downloaded from Microsoft CQD into Nectar 10 .DESCRIPTION Import a CSV list of locations downloaded from Microsoft CQD into Nectar 10. This will overwrite any existing locations with the same network ID. Useful for making wholesale changes without wiping and replacing everything. .PARAMETER Path The path to the CSV file to import into Nectar 10. The CSV file must be in the same format as downloaded from Microsoft CQD as per https://docs.microsoft.com/en-us/microsoftteams/cqd-upload-tenant-building-data .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER SkipGeoLocate Don't attempt geolocation. Do this if you don't have a valid Google Maps API key. .NOTES Version 1.0 #> Param ( [Parameter(Mandatory=$True)] [string]$Path, [Parameter(Mandatory=$False)] [string]$TenantName, [parameter(Mandatory=$False)] [switch]$SkipGeoLocate ) $Header = 'SearchQuery', 'NetworkName', 'NetworkMask', 'SiteName', 'OwnershipType', 'BuildingType', 'BuildingOfficeType', 'City', 'PostCode', 'Country', 'State', 'Region', 'IsExternal', 'ExpressRoute', 'IsVPN' $LocationList = Import-Csv $Path -Header $Header | Select-Object 'SearchQuery','NetworkMask','NetworkName','SiteName','City','PostCode','Country','State','Region','IsExternal','IsVPN' ForEach ($Location in $LocationList) { If ($Location.IsExternal -eq 0) { $Location.IsExternal = 1 } Else { $Location.IsExternal = 0 } If ($Location.IsVPN -eq 1) { $Location.IsVPN = 1 } Else { $Location.IsVPN = 0 } $LocationHashTable = @{} $Location.psobject.properties | ForEach { $LocationHashTable[$_.Name] = $_.Value } If ($TenantName) { $LocationHashTable += @{TenantName = $TenantName } }# Add the tenant name to the hashtable If ($SkipGeoLocate) { $LocationHashTable += @{SkipGeoLocate = $TRUE} } Try { Write-Host "Updating location with subnet $($Location.SearchQuery)" Write-Verbose $LocationHashTable Set-NectarLocation @LocationHashTable -ErrorAction:Stop } Catch { Write-Host "Location does not exist. Creating location $($Location.SearchQuery)" New-NectarLocation @LocationHashTable } } } ################################################################################################################################################# # # # DID Number Location Management Functions # # # ################################################################################################################################################# Function Get-NectarNumberLocation { <# .SYNOPSIS Returns a list of Nectar 10 service locations used in the DID Management tool. .DESCRIPTION Returns a list of Nectar 10 service locations used in the DID Management tool. .PARAMETER LocationName The name of the service location to get information on. Can be a partial match. To return an exact match and to avoid ambiguity, enclose service location name with ^ at the beginning and $ at the end. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-NectarNumberLocation Returns the first 10 service locations .EXAMPLE Get-NectarNumberLocation -ResultSize 100 Returns the first 100 service locations .EXAMPLE Get-NectarNumberLocation -LocationName Location2 Returns up to 10 service locations that contains "location2" anywhere in the name. The search is not case-sensitive. This example would return Location2, Location20, Location214, MyLocation299 etc .EXAMPLE Get-NectarNumberLocation -LocationName ^Location2 Returns up to 10 service locations that starts with "location2" in the name. The search is not case-sensitive. This example would return Location2, Location20, Location214 etc, but NOT MyLocation299 .EXAMPLE Get-NectarNumberLocation -LocationName ^Location2$ Returns a service location explicitly named "Location2". The search is not case-sensitive. .NOTES Version 1.1 #> [Alias("gnnl")] Param ( [Parameter(Mandatory=$False)] [string]$LocationName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize = 1000 ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri "https://$Global:NectarCloud/dapi/numbers/locations?pageNumber=1&tenant=$TenantName&pageSize=$ResultSize&q=$LocationName" If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} # Add the tenant name to the output which helps pipelining $JSON.elements | Add-Member -TypeName 'Nectar.Number.LocationList' Return $JSON.elements } Catch { Write-Error "Service location not found. Ensure you typed the name of the service location correctly." Get-JSONErrorStream -JSONResponse $_ } } } Function Set-NectarNumberLocation { <# .SYNOPSIS Update a Nectar 10 service location used in the DID Management tool. .DESCRIPTION Update a Nectar 10 service location used in the DID Management tool. .PARAMETER LocationName The name of the service location to get information on. Can be a partial match. To return an exact match and to avoid ambiguity, enclose location name with ^ at the beginning and $ at the end. .PARAMETER NewLocationName Replace the existing service location name with this one. .PARAMETER ServiceID The service ID associated with the telephony provider for this service location .PARAMETER ServiceProvider The name of the service provider that provides telephony service to this service location .PARAMETER NetworkLocation The phyiscal location for this service location .PARAMETER Notes Can be used for any additional information .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Set-NectarNumberLocation -LocationName Dallas -ServiceID 44FE98 -ServiceProvider Verizon -Notes "Head office" Returns the first 10 locations .NOTES Version 1.1 #> [Alias("snnl")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("name")] [string]$LocationName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$NewLocationName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] $ServiceID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("provider")] $ServiceProvider, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("location")] $NetworkLocation, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] $Notes, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("id")] [String]$Identity ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($LocationName -And !$Identity) { $LocationInfo = Get-NectarNumberLocation -LocationName $LocationName -Tenant $TenantName -ResultSize 1 $Identity = $LocationInfo.id } If ($LocationInfo.Count -gt 1) { Write-Error "Multiple number locations found that match $LocationName. Please refine your location name search query" Break } If ($NewLocationName) {$LocationName = $NewLocationName} If (-not $ServiceID) {$ServiceID = $LocationInfo.ServiceId} If (-not $ServiceProvider) {$ServiceProvider = $LocationInfo.provider} If (-not $NetworkLocation) {$NetworkLocation = $LocationInfo.location} If (-not $Notes) {$Notes = $LocationInfo.notes} $URI = "https://$Global:NectarCloud/dapi/numbers/location/$Identity/?tenant=$TenantName" $Body = @{ name = $LocationName serviceId = $ServiceID provider = $ServiceProvider location = $NetworkLocation notes = $Notes } $JSONBody = $Body | ConvertTo-Json Try { $JSON = Invoke-RestMethod -Method PUT -WebSession $Global:NectarSession -uri $URI -Body $JSONBody -ContentType 'application/json; charset=utf-8' Write-Verbose $JSONBody } Catch { Write-Error "Unable to apply changes for location $LocationName." Get-JSONErrorStream -JSONResponse $_ } } } Function New-NectarNumberLocation { <# .SYNOPSIS Create a new Nectar Service Location for DID Management used in the DID Management tool. .DESCRIPTION Create a new Nectar Service Location for DID Management used in the DID Management tool. .PARAMETER LocationName The name of the new service location. Must be unique. .PARAMETER ServiceID The service ID for telephony services at the newservice location. Can be used as desired. Not required. .PARAMETER ServiceProvider The service provider for telephony services at the newservice location. Can be used as desired. Not required. .PARAMETER ServiceProvider The network location to associate with the newservice location. Can be used as desired. Not required. .PARAMETER Notes Any relevent notes about the service location. Can be used as desired. Not required. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE New-NectarNumberLocation -LocationName Dallas -ServiceID 348FE22 -ServiceProvider Verizon -NetworkLocation Dallas -Notes "This is headquarters" .NOTES Version 1.1 #> [Alias("nnnl")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("name")] [string]$LocationName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$ServiceID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("provider")] [string]$ServiceProvider, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("location")] [string]$NetworkLocation, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Notes, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $URI = "https://$Global:NectarCloud/dapi/numbers/location/?tenant=$TenantName" $Body = @{ name = $LocationName serviceId = $ServiceID provider = $ServiceProvider location = $NetworkLocation notes = $Notes } $JSONBody = $Body | ConvertTo-Json Try { Invoke-RestMethod -Method POST -WebSession $Global:NectarSession -uri $URI -Body $JSONBody -ContentType 'application/json; charset=utf-8' Write-Verbose $JSONBody } Catch { Write-Error "Unable to create service location $LocationName. The service location may already exist." Get-JSONErrorStream -JSONResponse $_ } } } Function Remove-NectarNumberLocation { <# .SYNOPSIS Removes one or more service locations in the DID Management tool. .DESCRIPTION Removes one or more service locations in the DID Management tool. .PARAMETER LocationName The name of the number service location to remove. .PARAMETER Identity The numerical ID of the number service location. Can be obtained via Get-NectarNumberLocation and pipelined to Remove-NectarNumberLocation .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Remove-NectarNumberLocation Tokyo Removes the Toyota location. The command will fail if the location has number ranges assigned. .NOTES Version 1.1 #> [Alias("rnnl")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$LocationName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("id")] [string]$Identity ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($LocationName -And !$Identity) { $Identity = (Get-NectarNumberLocation -LocationName $LocationName -Tenant $TenantName -ResultSize 1 -ErrorVariable GetLocationError).ID } If ($Identity.Count -gt 1) { Write-Error "Multiple number locations found that match $LocationName. Please refine your location name search query" Break } If (!$GetLocationError) { $URI = "https://$Global:NectarCloud/dapi/numbers/location/$Identity/?tenant=$TenantName" Try { $JSON = Invoke-RestMethod -Method DELETE -WebSession $Global:NectarSession -uri $URI Write-Verbose "Successfully deleted $LocationName." } Catch { Write-Error "Unable to delete service location $LocationName. Ensure you typed the name of the service location correctly and that the service location has no assigned ranges." Get-JSONErrorStream -JSONResponse $_ } } } } ################################################################################################################################################# # # # DID Number Range Management Functions # # # ############################################################################################################################################################################################################### DID Management Functions - Number Range ############################################################## Function Get-NectarNumberRange { <# .SYNOPSIS Returns a list of Nectar 10 number ranges in the DID Management tool .DESCRIPTION Returns a list of Nectar 10 ranges in the DID Management tool .PARAMETER RangeName The name of the number range to get information on. Can be a partial match. To return an exact match and to avoid ambiguity, enclose range name with ^ at the beginning and $ at the end. .PARAMETER LocationName The name of the location to get information on. Will be an exact match. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-NectarNumberRange Returns the first 10 number ranges .EXAMPLE Get-NectarNumberRange -ResultSize 100 Returns the first 100 number ranges .EXAMPLE Get-NectarNumberRange -LocationName Tokyo Returns the first 10 number ranges at the Tokyo location .EXAMPLE Get-NectarNumberRange -RangeName Range2 Returns up to 10 ranges that contains "range2" anywhere in the name. The search is not case-sensitive. This example would return Range2, Range20, Range214, MyRange299 etc .EXAMPLE Get-NectarNumberRange -RangeName ^Range2 Returns up to 10 ranges that starts with "range2" in the name. The search is not case-sensitive. This example would return Range2, Range20, Range214 etc, but NOT MyRange299. .EXAMPLE Get-NectarNumberRange -RangeName ^Range2$ Returns any range explicitly named "Range2". The search is not case-sensitive. This example would return Range2 only. If there are multiple ranges with the name Range2, all will be returned. .EXAMPLE Get-NectarNumberRange -RangeName ^Range2$ -LocationName Tokyo Returns a range explicitly named "Range2" in the Tokyo location. The search is not case-sensitive. .NOTES Version 1.1 #> [Alias("gnnr")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$RangeName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$LocationName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize = 1000 ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($LocationName) { $LocationID = (Get-NectarNumberLocation -LocationName "$LocationName" -Tenant $TenantName -ErrorVariable LocError).ID } If ($LocError) { Break} Try { $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri "https://$Global:NectarCloud/dapi/numbers/ranges?pageNumber=1&tenant=$TenantName&pageSize=$ResultSize&serviceLocationId=$LocationID&q=$RangeName" If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} # Add the tenant name to the output which helps pipelining $JSON.elements | Add-Member -TypeName 'Nectar.Number.RangeList' Return $JSON.elements } Catch { Write-Error "An error occurred." Get-JSONErrorStream -JSONResponse $_ } } } Function Set-NectarNumberRange { <# .SYNOPSIS Make changes to a Nectar range for DID Management .DESCRIPTION Make changes to a Nectar range for DID Management .PARAMETER RangeName The name of the range. Must be unique. .PARAMETER RangeType The type of range. Can be either STANDARD (for DID ranges) or EXTENSION (for extension-based ranges). .PARAMETER FirstNumber The first number in a STANDARD range. Must be numeric, but can start with +. .PARAMETER LastNumber The last number in a STANDARD range. Must be numeric, but can start with +. Must be larger than FirstNumber, and must have the same number of digits. .PARAMETER BaseNumber The base DID for an EXTENSION range. Must be numeric, but can start with +. .PARAMETER ExtStart The first extension number in an EXTENSION range. Must be numeric. .PARAMETER ExtEnd The last extension number in an EXTENSION range. Must be numeric. Must be larger than ExtStart, and must have the same number of digits. .PARAMETER RangeSize The number of phone numbers/extensions in a range. Can be used instead of LastNumber/ExtEnd. .PARAMETER HoldDays The number of days to hold a newly-freed number before returning it to the pool of available numbers. .PARAMETER LocationName The service location to assign the range to. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Set-NectarNumberRange -RangeName DIDRange1 -RangeType STANDARD -FirstNumber +15552223333 -LastNumber +15552224444 -LocationName Dallas Edits a DID range for numbers that fall in the range of +15552223333 to +15552224444 .NOTES Version 1.1 #> [Alias("snnr")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("name")] [string]$RangeName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateSet("STANDARD","EXTENSION", IgnoreCase=$True)] [Alias("type")] [string]$RangeType, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] # [ValidatePattern("^(\+|%2B)?\d+$")] [string]$FirstNumber, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] # [ValidatePattern("^(\+|%2B)?\d+$")] [string]$LastNumber, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] # [ValidatePattern("^(\+|%2B)?\d+$")] [string]$BaseNumber, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] # [ValidatePattern("^\d+$")] [string]$ExtStart, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] # [ValidatePattern("^\d+$")] [string]$ExtEnd, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [int]$RangeSize, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [int]$HoldDays = 0, [parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("serviceLocationId")] [string]$LocationName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("id")] [int]$Identity ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($RangeName -And !$Identity) { $RangeInfo = Get-NectarNumberRange -RangeName $RangeName -Tenant $TenantName -ResultSize 1 $Identity = $RangeInfo.id If ($NewRangeName) {$RangeName = $NewRangeName} If (-not $FirstNumber) {$FirstNumber = $RangeInfo.firstNumber} If (-not $LastNumber) {$LastNumber = $RangeInfo.lastNumber} If (-not $RangeType) {$RangeType = $RangeInfo.type} If (-not $HoldDays) {$HoldDays = $RangeInfo.holdDays} If (-not $BaseNumber) {$BaseNumber = $RangeInfo.baseNumber} If (-not $ExtStart) {$ExtStart = $RangeInfo.extStart} If (-not $ExtEnd) {$ExtEnd = $RangeInfo.extEnd} # If (-not $LocationName) {$LocationID = $RangeInfo.serviceLocationId} } $LocationID = (Get-NectarNumberLocation -LocationName "$LocationName" -Tenant $TenantName -ErrorVariable LocError).ID $URI = "https://$Global:NectarCloud/dapi/numbers/range/$Identity/?tenant=$TenantName" $Body = @{ name = $RangeName type = $RangeType holdDays = $HoldDays serviceLocationId = $LocationID } If ($FirstNumber) { $Body.Add('firstNumber', $FirstNumber) } If ($LastNumber) { $Body.Add('lastNumber', $LastNumber) } If ($BaseNumber) { $Body.Add('baseNumber', $BaseNumber) } If ($ExtStart) { $Body.Add('extStart', $ExtStart) } If ($ExtEnd) { $Body.Add('extEnd', $ExtEnd) } If ($RangeSize) { $Body.Add('rangeSize', $RangeSize) } $JSONBody = $Body | ConvertTo-Json Try { $JSON = Invoke-RestMethod -Method PUT -WebSession $Global:NectarSession -uri $URI -Body $JSONBody -ContentType 'application/json; charset=utf-8' Write-Verbose $JSONBody } Catch { Write-Error "Unable to apply changes for range $RangeName." Get-JSONErrorStream -JSONResponse $_ } } } Function New-NectarNumberRange { <# .SYNOPSIS Create a new Nectar range for DID Management .DESCRIPTION Create a new Nectar range for DID Management .PARAMETER RangeName The name of the new range. Must be unique. .PARAMETER RangeType The type of range. Can be either STANDARD (for DID ranges) or EXTENSION (for extension-based ranges). .PARAMETER FirstNumber The first number in a STANDARD range. Must be numeric, but can start with +. .PARAMETER LastNumber The last number in a STANDARD range. Must be numeric, but can start with +. Must be larger than FirstNumber, and must have the same number of digits. .PARAMETER BaseNumber The base DID for an EXTENSION range. Must be numeric, but can start with +. .PARAMETER ExtStart The first extension number in an EXTENSION range. Must be numeric. .PARAMETER ExtEnd The last extension number in an EXTENSION range. Must be numeric. Must be larger than ExtStart, and must have the same number of digits. .PARAMETER RangeSize The number of phone numbers/extensions in a range. Can be used instead of LastNumber/ExtEnd. .PARAMETER HoldDays The number of days to hold a newly-freed number before returning it to the pool of available numbers. .PARAMETER LocationName The location to assign the range to. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE New-NectarNumberRange -RangeName DIDRange1 -RangeType STANDARD -FirstNumber +15552223333 -LastNumber +15552224444 -LocationName Dallas Creates a DID range for numbers that fall in the range of +15552223333 to +15552224444 .EXAMPLE New-NectarNumberRange -RangeName DIDRange1 -RangeType STANDARD -FirstNumber +15552223000 -RangeSize 1000 -LocationName Dallas Creates a DID range for numbers that fall in the range of +15552223000 to +15552223999 .EXAMPLE New-NectarNumberRange -RangeName ExtRange1 -RangeType EXTENSION -BaseNumber +15552223000 -ExtStart 2000 -ExtEnd 2999 -LocationName Dallas Creates an extension range for numbers that fall in the range of +15552223000 x2000 to x2999 .NOTES Version 1.2 #> [Alias("nnnr")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("name")] [string]$RangeName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [ValidateSet("STANDARD","EXTENSION", IgnoreCase=$True)] [string]$RangeType, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidatePattern("^(\+|%2B)?\d+$")] [string]$FirstNumber, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidatePattern("^(\+|%2B)?\d+$")] [string]$LastNumber, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidatePattern("^(\+|%2B)?\d+$")] [string]$BaseNumber, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidatePattern("^\d+$")] [string]$ExtStart, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidatePattern("^\d+$")] [string]$ExtEnd, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [int]$RangeSize, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [int]$HoldDays, [parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$LocationName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("id")] [int]$Identity ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $LocationID = (Get-NectarNumberLocation -LocationName $LocationName -Tenant $TenantName -ErrorVariable NumLocError).ID If ($LocationID.Count -gt 1) { Write-Error "Multiple locations found that match $LocationName. Please refine your location name search query" Break } $URI = "https://$Global:NectarCloud/dapi/numbers/range?tenant=$TenantName" $Body = @{ name = $RangeName type = $RangeType holdDays = $HoldDays serviceLocationId = $LocationID } If ($FirstNumber) { $Body.Add('firstNumber', $FirstNumber) } If ($LastNumber) { $Body.Add('lastNumber', $LastNumber) } If ($BaseNumber) { $Body.Add('baseNumber', $BaseNumber) } If ($ExtStart) { $Body.Add('extStart', $ExtStart) } If ($ExtEnd) { $Body.Add('extEnd', $ExtEnd) } If ($RangeSize) { $Body.Add('rangeSize', $RangeSize) } $JSONBody = $Body | ConvertTo-Json Try { Invoke-RestMethod -Method POST -WebSession $Global:NectarSession -uri $URI -Body $JSONBody -ContentType 'application/json; charset=utf-8' Write-Verbose $JSONBody } Catch { Write-Error "Unable to create range $RangeName." Get-JSONErrorStream -JSONResponse $_ } } } Function Remove-NectarNumberRange { <# .SYNOPSIS Removes one or more ranges from a service location in the DID Management tool. .DESCRIPTION Removes one or more ranges from a service location in the DID Management tool. .PARAMETER RangeName The name of the number range to remove. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER Identity The numerical ID of the number range. Can be obtained via Get-NectarNumberRange and pipelined to Remove-NectarNumberRange .EXAMPLE Remove-NectarNumberRange Range1 Removes the range Range1 .NOTES Version 1.1 #> [Alias("rnnr")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$RangeName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("id")] [String]$Identity ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($RangeName -And !$Identity) { $Identity = (Get-NectarNumberRange -RangeName $RangeName -Tenant $TenantName -ErrorVariable GetRangeError).ID } If ($Identity.Count -gt 1) { Write-Error "Multiple ranges found that match $RangeName. Please refine your range name search query" Break } If (!$GetRangeError) { $URI = "https://$Global:NectarCloud/dapi/numbers/range/$Identity/?tenant=$TenantName" Try { $JSON = Invoke-RestMethod -Method DELETE -WebSession $Global:NectarSession -uri $URI Write-Verbose "Successfully deleted $RangeName number range." } Catch { Write-Error "Unable to delete $RangeName number range. Ensure you typed the name of the range correctly." Get-JSONErrorStream -JSONResponse $_ } } } } ################################################################################################################################################# # # # DID Number Management Functions # # # ################################################################################################################################################# Function Get-NectarNumber { <# .SYNOPSIS Returns a list of Nectar 10 numbers from the DID Management tool .DESCRIPTION Returns a list of Nectar 10 numbers from the DID Management tool .PARAMETER PhoneNumber The phone number to return information about. Can be a partial match. To return an exact match and to avoid ambiguity, enclose number with ^ at the beginning and $ at the end. .PARAMETER LocationName The name of the location to get number information about. Will be an exact match. .PARAMETER RangeName The name of the range to get number information about. Will be an exact match. .PARAMETER NumberState Returns information about numbers that are either USED, UNUSED or RESERVED .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-NectarNumber Returns the first 10 numbers .EXAMPLE Get-NectarNumber -ResultSize 100 Returns the first 100 numbers .EXAMPLE Get-NectarNumber -LocationName Tokyo Returns the first 10 numbers at the Tokyo location .EXAMPLE Get-NectarNumber -RangeName Range2 Returns up to 10 numbers from a number range called Range2. .EXAMPLE Get-NectarNumber -RangeName Range2 -NumberState UNUSED -ResultSize 100 Returns up to 100 unused numbers in the Range2 range. .NOTES Version 1.1 #> [Alias("gnn")] Param ( [Parameter(Mandatory=$False)] [ValidatePattern("^(\+|%2B)?\d+$")] [string]$PhoneNumber, [Parameter(Mandatory=$False)] [string]$LocationName, [Parameter(Mandatory=$False)] [string]$RangeName, [Parameter(Mandatory=$False)] [ValidateSet("USED","UNUSED","RESERVED", IgnoreCase=$True)] [string]$NumberState, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$PageSize = 10000, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($LocationName) { $LocationID = (Get-NectarNumberLocation -LocationName ^$LocationName$ -Tenant $TenantName -ResultSize 1 -ErrorVariable NectarError).ID } If ($RangeName) { $RangeID = (Get-NectarNumberRange -RangeName $RangeName -LocationName $LocationName -Tenant $TenantName -ResultSize 1 -ErrorVariable +NectarError).ID } If ($PhoneNumber) { # Replace + with %2B if present $PhoneNumber = $PhoneNumber.Replace("+", "%2B") } $URI = "https://$Global:NectarCloud/dapi/numbers/" $Params = @{ 'orderByField' = 'number' 'orderDirection' = 'asc' } If ($ResultSize) { $Params.Add('pageSize', $ResultSize) } Else { $Params.Add('pageSize', $PageSize) } If ($LocationID) { $Params.Add('serviceLocationId', $LocationID) } If ($RangeID) { $Params.Add('numbersRangeId', $RangeID) } If ($NumberState) { $Params.Add('states', $NumberState) } If ($PhoneNumber) { $Params.Add('q', $PhoneNumber) } If ($TenantName) { $Params.Add('Tenant', $TenantName) } If (!$NectarError) { $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri $URI -Body $Params If ($TenantName) { $JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty } # Add the tenant name to the output which helps pipelining $JSON.elements | Add-Member -TypeName 'Nectar.Number.List' $JSON.elements $TotalPages = $JSON.totalPages If ($TotalPages -gt 1 -and !($ResultSize)) { $PageNum = 2 Write-Verbose "Page size: $PageSize" While ($PageNum -le $TotalPages) { Write-Verbose "Working on page $PageNum of $TotalPages" $PagedURI = $URI + "?pageNumber=$PageNum" $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri $PagedURI -Body $Params If ($TenantName) { $JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty } $JSON.elements | Add-Member -TypeName 'Nectar.Number.List' $JSON.elements $PageNum++ } } } } Catch { Write-Error "Unable to retrieve number information" Get-JSONErrorStream -JSONResponse $_ } } } Function Set-NectarNumber { <# .SYNOPSIS Makes changes to one or more phone numbers. .DESCRIPTION Makes changes to one or more phone numbers. .PARAMETER PhoneNumber A phone number to make changes to. Must be an exact match. .PARAMETER NumberState Change the state of a phone number to either UNUSED or RESERVED. A number marked USED cannot be modified. .PARAMETER Comment A comment to add to a reserved phone number. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Set-NectarNumber +12223334444 -NumberState RESERVED Reserves the number +12223334444 .NOTES Version 1.1 #> [Alias("snn")] Param ( [Parameter(ValueFromPipelineByPropertyName,Mandatory=$False)] [ValidatePattern("^(\+|%2B)?\d+$")] [string]$PhoneNumber, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateSet("UNUSED","RESERVED", IgnoreCase=$True)] [string]$NumberState, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateLength(0,254)] [string]$Comment, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(ValueFromPipelineByPropertyName)] [Alias("id")] [String]$Identity ) Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($PhoneNumber -And !$Identity) { $Identity = (Get-NectarNumber -PhoneNumber $PhoneNumber -Tenant $TenantName -ResultSize 1 -ErrorVariable PhoneNumError).ID } If (!$PhoneNumError) { $URI = "https://$Global:NectarCloud/dapi/numbers/$Identity/state?state=$NumberState&tenant=$TenantName" If (($Comment) -And ($NumberState -eq "RESERVED")) { # Convert special characters to URI-compatible versions #$Comment = [uri]::EscapeDataString($Comment) $URI += "&comment=$Comment" } Try { $JSON = Invoke-RestMethod -Method PUT -WebSession $Global:NectarSession -uri $URI Write-Verbose "Successfully applied changes to $PhoneNumber." } Catch { Write-Error "Unable to apply changes for phone number $PhoneNumber. The number may already be in the desired state." Get-JSONErrorStream -JSONResponse $_ } } } } Function Get-NectarUnallocatedNumber { <# .SYNOPSIS Returns the next available number in a given location/range. .DESCRIPTION Returns the next available number in a given location/range. .PARAMETER LocationName The service location to return a number for .PARAMETER RangeName The range to return a number for .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-NectarUnallocatedNumber -RangeName Jericho Returns the next available number in the Jericho range. .NOTES Version 1.1 #> [Alias("gnun")] Param ( [Parameter(Mandatory=$False)] [string]$LocationName, [Parameter(Mandatory=$False)] [string]$RangeName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $NextFreeNum = Get-NectarNumber -LocationName $LocationName -RangeName $RangeName -NumberState UNUSED -Tenant $TenantName -ResultSize 1 If ($NextFreeNum) { Return $NextFreeNum } Else { Write-Error "No available phone number found." } } ################################################################################################################################################# # # # Supported Device Functions # # # ################################################################################################################################################# Function Get-NectarSupportedDevice { <# .SYNOPSIS Get information about 1 or more Nectar 10 supported devices. .DESCRIPTION Get information about 1 or more Nectar 10 supported devices. .PARAMETER SearchQuery A full or partial match of the device's name .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER ResultSize The number of results to return. Defaults to 10000. .EXAMPLE Get-NectarSupportedDevice -SearchQuery Realtek .NOTES Version 1.1 #> [Alias("gnd")] Param ( [Parameter(Mandatory=$False)] [string]$SearchQuery, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize = 10000 ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $URI = "https://$Global:NectarCloud/aapi/supported/devices?q=$SearchQuery&tenant=$TenantName&pageSize=$ResultSize" Try { $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri $URI If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} # Add the tenant name to the output which helps pipelining $JSON.elements | Add-Member -TypeName 'Nectar.DeviceList' Return $JSON.elements } Catch { Write-Error "Unable to get device details." Get-JSONErrorStream -JSONResponse $_ } } } Function Set-NectarSupportedDevice { <# .SYNOPSIS Update 1 or more Nectar 10 supported device. .DESCRIPTION Update 1 or more Nectar 10 supported device. .PARAMETER DeviceName The name of the supported device .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER Identity The numerical identity of the supported device .EXAMPLE Set-NectarSupportedDevice Identity 233 -Supported $FALSE .EXAMPLE Get-NectarSupportedDevice -SearchQuery realtek | Set-NectarSupportedDevice -Supported $FALSE Sets all devices with 'Realtek' in the name to Unsupported .NOTES Version 1.1 #> [Alias("snsd")] Param ( [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$DeviceName, [Alias("deviceSupported")] [bool]$Supported, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("deviceKey")] [string]$Identity ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($DeviceName -and !$Identity) { $DeviceInfo = Get-NectarSupportedDevice -SearchQuery $DeviceName -Tenant $TenantName -ResultSize 1 $DeviceName = $DeviceInfo.DeviceName If ($Supported -eq $NULL) {$Supported = $DeviceInfo.deviceSupported} $Identity = $DeviceInfo.deviceKey } $URI = "https://$Global:NectarCloud/aapi/supported/device/$Identity/?tenant=$TenantName" $Body = @{ deviceKey = $Identity deviceSupported = $Supported } $JSONBody = $Body | ConvertTo-Json Try { $JSON = Invoke-RestMethod -Method PUT -WebSession $Global:NectarSession -uri $URI -Body $JSONBody -ContentType 'application/json; charset=utf-8' Write-Verbose $JSONBody } Catch { If ($DeviceName) { $IDText = $DeviceName } Else { $IDText = "with ID $Identity" } Write-Error "Unable to apply changes for device $IDText." Get-JSONErrorStream -JSONResponse $_ } } } ################################################################################################################################################# # # # Supported Client Functions # # # ################################################################################################################################################# Function Get-NectarSupportedClient { <# .SYNOPSIS Get information about 1 or more Nectar 10 supported client versions. .DESCRIPTION Get information about 1 or more Nectar 10 supported client versions. .PARAMETER SearchQuery A full or partial match of the client versions's name .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-NectarSupportedClient -SearchQuery Skype .NOTES Version 1.1 #> [Alias("gnsc")] Param ( [Parameter(Mandatory=$False)] [string]$SearchQuery, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize = 10000 ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $URI = "https://$Global:NectarCloud/aapi/supported/client/versions?q=$SearchQuery&tenant=$TenantName&pageSize=$ResultSize" Try { $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri $URI If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} # Add the tenant name to the output which helps pipelining $JSON.elements | Add-Member -TypeName 'Nectar.ClientList' Return $JSON.elements } Catch { Write-Error "Unable to get client version details." Get-JSONErrorStream -JSONResponse $_ } } } Function Set-NectarSupportedClient { <# .SYNOPSIS Update 1 or more Nectar 10 supported client versions. .DESCRIPTION Update 1 or more Nectar 10 supported client versions. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER Identity The numerical identity of the supported client version. .EXAMPLE Set-NectarSupportedClient Identity 233 -Supported $FALSE Sets the device with identity 233 to unsupported .NOTES Version 1.1 #> [Alias("snsc")] Param ( [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("version")] [string]$ClientVersion, [Alias("clientVersionSupported")] [bool]$Supported, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("versionId")] [int]$Identity, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Platform ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($ClientVersion -and !$Identity) { $ClientInfo = Get-NectarSupportedClient -SearchQuery $ClientVersion -Tenant $TenantName -ResultSize 1 $ClientVersion = $ClientInfo.version If ($Supported -eq $NULL) {$Supported = $ClientInfo.clientVersionSupported} $Identity = $ClientInfo.versionId $Platform = $ClientInfo.platform } $URI = "https://$Global:NectarCloud/aapi/supported/client/version?versionName=$ClientVersion&tenant=$TenantName" $Body = @{ clientVersionSupported = $Supported platform = $Platform versionId = $Identity } $JSONBody = $Body | ConvertTo-Json Try { $JSON = Invoke-RestMethod -Method PUT -WebSession $Global:NectarSession -uri $URI -Body $JSONBody -ContentType 'application/json; charset=utf-8' Write-Verbose $JSONBody } Catch { If ($ClientVersion) { $IDText = $ClientVersion } Else { $IDText = "with ID $Identity" } Write-Error "Unable to apply changes for client version $IDText." Get-JSONErrorStream -JSONResponse $_ } } } ################################################################################################################################################# # # # User Functions # # # ################################################################################################################################################# Function Get-NectarUser { <# .SYNOPSIS Get information about 1 or more users via Nectar 10. .DESCRIPTION Get information about 1 or more users via Nectar 10. .PARAMETER SearchQuery A full or partial match of the user name. Will do an 'includes' type search by default. So searching for user@domain.com would return Auser@domain.com, Buser@domain.com etc. For a specific match, enclose the name in square brackets IE: [user@domain.com] .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-NectarUser -SearchQuery tferguson .NOTES Version 1.1 #> [Alias("gnu")] Param ( [Parameter(Mandatory=$True)] [string]$SearchQuery, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize = 100 ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $SearchQuery = [System.Web.HttpUtility]::UrlEncode($SearchQuery) $URI = "https://$Global:NectarCloud/dapi/info/session/users?q=$SearchQuery&pageSize=$ResultSize&tenant=$TenantName" Write-Verbose $URI Try { $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri $URI If ($JSON.amount -eq 0) { Write-Error "Cannot find user with name $SearchQuery." } If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} # Add the tenant name to the output which helps pipelining Return $JSON.elements } Catch { Write-Error "Cannot find user with name $SearchQuery." Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarUserDetails { <# .SYNOPSIS Returns all information on a user. .DESCRIPTION Returns all information on a user. .PARAMETER EmailAddress The email address of the user. .PARAMETER Identity The numerical ID of the user. Can be obtained via Get-NectarUser and pipelined to Get-NectarUserDetails .EXAMPLE Get-NectarUserDetails tferguson@nectarcorp.com .NOTES Version 1.1 #> [Alias("gnadud")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("email")] [string]$EmailAddress, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("userId")] [string]$Identity ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($EmailAddress -and !$Identity) { $Identity = (Get-NectarUser -SearchQuery $EmailAddress -Tenant $TenantName -ResultSize 1 -ErrorVariable GetUserError).userId } If (!$GetUserError) { $URI = "https://$Global:NectarCloud/dapi/user/$Identity/advanced?tenant=$TenantName" Try { $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $URI If ($TenantName) {$JSON | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} # Add the tenant name to the output which helps pipelining Return $JSON } Catch { Write-Error "Unable to find user $EmailAddress. Ensure you typed the name of the user correctly." Get-JSONErrorStream -JSONResponse $_ } } } } ################################################################################################################################################# # # # Call Detail Functions # # # ################################################################################################################################################# Function Set-NectarFilterParams { <# .SYNOPSIS Sets the filter parameters used for querying call data .DESCRIPTION Sets the filter parameters used for querying call data .OUTPUTS WebSession cookie to use in other JSON requests .EXAMPLE Set-NectarFilterParams .NOTES Version 1.1 #> [Alias("snfp")] Param ( [parameter(Mandatory=$False)] [ValidateSet('LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM', IgnoreCase=$True)] [string]$TimePeriod = 'LAST_HOUR', [parameter(Mandatory=$False)] [Alias("StartDateFrom")] [string]$TimePeriodFrom, [parameter(Mandatory=$False)] [Alias("StartDateTo")] [string]$TimePeriodTo, [parameter(Mandatory=$False)] [ValidateSet('GOOD','POOR_0_25','PARTIALLY_GOOD_25_50','PARTIALLY_GOOD_50_75','PARTIALLY_GOOD_75_100','UNAVAILABLE','UNKNOWN', IgnoreCase=$True)] [string[]]$SessionQualities, [Parameter(Mandatory=$False)] [ValidateRange(0,99999999)] [int]$DurationFrom = 0, [Parameter(Mandatory=$False)] [ValidateRange(0,99999999)] [int]$DurationTo = 99999999, [parameter(Mandatory=$False)] [ValidateSet('AUDIO','VIDEO','APP_SHARING','FILE_TRANSFER','IM','UNKNOWN','TOTAL','VBSS','REMOTE_ASSISTANCE','APP_INVITE','FOCUS','UNKNOWN', IgnoreCase=$True)] [string[]]$Modalities, [parameter(Mandatory=$False)] [ValidateSet('TCP','UDP','Unknown', IgnoreCase=$False)] [string[]]$Protocols, [Parameter(Mandatory=$False)] [ValidateRange(0,608)] [string[]]$ResponseCodes, [parameter(Mandatory=$False)] [ValidateSet('INTERNAL','EXTERNAL','FEDERATED','INTERNAL_EXTERNAL','EXTERNAL_INTERNAL','FEDERATED_INTERNAL','INTERNAL_FEDERATED','FEDERATED_EXTERNAL','EXTERNAL_FEDERATED','UNKNOWN', IgnoreCase=$True)] [string[]]$SessionScenarios, [parameter(Mandatory=$False)] [ValidateSet('CONFERENCE','CONFERENCE_SESSION','PEER2PEER','PEER2PEER_MULTIMEDIA','PSTN', IgnoreCase=$False)] [string[]]$SessionTypes, [parameter(Mandatory=$False)] [string[]]$Codecs, [parameter(Mandatory=$False)] [string[]]$CallerCodecs, [parameter(Mandatory=$False)] [string[]]$CalleeCodecs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Devices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerCaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeCaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$RenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerRenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeRenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$DeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerDeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeDeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$IPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CallerIPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CalleeIPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Locations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerLocations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeLocations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtISPs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtISPs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtISPs, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$NetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$CallerNetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$CalleeNetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string]$Platform, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$Platforms, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$CallerPlatforms, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$CalleePlatforms, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$Scenarios, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$CallerScenarios, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$CalleeScenarios, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$Subnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CallerSubnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CalleeSubnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Users, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$FromUsers, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ToUsers, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ConfOrganizers, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$VPN, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$CallerVPN, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$CalleeVPN, [parameter(Mandatory=$False)] [ValidateRange(0,99999)] [int]$ParticipantsMinCount, [parameter(Mandatory=$False)] [ValidateRange(0,99999)] [int]$ParticipantsMaxCount, [parameter(Mandatory=$False)] [ValidateSet('BAD','POOR','FAIR','GOOD','EXCELLENT', IgnoreCase=$False)] [string[]]$FeedbackRating, [parameter(Mandatory=$False)] [ValidateSet('P2P','PING','AUDIO','VIDEO', IgnoreCase=$False)] [string[]]$TestTypes, [parameter(Mandatory=$False)] [ValidateSet('PASSED','FAILED','UNKNOWN', IgnoreCase=$False)] [string[]]$TestResults, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(Mandatory=$False)] [ValidateSet('DEFAULT','USERS','ENDPOINT_CLIENT', IgnoreCase=$True)] [string]$Scope = 'DEFAULT' ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } # Convert to all-caps If($Scope) { $Scope = $Scope.ToUpper() } If($TimePeriod) { $TimePeriod = $TimePeriod.ToUpper() } $FilterParams = @{ 'Scope' = $Scope 'TimePeriod' = $TimePeriod } $TextInfo = (Get-Culture).TextInfo # Convert any PowerShell array objects to comma-separated strings to add to the GET querystring If ($SessionQualities) { $SessionQualities.ToUpper() | %{$SessionQualitiesStr += ($(if($SessionQualitiesStr){","}) + $_)}; $FilterParams.Add('SessionQualities',$SessionQualitiesStr) } If ($DurationFrom) { $FilterParams.Add('DurationFrom',$DurationFrom) } If ($DurationTo) { $FilterParams.Add('DurationTo',$DurationTo) } If ($Modalities) { $Modalities | %{$ModalitiesStr += ($(if($ModalitiesStr){","}) + $_)}; $FilterParams.Add('Modalities',$ModalitiesStr) } If ($Protocols) { $Protocols | %{$ProtocolsStr += ($(if($ProtocolsStr){","}) + $_)}; $FilterParams.Add('Protocols',$ProtocolsStr) } If ($ResponseCodes) { $ResponseCodes | %{$ResponseCodesStr += ($(if($ResponseCodesStr){","}) + $_)}; $FilterParams.Add('ResponseCodes',$ResponseCodesStr) } If ($SessionScenarios) { $SessionScenarios.ToUpper() | %{$SessionScenariosStr += ($(if($SessionScenariosStr){","}) + $_)}; $FilterParams.Add('SessionScenarios',$SessionScenariosStr) } If ($SessionTypes) { $SessionTypes | %{$SessionTypesStr += ($(if($SessionTypesStr){","}) + $_)}; $FilterParams.Add('SessionTypes',$SessionTypesStr) } If ($SessionTypes) { If ($SessionTypes.IndexOf('CONFERENCE_SESSION') -ge 0) { $FilterParams.Add('showConferenceSessions','true') } } If ($Codecs) { $Codecs | %{$CodecsStr += ($(if($CodecsStr){","}) + $_)}; $FilterParams.Add('Codecs',$CodecsStr) } If ($CallerCodecs) { $CallerCodecs | %{$CallerCodecsStr += ($(if($CallerCodecsStr){","}) + $_)}; $FilterParams.Add('CallerCodecs',$CallerCodecsStr) } If ($CalleeCodecs) { $CalleeCodecs | %{$CalleeCodecsStr += ($(if($CalleeCodecsStr){","}) + $_)}; $FilterParams.Add('CalleeCodecs',$CalleeCodecsStr) } If ($Devices) { $Devices | %{$DevicesStr += ($(if($DevicesStr){","}) + $_)}; $FilterParams.Add('Devices',$DevicesStr) } If ($CallerDevices) { $CallerDevices | %{$CallerDevicesStr += ($(if($CallerDevicesStr){","}) + $_)}; $FilterParams.Add('CallerDevices',$CallerDevicesStr) } If ($CalleeDevices) { $CalleeDevices | %{$CalleeDevicesStr += ($(if($CalleeDevicesStr){","}) + $_)}; $FilterParams.Add('CalleeDevices',$CalleeDevicesStr) } If ($CaptureDevices) { $CaptureDevices | %{$CaptureDevicesStr += ($(if($CaptureDevicesStr){","}) + $_)}; $FilterParams.Add('CaptureDevices',$CaptureDevicesStr) } If ($CallerCaptureDevices) { $CallerCaptureDevices | %{$CallerCaptureDevicesStr += ($(if($CallerCaptureDevicesStr){","}) + $_)}; $FilterParams.Add('CallerCaptureDevices',$CallerCaptureDevicesStr) } If ($CalleeCaptureDevices) { $CalleeCaptureDevices | %{$CalleeCaptureDevicesStr += ($(if($CalleeCaptureDevicesStr){","}) + $_)}; $FilterParams.Add('CalleeCaptureDevices',$CalleeCaptureDevicesStr) } If ($RenderDevices) { $RenderDevices | %{$RenderDevicesStr += ($(if($RenderDevicesStr){","}) + $_)}; $FilterParams.Add('RenderDevices',$RenderDevicesStr) } If ($CallerRenderDevices) { $CallerRenderDevices | %{$CallerRenderDevicesStr += ($(if($CallerRenderDevicesStr){","}) + $_)}; $FilterParams.Add('CallerRenderDevices',$CallerRenderDevicesStr) } If ($CalleeRenderDevices) { $CalleeRenderDevices | %{$CalleeRenderDevicesStr += ($(if($CalleeRenderDevicesStr){","}) + $_)}; $FilterParams.Add('CalleeRenderDevices',$CalleeRenderDevicesStr) } If ($DeviceVersions) { $DeviceVersions | %{$DeviceVersionsStr += ($(if($DeviceVersionsStr){","}) + $_)}; $FilterParams.Add('DeviceVersions',$DeviceVersionsStr) } If ($CallerDeviceVersions) { $CallerDeviceVersions | %{$CallerDeviceVersionsStr += ($(if($CallerDeviceVersionsStr){","}) + $_)}; $FilterParams.Add('CallerDeviceVersions',$CallerDeviceVersionsStr) } If ($CalleeDeviceVersions) { $CalleeDeviceVersions | %{$CalleeDeviceVersionsStr += ($(if($CalleeDeviceVersionsStr){","}) + $_)}; $FilterParams.Add('CalleeDeviceVersions',$CalleeDeviceVersionsStr) } If ($IPAddresses) { $IPAddresses | %{[string]$IPAddressesStr += ($(if([string]$IPAddressesStr){","}) + $_)}; $FilterParams.Add('IPAddresses',$IPAddressesStr) } If ($CallerIPAddresses) { $CallerIPAddresses | %{[string]$CallerIPAddressesStr += ($(if([string]$CallerIPAddressesStr){","}) + $_)}; $FilterParams.Add('CallerIPAddresses',$CallerIPAddressesStr) } If ($CalleeIPAddresses) { $CalleeIPAddresses | %{[string]$CalleeIPAddressesStr += ($(if([string]$CalleeIPAddressesStr){","}) + $_)}; $FilterParams.Add('CalleeIPAddresses',$CalleeIPAddressesStr) } If ($Locations) { $Locations | %{$LocationsStr += ($(if($LocationsStr){","}) + $_)}; $FilterParams.Add('Locations',$LocationsStr) } If ($CallerLocations) { $CallerLocations | %{$CallerLocationsStr += ($(if($CallerLocationsStr){","}) + $_)}; $FilterParams.Add('CallerLocations',$CallerLocationsStr) } If ($CalleeLocations) { $CalleeLocations | %{$CalleeLocationsStr += ($(if($CalleeLocationsStr){","}) + $_)}; $FilterParams.Add('CalleeLocations',$CalleeLocationsStr) } If ($ExtCities) { $ExtCities | %{$ExtCitiesStr += ($(if($ExtCitiesStr){","}) + $_)}; $FilterParams.Add('ExtCities',$ExtCitiesStr) } If ($CallerExtCities) { $CallerExtCities | %{$CallerExtCitiesStr += ($(if($CallerExtCitiesStr){","}) + $_)}; $FilterParams.Add('CallerExtCities',$CallerExtCitiesStr) } If ($CalleeExtCities) { $CalleeExtCities | %{$CalleeExtCitiesStr += ($(if($CalleeExtCitiesStr){","}) + $_)}; $FilterParams.Add('CalleeExtCities',$CalleeExtCitiesStr) } If ($ExtCountries) { $ExtCountries | %{$ExtCountriesStr += ($(if($ExtCountriesStr){","}) + $_)}; $FilterParams.Add('ExtCountries',$ExtCountriesStr) } If ($CallerExtCountries) { $CallerExtCountries | %{$CallerExtCountriesStr += ($(if($CallerExtCountriesStr){","}) + $_)}; $FilterParams.Add('CallerExtCountries',$CallerExtCountriesStr) } If ($CalleeExtCountries) { $CalleeExtCountries | %{$CalleeExtCountriesStr += ($(if($CalleeExtCountriesStr){","}) + $_)}; $FilterParams.Add('CalleeExtCountries',$CalleeExtCountriesStr) } If ($ExtISPs) { $ExtISPs | %{$ExtISPsStr += ($(if($ExtISPsStr){","}) + $_)}; $FilterParams.Add('extIsps',$ExtISPsStr) } If ($CallerExtISPs) { $CallerExtISPs | %{$CallerExtISPsStr += ($(if($CallerExtISPsStr){","}) + $_)}; $FilterParams.Add('callerExtIsps',$CallerExtISPsStr) } If ($CalleeExtISPs) { $CalleeExtISPs | %{$CalleeExtISPsStr += ($(if($CalleeExtISPsStr){","}) + $_)}; $FilterParams.Add('calleeExtIsps',$CalleeExtISPsStr) } If ($NetworkTypes) { $NetworkTypes | %{$NetworkTypesStr += ($(if($NetworkTypesStr){","}) + $_)}; $FilterParams.Add('NetworkTypes',$NetworkTypesStr) } If ($CallerNetworkTypes) { $CallerNetworkTypes | %{$CallerNetworkTypesStr += ($(if($CallerNetworkTypesStr){","}) + $_)}; $FilterParams.Add('CallerNetworkTypes',$CallerNetworkTypesStr) } If ($CalleeNetworkTypes) { $CalleeNetworkTypes | %{$CalleeNetworkTypesStr += ($(if($CalleeNetworkTypesStr){","}) + $_)}; $FilterParams.Add('CalleeNetworkTypes',$CalleeNetworkTypesStr) } If ($Platform) { $FilterParams.Add('platform',$Platform) } If ($Platforms) { $Platforms.ToUpper() | %{$PlatformsStr += ($(if($PlatformsStr){","}) + $_)}; $FilterParams.Add('Platforms',$PlatformsStr) } If ($CallerPlatforms) { $CallerPlatforms.ToUpper() | %{$CallerPlatformsStr += ($(if($CallerPlatformsStr){","}) + $_)}; $FilterParams.Add('CallerPlatforms',$CallerPlatformsStr) } If ($CalleePlatforms) { $CalleePlatforms.ToUpper() | %{$CalleePlatformsStr += ($(if($CalleePlatformsStr){","}) + $_)}; $FilterParams.Add('CalleePlatforms',$CalleePlatformsStr) } If ($Scenarios) { $Scenarios.ToUpper() | %{$ScenariosStr += ($(if($ScenariosStr){","}) + $_)}; $FilterParams.Add('Scenarios',$ScenariosStr) } If ($CallerScenarios) { $CallerScenarios.ToUpper() | %{$CallerScenariosStr += ($(if($CallerScenariosStr){","}) + $_)}; $FilterParams.Add('CallerScenarios',$CallerScenariosStr) } If ($CalleeScenarios) { $CalleeScenarios.ToUpper() | %{$CalleeScenariosStr += ($(if($CalleeScenariosStr){","}) + $_)}; $FilterParams.Add('CalleeScenarios',$CalleeScenariosStr) } If ($Subnets) { $Subnets | %{[string]$SubnetsStr += ($(if([string]$SubnetsStr){","}) + $_)}; $FilterParams.Add('Subnets',$SubnetsStr) } If ($CallerSubnets) { $CallerSubnets | %{[string]$CallerSubnetsStr += ($(if([string]$CallerSubnetsStr){","}) + $_)}; $FilterParams.Add('CallerSubnets',$CallerSubnetsStr) } If ($CalleeSubnets) { $CalleeSubnets | %{[string]$CalleeSubnetsStr += ($(if($CalleeSubnetsStr){","}) + $_)}; $FilterParams.Add('CalleeSubnets',$CalleeSubnetsStr) } If ($VPN) { $VPN.ToUpper() | %{$VPNStr += ($(if($VPNStr){","}) + $_)}; $FilterParams.Add('Vpn',$VPNStr) } If ($CallerVPN) { $CallerVPN.ToUpper() | %{$CallerVPNStr += ($(if($CallerVPNStr){","}) + $_)}; $FilterParams.Add('CallerVpn',$VPNStr) } If ($CalleeVPN) { $CalleeVPN.ToUpper() | %{$CalleeVPNStr += ($(if($CalleeVPNStr){","}) + $_)}; $FilterParams.Add('CalleeVpn',$VPNStr) } If ($ParticipantsMinCount) { $FilterParams.Add('participantsMinCount',$ParticipantsMinCount) } If ($ParticipantsMaxCount) { $FilterParams.Add('participantsMaxCount',$ParticipantsMaxCount) } If ($FeedbackRating) { $FeedbackRating.ToUpper() | %{$FeedbackRatingStr += ($(if($FeedbackRatingStr){","}) + $_)}; $FilterParams.Add('Ratings',$FeedbackRatingStr) } If ($TestTypes) { $TestTypes.ToUpper() | %{$TestTypeStr += ($(if($TestTypeStr){","}) + $_)}; $FilterParams.Add('testTypes',$TestTypeStr) } If ($TestResults) { $TestResults.ToUpper() | %{$TestResultsStr += ($(if($TestResultsStr){","}) + $_)}; $FilterParams.Add('testResults',$TestResultsStr) } If ($TenantName) { $FilterParams.Add('Tenant',$TenantName) } # Get the user IDs for any entered users If ($Users) { $UserIDs = @() ForEach($User in $Users) { $UserIDs += (Get-NectarUser $User -TenantName $TenantName -ErrorAction:Stop).id } $UserIDs | %{$UserIDsStr += ($(if($UserIDsStr){","}) + $_)} $FilterParams.Add('Users',$UserIDsStr) } If ($FromUsers) { $FromUserIDs = @() ForEach($User in $FromUsers) { $FromUserIDs += (Get-NectarUser $User -TenantName $TenantName -ErrorAction:Stop).id } $FromUserIDs | %{$FromUserIDsStr += ($(if($FromUserIDsStr){","}) + $_)} $FilterParams.Add('FromUsers',$FromUserIDsStr) } If ($ToUsers) { $ToUserIDs = @() ForEach($User in $ToUsers) { $ToUserIDs += (Get-NectarUser $User -TenantName $TenantName -ErrorAction:Stop).id } $ToUserIDs | %{$ToUserIDsStr += ($(if($ToUserIDsStr){","}) + $_)} $FilterParams.Add('ToUsers',$ToUserIDsStr) } If ($ConfOrganizers) { $ConfOrganizerIDs = @() ForEach($Organizer in $ConfOrganizers) { $ConfOrganizerIDs += (Get-NectarUser $Organizer -TenantName $TenantName -ErrorAction:Stop).id } $ConfOrganizerIDs | %{$ConfOrganizerIDsStr += ($(if($ConfOrganizerIDsStr){","}) + $_)} $FilterParams.Add('organizersOrSpaces',$ConfOrganizerIDsStr) } # Convert date to UNIX timestamp If ($TimePeriodFrom) { $TimePeriodFrom = (Get-Date -Date $TimePeriodFrom -UFormat %s) + '000' $FilterParams.Add('StartDateFrom',$TimePeriodFrom) Write-Verbose "TimePeriodFrom: $TimePeriodFrom" } If ($TimePeriodTo) { $TimePeriodTo = (Get-Date -Date $TimePeriodTo -UFormat %s) + '000' $FilterParams.Add('StartDateTo',$TimePeriodTo) Write-Verbose "TimePeriodTo: $TimePeriodTo" } # Try { # Run the filter POST and obtain the session cookie for the GET $URI = "https://$Global:NectarCloud/dapi/filter/apply" $FilterResults = Invoke-WebRequest -Method POST -Credential $Global:NectarCred -uri $URI -Body $FilterParams -UseBasicParsing -SessionVariable Session $Cookie = $Session.Cookies.GetCookies($URI) | Where {$_.Name -eq 'SESSION'} $FilterSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession $FilterSession.Cookies.Add($Cookie) Write-Verbose "Successfully set filter parameters." Write-Verbose $Uri Write-Verbose $FilterResults Return $FilterSession # } # Catch { # Write-Error "Unable to set filter parameters." # (Get-JSONErrorStream -JSONResponse $_).Replace("startDate","TimePeriod") # } } } Function Get-NectarSessions { <# .SYNOPSIS Returns all session information. .DESCRIPTION Returns all session information as presented on the SESSION LIST section of the CALL DETAILS page UI_ELEMENT .PARAMETER TimePeriod The time period to show session data from. Select from 'LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM'. CUSTOM requires using TimePeriodFrom and TimePeriodTo parameters. .PARAMETER TimePeriodFrom The earliest date/time to show session data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodTo parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. .PARAMETER TimePeriodTo The latest date/time to show session data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodFrom parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. .PARAMETER SessionQualities Show sessions that match a given quality rating. Case sensitive. Choose one or more from: 'GOOD','POOR_0_25','PARTIALLY_GOOD_25_50','PARTIALLY_GOOD_50_75','PARTIALLY_GOOD_75_100','UNAVAILABLE','UNKNOWN' .PARAMETER DurationFrom The shortest call length (in seconds) to show session data. .PARAMETER DurationTo The longest call length (in seconds) to show session data. .PARAMETER Modalities Show sessions that match one or more modality types. Not case sensitive. Choose one or more from: 'AUDIO','VIDEO','APP_SHARING','FILE_TRANSFER','IM','UNKNOWN','TOTAL','VBSS','REMOTE_ASSISTANCE','APP_INVITE','FOCUS','UNKNOWN' .PARAMETER Protocols Show sessions that match one or more network protocol types. Case sensitive. Choose one or more from: 'TCP','UDP','Unknown' .PARAMETER ResponseCodes Show sessions that match one or more SIP response codes. Accepts numbers from 200 to 699 .PARAMETER SessionScenarios Show sessions that match one or more session scenarios. Not case sensitive. Choose one or more from: 'External','Internal','Internal-External','External-Internal','Federated','Internal-Federated','External-Federated','Unknown' .PARAMETER SessionTypes Show sessions that match one or more session scenarios. Case sensitive. Choose one or more from: 'Conference','Peer To Peer','Peer To Peer (Multimedia)','PSTN/External' .PARAMETER Codecs Show sessions where the selected codec was used by either caller or callee. Can query for multiple codecs. Case sensitive. Use Get-NectarCodecs for a list of valid codecs. .PARAMETER CallerCodecs Show sessions where the selected codec was used by the caller. Can query for multiple codecs. Case sensitive. Use Get-NectarCodecs for a list of valid codecs. .PARAMETER CalleeCodecs Show sessions where the selected codec was used by the callee. Can query for multiple codecs. Case sensitive. Use Get-NectarCodecs for a list of valid codecs. .PARAMETER Devices Show sessions where the selected device was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CallerDevices Show sessions where the selected device was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CalleeDevices Show sessions where the selected device was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CaptureDevices Show sessions where the selected capture device (microphone) was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CallerCaptureDevices Show sessions where the selected capture device (microphone) was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CalleeCaptureDevices Show sessions where the selected capture device (microphone) was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER RenderDevices Show sessions where the selected render device (speaker) was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CallerRenderDevices Show sessions where the selected render device (speaker) was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CalleeRenderDevices Show sessions where the selected render device (speaker) was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER DeviceVersions Show sessions where the selected device version was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarClientVersion for a list of valid client versions. .PARAMETER CallerDeviceVersions Show sessions where the selected device version was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarClientVersion for a list of valid client versions. .PARAMETER CalleeDeviceVersions Show sessions where the selected device version was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarClientVersion for a list of valid client versions. .PARAMETER IPAddresses Show sessions where the selected IP address was used by either caller or callee. Can query for multiple IPs. .PARAMETER CallerIPAddresses Show sessions where the selected IP address was used by the caller. Can query for multiple IPs. .PARAMETER CalleeIPAddresses Show sessions where the selected IP address was used by the callee. Can query for multiple IPs. .PARAMETER Locations Show sessions where the selected location was used by either caller or callee. Can query for multiple locations. .PARAMETER CallerLocations Show sessions where the selected location was used by the caller. Can query for multiple locations. .PARAMETER CalleeLocations Show sessions where the selected location was used by the callee. Can query for multiple locations. .PARAMETER ExtCities Show sessions where the caller or callee was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER CallerExtCities Show sessions where the caller was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER CalleeExtCities Show sessions where the callee was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER ExtCountries Show sessions where the caller or callee was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER CallerExtCountries Show sessions where the caller was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER CalleeExtCountries Show sessions where the callee was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER ExtISPs Show sessions where the caller or callee was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER CallerExtISPs Show sessions where the caller was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER CalleeExtISPs Show sessions where the callee was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER NetworkTypes Show sessions where the selected network type was used by either caller or callee. Can query for multiple network types. Case sensitive. Choose one or more from: 'Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable' .PARAMETER CallerNetworkTypes Show sessions where the selected network type was used by the caller. Can query for multiple network types. Case sensitive. Choose one or more from: 'Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable' .PARAMETER CalleeNetworkTypes Show sessions where the selected network type was used by the callee. Can query for multiple network types. Case sensitive. Choose one or more from: 'Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable' .PARAMETER Platforms Show sessions where the selected platform was used by either caller or callee. Can query for multiple platforms. Case sensitive. Choose one or more from: 'SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS' .PARAMETER CallerPlatforms Show sessions where the selected platform was used by the caller. Can query for multiple platforms. Case sensitive. Choose one or more from: 'SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS' .PARAMETER CalleePlatforms Show sessions where the selected platform was used by the callee. Can query for multiple platforms. Case sensitive. Choose one or more from: 'SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS' .PARAMETER Scenarios Show sessions where the selected scenario was used by either caller or callee. Can query for multiple scenarios. Choose one or more from: 'External','Internal','Federated','Unknown' .PARAMETER CallerScenarios Show sessions where the selected scenario was used by the caller. Can query for multiple scenarios. Choose one or more from: 'External','Internal','Federated','Unknown' .PARAMETER CalleeScenarios Show sessions where the selected scenario was used by the callee. Can query for multiple scenarios. Choose one or more from: 'External','Internal','Federated','Unknown' .PARAMETER Subnets Show sessions where the selected subnet was used by either caller or callee. Can query for multiple subnets. .PARAMETER CallerSubnets Show sessions where the selected subnet was used by the caller. Can query for multiple subnets. .PARAMETER CalleeSubnets Show sessions where the selected subnet was used by the callee. Can query for multiple subnets. .PARAMETER Users Show sessions where the selected user was either caller or callee. Can query for multiple users. .PARAMETER FromUsers Show sessions where the selected user was the caller. Can query for multiple users. .PARAMETER ToUsers Show sessions where the selected user was the callee. Can query for multiple users. .PARAMETER ConfOrganizers Show sessions hosted by a specified conference organizer. Can query for multiple organizers. .PARAMETER VPN Show sessions where the selected VPN was used by either caller or callee. .PARAMETER CallerVPN Show sessions where the selected VPN was used by the caller. .PARAMETER CalleeVPN Show sessions where the selected VPN was used by the callee. .PARAMETER ParticipantsMinCount Show sessions where the number of participants is greater than or equal to the entered value .PARAMETER ParticipantsMaxCount Show sessions where the number of participants is less than or equal to the entered value .PARAMETER FeedbackRating Show sessions where users provided specific feedback ratings from BAD to EXCELLENT. Allowed values are BAD, POOR, FAIR, GOOD, EXCELLENT. Corresponds to ratings from 1 to 5 stars. .PARAMETER OrderByField Sort the output by the selected field .PARAMETER OrderDirection Sort direction. Use with OrderByField. Not case sensitive. Choose from: ASC, DESC .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER PageSize The size of the page used to return data. Defaults to 1000 .PARAMETER ResultSize The total number of results to return. Defaults to 1000. Maximum result size is 9,999,999 results .EXAMPLE Get-NectarSessions -TimePeriod LAST_HOUR -Platforms TEAMS -Modalities AUDIO -SessionQualities POOR Returns a list of all Teams audio sessions for the past hour where the quality was rated Poor .EXAMPLE (Get-NectarSessions -SessionTypes CONFERENCE -TimePeriod CUSTOM -TimePeriodFrom '2021-05-06' -TimePeriodTo '2021-05-07').Count Returns a count of all conferences between May 6 and 7 (all times/dates UTC) .EXAMPLE Get-NectarSessions -SessionTypes PEER2PEER,PEER2PEER_MULTIMEDIA -TimePeriod CUSTOM -TimePeriodFrom '2021-05-06 14:00' -TimePeriodTo '2021-05-06 15:00' Returns a list of all P2P calls between 14:00 and 15:00 UTC on May 6 .EXAMPLE Get-NectarSessions -TimePeriod LAST_WEEK -SessionTypes CONFERENCE | Select-Object confOrganizerOrSpace | Group-Object confOrganizerOrSpace | Select-Object Name, Count | Sort-Object Count -Descending Returns a list of conference organizers and a count of the total conferences organized by each, sorted by count. .NOTES Version 1.2 #> [Alias("gns")] [CmdletBinding(PositionalBinding=$False)] Param ( [parameter(Mandatory=$False)] [ValidateSet('LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM', IgnoreCase=$True)] [string]$TimePeriod = 'LAST_HOUR', [parameter(Mandatory=$False)] [Alias("StartDateFrom")] [string]$TimePeriodFrom, [parameter(Mandatory=$False)] [Alias("StartDateTo")] [string]$TimePeriodTo, [parameter(Mandatory=$False)] [ValidateSet('GOOD','POOR_0_25','PARTIALLY_GOOD_25_50','PARTIALLY_GOOD_50_75','PARTIALLY_GOOD_75_100','UNAVAILABLE','UNKNOWN', IgnoreCase=$True)] [string[]]$SessionQualities, [Parameter(Mandatory=$False)] [ValidateRange(0,99999999)] [int]$DurationFrom = 0, [Parameter(Mandatory=$False)] [ValidateRange(0,99999999)] [int]$DurationTo = 99999999, [parameter(Mandatory=$False)] [ValidateSet('AUDIO','VIDEO','APP_SHARING','FILE_TRANSFER','IM','UNKNOWN','TOTAL','VBSS','REMOTE_ASSISTANCE','APP_INVITE','FOCUS','UNKNOWN', IgnoreCase=$True)] [string[]]$Modalities, [parameter(Mandatory=$False)] [ValidateSet('TCP','UDP','Unknown', IgnoreCase=$False)] [string[]]$Protocols, [Parameter(Mandatory=$False)] [ValidateRange(200,699)] [string[]]$ResponseCodes, [parameter(Mandatory=$False)] [ValidateSet('INTERNAL','EXTERNAL','FEDERATED','INTERNAL_EXTERNAL','EXTERNAL_INTERNAL','FEDERATED_INTERNAL','INTERNAL_FEDERATED','FEDERATED_EXTERNAL','EXTERNAL_FEDERATED','UNKNOWN', IgnoreCase=$True)] [string[]]$SessionScenarios, [parameter(Mandatory=$False)] [ValidateSet('CONFERENCE','CONFERENCE_SESSION','PEER2PEER','PEER2PEER_MULTIMEDIA','PSTN', IgnoreCase=$False)] [string[]]$SessionTypes, [parameter(Mandatory=$False)] [string[]]$Codecs, [parameter(Mandatory=$False)] [string[]]$CallerCodecs, [parameter(Mandatory=$False)] [string[]]$CalleeCodecs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Devices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerCaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeCaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$RenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerRenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeRenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$DeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerDeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeDeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$IPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CallerIPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CalleeIPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Locations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerLocations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeLocations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtISPs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtISPs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtISPs, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$NetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$CallerNetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$CalleeNetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$Platforms, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$CallerPlatforms, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$CalleePlatforms, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$Scenarios, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$CallerScenarios, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$CalleeScenarios, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$Subnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CallerSubnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CalleeSubnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Users, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$FromUsers, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ToUsers, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ConfOrganizers, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$VPN, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$CallerVPN, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$CalleeVPN, [parameter(Mandatory=$False)] [ValidateRange(0,99999)] [int]$ParticipantsMinCount, [parameter(Mandatory=$False)] [ValidateRange(0,99999)] [int]$ParticipantsMaxCount, [parameter(Mandatory=$False)] [ValidateSet('BAD','POOR','FAIR','GOOD','EXCELLENT', IgnoreCase=$False)] [string[]]$FeedbackRating, [parameter(Mandatory=$False)] [string]$OrderByField, [parameter(Mandatory=$False)] [ValidateSet('ASC','DESC', IgnoreCase=$True)] [string]$OrderDirection, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(Mandatory=$False)] [ValidateSet('DEFAULT','USERS', IgnoreCase=$True)] [string]$Scope = 'DEFAULT', [Parameter(Mandatory=$False)] [ValidateRange(1,10000)] [int]$PageSize = 1000, [Parameter(Mandatory=$False)] [ValidateRange(1,50000)] [int]$ResultSize ) Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($PageSize) { $PSBoundParameters.Remove('PageSize') | Out-Null } If ($ResultSize) { $PSBoundParameters.Remove('ResultSize') | Out-Null } If ($OrderByField) { $PSBoundParameters.Remove('OrderByField') | Out-Null } If ($OrderDirection) { $PSBoundParameters.Remove('OrderDirection') | Out-Null } $FilterSession = Set-NectarFilterParams @PsBoundParameters $URI = "https://$Global:NectarCloud/dapi/session" # Set the page size to the result size if -ResultSize switch is used to limit the number of returned items # Otherwise, set page size (defaults to 1000) If ($ResultSize) { $Params = @{ 'pageSize' = $ResultSize } } Else { $Params = @{ 'pageSize' = $PageSize } } If($OrderByField) { $Params.Add('OrderByField',$OrderByField) } If($OrderDirection) { $Params.Add('OrderDirection',$OrderDirection) } If ($TenantName) { $Params.Add('Tenant',$TenantName) } If ($SessionTypes) { If ($SessionTypes.IndexOf('CONFERENCE_SESSION') -ge 0) { $Params.Add('showConferenceSessions','true') | Out-Null } Else { $Params.Add('showConferenceSessions','false') | Out-Null } } Else { $Params.Add('showConferenceSessions','false') | Out-Null } # Return results in pages Try { Write-Verbose $URI Write-Verbose $TimePeriodFrom Write-Verbose $TimePeriodTo $JSON = Invoke-RestMethod -Method GET -uri $URI -Body $Params -WebSession $FilterSession $TotalPages = $JSON.totalPages If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} $JSON.elements | Add-Member -TypeName 'Nectar.SessionList' $JSON.elements If ($TotalPages -gt 1 -and !($ResultSize)) { $PageNum = 2 Write-Verbose "Page size: $PageSize" While ($PageNum -le $TotalPages) { Write-Verbose "Working on page $PageNum of $TotalPages" $PagedURI = $URI + "?pageNumber=$PageNum" $JSON = Invoke-RestMethod -Method GET -uri $PagedURI -Body $Params -WebSession $FilterSession If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} $JSON.elements | Add-Member -TypeName 'Nectar.SessionList' $JSON.elements $PageNum++ } } } Catch { Write-Error "No results. Try specifying a less-restrictive filter" Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarSessionHistograms { <# .SYNOPSIS Returns session histogram for a given timeframe. .DESCRIPTION Returns session histogram for a given timeframe. This returns the numbers used to build the chart in the SESSIONS section of the CALL DETAILS screen UI_ELEMENT .PARAMETER TimePeriod The time period to show session data from. Select from 'LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM'. CUSTOM requires using TimePeriodFrom and TimePeriodTo parameters. .PARAMETER TimePeriodFrom The earliest date/time to show session data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodTo parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. .PARAMETER TimePeriodTo The latest date/time to show session data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodFrom parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. .PARAMETER SessionQualities Show sessions that match a given quality rating. Case sensitive. Choose one or more from: 'GOOD','POOR_0_25','PARTIALLY_GOOD_25_50','PARTIALLY_GOOD_50_75','PARTIALLY_GOOD_75_100','UNAVAILABLE','UNKNOWN' .PARAMETER DurationFrom The shortest call length (in seconds) to show session data. .PARAMETER DurationTo The longest call length (in seconds) to show session data. .PARAMETER Modalities Show sessions that match one or more modality types. Not case sensitive. Choose one or more from: 'AUDIO','VIDEO','APP_SHARING','FILE_TRANSFER','IM','UNKNOWN','TOTAL','VBSS','REMOTE_ASSISTANCE','APP_INVITE','FOCUS','UNKNOWN' .PARAMETER Protocols Show sessions that match one or more network protocol types. Case sensitive. Choose one or more from: 'TCP','UDP','Unknown' .PARAMETER ResponseCodes Show sessions that match one or more SIP response codes. Accepts numbers from 200 to 699 .PARAMETER SessionScenarios Show sessions that match one or more session scenarios. Not case sensitive. Choose one or more from: 'External','Internal','Internal-External','External-Internal','Federated','Internal-Federated','External-Federated','Unknown' .PARAMETER SessionTypes Show sessions that match one or more session scenarios. Case sensitive. Choose one or more from: 'Conference','Peer To Peer','Peer To Peer (Multimedia)','PSTN/External' .PARAMETER Codecs Show sessions where the selected codec was used by either caller or callee. Can query for multiple codecs. Case sensitive. Use Get-NectarCodecs for a list of valid codecs. .PARAMETER CallerCodecs Show sessions where the selected codec was used by the caller. Can query for multiple codecs. Case sensitive. Use Get-NectarCodecs for a list of valid codecs. .PARAMETER CalleeCodecs Show sessions where the selected codec was used by the callee. Can query for multiple codecs. Case sensitive. Use Get-NectarCodecs for a list of valid codecs. .PARAMETER Devices Show sessions where the selected device was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CallerDevices Show sessions where the selected device was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CalleeDevices Show sessions where the selected device was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CaptureDevices Show sessions where the selected capture device (microphone) was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CallerCaptureDevices Show sessions where the selected capture device (microphone) was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CalleeCaptureDevices Show sessions where the selected capture device (microphone) was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER RenderDevices Show sessions where the selected render device (speaker) was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CallerRenderDevices Show sessions where the selected render device (speaker) was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CalleeRenderDevices Show sessions where the selected render device (speaker) was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER DeviceVersions Show sessions where the selected device version was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarClientVersion for a list of valid client versions. .PARAMETER CallerDeviceVersions Show sessions where the selected device version was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarClientVersion for a list of valid client versions. .PARAMETER CalleeDeviceVersions Show sessions where the selected device version was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarClientVersion for a list of valid client versions. .PARAMETER IPAddresses Show sessions where the selected IP address was used by either caller or callee. Can query for multiple IPs. .PARAMETER CallerIPAddresses Show sessions where the selected IP address was used by the caller. Can query for multiple IPs. .PARAMETER CalleeIPAddresses Show sessions where the selected IP address was used by the callee. Can query for multiple IPs. .PARAMETER Locations Show sessions where the selected location was used by either caller or callee. Can query for multiple locations. .PARAMETER CallerLocations Show sessions where the selected location was used by the caller. Can query for multiple locations. .PARAMETER CalleeLocations Show sessions where the selected location was used by the callee. Can query for multiple locations. .PARAMETER ExtCities Show sessions where the caller or callee was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER CallerExtCities Show sessions where the caller was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER CalleeExtCities Show sessions where the callee was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER ExtCountries Show sessions where the caller or callee was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER CallerExtCountries Show sessions where the caller was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER CalleeExtCountries Show sessions where the callee was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER ExtISPs Show sessions where the caller or callee was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER CallerExtISPs Show sessions where the caller was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER CalleeExtISPs Show sessions where the callee was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER NetworkTypes Show sessions where the selected network type was used by either caller or callee. Can query for multiple network types. Case sensitive. Choose one or more from: 'Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable' .PARAMETER CallerNetworkTypes Show sessions where the selected network type was used by the caller. Can query for multiple network types. Case sensitive. Choose one or more from: 'Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable' .PARAMETER CalleeNetworkTypes Show sessions where the selected network type was used by the callee. Can query for multiple network types. Case sensitive. Choose one or more from: 'Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable' .PARAMETER Platforms Show sessions where the selected platform was used by either caller or callee. Can query for multiple platforms. Case sensitive. Choose one or more from: 'SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS' .PARAMETER CallerPlatforms Show sessions where the selected platform was used by the caller. Can query for multiple platforms. Case sensitive. Choose one or more from: 'SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS' .PARAMETER CalleePlatforms Show sessions where the selected platform was used by the callee. Can query for multiple platforms. Case sensitive. Choose one or more from: 'SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS' .PARAMETER Scenarios Show sessions where the selected scenario was used by either caller or callee. Can query for multiple scenarios. Choose one or more from: 'External','Internal','Federated','Unknown' .PARAMETER CallerScenarios Show sessions where the selected scenario was used by the caller. Can query for multiple scenarios. Choose one or more from: 'External','Internal','Federated','Unknown' .PARAMETER CalleeScenarios Show sessions where the selected scenario was used by the callee. Can query for multiple scenarios. Choose one or more from: 'External','Internal','Federated','Unknown' .PARAMETER Subnets Show sessions where the selected subnet was used by either caller or callee. Can query for multiple subnets. .PARAMETER CallerSubnets Show sessions where the selected subnet was used by the caller. Can query for multiple subnets. .PARAMETER CalleeSubnets Show sessions where the selected subnet was used by the callee. Can query for multiple subnets. .PARAMETER Users Show sessions where the selected user was either caller or callee. Can query for multiple users. .PARAMETER FromUsers Show sessions where the selected user was the caller. Can query for multiple users. .PARAMETER ToUsers Show sessions where the selected user was the callee. Can query for multiple users. .PARAMETER ConfOrganizers Show sessions hosted by a specified conference organizer. Can query for multiple organizers. .PARAMETER VPN Show sessions where the selected VPN was used by either caller or callee. .PARAMETER CallerVPN Show sessions where the selected VPN was used by the caller. .PARAMETER CalleeVPN Show sessions where the selected VPN was used by the callee. .PARAMETER ParticipantsMinCount Show sessions where the number of participants is greater than or equal to the entered value .PARAMETER ParticipantsMaxCount Show sessions where the number of participants is less than or equal to the entered value .PARAMETER FeedbackRating Show sessions where users provided specific feedback ratings from BAD to EXCELLENT. Allowed values are BAD, POOR, FAIR, GOOD, EXCELLENT. Corresponds to ratings from 1 to 5 stars. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-NectarSessionHistograms -TimePeriod LAST_HOUR Returns a minute-by-minute count of the number of sessions occuring over the past hour .NOTES Version 1.0 #> [Alias("gnsh")] Param ( [parameter(Mandatory=$False)] [ValidateSet('LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM', IgnoreCase=$True)] [string]$TimePeriod = 'LAST_HOUR', [parameter(Mandatory=$False)] [Alias("StartDateFrom")] [string]$TimePeriodFrom, [parameter(Mandatory=$False)] [Alias("StartDateTo")] [string]$TimePeriodTo, [parameter(Mandatory=$False)] [ValidateSet('GOOD','POOR_0_25','PARTIALLY_GOOD_25_50','PARTIALLY_GOOD_50_75','PARTIALLY_GOOD_75_100','UNAVAILABLE','UNKNOWN', IgnoreCase=$True)] [string[]]$SessionQualities, [Parameter(Mandatory=$False)] [ValidateRange(0,99999999)] [int]$DurationFrom = 0, [Parameter(Mandatory=$False)] [ValidateRange(0,99999999)] [int]$DurationTo = 99999999, [parameter(Mandatory=$False)] [ValidateSet('AUDIO','VIDEO','APP_SHARING','FILE_TRANSFER','IM','UNKNOWN','TOTAL','VBSS','REMOTE_ASSISTANCE','APP_INVITE','FOCUS','UNKNOWN', IgnoreCase=$True)] [string[]]$Modalities = 'TOTAL', [parameter(Mandatory=$False)] [ValidateSet('TCP','UDP','Unknown', IgnoreCase=$False)] [string[]]$Protocols, [Parameter(Mandatory=$False)] [ValidateRange(200,699)] [string[]]$ResponseCodes, [parameter(Mandatory=$False)] [ValidateSet('INTERNAL','EXTERNAL','FEDERATED','INTERNAL_EXTERNAL','EXTERNAL_INTERNAL','FEDERATED_INTERNAL','INTERNAL_FEDERATED','FEDERATED_EXTERNAL','EXTERNAL_FEDERATED','UNKNOWN', IgnoreCase=$True)] [string[]]$SessionScenarios, [parameter(Mandatory=$False)] [ValidateSet('CONFERENCE','CONFERENCE_SESSION','PEER2PEER','PEER2PEER_MULTIMEDIA','PSTN', IgnoreCase=$False)] [string[]]$SessionTypes, [parameter(Mandatory=$False)] [string[]]$Codecs, [parameter(Mandatory=$False)] [string[]]$CallerCodecs, [parameter(Mandatory=$False)] [string[]]$CalleeCodecs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Devices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerCaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeCaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$RenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerRenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeRenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$DeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerDeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeDeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$IPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CallerIPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CalleeIPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Locations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerLocations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeLocations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtISPs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtISPs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtISPs, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$NetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$CallerNetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$CalleeNetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$Platforms, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$CallerPlatforms, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$CalleePlatforms, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$Scenarios, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$CallerScenarios, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$CalleeScenarios, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$Subnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CallerSubnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CalleeSubnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Users, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$FromUsers, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ToUsers, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ConfOrganizers, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$VPN, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$CallerVPN, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$CalleeVPN, [parameter(Mandatory=$False)] [ValidateRange(0,99999)] [int]$ParticipantsMinCount, [parameter(Mandatory=$False)] [ValidateRange(0,99999)] [int]$ParticipantsMaxCount, [parameter(Mandatory=$False)] [ValidateSet('BAD','POOR','FAIR','GOOD','EXCELLENT', IgnoreCase=$False)] [string[]]$FeedbackRating, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $Params = @{} If ($Modalities) { $Modalities | %{$ModalitiesStr += ($(if($ModalitiesStr){","}) + $_)}; $Params.Add('Modalities',$ModalitiesStr) } If ($TenantName) { $Params.Add('Tenant',$TenantName) } #Remove Modalities from FilterSession POST. For some reason, TOTAL results come back as empty if this is set $PSBoundParameters.Remove('Modalities') | Out-Null $FilterSession = Set-NectarFilterParams @PsBoundParameters $URI = "https://$Global:NectarCloud/dapi/session/histograms" Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -uri $URI -Body $Params -WebSession $FilterSession Return $JSON } Catch { Write-Error 'Session histogram not found.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarSessionSummary { <# .SYNOPSIS Returns call summary information for a given session .DESCRIPTION Returns call summary information for a given session. This is used to populate the top few sections of an individual session on the session OVERVIEW screen. UI_ELEMENT .PARAMETER SessionID The session ID of the selected session .PARAMETER Platform The platform where the session took place .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-NectarSessionSummary 2021-04-30T16:04:28.572701_1_1_*_*_*_6_*_29fe15a4-99e5-4a2c-92a6-fbf3024944fc_29abe23a4-33e5-4a2c-92a6-faf30445e5bc_* -Platform TEAMS Returns summary information for a specific Teams session .EXAMPLE Get-NectarSessions -Platform TEAMS -Users tferguson@contoso.com -SessionTypes PEER2PEER -TimePeriod LAST_DAY | Get-NectarSessionSummary -Platform TEAMS Returns summary information for all Teams peer-to-peer calls for TFerguson for the last day. .NOTES Version 1.1 #> [Alias("gnss")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$SessionID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias('callerPlatform', 'calleePlatform', 'Platforms')] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$Platform, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $Platform = $Platform.ToUpper() $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/dapi/session/$SessionID/summary?platform=$Platform&tenant=$TenantName" $SessionSummary = [pscustomobject][ordered]@{ SessionID = $SessionID StartTime = $JSON.startTime EndTime = $JSON.endTime Duration = $JSON.duration Quality = $JSON.quality CallerRenderDevice = $JSON.caller.renderDevice.value CallerCaptureDevice = $JSON.caller.captureDevice.value CallerClientVersion = $JSON.caller.clientVersion.value CallerNetworkType = $JSON.caller.networkType.value CallerNetworkWarning = $JSON.caller.networkType.warning CallerServer = $JSON.caller.server.value CallerServerAlertLevel = $JSON.caller.server.alertLevel CalleeRenderDevice = $JSON.callee.renderDevice.value CalleeCaptureDevice = $JSON.callee.captureDevice.value CalleeClientVersion = $JSON.callee.clientVersion.value CalleeNetworkType = $JSON.callee.networkType.value CalleeNetworkWarning = $JSON.callee.networkType.warning CalleeServer = $JSON.callee.server.value CalleeServerAlertLevel = $JSON.callee.server.alertLevel } Return $SessionSummary } Catch { Write-Error 'Session diagnostics not found.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarSessionDetails { <# .SYNOPSIS Returns details for a given session .DESCRIPTION Returns details for a given session. This is used to populate the session ADVANCED screen for a given session. UI_ELEMENT .PARAMETER SessionID The session ID of the selected session .PARAMETER Platform The platform where the session took place .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-NectarSessionDetails 2021-04-30T16:04:28.572701_1_1_*_*_*_6_*_29fe15a4-99e5-4a2c-92a6-fbf3024944fc_29abe23a4-33e5-4a2c-92a6-faf30445e5bc_* -Platform TEAMS Returns detailed information for a specific Teams session .EXAMPLE Get-NectarSessions -Platform TEAMS -Users tferguson@contoso.com -SessionTypes PEER2PEER,PEER2PEER_MULTIMEDIA -TimePeriod LAST_DAY | Get-NectarSessionDetails -Platform TEAMS Returns detailed information for all Teams peer-to-peer and multimedia P2P calls for TFerguson for the last day. .NOTES Version 1.2 #> [Alias("gnsd")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("ID")] [string]$SessionID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias('callerPlatform', 'calleePlatform', 'Platforms')] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$Platform, [Parameter(ValueFromPipelineByPropertyName=$True, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $Platform = $Platform.ToUpper() $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/dapi/session/$SessionID/advanced?platform=$Platform&tenant=$TenantName" $SessionDetails = [pscustomobject][ordered]@{ SessionID = $SessionID SessionType = $JSON.type Caller = $JSON.caller Callee = $JSON.callee } ForEach ($DataGroup in $JSON.groups) { ForEach ($DataElement in $DataGroup.data.PsObject.Properties) { $SessionDetails | Add-Member -NotePropertyName $DataElement.Name -NotePropertyValue $DataElement.Value.Value } } Return $SessionDetails } Catch { Write-Error 'Session details not found.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarSessionAlerts { <# .SYNOPSIS Returns alerts for a given session .DESCRIPTION Returns alerts for a given session. This is used to populate the SESSION ALERTS portion of the session OVERVIEW screen. UI_ELEMENT .PARAMETER SessionID The session ID of the selected session .PARAMETER Platform The platform where the session took place .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-NectarSessionAlerts 2021-04-30T16:04:28.572701_1_1_*_*_*_6_*_29fe15a4-99e5-4a2c-92a6-fbf3024944fc_29abe23a4-33e5-4a2c-92a6-faf30445e5bc_* -Platform TEAMS Returns alert information for a specific Teams session .EXAMPLE Get-NectarSessions -Platform TEAMS -Users tferguson@contoso.com -SessionTypes PEER2PEER -TimePeriod LAST_DAY | Get-NectarSessionAlerts -Platform TEAMS Returns session alerts for all Teams peer-to-peer calls for TFerguson for the last day. .NOTES Version 1.2 #> [Alias("gnsa")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$SessionID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias('callerPlatform', 'calleePlatform', 'Platforms')] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$Platform, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $Platform = $Platform.ToUpper() $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/dapi/session/$SessionID/alerts?platform=$Platform&tenant=$TenantName" $UserList = 'Caller','Callee' ForEach ($User in $UserList) { ForEach ($Alert in $JSON.$User.alerts.PsObject.Properties) { $AlertDetails = [pscustomobject][ordered]@{ SessionID = $SessionID User = $User Parameter = $Alert.Name Value = $Alert.Value.Value AlertLevel = $Alert.Value.AlertLevel } $AlertDetails } } } Catch { Write-Error 'Session alerts not found.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarSessionDiagnostics { <# .SYNOPSIS Returns call diagnostics for a given session .DESCRIPTION Returns call diagnostics for a given session. This is used to populate the session DIAGNOSTICS screen for a given session. UI_ELEMENT .PARAMETER SessionID The session ID of the selected session .PARAMETER Platform The platform where the session took place .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-NectarSessionDetails 2021-04-30T16:04:28.572701_1_1_*_*_*_6_*_29fe15a4-99e5-4a2c-92a6-fbf3024944fc_29abe23a4-33e5-4a2c-92a6-faf30445e5bc_* -Platform SKYPE Returns diagnostic information for a specific Skype for Business session .EXAMPLE Get-NectarSessions -Platform SKYPE -Users tferguson@contoso.com -SessionTypes PEER2PEER -TimePeriod LAST_DAY | Get-NectarSessionDetails -Platform SKYPE Returns detailed information for all Skype for Business peer-to-peer calls for TFerguson for the last day. .NOTES Version 1.2 #> [Alias("gnsd")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("id")] [string]$SessionID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias('callerPlatform', 'calleePlatform', 'Platforms')] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$Platform, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $Platform = $Platform.ToUpper() $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/dapi/session/$SessionID/diagnostic?platform=$Platform&tenant=$TenantName" $JSON.elements | Add-Member -NotePropertyName 'sessionId' -NotePropertyValue $SessionID Return $JSON.elements } Catch { Write-Error 'Session diagnostics not found.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarConferenceSummary { <# .SYNOPSIS Returns session summaries for a given conference .DESCRIPTION Returns session summaries for a given conference. This is used to populate the CONFERENCE section of the details of a specific conference. UI_ELEMENT .PARAMETER ConferenceID The conference ID of the selected conference .PARAMETER Platform The platform where the conference took place .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-NectarConferenceSummary 2021-05-06T13:30:34.795296_*_*_*_*_*_*_173374c1-a15a-47dd-b11c-d32ab5442774_*_* Returns conference summary information for a specific conference .EXAMPLE Get-NectarSessions -TimePeriod LAST_DAY -Users tferguson@contoso.com-SessionTypes CONFERENCE -Platforms TEAMS | Get-NectarConferenceSummary -Platform TEAMS Returns the conference summary information for all Teams conferences participated by tferguson@contoso.com for the past day .NOTES Version 1.2 #> [Alias("gncs")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("id")] [string]$ConferenceID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias('callerPlatform', 'calleePlatform', 'Platforms')] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$Platform, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $Platform = $Platform.ToUpper() $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/dapi/conference/$ConferenceID/?platform=$Platform&tenant=$TenantName" $ConferenceSummary = [pscustomobject][ordered]@{ ConferenceID = $ConferenceID StartTime = $JSON.conference.startTime EndTime = $JSON.conference.endTime Duration = $JSON.duration AverageMOS = $JSON.avgMos Participants = $JSON.participants TotalSessions = $JSON.sessions.total GoodSessions = $JSON.sessions.good PoorSessions = $JSON.sessions.poor UnknownSessions = $JSON.sessions.unknown } Return $ConferenceSummary } Catch { Write-Error 'Conference not found.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarConferenceTimeline { <# .SYNOPSIS Returns session timeline details for a given conference .DESCRIPTION Returns session timeline details for a given conference. This is used to build the Gantt chart view of a specific conference. UI_ELEMENT .PARAMETER ConferenceID The conference ID of the selected conference .PARAMETER Platform The platform where the conference took place .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-NectarConferenceTimeline 2021-05-06T13:30:34.795296_*_*_*_*_*_*_173374c1-a15a-47dd-b11c-d32ab5442774_*_* -Platform TEAMS Returns conference summary information for a specific conference .EXAMPLE Get-NectarSessions -TimePeriod LAST_DAY -Users tferguson@contoso.com-SessionTypes CONFERENCE -Platforms TEAMS | Get-NectarConferenceTimeline -Platform TEAMS Returns the conference timeline information for all Teams conferences participated by tferguson@contoso.com for the past day .NOTES Version 1.2 #> [Alias("gnct")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("id")] [string]$ConferenceID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias('callerPlatform', 'calleePlatform', 'Platforms')] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$Platform, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $Platform = $Platform.ToUpper() $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/dapi/conference/$ConferenceID/timeline?platform=$Platform&tenant=$TenantName" $JSON | Add-Member -NotePropertyName 'conferenceId' -NotePropertyValue $ConferenceID $JSON | Add-Member -TypeName 'Nectar.Conference.Timeline' Return $JSON } Catch { Write-Error 'Conference not found.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarConferenceParticipants { <# .SYNOPSIS Returns session participant details for a given conference .DESCRIPTION Returns session participant details for a given conference. This is used to build the PARTICIPANTS section of a specific conference. UI_ELEMENT .PARAMETER ConferenceID The conference ID of the selected conference .PARAMETER Platform The platform where the conference took place .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-NectarConferenceParticipants 2021-05-06T13:30:34.795296_*_*_*_*_*_*_173374c1-a15a-47dd-b11c-d32ab5442774_*_* -Platform TEAMS Returns conference participant information for a specific conference .EXAMPLE Get-NectarSessions -TimePeriod LAST_DAY -Users tferguson@contoso.com-SessionTypes CONFERENCE -Platforms TEAMS | Get-NectarConferenceParticipants -Platform TEAMS Returns the conference participant information for all Teams conferences participated by tferguson@contoso.com for the past day .NOTES Version 1.1 #> [Alias("gncp")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("id")] [string]$ConferenceID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias('callerPlatform', 'calleePlatform', 'Platforms')] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$Platform, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $Platform = $Platform.ToUpper() $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/dapi/conference/$ConferenceID/participants?platform=$Platform&tenant=$TenantName" $JSON | Add-Member -NotePropertyName 'conferenceId' -NotePropertyValue $ConferenceID $JSON | Add-Member -TypeName 'Nectar.Conference.Participants' Return $JSON } Catch { Write-Error 'Conference not found.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarSessionMultiTimeline { <# .SYNOPSIS Returns multimedia session timeline details for a given P2P multimedia session .DESCRIPTION Returns multimedia session (an audio/video P2P session or an audio/appsharing P2P session) timeline details for a given session Used to build the Gantt chart view for a given P2P multimedia session on the Session Details screen. UI_ELEMENT .PARAMETER SessionID The sessionID of a multimedia P2P session .PARAMETER Platform The platform where the P2P session took place .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-NectarSessionMultiTimeline 2021-05-05T19:17:14.324027_1_1_*_*_*_6_*_6efc12345-4229-4c11-9001-9a00667a761e9_6efc8348-4809-4caf-9141-9afa87a761e9_* -Platform TEAMS Returns multimedia session timeline information for a specific multimedia P2P session .EXAMPLE Get-NectarSessions -TimePeriod LAST_DAY -Users tferguson@contoso.com-SessionTypes PEER2PEER_MULTIMEDIA -Platforms TEAMS | Get-NectarSessionMultiTimeline -Platform TEAMS Returns the multimedia session timeline information for all Teams P2P multimedia sessions participated by tferguson@contoso.com for the past day .NOTES Version 1.3 #> [Alias("gnsmt")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$SessionID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias('callerPlatform', 'calleePlatform', 'Platforms')] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$Platform, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/dapi/session/$SessionID/multi/timeline?platform=$Platform&tenant=$TenantName" #$JSON | Add-Member -NotePropertyName 'sessionId' -NotePropertyValue $SessionID Return $JSON } Catch { Write-Error 'Multimedia session not found.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarModalityQualitySummary { <# .SYNOPSIS Returns summary quality information for different modalities over a given timeperiod. .DESCRIPTION Returns summary quality information for audio, video and appsharing modalities. Used to build the QUALITY section of the CALL DETAILS screen. UI_ELEMENT .PARAMETER Modality Show sessions that match one or more modality types. Not case sensitive. Choose one or more from: 'AUDIO','VIDEO','APPSHARING' .PARAMETER TimePeriod The time period to show session data from. Select from 'LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM'. CUSTOM requires using TimePeriodFrom and TimePeriodTo parameters. .PARAMETER TimePeriodFrom The earliest date/time to show session data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodTo parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. .PARAMETER TimePeriodTo The latest date/time to show session data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodFrom parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. .PARAMETER SessionQualities Show sessions that match a given quality rating. Case sensitive. Choose one or more from: 'GOOD','POOR_0_25','PARTIALLY_GOOD_25_50','PARTIALLY_GOOD_50_75','PARTIALLY_GOOD_75_100','UNAVAILABLE','UNKNOWN' .PARAMETER DurationFrom The shortest call length (in seconds) to show session data. .PARAMETER DurationTo The longest call length (in seconds) to show session data. .PARAMETER Protocols Show sessions that match one or more network protocol types. Case sensitive. Choose one or more from: 'TCP','UDP','Unknown' .PARAMETER ResponseCodes Show sessions that match one or more SIP response codes. Accepts numbers from 200 to 699 .PARAMETER SessionScenarios Show sessions that match one or more session scenarios. Not case sensitive. Choose one or more from: 'External','Internal','Internal-External','External-Internal','Federated','Internal-Federated','External-Federated','Unknown' .PARAMETER SessionTypes Show sessions that match one or more session scenarios. Case sensitive. Choose one or more from: 'Conference','Peer To Peer','Peer To Peer (Multimedia)','PSTN/External' .PARAMETER Codecs Show sessions where the selected codec was used by either caller or callee. Can query for multiple codecs. Case sensitive. Use Get-NectarCodecs for a list of valid codecs. .PARAMETER CallerCodecs Show sessions where the selected codec was used by the caller. Can query for multiple codecs. Case sensitive. Use Get-NectarCodecs for a list of valid codecs. .PARAMETER CalleeCodecs Show sessions where the selected codec was used by the callee. Can query for multiple codecs. Case sensitive. Use Get-NectarCodecs for a list of valid codecs. .PARAMETER Devices Show sessions where the selected device was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CallerDevices Show sessions where the selected device was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CalleeDevices Show sessions where the selected device was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CaptureDevices Show sessions where the selected capture device (microphone) was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CallerCaptureDevices Show sessions where the selected capture device (microphone) was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CalleeCaptureDevices Show sessions where the selected capture device (microphone) was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER RenderDevices Show sessions where the selected render device (speaker) was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CallerRenderDevices Show sessions where the selected render device (speaker) was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CalleeRenderDevices Show sessions where the selected render device (speaker) was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER DeviceVersions Show sessions where the selected device version was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarClientVersion for a list of valid client versions. .PARAMETER CallerDeviceVersions Show sessions where the selected device version was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarClientVersion for a list of valid client versions. .PARAMETER CalleeDeviceVersions Show sessions where the selected device version was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarClientVersion for a list of valid client versions. .PARAMETER IPAddresses Show sessions where the selected IP address was used by either caller or callee. Can query for multiple IPs. .PARAMETER CallerIPAddresses Show sessions where the selected IP address was used by the caller. Can query for multiple IPs. .PARAMETER CalleeIPAddresses Show sessions where the selected IP address was used by the callee. Can query for multiple IPs. .PARAMETER Locations Show sessions where the selected location was used by either caller or callee. Can query for multiple locations. .PARAMETER CallerLocations Show sessions where the selected location was used by the caller. Can query for multiple locations. .PARAMETER CalleeLocations Show sessions where the selected location was used by the callee. Can query for multiple locations. .PARAMETER ExtCities Show sessions where the caller or callee was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER CallerExtCities Show sessions where the caller was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER CalleeExtCities Show sessions where the callee was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER ExtCountries Show sessions where the caller or callee was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER CallerExtCountries Show sessions where the caller was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER CalleeExtCountries Show sessions where the callee was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER ExtISPs Show sessions where the caller or callee was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER CallerExtISPs Show sessions where the caller was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER CalleeExtISPs Show sessions where the callee was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER NetworkTypes Show sessions where the selected network type was used by either caller or callee. Can query for multiple network types. Case sensitive. Choose one or more from: 'Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable' .PARAMETER CallerNetworkTypes Show sessions where the selected network type was used by the caller. Can query for multiple network types. Case sensitive. Choose one or more from: 'Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable' .PARAMETER CalleeNetworkTypes Show sessions where the selected network type was used by the callee. Can query for multiple network types. Case sensitive. Choose one or more from: 'Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable' .PARAMETER Platforms Show sessions where the selected platform was used by either caller or callee. Can query for multiple platforms. Case sensitive. Choose one or more from: 'SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS' .PARAMETER CallerPlatforms Show sessions where the selected platform was used by the caller. Can query for multiple platforms. Case sensitive. Choose one or more from: 'SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS' .PARAMETER CalleePlatforms Show sessions where the selected platform was used by the callee. Can query for multiple platforms. Case sensitive. Choose one or more from: 'SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS' .PARAMETER Scenarios Show sessions where the selected scenario was used by either caller or callee. Can query for multiple scenarios. Choose one or more from: 'External','Internal','Federated','Unknown' .PARAMETER CallerScenarios Show sessions where the selected scenario was used by the caller. Can query for multiple scenarios. Choose one or more from: 'External','Internal','Federated','Unknown' .PARAMETER CalleeScenarios Show sessions where the selected scenario was used by the callee. Can query for multiple scenarios. Choose one or more from: 'External','Internal','Federated','Unknown' .PARAMETER Subnets Show sessions where the selected subnet was used by either caller or callee. Can query for multiple subnets. .PARAMETER CallerSubnets Show sessions where the selected subnet was used by the caller. Can query for multiple subnets. .PARAMETER CalleeSubnets Show sessions where the selected subnet was used by the callee. Can query for multiple subnets. .PARAMETER Users Show sessions where the selected user was either caller or callee. Can query for multiple users. .PARAMETER FromUsers Show sessions where the selected user was the caller. Can query for multiple users. .PARAMETER ToUsers Show sessions where the selected user was the callee. Can query for multiple users. .PARAMETER ConfOrganizers Show sessions hosted by a specified conference organizer. Can query for multiple organizers. .PARAMETER VPN Show sessions where the selected VPN was used by either caller or callee. .PARAMETER CallerVPN Show sessions where the selected VPN was used by the caller. .PARAMETER CalleeVPN Show sessions where the selected VPN was used by the callee. .PARAMETER ParticipantsMinCount Show sessions where the number of participants is greater than or equal to the entered value .PARAMETER ParticipantsMaxCount Show sessions where the number of participants is less than or equal to the entered value .PARAMETER FeedbackRating Show sessions where users provided specific feedback ratings from BAD to EXCELLENT. Allowed values are BAD, POOR, FAIR, GOOD, EXCELLENT. Corresponds to ratings from 1 to 5 stars. .PARAMETER OrderByField Sort the output by the selected field .PARAMETER OrderDirection Sort direction. Use with OrderByField. Not case sensitive. Choose from: ASC, DESC .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER PageSize The size of the page used to return data. Defaults to 1000 .PARAMETER ResultSize The total number of results to return. Defaults to 1000. Maximum result size is 9,999,999 results .EXAMPLE Get-NectarModalityQualitySummary -Modality AUDIO -TimePeriod LAST_DAY Returns the modality quality summary for the past day .NOTES Version 1.1 #> [Alias("gnmqs")] Param ( [parameter(Mandatory=$True)] [ValidateSet('AUDIO','VIDEO','APPSHARE', IgnoreCase=$True)] [string]$Modality, [parameter(Mandatory=$False)] [ValidateSet('LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM', IgnoreCase=$True)] [string]$TimePeriod = 'LAST_HOUR', [parameter(Mandatory=$False)] [Alias("StartDateFrom")] [string]$TimePeriodFrom, [parameter(Mandatory=$False)] [Alias("StartDateTo")] [string]$TimePeriodTo, [parameter(Mandatory=$False)] [ValidateSet('GOOD','POOR_0_25','PARTIALLY_GOOD_25_50','PARTIALLY_GOOD_50_75','PARTIALLY_GOOD_75_100','UNAVAILABLE','UNKNOWN', IgnoreCase=$True)] [string[]]$SessionQualities, [Parameter(Mandatory=$False)] [ValidateRange(0,99999999)] [int]$DurationFrom = 0, [Parameter(Mandatory=$False)] [ValidateRange(0,99999999)] [int]$DurationTo = 99999999, [parameter(Mandatory=$False)] [ValidateSet('TCP','UDP','Unknown', IgnoreCase=$False)] [string[]]$Protocols, [Parameter(Mandatory=$False)] [ValidateRange(200,699)] [string[]]$ResponseCodes, [parameter(Mandatory=$False)] [ValidateSet('INTERNAL','EXTERNAL','FEDERATED','INTERNAL_EXTERNAL','EXTERNAL_INTERNAL','FEDERATED_INTERNAL','INTERNAL_FEDERATED','FEDERATED_EXTERNAL','EXTERNAL_FEDERATED','UNKNOWN', IgnoreCase=$True)] [string[]]$SessionScenarios, [parameter(Mandatory=$False)] [ValidateSet('CONFERENCE','CONFERENCE_SESSION','PEER2PEER','PEER2PEER_MULTIMEDIA','PSTN', IgnoreCase=$False)] [string[]]$SessionTypes, [parameter(Mandatory=$False)] [string[]]$Codecs, [parameter(Mandatory=$False)] [string[]]$CallerCodecs, [parameter(Mandatory=$False)] [string[]]$CalleeCodecs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Devices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerCaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeCaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$RenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerRenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeRenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$DeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerDeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeDeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$IPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CallerIPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CalleeIPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("SiteName")] [string[]]$Locations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerLocations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeLocations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtISPs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtISPs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtISPs, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$NetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$CallerNetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$CalleeNetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$Platforms, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$CallerPlatforms, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$CalleePlatforms, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$Scenarios, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$CallerScenarios, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$CalleeScenarios, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$Subnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CallerSubnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CalleeSubnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Users, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$FromUsers, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ToUsers, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ConfOrganizers, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$VPN, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$CallerVPN, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$CalleeVPN, [parameter(Mandatory=$False)] [ValidateRange(0,99999)] [int]$ParticipantsMinCount, [parameter(Mandatory=$False)] [ValidateRange(0,99999)] [int]$ParticipantsMaxCount, [parameter(Mandatory=$False)] [ValidateSet('BAD','POOR','FAIR','GOOD','EXCELLENT', IgnoreCase=$False)] [string[]]$FeedbackRating, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(Mandatory=$False)] [ValidateSet('DEFAULT','USERS', IgnoreCase=$True)] [string]$Scope = 'DEFAULT', [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$PageSize = 1000, [Parameter(Mandatory=$False)] [ValidateRange(1,9999999)] [int]$ResultSize ) Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $PSBoundParameters.Remove('Modality') | Out-Null $FilterSession = Set-NectarFilterParams @PsBoundParameters $Modality = $Modality.ToLower() $URI = "https://$Global:NectarCloud/dapi/quality/session/$Modality" If ($TenantName) { $Params = @{'Tenant' = $TenantName} } Try { Write-Verbose $PsBoundParameters $JSON = Invoke-RestMethod -Method GET -uri $URI -Body $Params -WebSession $FilterSession ForEach ($SessionType in $JSON) { If ($SessionType.QualityType -in $SessionTypes -or !$SessionTypes) { $QualitySummary = [pscustomobject][ordered]@{} If ($Modality -ne 'appshare') { $QualitySummary | Add-Member -NotePropertyName 'SessionType' -NotePropertyValue $SessionType.QualityType } ForEach ($QualityMetric in $SessionType.QualityMetrics) { $QualitySummary | Add-Member -NotePropertyName $QualityMetric.name -NotePropertyValue $QualityMetric.value $TrendMetricName = $QualityMetric.name + '_TREND' $QualitySummary | Add-Member -NotePropertyName $TrendMetricName -NotePropertyValue $QualityMetric.trends } If ($TenantName) { $QualitySummary | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $TenantName } If ($PSItem.SiteName) { $QualitySummary | Add-Member -NotePropertyName 'SiteName' -NotePropertyValue $PSItem.SiteName } $QualitySummary | Add-Member -TypeName 'Nectar.ModalityQuality' $QualitySummary } } } Catch { Write-Error "No results. Try specifying a less-restrictive filter" Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarLocationQualitySummary { <# .SYNOPSIS Returns summary information for different locations over a given timeperiod. .DESCRIPTION Returns summary information for different locations over a given timeperiod. This is used to build the SUMMARY page in Nectar 10. UI_ELEMENT .PARAMETER TimePeriod The time period to show session data from. Select from 'LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM'. CUSTOM requires using TimePeriodFrom and TimePeriodTo parameters. .PARAMETER TimePeriodFrom The earliest date/time to show session data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodTo parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. .PARAMETER TimePeriodTo The latest date/time to show session data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodFrom parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. .PARAMETER SessionQualities Show sessions that match a given quality rating. Case sensitive. Choose one or more from: 'GOOD','POOR_0_25','PARTIALLY_GOOD_25_50','PARTIALLY_GOOD_50_75','PARTIALLY_GOOD_75_100','UNAVAILABLE','UNKNOWN' .PARAMETER DurationFrom The shortest call length (in seconds) to show session data. .PARAMETER DurationTo The longest call length (in seconds) to show session data. .PARAMETER Modalities Show sessions that match one or more modality types. Not case sensitive. Choose one or more from: 'AUDIO','VIDEO','APP_SHARING','FILE_TRANSFER','IM','UNKNOWN','TOTAL','VBSS','REMOTE_ASSISTANCE','APP_INVITE','FOCUS','UNKNOWN' .PARAMETER Protocols Show sessions that match one or more network protocol types. Case sensitive. Choose one or more from: 'TCP','UDP','Unknown' .PARAMETER ResponseCodes Show sessions that match one or more SIP response codes. Accepts numbers from 200 to 699 .PARAMETER SessionScenarios Show sessions that match one or more session scenarios. Not case sensitive. Choose one or more from: 'External','Internal','Internal-External','External-Internal','Federated','Internal-Federated','External-Federated','Unknown' .PARAMETER SessionTypes Show sessions that match one or more session scenarios. Case sensitive. Choose one or more from: 'Conference','Peer To Peer','Peer To Peer (Multimedia)','PSTN/External' .PARAMETER Codecs Show sessions where the selected codec was used by either caller or callee. Can query for multiple codecs. Case sensitive. Use Get-NectarCodecs for a list of valid codecs. .PARAMETER CallerCodecs Show sessions where the selected codec was used by the caller. Can query for multiple codecs. Case sensitive. Use Get-NectarCodecs for a list of valid codecs. .PARAMETER CalleeCodecs Show sessions where the selected codec was used by the callee. Can query for multiple codecs. Case sensitive. Use Get-NectarCodecs for a list of valid codecs. .PARAMETER Devices Show sessions where the selected device was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CallerDevices Show sessions where the selected device was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CalleeDevices Show sessions where the selected device was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CaptureDevices Show sessions where the selected capture device (microphone) was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CallerCaptureDevices Show sessions where the selected capture device (microphone) was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CalleeCaptureDevices Show sessions where the selected capture device (microphone) was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER RenderDevices Show sessions where the selected render device (speaker) was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CallerRenderDevices Show sessions where the selected render device (speaker) was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER CalleeRenderDevices Show sessions where the selected render device (speaker) was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarSupportedDevice for a list of valid devices. .PARAMETER DeviceVersions Show sessions where the selected device version was used by either caller or callee. Can query for multiple devices. Case sensitive. Use Get-NectarClientVersion for a list of valid client versions. .PARAMETER CallerDeviceVersions Show sessions where the selected device version was used by the caller. Can query for multiple devices. Case sensitive. Use Get-NectarClientVersion for a list of valid client versions. .PARAMETER CalleeDeviceVersions Show sessions where the selected device version was used by the callee. Can query for multiple devices. Case sensitive. Use Get-NectarClientVersion for a list of valid client versions. .PARAMETER IPAddresses Show sessions where the selected IP address was used by either caller or callee. Can query for multiple IPs. .PARAMETER CallerIPAddresses Show sessions where the selected IP address was used by the caller. Can query for multiple IPs. .PARAMETER CalleeIPAddresses Show sessions where the selected IP address was used by the callee. Can query for multiple IPs. .PARAMETER Locations Show sessions where the selected location was used by either caller or callee. Can query for multiple locations. .PARAMETER CallerLocations Show sessions where the selected location was used by the caller. Can query for multiple locations. .PARAMETER CalleeLocations Show sessions where the selected location was used by the callee. Can query for multiple locations. .PARAMETER ExtCities Show sessions where the caller or callee was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER CallerExtCities Show sessions where the caller was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER CalleeExtCities Show sessions where the callee was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER ExtCountries Show sessions where the caller or callee was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER CallerExtCountries Show sessions where the caller was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER CalleeExtCountries Show sessions where the callee was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER ExtISPs Show sessions where the caller or callee was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER CallerExtISPs Show sessions where the caller was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER CalleeExtISPs Show sessions where the callee was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER NetworkTypes Show sessions where the selected network type was used by either caller or callee. Can query for multiple network types. Case sensitive. Choose one or more from: 'Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable' .PARAMETER CallerNetworkTypes Show sessions where the selected network type was used by the caller. Can query for multiple network types. Case sensitive. Choose one or more from: 'Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable' .PARAMETER CalleeNetworkTypes Show sessions where the selected network type was used by the callee. Can query for multiple network types. Case sensitive. Choose one or more from: 'Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable' .PARAMETER Platforms Show sessions where the selected platform was used by either caller or callee. Can query for multiple platforms. Case sensitive. Choose one or more from: 'SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS' .PARAMETER CallerPlatforms Show sessions where the selected platform was used by the caller. Can query for multiple platforms. Case sensitive. Choose one or more from: 'SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS' .PARAMETER CalleePlatforms Show sessions where the selected platform was used by the callee. Can query for multiple platforms. Case sensitive. Choose one or more from: 'SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS' .PARAMETER Scenarios Show sessions where the selected scenario was used by either caller or callee. Can query for multiple scenarios. Choose one or more from: 'External','Internal','Federated','Unknown' .PARAMETER CallerScenarios Show sessions where the selected scenario was used by the caller. Can query for multiple scenarios. Choose one or more from: 'External','Internal','Federated','Unknown' .PARAMETER CalleeScenarios Show sessions where the selected scenario was used by the callee. Can query for multiple scenarios. Choose one or more from: 'External','Internal','Federated','Unknown' .PARAMETER Subnets Show sessions where the selected subnet was used by either caller or callee. Can query for multiple subnets. .PARAMETER CallerSubnets Show sessions where the selected subnet was used by the caller. Can query for multiple subnets. .PARAMETER CalleeSubnets Show sessions where the selected subnet was used by the callee. Can query for multiple subnets. .PARAMETER Users Show sessions where the selected user was either caller or callee. Can query for multiple users. .PARAMETER FromUsers Show sessions where the selected user was the caller. Can query for multiple users. .PARAMETER ToUsers Show sessions where the selected user was the callee. Can query for multiple users. .PARAMETER ConfOrganizers Show sessions hosted by a specified conference organizer. Can query for multiple organizers. .PARAMETER VPN Show sessions where the selected VPN was used by either caller or callee. .PARAMETER CallerVPN Show sessions where the selected VPN was used by the caller. .PARAMETER CalleeVPN Show sessions where the selected VPN was used by the callee. .PARAMETER ParticipantsMinCount Show sessions where the number of participants is greater than or equal to the entered value .PARAMETER ParticipantsMaxCount Show sessions where the number of participants is less than or equal to the entered value .PARAMETER FeedbackRating Show sessions where users provided specific feedback ratings from BAD to EXCELLENT. Allowed values are BAD, POOR, FAIR, GOOD, EXCELLENT. Corresponds to ratings from 1 to 5 stars. .PARAMETER OrderByField Sort the output by the selected field .PARAMETER OrderDirection Sort direction. Use with OrderByField. Not case sensitive. Choose from: ASC, DESC .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER PageSize The size of the page used to return data. Defaults to 1000 .PARAMETER ResultSize The total number of results to return. Defaults to 1000. Maximum result size is 9,999,999 results .EXAMPLE Get-NectarLocationQualitySummary -TimePeriod LAST_DAY Returns the quality summary for each location for the last day .NOTES Version 1.1 #> [Alias("gnlqs")] Param ( [parameter(Mandatory=$False)] [ValidateSet('LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM', IgnoreCase=$True)] [string]$TimePeriod = 'LAST_HOUR', [parameter(Mandatory=$False)] [Alias("StartDateFrom")] [string]$TimePeriodFrom, [parameter(Mandatory=$False)] [Alias("StartDateTo")] [string]$TimePeriodTo, [parameter(Mandatory=$False)] [ValidateSet('GOOD','POOR_0_25','PARTIALLY_GOOD_25_50','PARTIALLY_GOOD_50_75','PARTIALLY_GOOD_75_100','UNAVAILABLE','UNKNOWN', IgnoreCase=$True)] [string[]]$SessionQualities, [Parameter(Mandatory=$False)] [ValidateRange(0,99999999)] [int]$DurationFrom = 0, [Parameter(Mandatory=$False)] [ValidateRange(0,99999999)] [int]$DurationTo = 99999999, [parameter(Mandatory=$False)] [ValidateSet('AUDIO','VIDEO','APP_SHARING','FILE_TRANSFER','IM','UNKNOWN','TOTAL','VBSS','REMOTE_ASSISTANCE','APP_INVITE','FOCUS','UNKNOWN', IgnoreCase=$True)] [string[]]$Modalities, [parameter(Mandatory=$False)] [ValidateSet('TCP','UDP','Unknown', IgnoreCase=$False)] [string[]]$Protocols, [Parameter(Mandatory=$False)] [ValidateRange(200,699)] [string[]]$ResponseCodes, [parameter(Mandatory=$False)] [ValidateSet('INTERNAL','EXTERNAL','FEDERATED','INTERNAL_EXTERNAL','EXTERNAL_INTERNAL','FEDERATED_INTERNAL','INTERNAL_FEDERATED','FEDERATED_EXTERNAL','EXTERNAL_FEDERATED','UNKNOWN', IgnoreCase=$True)] [string[]]$SessionScenarios, [parameter(Mandatory=$False)] [ValidateSet('CONFERENCE','CONFERENCE_SESSION','PEER2PEER','PEER2PEER_MULTIMEDIA','PSTN', IgnoreCase=$False)] [string[]]$SessionTypes, [parameter(Mandatory=$False)] [string[]]$Codecs, [parameter(Mandatory=$False)] [string[]]$CallerCodecs, [parameter(Mandatory=$False)] [string[]]$CalleeCodecs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Devices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerCaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeCaptureDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$RenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerRenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeRenderDevices, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$DeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerDeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeDeviceVersions, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$IPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CallerIPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CalleeIPAddresses, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Locations, [parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerLocations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeLocations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtISPs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtISPs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtISPs, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$NetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$CallerNetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('Enterprise WiFi','Enterprise Wired','External Mobile','External WiFi','External Wired','Unknown','Unavailable', IgnoreCase=$False)] [string[]]$CalleeNetworkTypes, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$Platforms, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$CallerPlatforms, [parameter(Mandatory=$False)] [ValidateSet('SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string[]]$CalleePlatforms, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$Scenarios, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$CallerScenarios, [parameter(Mandatory=$False)] [ValidateSet('External','Internal','Federated','Unknown', IgnoreCase=$True)] [string[]]$CalleeScenarios, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$Subnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CallerSubnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ipaddress[]]$CalleeSubnets, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Users, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$FromUsers, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ToUsers, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ConfOrganizers, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$VPN, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$CallerVPN, [parameter(Mandatory=$False)] [ValidateSet('TRUE','FALSE','UNKNOWN', IgnoreCase=$True)] [string[]]$CalleeVPN, [parameter(Mandatory=$False)] [ValidateRange(0,99999)] [int]$ParticipantsMinCount, [parameter(Mandatory=$False)] [ValidateRange(0,99999)] [int]$ParticipantsMaxCount, [parameter(Mandatory=$False)] [ValidateSet('BAD','POOR','FAIR','GOOD','EXCELLENT', IgnoreCase=$False)] [string[]]$FeedbackRating, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(Mandatory=$False)] [ValidateSet('DEFAULT','USERS', IgnoreCase=$True)] [string]$Scope = 'DEFAULT', [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$PageSize = 1000, [Parameter(Mandatory=$False)] [ValidateRange(1,9999999)] [int]$ResultSize ) Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $FilterSession = Set-NectarFilterParams @PsBoundParameters If ($TenantName) { $Params = @{'Tenant' = $TenantName} } Try { $JSON = Invoke-RestMethod -Method GET -uri "https://$Global:NectarCloud/dapi/quality/locations" -Body $Params -WebSession $FilterSession $JSONGoodPoor = Invoke-RestMethod -Method GET -uri "https://$Global:NectarCloud/dapi/quality/map" -Body $Params -WebSession $FilterSession ForEach ($Location in $JSON) { $LocGoodPoor = $JSONGoodPoor | Where {$_.name -eq $Location.Name} $QualitySummary = [pscustomobject][ordered]@{ 'LocationName' = $Location.Name 'Sessions' = $Location.Data.Sessions 'CurrentSessions' = $Location.Data.currentSession 'GoodSessions' = $LocGoodPoor.Sessions.Good 'PoorSessions' = $LocGoodPoor.Sessions.Poor 'UnknownSessions' = $LocGoodPoor.Sessions.Unknown 'AvgMOS' = $Location.Data.avgMos 'MOSTrend' = $Location.Data.mos } If ($TenantName) { $QualitySummary | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $TenantName } $QualitySummary | Add-Member -TypeName 'Nectar.LocationQuality' If ($Location.Name -ne 'All') { $QualitySummary } } } Catch { Write-Error "No results. Try specifying a less-restrictive filter" Get-JSONErrorStream -JSONResponse $_ } } } ################################################################################################################################################# # # # Event Functions # # # ################################################################################################################################################# Function Get-NectarEvent { <# .SYNOPSIS Return a list of current or historic Nectar monitored device events .DESCRIPTION Return a list of current or historic Nectar monitored device events .PARAMETER TimePeriod The time period to show event data from. Select from 'LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM'. CUSTOM requires using TimePeriodFrom and TimePeriodTo parameters. .PARAMETER TimePeriodFrom The earliest date/time to show event data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodTo parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. .PARAMETER TimePeriodTo The latest date/time to show event data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodFrom parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. .PARAMETER LastTimeAfter Only return results that occurred more recently than the entered value. Use date-time format as in 2020-04-20T17:46:37.554 .PARAMETER EventAlertLevels Return only events that meet the specified alert level. Choose one or more from CRITICAL, MAJOR, MINOR, WARNING, GOOD, NO_ACTIVITY .PARAMETER Locations Show alerts for one or more specified locations .PARAMETER SearchQuery Search for events that contain the specified string .PARAMETER OrderByField Order the resultset by the specified field. Choose from id, type, lastTime, displayName, deviceName, description, eventId, time, delay, source, location, sourceId .PARAMETER EventState Return either current events or previously acknowledged events .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER PageSize The size of the page used to return data. Defaults to 1000 .PARAMETER ResultSize The total number of results to return. Defaults to 1000. Maximum result size is 9,999,999 results .EXAMPLE Get-NectarEvent -EventAlertLevels CRITICAL,MAJOR Returns a list of current events in the last hour that are either critical or major .EXAMPLE Get-NectarEvent -SearchQuery BadServer -EventState Historic -TimePeriod LAST_WEEK Returns a list of historical events from the last week that include the word 'badserver' .NOTES Version 1.0 #> Param ( [parameter(Mandatory=$False)] [ValidateSet('LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM', IgnoreCase=$True)] [string]$TimePeriod = 'LAST_HOUR', [parameter(Mandatory=$False)] [Alias("StartDateFrom")] [string]$TimePeriodFrom, [parameter(Mandatory=$False)] [Alias("StartDateTo")] [string]$TimePeriodTo, [parameter(Mandatory=$False)] [string]$LastTimeAfter, [parameter(Mandatory=$False)] [ValidateSet('CRITICAL', 'MAJOR', 'MINOR', 'WARNING', 'GOOD', 'NO_ACTIVITY', IgnoreCase=$True)] [string[]]$EventAlertLevels, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("SiteName")] [string[]]$Locations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$SearchQuery, [parameter(Mandatory=$False)] [ValidateSet('id', 'type', 'lastTime', 'displayName', 'deviceName', 'description', 'eventId', 'time', 'delay', 'source', 'location', 'sourceId', IgnoreCase=$True)] [string]$OrderByField, [parameter(Mandatory=$False)] [ValidateSet('asc', 'desc', IgnoreCase=$True)] [string]$OrderDirection, [parameter(Mandatory=$False)] [ValidateSet('Current', 'Historic', IgnoreCase=$True)] [string]$EventState = 'Current', [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$PageSize = 1000, [Parameter(Mandatory=$False)] [ValidateRange(1,9999999)] [int]$ResultSize ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $Params = @{ 'TimePeriod' = $TimePeriod } # Convert any PowerShell array objects to comma-separated strings to add to the GET querystring If ($LastTimeAfter) { $Params.Add('LastTimeAfter',$LastTimeAfter) } If ($EventAlertLevels) { $EventAlertLevels | %{$EventAlertLevelsStr += ($(if($EventAlertLevelsStr){","}) + $_)}; $Params.Add('EventAlertLevels',$EventAlertLevelsStr) } If ($Locations) { $Locations | %{$LocationsStr += ($(if($LocationsStr){","}) + $_)}; $Params.Add('Locations',$LocationsStr) } If ($SearchQuery) { $Params.Add('q',$SearchQuery) } If ($OrderByField) { $Params.Add('OrderByField',$OrderByField) } If ($OrderDirection) { $Params.Add('OrderDirection',$OrderDirection) } If ($TenantName) { $Params.Add('Tenant',$TenantName) } # Convert date to UNIX timestamp If($TimePeriodFrom) { $TimePeriodFrom = (Get-Date -Date $TimePeriodFrom -UFormat %s) + '000' $Params.Add('StartDateFrom',$TimePeriodFrom) } If($TimePeriodTo) { $TimePeriodTo = (Get-Date -Date $TimePeriodTo -UFormat %s) + '000' $Params.Add('StartDateTo',$TimePeriodTo) } # Set the page size to the result size if -ResultSize switch is used to limit the number of returned items # Otherwise, set page size (defaults to 1000) If ($ResultSize) { $Params.Add('pageSize',$ResultSize) } Else { $Params.Add('pageSize',$PageSize) } If ($EventState -eq 'Current') { $URI = "https://$Global:NectarCloud/dapi/event/current" } Else { $URI = "https://$Global:NectarCloud/dapi/event/historic" } Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -WebSession $Global:NectarSession -uri $URI -Body $Params $TotalPages = $JSON.totalPages If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} $JSON.elements | Add-Member -TypeName 'Nectar.EventList' $JSON.elements If ($TotalPages -gt 1 -and !($ResultSize)) { $PageNum = 2 Write-Verbose "Page size: $PageSize" While ($PageNum -le $TotalPages) { Write-Verbose "Working on page $PageNum of $TotalPages" $PagedURI = $URI + "?pageNumber=$PageNum" $JSON = Invoke-RestMethod -Method GET -uri $PagedURI -Body $Params -WebSession $Global:NectarSession If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} $JSON.elements | Add-Member -TypeName 'Nectar.SessionList' $JSON.elements $PageNum++ } } } Catch { Write-Error "No results. Try specifying a less-restrictive filter" Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarEventDetail { <# .SYNOPSIS Return information about a specific event .DESCRIPTION Return information about a specific event .PARAMETER EventID The ID of the event to return details about .PARAMETER EventState Return either current events or previously acknowledged events .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-MSTeamsCallRecord -CallRecordID ed672235-5417-40ce-8425-12b8b702a505 .NOTES Version 1.0 #> Param ( [parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$ID, [parameter(Mandatory=$False)] [ValidateSet('Current', 'Historic', IgnoreCase=$True)] [string]$EventState = 'Current', [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $Params = @{ 'eventId' = $ID } If ($TenantName) { $Params.Add('Tenant',$TenantName) } If ($EventState -eq 'Current') { $URI = "https://$Global:NectarCloud/dapi/event/current/view" } Else { $URI = "https://$Global:NectarCloud/dapi/event/historic/view" } Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $URI -Body $Params If ($TenantName) {$JSON | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} $JSON | Add-Member -TypeName 'Nectar.EventDetail' $JSON } Catch { Write-Error "Could not find event with ID $EventID" Get-JSONErrorStream -JSONResponse $_ } } } ################################################################################################################################################# # # # Platform Functions # # # ################################################################################################################################################# Function Get-NectarPlatformItems { <# .SYNOPSIS Return information about all the platforms installed .DESCRIPTION Return information about all the platforms installed .PARAMETER Platform Show information about selected platform. Choose one or more from: 'AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','CISCO','CISCO_CMS','CISCO_VKM','SKYPE','SKYPE_ONLINE','TEAMS' .PARAMETER TimePeriod The time period to show event data from. Select from 'LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM'. CUSTOM requires using TimePeriodFrom and TimePeriodTo parameters. .PARAMETER TimePeriodFrom The earliest date/time to show event data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodTo parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. Use date-time format as in 2020-04-20T17:46:37.554 .PARAMETER TimePeriodTo The latest date/time to show event data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodFrom parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. Use date-time format as in 2020-04-20T17:46:37.554 .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER PageSize The size of the page used to return data. Defaults to 1000 .PARAMETER ResultSize The total number of results to return. Defaults to 1000. Maximum result size is 9,999,999 results .EXAMPLE Get-NectarPlatformItems -Platform CISCO .NOTES Version 1.0 #> Param ( [parameter(Mandatory=$True)] [ValidateSet('AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string]$Platform, [parameter(Mandatory=$False)] [ValidateSet('LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM', IgnoreCase=$True)] [string]$TimePeriod = 'LAST_HOUR', [parameter(Mandatory=$False)] [Alias("StartDateFrom")] [string]$TimePeriodFrom, [parameter(Mandatory=$False)] [Alias("StartDateTo")] [string]$TimePeriodTo, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$PageSize = 1000, [Parameter(Mandatory=$False)] [ValidateRange(1,9999999)] [int]$ResultSize ) Begin { Connect-NectarCloud } Process { $Params = @{ 'TimePeriod' = $TimePeriod 'Platform' = $Platform } # Convert date to UNIX timestamp If($TimePeriodFrom) { $TimePeriodFrom = (Get-Date -Date $TimePeriodFrom -UFormat %s) + '000' $Params.Add('StartDateFrom',$TimePeriodFrom) } If($TimePeriodTo) { $TimePeriodTo = (Get-Date -Date $TimePeriodTo -UFormat %s) + '000' $Params.Add('StartDateTo',$TimePeriodTo) } Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($TenantName) { $Params.Add('Tenant',$TenantName) } $URI = "https://$Global:NectarCloud/dapi/platform/clusters" Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $URI -Body $Params If ($TenantName) {$JSON | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} $JSON.elements } Catch { Write-Error "Could not get platform items" Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarPlatformItemSummary { <# .SYNOPSIS Return summary information about a specific platform item .DESCRIPTION Return summary information about a specific platform item .PARAMETER ClusterID The ID of a cluster to return summary information .PARAMETER Platform Show information about selected platform. Choose one or more from: 'AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','CISCO','CISCO_CMS','CISCO_VKM','SKYPE','SKYPE_ONLINE','TEAMS' .PARAMETER Source Show information about either events, current status or both .PARAMETER TimePeriod The time period to show event data from. Select from 'LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM'. CUSTOM requires using TimePeriodFrom and TimePeriodTo parameters. .PARAMETER TimePeriodFrom The earliest date/time to show event data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodTo parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. Use date-time format as in 2020-04-20T17:46:37.554 .PARAMETER TimePeriodTo The latest date/time to show event data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodFrom parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. Use date-time format as in 2020-04-20T17:46:37.554 .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER PageSize The size of the page used to return data. Defaults to 1000 .PARAMETER ResultSize The total number of results to return. Defaults to 1000. Maximum result size is 9,999,999 results .EXAMPLE Get-NectarPlatformItemSummary -Platform CISCO -ClusterID 3_1 .NOTES Version 1.0 #> Param ( [parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$ClusterID, [parameter(Mandatory=$True)] [ValidateSet('AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string]$Platform, [parameter(Mandatory=$False)] [ValidateSet('Events','Current','All', IgnoreCase=$True)] [string]$Source = 'All', [parameter(Mandatory=$False)] [ValidateSet('LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM', IgnoreCase=$True)] [string]$TimePeriod = 'LAST_HOUR', [parameter(Mandatory=$False)] [Alias("StartDateFrom")] [string]$TimePeriodFrom, [parameter(Mandatory=$False)] [Alias("StartDateTo")] [string]$TimePeriodTo, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$PageSize = 1000, [Parameter(Mandatory=$False)] [ValidateRange(1,9999999)] [int]$ResultSize ) Begin { Connect-NectarCloud } Process { $Params = @{ 'TimePeriod' = $TimePeriod 'Platform' = $Platform } # Convert date to UNIX timestamp If($TimePeriodFrom) { $TimePeriodFrom = (Get-Date -Date $TimePeriodFrom -UFormat %s) + '000' $Params.Add('StartDateFrom',$TimePeriodFrom) } If($TimePeriodTo) { $TimePeriodTo = (Get-Date -Date $TimePeriodTo -UFormat %s) + '000' $Params.Add('StartDateTo',$TimePeriodTo) } Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($TenantName) { $Params.Add('Tenant',$TenantName) } $URI = "https://$Global:NectarCloud/dapi/platform/cluster/$ClusterID/summary" Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $URI -Body $Params If ($TenantName) {$JSON | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} If ($Source -ne 'All') { $JSON.$Source } Else { $JSON } } Catch { Write-Error "Could not get platform item summary" Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarPlatformItemResources { <# .SYNOPSIS Return resource information about a specific platform item .DESCRIPTION Return resource information about a specific platform item .PARAMETER ClusterID The ID of a cluster to return resource information .PARAMETER Platform Show information about selected platform. Choose one or more from: 'AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','CISCO','CISCO_CMS','CISCO_VKM','SKYPE','SKYPE_ONLINE','TEAMS' .PARAMETER TimePeriod The time period to show event data from. Select from 'LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM'. CUSTOM requires using TimePeriodFrom and TimePeriodTo parameters. .PARAMETER TimePeriodFrom The earliest date/time to show event data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodTo parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. Use date-time format as in 2020-04-20T17:46:37.554 .PARAMETER TimePeriodTo The latest date/time to show event data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodFrom parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. Use date-time format as in 2020-04-20T17:46:37.554 .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER PageSize The size of the page used to return data. Defaults to 1000 .PARAMETER ResultSize The total number of results to return. Defaults to 1000. Maximum result size is 9,999,999 results .EXAMPLE Get-NectarPlatformItemResources -Platform CISCO -ClusterID 3_1 .NOTES Version 1.0 #> Param ( [parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$ClusterID, [parameter(Mandatory=$True)] [ValidateSet('AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string]$Platform, [parameter(Mandatory=$False)] [ValidateSet('LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM', IgnoreCase=$True)] [string]$TimePeriod = 'LAST_HOUR', [parameter(Mandatory=$False)] [Alias("StartDateFrom")] [string]$TimePeriodFrom, [parameter(Mandatory=$False)] [Alias("StartDateTo")] [string]$TimePeriodTo, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$PageSize = 1000, [Parameter(Mandatory=$False)] [ValidateRange(1,9999999)] [int]$ResultSize ) Begin { Connect-NectarCloud } Process { $Params = @{ 'TimePeriod' = $TimePeriod 'Platform' = $Platform } # Convert date to UNIX timestamp If($TimePeriodFrom) { $TimePeriodFrom = (Get-Date -Date $TimePeriodFrom -UFormat %s) + '000' $Params.Add('StartDateFrom',$TimePeriodFrom) } If($TimePeriodTo) { $TimePeriodTo = (Get-Date -Date $TimePeriodTo -UFormat %s) + '000' $Params.Add('StartDateTo',$TimePeriodTo) } Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($TenantName) { $Params.Add('Tenant',$TenantName) } $URI = "https://$Global:NectarCloud/dapi/platform/cluster/$ClusterID/resources" Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $URI -Body $Params If ($TenantName) {$JSON | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} $JSON } Catch { Write-Error "Could not get platform item server resources" Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarPlatformItemServers { <# .SYNOPSIS Return information about a specific platform item's servers .DESCRIPTION Return information about a specific platform item's servers .PARAMETER ClusterID The ID of a cluster to return server information .PARAMETER Platform Show information about selected platform. Choose one or more from: 'AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','CISCO','CISCO_CMS','CISCO_VKM','SKYPE','SKYPE_ONLINE','TEAMS' .PARAMETER Type Show information about either publishers, subscribers or both .PARAMETER TimePeriod The time period to show event data from. Select from 'LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM'. CUSTOM requires using TimePeriodFrom and TimePeriodTo parameters. .PARAMETER TimePeriodFrom The earliest date/time to show event data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodTo parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. Use date-time format as in 2020-04-20T17:46:37.554 .PARAMETER TimePeriodTo The latest date/time to show event data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodFrom parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. Use date-time format as in 2020-04-20T17:46:37.554 .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER PageSize The size of the page used to return data. Defaults to 1000 .PARAMETER ResultSize The total number of results to return. Defaults to 1000. Maximum result size is 9,999,999 results .EXAMPLE Get-NectarPlatformItemServers -Platform CISCO -ClusterID 3_1 .NOTES Version 1.0 #> Param ( [parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$ClusterID, [parameter(Mandatory=$True)] [ValidateSet('AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$True)] [string]$Platform, [parameter(Mandatory=$False)] [ValidateSet('Publisher','Subscribers','All', IgnoreCase=$True)] [string]$Type = 'All', [parameter(Mandatory=$False)] [ValidateSet('LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM', IgnoreCase=$True)] [string]$TimePeriod = 'LAST_HOUR', [parameter(Mandatory=$False)] [Alias("StartDateFrom")] [string]$TimePeriodFrom, [parameter(Mandatory=$False)] [Alias("StartDateTo")] [string]$TimePeriodTo, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$PageSize = 1000, [Parameter(Mandatory=$False)] [ValidateRange(1,9999999)] [int]$ResultSize ) Begin { Connect-NectarCloud } Process { $Params = @{ 'TimePeriod' = $TimePeriod 'Platform' = $Platform } # Convert date to UNIX timestamp If($TimePeriodFrom) { $TimePeriodFrom = (Get-Date -Date $TimePeriodFrom -UFormat %s) + '000' $Params.Add('StartDateFrom',$TimePeriodFrom) } If($TimePeriodTo) { $TimePeriodTo = (Get-Date -Date $TimePeriodTo -UFormat %s) + '000' $Params.Add('StartDateTo',$TimePeriodTo) } Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($TenantName) { $Params.Add('Tenant',$TenantName) } $URI = "https://$Global:NectarCloud/dapi/platform/cluster/$ClusterID/servers" Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $URI -Body $Params If ($TenantName) {$JSON | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} If ($Type -ne 'All') { $JSON.$Type } Else { $JSON } } Catch { Write-Error "Could not get platform item servers" Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarPlatformServerServices { <# .SYNOPSIS Return service information about a specific platform item .DESCRIPTION Return service information about a specific platform item .PARAMETER ClusterID The ID of a cluster to return service information .PARAMETER ServerID The ID of a server within a cluster to return service information .PARAMETER Platform Show information about selected platform. Choose one or more from: 'AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','CISCO','CISCO_CMS','CISCO_VKM','SKYPE','SKYPE_ONLINE','TEAMS' .PARAMETER TimePeriod The time period to show event data from. Select from 'LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM'. CUSTOM requires using TimePeriodFrom and TimePeriodTo parameters. .PARAMETER TimePeriodFrom The earliest date/time to show event data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodTo parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. Use date-time format as in 2020-04-20T17:46:37.554 .PARAMETER TimePeriodTo The latest date/time to show event data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodFrom parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. Use date-time format as in 2020-04-20T17:46:37.554 .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER PageSize The size of the page used to return data. Defaults to 1000 .PARAMETER ResultSize The total number of results to return. Defaults to 1000. Maximum result size is 9,999,999 results .EXAMPLE Get-NectarPlatformServerServices -Platform CISCO -ClusterID 3_1 -ServerID 1 .NOTES Version 1.0 #> Param ( [parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("PlatformItemId")] [string]$ClusterID, [parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("Id")] [string]$ServerID, [parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [ValidateSet('EDGE','FE','MEDIATION','GATEWAY','PUBLISHER','SUBSCRIBER','CUBE','IM_PRESENCE','VG224','CUCM','CMS','HW_CFB','CUCM_SW_CFB', IgnoreCase=$False)] [string]$Type, [parameter(Mandatory=$True)] [ValidateSet('AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','SKYPE','CISCO','CISCO_CMS','CISCO_VKM','TEAMS','SKYPE_ONLINE','CISCO_CMS_VKM','AVAYA_AURA_CM','RIG','LYNC_VKM','SKYPE_FOR_BUSINESS_VKM','CISCO_UNITY','CISCO_EXPRESSWAY','AVAYA_MEDIA_GATEWAY','AVAYA_SESSION_MANAGER','AVAYA_VOICE_PORTAL','ZOOM','ENDPOINT_CLIENT','MISCELLANEOUS', IgnoreCase=$False)] [string]$Platform, [parameter(Mandatory=$False)] [ValidateSet('LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM', IgnoreCase=$False)] [string]$TimePeriod = 'LAST_HOUR', [parameter(Mandatory=$False)] [Alias("StartDateFrom")] [string]$TimePeriodFrom, [parameter(Mandatory=$False)] [Alias("StartDateTo")] [string]$TimePeriodTo, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$PageSize = 1000, [Parameter(Mandatory=$False)] [ValidateRange(1,9999999)] [int]$ResultSize ) Begin { Connect-NectarCloud } Process { $Params = @{ 'TimePeriod' = $TimePeriod 'Platform' = $Platform 'type' = $Type } # Convert date to UNIX timestamp If($TimePeriodFrom) { $TimePeriodFrom = (Get-Date -Date $TimePeriodFrom -UFormat %s) + '000' $Params.Add('StartDateFrom',$TimePeriodFrom) } If($TimePeriodTo) { $TimePeriodTo = (Get-Date -Date $TimePeriodTo -UFormat %s) + '000' $Params.Add('StartDateTo',$TimePeriodTo) } Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($TenantName) { $Params.Add('Tenant',$TenantName) } $URI = "https://$Global:NectarCloud/dapi/platform/cluster/$ClusterID/server/$ServerID/services" Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $URI -Body $Params If ($TenantName) {$JSON | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} $JSON } Catch { Write-Error "Could not get platform item server services" Get-JSONErrorStream -JSONResponse $_ } } } ################################################################################################################################################# # # # Endpoint Client Functions # # # ################################################################################################################################################# Function Get-NectarEndpoint { <# .SYNOPSIS Returns a list of Nectar 10 endpoints .DESCRIPTION Returns a list of Nectar 10 endpoints .PARAMETER SearchQuery A string to search for. Will search for match against all fields .PARAMETER OrderByField Sort the output by the selected field .PARAMETER OrderDirection Sort ordered output in ascending or descending order .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER PageSize The size of the page used to return data. Defaults to 1000 .PARAMETER ResultSize The total number of results to return. Defaults to 100000. Maximum result size is 9,999,999 results .EXAMPLE Get-NectarEndpoint Returns the first 100000 endpoints .EXAMPLE Get-NectarEndpoint -ResultSize 100 Returns the first 100 endpoints .EXAMPLE Get-NectarEndpoint -SearchQuery US Returns all endpoints that have US in any of the data fields .NOTES Version 1.0 #> [Alias("gne")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$SearchQuery, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateSet('id','uuid','name','ipAddress','isHub','mapped','userName','userDisplayName','location', IgnoreCase=$False)] [string]$OrderByField = 'id', [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateSet('asc','desc', IgnoreCase=$False)] [string]$OrderDirection = 'asc', [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$PageSize = 1000, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize = 100000 ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $URI = "https://$Global:NectarCloud/aapi/testing/entities/" $Params = @{ 'orderByField' = $OrderByField 'orderDirection' = $OrderDirection } If ($SearchQuery) { $Params.Add('searchQuery', $SearchQuery) } If ($ResultSize) { $Params.Add('pageSize', $ResultSize) } Else { $Params.Add('pageSize', $PageSize) } If ($TenantName) { $Params.Add('tenant', $TenantName) } $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $URI -Body $Params If ($TenantName) { $JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty } # Add the tenant name to the output which helps pipelining $JSON.elements $TotalPages = $JSON.totalPages If ($TotalPages -gt 1 -and !($ResultSize)) { $PageNum = 2 Write-Verbose "Page size: $PageSize" While ($PageNum -le $TotalPages) { Write-Verbose "Working on page $PageNum of $TotalPages" $PagedURI = $URI + "?pageNumber=$PageNum" $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $PagedURI -Body $Params If ($TenantName) { $JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty } $JSON.elements $PageNum++ } } } Catch { Write-Error "Unable to retrieve endpoint information" Get-JSONErrorStream -JSONResponse $_ } } } Function Set-NectarEndpoint { <# .SYNOPSIS Modify properties of a Nectar 10 endpoint .DESCRIPTION Modify properties of a Nectar 10 endpoint .PARAMETER SearchQuery A string to search for to return a specific endpoint. Will search for match against all fields .PARAMETER UserName The username to associate with the given endpoint. Set to $NULL to de-associate a user with an endpoint .PARAMETER IsHub Set the endpoint to be a hub endpoint or not. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Set-NectarEndpoint -SearchQuery ff4bf84a-04d8-11ec-ad2e-17d4f8d1df89 -UserName tferguson@contoso.com -IsHub $False Sets a specific endpoint's associated username to 'tferguson' .NOTES Version 1.0 #> [Alias("gne")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias('UUID')] [string]$SearchQuery, [Parameter(Mandatory=$False)] [string]$UserName, [Parameter(Mandatory=$False)] [string]$DisplayName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [bool]$IsHub, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$PageSize = 1000, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize = 100000 ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $ECSearchURI = "https://$Global:NectarCloud/aapi/testing/entities/" $ECUpdateURI = "https://$Global:NectarCloud/aapi/testing/entity/" $UserNameURI = "https://$Global:NectarCloud/aapi/testing/entities/usernames/" Try { # Search for a single valid endpoint. Throw error if zero or more than 1 endpoint returned $ECSearchParams = @{ 'searchQuery' = $SearchQuery } If ($TenantName) { $ECSearchParams.Add('tenant', $TenantName) } $ECSearchJSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $ECSearchURI -Body $ECSearchParams If ($ECSearchJSON.totalElements -gt 1) { Write-Error 'Too many endpoints returned. Please refine your search query to return only a single endpoint.' Return } ElseIf ($ECSearchJSON.totalElements -eq 0) { Write-Error "Could not find endpoint $SearchQuery" Return } $EndpointDetails = $ECSearchJSON.elements # Search for a single valid username. Throw error if zero or more than 1 username returned If ($UserName) { $UserNameParams = @{ 'searchQuery' = $UserName } If ($TenantName) { $UserNameParams.Add('tenant', $TenantName) } $UserSearchJSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $UserNameURI -Body $UserNameParams If ($UserSearchJSON.Count -gt 1) { Write-Error 'Too many usernames returned. Please refine your search query to return only a single username.' Return } ElseIf ($UserSearchJSON.Count -eq 0) { Write-Error "Could not find user with name $UserName" Return } $UserName = $UserSearchJSON.UserName } If ($PSBoundParameters.Keys.Contains('UserName')) { If ($UserName) { $EndpointDetails[0].userName = $UserName } Else { $EndpointDetails[0].userName = $NULL } } If ($DisplayName) { $EndpointDetails[0].userDisplayName = $DisplayName } If ($PSBoundParameters.Keys.Contains('IsHub')) { $EndpointDetails[0].IsHub = $IsHub $EndpointDetails[0].userName = $NULL } $EndpointJSON = $EndpointDetails | ConvertTo-Json Write-Verbose $EndpointJSON If ($TenantName) { $ECUpdateURI = $ECUpdateURI + "?tenant=$TenantName" } $EndpointUpdate = Invoke-RestMethod -Method PUT -Credential $Global:NectarCred -uri $ECUpdateURI -Body $EndpointJSON -ContentType 'application/json; charset=utf-8' } Catch { Write-Error "Unable to set endpoint information" Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarEndpointTests { <# .SYNOPSIS Returns information about Endpoint Client tests .DESCRIPTION Returns information about Endpoint Client tests UI_ELEMENT .PARAMETER TimePeriod The time period to show session data from. Select from 'LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM'. CUSTOM requires using TimePeriodFrom and TimePeriodTo parameters. .PARAMETER TimePeriodFrom The earliest date/time to show session data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodTo parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. .PARAMETER TimePeriodTo The latest date/time to show session data from. Must be used in conjunction with -TimePeriod CUSTOM and TimePeriodFrom parameters. Use format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. All time/dates in UTC. .PARAMETER TestTypes The types of EPC tests to return. Choose from one or more of 'P2P', 'PING', 'AUDIO', or 'VIDEO' .PARAMETER TestResults The result type to return. Choose from one or more of 'PASSED','FAILED', or 'UNKNOWN' .PARAMETER ResponseCodes Show tests that match one or more SIP response codes. Accepts numbers from 0 to 699 .PARAMETER Locations Show sessions where the selected location was used by either caller or callee. Can query for multiple locations. .PARAMETER CallerLocations Show sessions where the selected location was used by the caller. Can query for multiple locations. .PARAMETER CalleeLocations Show sessions where the selected location was used by the callee. Can query for multiple locations. .PARAMETER ExtCities Show sessions where the caller or callee was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER CallerExtCities Show sessions where the caller was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER CalleeExtCities Show sessions where the callee was located in the selected city (as detected via geolocating the user's external IP address). Can query for multiple cities. .PARAMETER ExtCountries Show sessions where the caller or callee was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER CallerExtCountries Show sessions where the caller was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER CalleeExtCountries Show sessions where the callee was located in the selected country (as detected via geolocating the user's external IP address). Can query for multiple countries. .PARAMETER ExtISPs Show sessions where the caller or callee was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER CallerExtISPs Show sessions where the caller was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER CalleeExtISPs Show sessions where the callee was located in the selected ISP (as detected via geolocating the user's external IP address). Can query for multiple ISPs. .PARAMETER Users Show sessions where the selected user was either caller or callee. Can query for multiple users. .PARAMETER FromUsers Show sessions where the selected user was the caller. Can query for multiple users. .PARAMETER ToUsers Show sessions where the selected user was the callee. Can query for multiple users. .PARAMETER OrderByField Sort the output by the selected field .PARAMETER OrderDirection Sort direction. Use with OrderByField. Not case sensitive. Choose from: ASC, DESC .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER PageSize The size of the page used to return data. Defaults to 1000 .PARAMETER ResultSize The total number of results to return. Defaults to 1000. Maximum result size is 9,999,999 results .EXAMPLE Get-NectarSessions -TimePeriod LAST_HOUR -Platforms TEAMS -Modalities AUDIO -SessionQualities POOR Returns a list of all Teams audio sessions for the past hour where the quality was rated Poor .EXAMPLE (Get-NectarSessions -SessionTypes CONFERENCE -TimePeriod CUSTOM -TimePeriodFrom '2021-05-06' -TimePeriodTo '2021-05-07').Count Returns a count of all conferences between May 6 and 7 (all times/dates UTC) .EXAMPLE Get-NectarSessions -SessionTypes PEER2PEER,PEER2PEER_MULTIMEDIA -TimePeriod CUSTOM -TimePeriodFrom '2021-05-06 14:00' -TimePeriodTo '2021-05-06 15:00' Returns a list of all P2P calls between 14:00 and 15:00 UTC on May 6 .EXAMPLE Get-NectarSessions -TimePeriod LAST_WEEK -SessionTypes CONFERENCE | Select-Object confOrganizerOrSpace | Group-Object confOrganizerOrSpace | Select-Object Name, Count | Sort-Object Count -Descending Returns a list of conference organizers and a count of the total conferences organized by each, sorted by count. .NOTES Version 1.0 #> [CmdletBinding(PositionalBinding=$False)] Param ( [parameter(Mandatory=$False)] [ValidateSet('LAST_HOUR','LAST_DAY','LAST_WEEK','LAST_MONTH','CUSTOM', IgnoreCase=$True)] [string]$TimePeriod = 'LAST_HOUR', [parameter(Mandatory=$False)] [Alias("StartDateFrom")] [string]$TimePeriodFrom, [parameter(Mandatory=$False)] [Alias("StartDateTo")] [string]$TimePeriodTo, [parameter(Mandatory=$False)] [ValidateSet('P2P','PING','AUDIO','VIDEO', IgnoreCase=$False)] [string[]]$TestTypes, [parameter(Mandatory=$False)] [ValidateSet('PASSED','FAILED','UNKNOWN', IgnoreCase=$False)] [string[]]$TestResults, [Parameter(Mandatory=$False)] [ValidateRange(0,699)] [string[]]$ResponseCodes, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Users, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$FromUsers, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ToUsers, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$Locations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerLocations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeLocations, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtCities, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtCountries, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$ExtISPs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CallerExtISPs, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string[]]$CalleeExtISPs, [parameter(Mandatory=$False)] [string]$OrderByField, [parameter(Mandatory=$False)] [ValidateSet('ASC','DESC', IgnoreCase=$True)] [string]$OrderDirection, [parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,10000)] [int]$PageSize = 1000, [Parameter(Mandatory=$False)] [ValidateRange(1,50000)] [int]$ResultSize ) Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($PageSize) { $PSBoundParameters.Remove('PageSize') | Out-Null } If ($ResultSize) { $PSBoundParameters.Remove('ResultSize') | Out-Null } If ($OrderByField) { $PSBoundParameters.Remove('OrderByField') | Out-Null } If ($OrderDirection) { $PSBoundParameters.Remove('OrderDirection') | Out-Null } $FilterSession = Set-NectarFilterParams @PsBoundParameters -Platform ENDPOINT_CLIENT -Scope ENDPOINT_CLIENT $URI = "https://$Global:NectarCloud/dapi/testing/results" # Set the page size to the result size if -ResultSize switch is used to limit the number of returned items # Otherwise, set page size (defaults to 1000) $Params = @{ 'Scope' = 'ENDPOINT_CLIENT' 'Platform' = 'ENDPOINT_CLIENT' } If ($ResultSize) { $Params.Add( 'pageSize',$ResultSize) } Else { $Params.Add('pageSize',$PageSize) } If($OrderByField) { $Params.Add('orderByField',$OrderByField) } If($OrderDirection) { $Params.Add('orderDirection',$OrderDirection) } If ($TenantName) { $Params.Add('tenant',$TenantName) } # Return results in pages Try { Write-Verbose $URI foreach($k in $Params.Keys){Write-Verbose "$k $($Params[$k])"} $JSON = Invoke-RestMethod -Method GET -uri $URI -Body $Params -WebSession $FilterSession $TotalPages = $JSON.totalPages If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} $JSON.elements | Add-Member -TypeName 'Nectar.SessionList' $JSON.elements If ($TotalPages -gt 1 -and !($ResultSize)) { $PageNum = 2 Write-Verbose "Page size: $PageSize" While ($PageNum -le $TotalPages) { Write-Verbose "Working on page $PageNum of $TotalPages" $PagedURI = $URI + "?pageNumber=$PageNum" $JSON = Invoke-RestMethod -Method GET -uri $PagedURI -Body $Params -WebSession $FilterSession If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} $JSON.elements $PageNum++ } } } Catch { Write-Error "No results. Try specifying a less-restrictive filter" Get-JSONErrorStream -JSONResponse $_ } } } ################################################################################################################################################# # # # MS Teams Functions # # # ################################################################################################################################################# Function Get-MSGraphAccessToken { <# .SYNOPSIS Get a Microsoft Graph access token for a given MS tenant. Needed to run other Graph API queries. .DESCRIPTION Get a Microsoft Graph access token for a given MS tenant. Needed to run other Graph API queries. .PARAMETER MSClientID The MS client ID for the application granted access to Azure AD. .PARAMETER MSClientSecret The MS client secret for the application granted access to Azure AD. .PARAMETER MSTenantID The MS tenant ID for the O365 customer granted access to Azure AD. .PARAMETER CertFriendlyName The friendly name of an installed certificate to be used for certificate authentication. Can be used instead of MSClientSecret .PARAMETER CertThumbprint The thumbprint of an installed certificate to be used for certificate authentication. Can be used instead of MSClientSecret .PARAMETER CertPath The path to a PFX certificate to be used for certificate authentication. Can be used instead of MSClientSecret .PARAMETER CertStore The certificate store to be used for certificate authentication. Select either LocalMachine or CurrentUser. Used in conjunction with CertThumbprint or CertFriendlyName Can be used instead of MSClientSecret. .EXAMPLE $AuthToken = Get-MSGraphAccessToken -MSClientID 41a228ad-db6c-4e4e-4184-6d8a1175a35f -MSClientSecret 43Rk5Xl3K349w-pFf0i_Rt45Qd~ArqkE32. -MSTenantID 17e1e614-8119-48ab-8ba1-6ff1d94a6930 Obtains an authtoken for the given tenant using secret-based auth and saves the results for use in other commands in a variable called $AuthToken .EXAMPLE $AuthToken = Get-MSGraphAccessToken -MSClientID 029834092-234234-234234-23442343 -MSTenantID 234234234-234234-234-23-42342342 -CertFriendlyName 'CertAuth' -CertStore LocalMachine Obtains an authtoken for the given tenant using certificate auth and saves the results for use in other commands in a variable called $AuthToken .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$MSClientID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientSecret, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$MSTenantID, [Parameter(Mandatory=$False)] [switch]$N10Cert, [Parameter(Mandatory=$False)] [string]$CertFriendlyName, [Parameter(Mandatory=$False)] [string]$CertThumbprint, [Parameter(Mandatory=$False)] [string]$CertPath, [Parameter(Mandatory=$False)] [ValidateSet('LocalMachine','CurrentUser', IgnoreCase=$True)] [string]$CertStore = 'CurrentUser' ) Begin { $Scope = 'https://graph.microsoft.com/.default' } Process { If ($MSClientSecret) { Try { # Get the Azure Graph API auth token $AuthBody = @{ grant_type = 'client_credentials' client_id = $MSClientID client_secret = $MSClientSecret scope = $Scope } $JSON_Auth = Invoke-RestMethod -Method POST -uri "https://login.microsoftonline.com/$MSTenantID/oauth2/v2.0/token" -Body $AuthBody $AuthToken = $JSON_Auth.access_token Return $AuthToken } Catch { Write-Error "Failed to get access token. Ensure the values for MSTenantID, MSClientID and MSClientSecret are correct." Get-JSONErrorStream -JSONResponse $_ } } Else { # Needs access to the full certificate stored in Nectar 10, and can be exported to PEM via Get-NectarMSTeamsSubscription (only if you have global admin privs) # Get-NectarMSTeamsSubscription -ExportCertificate # Need to create a certificate from the resulting PEM files using the following command: # .\openssl.exe pkcs12 -export -in D:\OneDrive\Nectar\Scripts\TeamsCert.pem -inkey D:\OneDrive\Nectar\Scripts\TeamsPriv.key -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider" -out D:\OneDrive\Nectar\Scripts\FullCert.pfx # Requires that OpenSSL is installed # Then use the resulting certificate to obtain an access token: # $GraphToken = Get-MSGraphAccessToken -MSTenantID <tenantID> -MSClientID <Client/AppID> -CertPath .\FullCert.pfx # Then use the resulting token in other commands like: # Test-MSTeamsConnectivity -AuthToken $GraphToken # Get the certificate information via one of several methods If ($CertThumbprint) { $Certificate = Get-Item Cert:\$CertStore\My\$CertThumbprint } If ($CertFriendlyName) { $Certificate = Get-ChildItem Cert:\$CertStore\My | Where {$_.FriendlyName -eq $CertFriendlyName} } If ($CertPath) { $Certificate = Get-PfxCertificate -FilePath $CertPath } If ($N10Cert) { # Get certificate BASE64 encoding from N10 $CertBlob = (Get-NectarMSTeamsSubscription).msClientCertificateDto.certificate $KeyBlob = (Get-NectarMSTeamsSubscription).msClientCertificateDto.privateKey $CertRaw = $CertBlob -replace "-----BEGIN CERTIFICATE-----", $NULL -replace "-----END CERTIFICATE-----", $NULL $KeyRaw = $KeyBlob -replace "-----BEGIN PRIVATE KEY-----", $NULL -replace "-----END PRIVATE KEY-----", $NULL $CertConv = [Convert]::FromBase64String($CertRaw) $KeyConv = [Convert]::FromBase64String($KeyRaw) $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $Certificate.Import([Convert]::FromBase64String($CertRaw)) } If ($Certificate) { # Adapted from https://adamtheautomator.com/microsoft-graph-api-powershell/ # Create base64 hash of certificate $CertificateBase64Hash = [System.Convert]::ToBase64String($Certificate.GetCertHash()) # Create JWT timestamp for expiration $StartDate = (Get-Date "1970-01-01T00:00:00Z" ).ToUniversalTime() $JWTExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End (Get-Date).ToUniversalTime().AddMinutes(2)).TotalSeconds $JWTExpiration = [math]::Round($JWTExpirationTimeSpan,0) # Create JWT validity start timestamp $NotBeforeExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End ((Get-Date).ToUniversalTime())).TotalSeconds $NotBefore = [math]::Round($NotBeforeExpirationTimeSpan,0) # Create JWT header $JWTHeader = @{ alg = "RS256" typ = "JWT" # Use the CertificateBase64Hash and replace/strip to match web encoding of base64 x5t = $CertificateBase64Hash -replace '\+','-' -replace '/','_' -replace '=' } # Create JWT payload $JWTPayload = @{ # What endpoint is allowed to use this JWT aud = "https://login.microsoftonline.com/$MSTenantID/oauth2/token" # Expiration timestamp exp = $JWTExpiration # Issuer = your application iss = $MSClientID # JWT ID: random guid jti = [guid]::NewGuid() # Not to be used before nbf = $NotBefore # JWT Subject sub = $MSClientID } # Convert header and payload to base64 $JWTHeaderToByte = [System.Text.Encoding]::UTF8.GetBytes(($JWTHeader | ConvertTo-Json)) $EncodedHeader = [System.Convert]::ToBase64String($JWTHeaderToByte) $JWTPayloadToByte = [System.Text.Encoding]::UTF8.GetBytes(($JWTPayload | ConvertTo-Json)) $EncodedPayload = [System.Convert]::ToBase64String($JWTPayloadToByte) # Join header and Payload with "." to create a valid (unsigned) JWT $JWT = $EncodedHeader + "." + $EncodedPayload # Get the private key object of your certificate $PrivateKey = $Certificate.PrivateKey # Define RSA signature and hashing algorithm $RSAPadding = [Security.Cryptography.RSASignaturePadding]::Pkcs1 $HashAlgorithm = [Security.Cryptography.HashAlgorithmName]::SHA256 # Create a signature of the JWT # Breaks down here $Signature = [Convert]::ToBase64String($PrivateKey.SignData([System.Text.Encoding]::UTF8.GetBytes($JWT),$HashAlgorithm,$RSAPadding)) -replace '\+','-' -replace '/','_' -replace '=' # Join the signature to the JWT with "." $JWT = $JWT + "." + $Signature # Create a hash with body parameters $Body = @{ client_id = $MSClientID client_assertion = $JWT client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" scope = $Scope grant_type = "client_credentials" } $Uri = "https://login.microsoftonline.com/$MSTenantID/oauth2/v2.0/token" # Use the self-generated JWT as Authorization $Header = @{ Authorization = "Bearer $JWT" } # Splat the parameters for Invoke-Restmethod for cleaner code $PostSplat = @{ ContentType = 'application/x-www-form-urlencoded' Method = 'POST' Body = $Body Uri = $Uri Headers = $Header } $Request = Invoke-RestMethod @PostSplat $AuthToken = $Request.access_token Return $AuthToken } Else { Write-Error "Could not find certificate in $CertStore" } } } } Function Get-MSOfficeAccessToken { <# .SYNOPSIS Get an Office 365 access token for a given MS tenant. Needed to run specific Office365 API queries (not Graph). .DESCRIPTION Get an access token for a given MS tenant. Needed to run specific Office365 API queries (not Graph). .EXAMPLE $MSOAccessToken = Get-MSOfficeAccessToken -MSClientID 41a228ad-db6c-4e4e-4184-6d8a1175a35f -MSClientSecret 43Rk5Xl3K349w-pFf0i_Rt45Qd~ArqkE32. -MSTenantID 17e1e614-8119-48ab-8ba1-6ff1d94a6930 Obtains an MS Office access token using the supplied clientID/secret/tenantID and stores the results for later use. .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$MSClientID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$MSClientSecret, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$MSTenantID ) Process { Try { # Get the Azure Graph API auth token $AuthBody = @{ grant_type = 'client_credentials' client_id = $MSClientID client_secret = $MSClientSecret resource = 'https://manage.office.com' } $JSON_Auth = Invoke-RestMethod -Method POST -uri "https://login.microsoftonline.com/$MSTenantID/oauth2/token?api-version=1.0" -Body $AuthBody $AuthToken = $JSON_Auth.access_token Return $AuthToken } Catch { Write-Error "Failed to get access token." Get-JSONErrorStream -JSONResponse $_ } } } Function Test-MSTeamsConnectivity { <# .SYNOPSIS Tests if we are able to retrieve Teams call data and Azure AD information from a O365 tenant. .DESCRIPTION Tests if we are able to retrieve Teams call data and Azure AD information from a O365 tenant. .PARAMETER MSClientID The MS client ID for the application granted access to Azure AD. .PARAMETER MSClientSecret The MS client secret for the application granted access to Azure AD. .PARAMETER MSTenantID The MS tenant ID for the O365 customer granted access to Azure AD. .PARAMETER SkipUserCount Skips the user count .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER AuthToken The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken .EXAMPLE Get-NectarMSTeamsSubscription -TenantName contoso | Test-MSAzureADAccess .NOTES Version 1.1 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientSecret, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSTenantID, [Parameter(Mandatory=$False)] [switch]$SkipUserCount, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AuthToken ) Process { If ($MSTenantID) { $AuthToken = Get-MSGraphAccessToken -MSClientID $MSClientID -MSClientSecret $MSClientSecret -MSTenantID $MSTenantID } ElseIf (!$AuthToken) { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken } $Headers = @{ Authorization = "Bearer $AuthToken" } # Test MS Teams call record access Try { $FromDateTime = (Get-Date -Format 'yyyy-MM-dd') $ToDateTime = ((Get-Date).AddDays(+1).ToString('yyyy-MM-dd')) $URI = "https://graph.microsoft.com/beta/communications/callRecords/getDirectRoutingCalls(fromDateTime=$FromDateTime,toDateTime=$ToDateTime)" If ($TenantName) { Write-Host "TenantName: $TenantName - " -NoNewLine } Write-Host 'Teams CR Status: ' -NoNewLine $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers Write-Host 'PASS' -ForegroundColor Green } Catch { Write-Host 'FAIL' -ForegroundColor Red } # Test Azure AD user access Try { $URI = 'https://graph.microsoft.com/v1.0/users' If ($TenantName) { Write-Host "TenantName: $TenantName - " -NoNewLine } Write-Host 'Azure AD User Status: ' -NoNewLine $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers Write-Host 'PASS' -ForegroundColor Green } Catch { Write-Host 'FAIL' -ForegroundColor Red Get-JSONErrorStream -JSONResponse $_ } # Test Azure AD group access Try { $URI = 'https://graph.microsoft.com/v1.0/groups' If ($TenantName) { Write-Host "TenantName: $TenantName - " -NoNewLine } Write-Host 'Azure AD Group Status: ' -NoNewLine $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers Write-Host 'PASS' -ForegroundColor Green } Catch { Write-Host 'FAIL' -ForegroundColor Red } If (!$SkipUserCount) { Get-MSTeamsUserLicenseCount -AuthToken $AuthToken } Clear-Variable AuthToken } } Function Get-MSAzureGraphSubscriptions { <# .SYNOPSIS Return data about the subscriptions for a given Azure tenant. .DESCRIPTION Return data about the subscriptions for a given Azure tenant. .PARAMETER MSClientID The MS client ID for the application granted access to Azure AD. .PARAMETER MSClientSecret The MS client secret for the application granted access to Azure AD. .PARAMETER MSTenantID The MS tenant ID for the O365 customer granted access to Azure AD. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER AuthToken The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken .EXAMPLE Get-MSAzureGraphSubscriptions .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientSecret, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSTenantID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AuthToken ) Process { Try { If ($MSTenantID) { $AuthToken = Get-MSGraphAccessToken -MSClientID $MSClientID -MSClientSecret $MSClientSecret -MSTenantID $MSTenantID } ElseIf (!$AuthToken) { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken } # Test Azure AD access $Headers = @{ Authorization = "Bearer $AuthToken" } $JSON = Invoke-RestMethod -Method GET -uri "https://graph.microsoft.com/v1.0/subscriptions" -Headers $Headers Return $JSON.value } Catch { Write-Error "Could not obtain subscription information." Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarMSTeamsSubscription { <# .SYNOPSIS Return data about the MSTeams subscription for a given tenant. .DESCRIPTION Return data about the MSTeams subscription for a given tenant. Requires a global admin account. Not available to tenant-level admins. .EXAMPLE Get-NectarMSTeamsSubscription .NOTES Version 1.0 #> [Alias("gnmtai","Get-MSTeamsAppInfo")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [switch]$ExportCertificate, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { If (!$TenantName) { $TenantName = Get-NectarDefaultTenantName } $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/aapi/clouddatasources/configuration/msteams/subscription?tenant=$TenantName" If ($ExportCertificate) { Try { $JSON.data.msClientCertificateDto.certificate | Out-File TeamsCert.pem -Encoding ASCII $JSON.data.msClientCertificateDto.privateKey | Out-File TeamsPriv.key -Encoding ASCII Write-Host 'Successfully exported certificate to .\TeamsCert.pem and .\TeamsPriv.key' } Catch { Get-JSONErrorStream -JSONResponse $_ } } Else { Return $JSON.data } } Catch { Write-Error "TenantName: $TenantName - No MS Teams information found, or insufficient permissions." Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarAzureADSubscription { <# .SYNOPSIS Return data about the Azure AD subscription for a given tenant. .DESCRIPTION Return data about the Azure AD subscription for a given tenant. Requires a global admin account. Not available to tenant-level admins. .EXAMPLE Get-NectarAzureADSubscription .NOTES Version 1.0 #> [Alias("gnads")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [switch]$ExportCertificate, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { If (!$TenantName) { $TenantName = Get-NectarDefaultTenantName } $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/aapi/clouddatasources/configuration/azuread/subscription?tenant=$TenantName" If ($ExportCertificate) { Try { $JSON.data.msClientCertificateDto.certificate | Out-File TeamsCert.pem $JSON.data.msClientCertificateDto.privateKey | Out-File TeamsPriv.key Write-Host 'Successfully exported certificate to .\TeamsCert.pem and .\TeamsPriv.key' } Catch { Get-JSONErrorStream -JSONResponse $_ } } Else { Return $JSON.data } } Catch { Write-Error "TenantName: $TenantName - No Azure AD information found, or insufficient permissions." Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarMSTeamsConfig { <# .SYNOPSIS Return information about an existing MS Teams call data integration with Nectar 10 .DESCRIPTION Return information about an existing MS Teams call data integration with Nectar 10. Requires a global admin account. Not available to tenant-level admins. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-NectarMSTeamsConfig .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } # Get the existing MS Teams configurations $TeamsURI = "https://$Global:NectarCloud/aapi/clouddatasources/configuration/msteams/cdr?tenant=$TenantName" $TeamsBody = (Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $TeamsURI).data Return $TeamsBody } } Function Get-NectarMSAzureConfig { <# .SYNOPSIS Return information about an existing MS Azure AD integration with Nectar 10 .DESCRIPTION Return information about an existing MS Azure AD integration with Nectar 10. Requires a global admin account. Not available to tenant-level admins. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-NectarMSAzureConfig .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } # Get the existing MS Teams configurations $AzureURI = "https://$Global:NectarCloud/aapi/clouddatasources/configuration/azuread?tenant=$TenantName" $AzureBody = (Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $AzureURI).data Return $AzureBody } } Function Set-NectarMSTeamsConfig { <# .SYNOPSIS Modify an existing MS Teams call data integration with Nectar 10 .DESCRIPTION Modify an existing MS Teams call data integration with Nectar 10. Requires a global admin account. Not available to tenant-level admins. .PARAMETER MSClientID The MS client ID for the application granted access to Azure AD. .PARAMETER MSClientSecret The MS client secret for the application granted access to Azure AD. Use either this or CertID, but not both. .PARAMETER CertID The Nectar 10 certificate ID. Use either this or MSClientSecret but not both. Obtain the cert ID by running Get-NectarMSTeamsCertificate. .PARAMETER MSTenantID The MS tenant ID for the O365 customer granted access to Azure AD. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Set-NectarMSTeamsConfig -CertID .NOTES Version 1.0 #> [cmdletbinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientSecret, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [int]$CertID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSTenantID ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } # Get the existing MS Teams configurations $TeamsURI = "https://$Global:NectarCloud/aapi/clouddatasources/configuration/msteams/cdr?tenant=$TenantName" $AzureURI = "https://$Global:NectarCloud/aapi/clouddatasources/configuration/azuread?tenant=$TenantName" $TeamsBody = (Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $TeamsURI).data[0] $AzureBody = (Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $AzureURI).data[0] # Build body for updating If (!$MSTenantID) { $MSTenantID = $TeamsBody.MSTenantID } If (!$MSClientID) { $MSClientID = $TeamsBody.msClientID } If (!$MSClientSecret -and !$CertID) { $MSClientSecret = $TeamsBody.msClientSecret; $CertID = $TeamsBody.msClientCertificateId } $TeamsUpdateBody = @{ tenant = $TenantName cloudAgentName = 'cloudconnector_agent' displayName = $TeamsBody.displayName msTenantId = $MSTenantID msClientId = $MSClientID msClientSecret = $MSClientSecret msClientCertificateId = $CertID kafkaTopic = $TeamsBody.kafkaTopic sourceId = 1 } $AzureUpdateBody = @{ tenant = $TenantName cloudAgentName = 'cloudconnector_agent' displayName = $AzureBody.displayName msTenantId = $MSTenantID msClientId = $MSClientID msClientSecret = $MSClientSecret msClientCertificateId = $CertID kafkaTopic = $AzureBody.kafkaTopic sourceId = 1 primaryAD = $AzureBody.isPrimary } If ($CertID -and !$MSClientSecret) { $TeamsUpdateBody.msClientSecret = $NULL $AzureUpdateBody.msClientSecret = $NULL } ElseIf ($MSClientSecret -and !$CertID) { $TeamsUpdateBody.msClientCertificateId = $NULL $AzureUpdateBody.msClientCertificateId = $NULL } $TeamsJSONBody = $TeamsUpdateBody | ConvertTo-Json $AzureJSONBody = $AzureUpdateBody | ConvertTo-Json $TeamsUpdateURI = "https://$Global:NectarCloud/aapi/clouddatasources/configuration/msteams/cdr/$($TeamsBody.id)" $AzureUpdateURI = "https://$Global:NectarCloud/aapi/clouddatasources/configuration/azuread/$($AzureBody.id)" Write-Verbose $TeamsUpdateURI Write-Verbose $TeamsJSONBody Write-Verbose $AzureUpdateURI Write-Verbose $AzureJSONBody If ($PSCmdlet.ShouldProcess(("Updating Nectar 10 MS Teams config on tenant {0}" -f $TenantName), ("Update Nectar 10 MS Teams config on tenant {0}?" -f $TenantName), 'Nectar 10 MS Teams Config Update')) { Try { $JSON = Invoke-RestMethod -Method PUT -Credential $Global:NectarCred -uri $TeamsUpdateURI -Body $TeamsJSONBody -ContentType 'application/json; charset=utf-8' } Catch { Get-JSONErrorStream -JSONResponse $_ } Try { $JSON = Invoke-RestMethod -Method PUT -Credential $Global:NectarCred -uri $AzureUpdateURI -Body $AzureJSONBody -ContentType 'application/json; charset=utf-8' } Catch { Get-JSONErrorStream -JSONResponse $_ } } } } Function New-NectarMSTeamsConfig { <# .SYNOPSIS Enables MS Teams call data and Azure AD integration with Nectar 10 .DESCRIPTION Enables MS Teams call data and Azure AD integration with Nectar 10. Requires a global admin account. Not available to tenant-level admins. .PARAMETER MSClientID The MS client ID for the application granted access to Azure AD. .PARAMETER MSClientSecret The MS client secret for the application granted access to Azure AD. Use either this or CertID, but not both. .PARAMETER CertID The Nectar 10 certificate ID. Use either this or MSClientSecret but not both. Obtain the cert ID by running Get-NectarMSTeamsCertificate. .PARAMETER MSTenantID The MS tenant ID for the O365 customer granted access to Azure AD. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE New-NectarMSTeamsConfig .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientSecret, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [int]$CertID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSTenantID ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $TeamsURI = "https://$Global:NectarCloud/aapi/clouddatasources/configuration/msteams/cdr" $AzureURI = "https://$Global:NectarCloud/aapi/clouddatasources/configuration/azuread" # Get the cloud agent name $CloudAgentURI = "https://$Global:NectarCloud/aapi/cloudagents" $CloudAgentName = (Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $CloudAgentURI)[0].cloudAgentName # Check for existing Teams/Azure config # Will increment the sourceID if one already exists [int]$TeamsSourceID = ((Get-NectarMSTeamsConfig).sourceID | Measure-Object -Maximum).Maximum + 1 [int]$AzureSourceID = ((Get-NectarMSAzureConfig).sourceID | Measure-Object -Maximum).Maximum + 1 # Build the JSON body for creating the config $TeamsBody = @{ tenant = $TenantName cloudAgentName = $CloudAgentName displayName = 'MS Teams' msTenantId = $MSTenantID msClientId = $MSClientID kafkaTopic = "$($TenantName)_msteams" sourceId = $TeamsSourceID } $AzureBody = @{ tenant = $TenantName cloudAgentName = $CloudAgentName displayName = 'Azure AD' msTenantId = $MSTenantID msClientId = $MSClientID kafkaTopic = "$($TenantName)_azuread" sourceId = $AzureSourceID primaryAD = $TRUE } If ($CertID) { $TeamsBody.Add('msClientCertificateId',$CertID) $AzureBody.Add('msClientCertificateId',$CertID) } Else { $TeamsBody.Add('msClientSecret',$MSClientSecret) $AzureBody.Add('msClientSecret',$MSClientSecret) } $TeamsJSONBody = $TeamsBody | ConvertTo-Json $AzureJSONBody = $AzureBody | ConvertTo-Json Write-Verbose $TeamsURI Write-Verbose $TeamsJSONBody Write-Verbose $AzureURI Write-Verbose $AzureJSONBody Try { $JSON = Invoke-RestMethod -Method POST -Credential $Global:NectarCred -uri $TeamsURI -Body $TeamsJSONBody -ContentType 'application/json; charset=utf-8' } Catch { Get-JSONErrorStream -JSONResponse $_ } Try { $JSON = Invoke-RestMethod -Method POST -Credential $Global:NectarCred -uri $AzureURI -Body $AzureJSONBody -ContentType 'application/json; charset=utf-8' } Catch { Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarMSTeamsCertificate { <# .SYNOPSIS Returns information about a self-signed certificate for Azure app certificate-based authentication .DESCRIPTION MS Azure allows for either client secret or certificate-based authentication for Azure applications. This shows the certificate details for a given tenant. Requires a global admin account. Not available to tenant-level admins. .PARAMETER CertID The Nectar 10 certificate ID. Typically starts at 1 and increments from there .PARAMETER CertFilePath Exports the certificate to a .cer file at the listed path .EXAMPLE Get-NectarMSTeamsCertificate -CertID 1 .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [int]$CertID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$CertFilePath ) Begin { Connect-NectarCloud } Process { Try { $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/aapi/client/certificate?id=$CertID" If ($CertFilePath) { $JSON.data.certificate | Out-File -FilePath $CertFilePath Write-Host "Certificate file written to $CertFilePath" } Return $JSON.data } Catch { Get-JSONErrorStream -JSONResponse $_ } } } Function New-NectarMSTeamsCertificate { <# .SYNOPSIS Creates a self-signed certificate for Azure app certificate-based authentication .DESCRIPTION MS Azure allows for either client secret or certificate-based authentication for Azure applications. This command will create a self-signed certificate to use for this purpose. It will create the private key, associate it with the Nectar 10 tenant and present the public portion to be added to the MS Azure app. Requires a global admin account. Not available to tenant-level admins. .PARAMETER CertDisplayName The display name to assign to the certificate .PARAMETER ValidityPeriod The number of days the certificate should be valid for. Defaults to 365 days (1 year) .EXAMPLE New-NectarMSTeamsCertificate -CertDisplayName 'CompanyName Certificate for Nectar 10' -ValidityPeriod 730 .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$CertDisplayName = 'Nectar 10 Certificate for Azure App', [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [int]$ValidityPeriod = 365 ) Begin { Connect-NectarCloud } Process { Try { If (!$CertDisplayName) { } $JSON = Invoke-RestMethod -Method POST -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/aapi/client/certificate?name=$CertDisplayName&validityDays=$ValidityPeriod" Return $JSON.data } Catch { Get-JSONErrorStream -JSONResponse $_ } } } Function Remove-NectarMSTeamsCertificate { <# .SYNOPSIS Removes a previously created certificate used for MS Teams Azure app authentication .DESCRIPTION MS Azure allows for either client secret or certificate-based authentication for Azure applications. This command will delete a previously created self-signed certificate. Requires a global admin account. Not available to tenant-level admins. .PARAMETER CertID The Nectar 10 certificate ID. Typically starts at 1 and increments from there .EXAMPLE Remove-NectarMSTeamsCertificate -CertID 1 .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [int]$CertID ) Begin { Connect-NectarCloud } Process { Try { $JSON = Invoke-RestMethod -Method DELETE -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/aapi/client/certificate?id=$CertID" } Catch { Get-JSONErrorStream -JSONResponse $_ } } } Function Set-NectarMSTeamsUserMonitoringLimit { <# .SYNOPSIS Configure a limit to the number of users that are monitored in Nectar 10 .DESCRIPTION Configure a limit to the number of users that are monitored in Nectar 10. Uses membership in a specified Azure AD group to determine the users we pull call records. Requires a global admin account. Not available to tenant-level admins. .PARAMETER MaxUsers The maximum number of users that will be monitored. Any group members above MaxUsers will not be monitored. .PARAMETER GroupName The display name of the group that contains the users who will be monitored. If the group contains spaces, enclose the name in quotes .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Set-NectarMSTeamsUserMonitoringLimit -MaxUsers 1000 -GroupName N10Users -TenantName contoso Sets the maximum number of monitored users on the Contoso tenant to 1000 and uses the members of the N10Users group as the list of users to be monitored .NOTES Version 1.0 #> [Alias("sntuml")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [ValidateRange(1,999999)] [int]$MaxUsers, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$GroupName, [Parameter(Mandatory=$False)] [bool]$Enabled = $True, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($Enabled) { # Validate the group exists in Azure AD $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken $Headers = @{ Authorization = "Bearer $AuthToken" } $URI = "https://graph.microsoft.com/v1.0/groups?`$filter=displayName eq '$GroupName'" Write-Verbose $URI Try { $GroupData = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers If ($GroupData.Value.Count -eq 1) { Write-Host "Found Azure AD group " -NoNewLine Write-Host $GroupName -ForegroundColor Green $CountURI = "https://graph.microsoft.com/v1.0/groups/$($GroupData.Value.id)/transitivemembers/microsoft.graph.user/$count" # Add header $Headers.Add('ConsistencyLevel', 'eventual') Write-Verbose $CountURI $MemberCount = Invoke-RestMethod -Method GET -uri $CountURI -Headers $Headers $TotalCount = $MemberCount.value.count While ($MemberCount.'@odata.nextLink') { $NextURI = $MemberCount.'@odata.nextLink' $MemberCount = Invoke-RestMethod -Method GET -uri $NextURI -Headers $Headers $TotalCount += $MemberCount.value.count } Write-Host "Group has $TotalCount members" } Else { Write-Host "No group called '$GroupName' could be found in Azure AD" -ForegroundColor Red Break } } Catch { Write-Host "There was an error while attempting to locate the group $GroupName in Azure AD" -ForegroundColor Red Break } } # Get the MS Teams Config ID for constructing the UrlEncode $MSTeamsConfigID = (Get-NectarMSTeamsSubscription).msTeamsConfigId $URI = "https://$Global:NectarCloud/aapi/clouddatasources/configuration/msteams/cdr/partialmonitoring?id=$MSTeamsConfigID" Write-Verbose $URI $Body = @{ tenant = $TenantName enabled = $Enabled azureAdGroup = $GroupName maxUsers = $MaxUsers } $JSONBody = $Body | ConvertTo-Json Write-Verbose $JSONBody Try { $JSON = Invoke-RestMethod -Method PUT -Credential $Global:NectarCred -uri $URI -Body $JSONBody -ContentType 'application/json; charset=utf-8' If ($Enabled) { Write-Host "Successfully enabled Teams partial user monitoring on " -NoNewLine Write-Host $TenantName -NoNewLine -ForegroundColor Green Write-Host " for up to " -NoNewLine Write-Host $MaxUsers -NoNewLine -ForegroundColor Green Write-Host " users using group named " -NoNewLine Write-Host $GroupName -NoNewLine -ForegroundColor Green } Else { Write-Host "Successfully disabled Teams partial user monitoring on " -NoNewLine Write-Host $TenantName -ForegroundColor Green } } Catch { Write-Error "Error while setting parameters" Get-JSONErrorStream -JSONResponse $_ } } } Function Get-MSTeamsCallRecord { <# .SYNOPSIS Return a specific call record details directly from MS Azure tenant. .DESCRIPTION Return a specific call record details directly from MS Azure tenant. .PARAMETER CallRecordID The MS call record ID .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER AuthToken The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken .EXAMPLE Get-MSTeamsCallRecord -CallRecordID ed672235-5417-40ce-8425-12b8b702a505 -AuthToken $AuthToken Returns the given MS Teams call record using a previously-obtained authtoken .EXAMPLE Get-MSTeamsCallRecord -CallRecordID ed672235-5417-40ce-8425-12b8b702a505 -AuthToken $AuthToken -TextOutput | Out-File Call.json Returns the given MS Teams call record using a previously-obtained authtoken and saves the results to a .JSON file. .NOTES Version 1.3 #> [Alias("gmtcr")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("calldCallRecordId")] [string]$CallRecordID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AuthToken, [Parameter(Mandatory=$False)] [switch]$TextOutput, [Parameter(Mandatory=$False)] [switch]$BetaCallRecord ) Begin { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If (!$AuthToken) { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Where {$_.SubscriptionStatus -eq 'ACTIVE'} | Get-MSGraphAccessToken } } Catch { Write-Error "Could not obtain authorization token." Get-JSONErrorStream -JSONResponse $_ } } Process { Try { $Headers = @{ Authorization = "Bearer $AuthToken" } # Change case to lower $CallRecordID = $CallRecordID.ToLower() If ($BetaCallRecord) { $URI = "https://graph.microsoft.com/beta/communications/callRecords/$CallRecordID/?`$expand=sessions(`$expand=segments)" } Else { $URI = "https://graph.microsoft.com/v1.0/communications/callRecords/$CallRecordID/?`$expand=sessions(`$expand=segments)" } Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers If ($JSON.'sessions@odata.nextLink') { $NextURI = $JSON.'sessions@odata.nextLink' Do { Write-Verbose "Getting next session page: $NextURI" $NextJSON = Invoke-RestMethod -Method GET -uri $NextURI -Headers $Headers $JSON.sessions += $NextJSON.value Write-Verbose "Session count: $($NextJSON.value.count)" $NextURI = $NextJSON.'@odata.nextLink' } Until (!$NextURI) } If ($TextOutput) { $JSON = $JSON | ConvertTo-Json -Depth 8 } Return $JSON } Catch { Write-Error "Could not get call record." Write-Error $_ } } } Function Get-MSTeamsDRCalls { <# .SYNOPSIS Return a list of MS Teams Direct Routing calls .DESCRIPTION Return a list of MS Teams Direct Routing calls .PARAMETER FromDateTime Start of time range to query. UTC, inclusive. Time range is based on the call start time. Defaults to today .PARAMETER ToDateTime End of time range to query. UTC, inclusive. Defaults to today .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER MSClientID The MS client ID for the application granted access to Azure AD. .PARAMETER MSClientSecret The MS client secret for the application granted access to Azure AD. .PARAMETER MSTenantID The MS tenant ID for the O365 customer granted access to Azure AD. .PARAMETER AuthToken The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-MSTeamsDRCalls -MSClientID 41a228ad-db6c-4e4e-4184-6d8a1175a35f -MSClientSecret 43Rk5Xl3K349w-pFf0i_Rt45Qd~ArqkE32. -MSTenantID 17e1e614-8119-48ab-8ba1-6ff1d94a6930 Returns all Teams DR calls for the specified tenant/clientID/secret combination .EXAMPLE Get-MSTeamsDRCalls -AuthToken $AuthToken Returns all Teams DR calls using a previously obtained authtoken .NOTES Version 1.0 #> Param ( [Parameter(Mandatory=$False)] [string]$FromDateTime = (Get-Date -Format 'yyyy-MM-dd'), [Parameter(Mandatory=$False)] [string]$ToDateTime = ((Get-Date).AddDays(+1).ToString('yyyy-MM-dd')), [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientSecret, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSTenantID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AuthToken, [Parameter(Mandatory=$False)] [ValidateRange(1,500000)] [int]$ResultSize ) Process { $Body = @{} $Params = @{} Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($MSTenantID) { $AuthToken = Get-MSGraphAccessToken -MSClientID $MSClientID -MSClientSecret $MSClientSecret -MSTenantID $MSTenantID } ElseIf (!$AuthToken) { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken } } Catch { Write-Error "Could not obtain authorization token." Get-JSONErrorStream -JSONResponse $_ } If ($AuthToken) { $Headers = @{ Authorization = "Bearer $AuthToken" } $URI = "https://graph.microsoft.com/beta/communications/callRecords/getDirectRoutingCalls(fromDateTime=$FromDateTime,toDateTime=$ToDateTime)" Write-Verbose $URI $Params = @{} If ($AuthToken) { $Params.Add('AuthToken',$AuthToken) } If ($TenantName) { $Params.Add('Tenant',$TenantName) } $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $JSON.value $PageSize = $JSON.value.count While (($JSON.'@odata.nextLink') -And ($PageSize -lt $ResultSize)) { $NextURI = $JSON.'@odata.nextLink' $JSON = Invoke-RestMethod -Method GET -uri $NextURI -Headers $Headers $JSON.value $PageSize += $JSON.value.count } Clear-Variable -Name AuthToken } } } Function Get-MSAzureUsers { <# .SYNOPSIS Return a list of users from Azure AD. .DESCRIPTION Return a list of users from Azure AD. .PARAMETER Properties A comma-delimited list of properties to return in the results Format as per https://docs.microsoft.com/en-us/graph/query-parameters?view=graph-rest-1.0#select-parameter Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0#properties .PARAMETER Filter Add filter parameters as per https://docs.microsoft.com/en-us/graph/query-parameters?context=graph%2Fapi%2F1.0&view=graph-rest-1.0#filter-parameter Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0#properties .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER HideProgressBar Don't show the progress bar. Cleans up logs when running on Docker. .PARAMETER AuthToken The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken .PARAMETER TotalCount Only return a total count of objects .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-MSAzureUsers Returns all MS Azure user accounts .EXAMPLE Get-MSAzureUsers -Properties 'id,userPrincipalName' -AuthToken $AuthToken Returns a list of Azure users' ID and userPrincipalNames using a previously-obtained authtoken .EXAMPLE Get-MSAzureUsers -Filter "startswith(displayName,'Meeting Room')" Returns a list of Azure users whose display names start with 'Meeting Room' .NOTES Version 1.3 #> Param ( [Parameter(Mandatory=$False)] [string]$Properties, [Parameter(Mandatory=$False)] [string]$Filter, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [switch]$TotalCount, [Parameter(Mandatory=$False)] [switch]$HideProgressBar, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AuthToken, [Parameter(Mandatory=$False)] [ValidateRange(1,500000)] [int]$ResultSize, [Parameter(Mandatory=$False)] [ValidateRange(1,99999)] [int]$ProgressUpdateFreq = 1 ) Process { $Body = @{} $Params = @{} Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If (!$AuthToken) { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken } } Catch { Write-Error "Could not obtain authorization token." Get-JSONErrorStream -JSONResponse $_ } If ($AuthToken) { Try { $Headers = @{ Authorization = "Bearer $AuthToken" } If ($Properties) { $Body.Add('$select',$Properties) $Params.Add('Properties',$Properties) } If ($Filter) { $Body.Add('$filter',$Filter) $Params.Add('Filter',$Filter) } If ($TotalCount) { $Body.Add('ConsistencyLevel','eventual') $URI = 'https://graph.microsoft.com/v1.0/users/$count' $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $UserCount = New-Object PsObject $UserCount | Add-Member -NotePropertyName 'UserCount' -NotePropertyValue $JSON If ($TenantName) { $UserCount | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $TenantName } Clear-Variable -Name AuthToken Return $UserCount } Else { $URI = "https://graph.microsoft.com/v1.0/users" Write-Verbose $URI $Params = @{} If ($AuthToken) { $Params.Add('AuthToken',$AuthToken) } If ($TenantName) { $Params.Add('Tenant',$TenantName) } $TotalUsers = Get-MSAzureUsers @Params -TotalCount $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $JSON.value $PageSize = $JSON.value.count $Message = "Getting Azure AD users for tenant $TenantName." If ($HideProgressBar) { Write-Host $Message } $UserCount = 0 $LastCount = 0 $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew() While (($JSON.'@odata.nextLink') -And ($PageSize -lt $ResultSize)) { $NextURI = $JSON.'@odata.nextLink' $JSON = Invoke-RestMethod -Method GET -uri $NextURI -Headers $Headers $JSON.value $PageSize += $JSON.value.count If ($Stopwatch.Elapsed.TotalSeconds -ge $ProgressUpdateFreq) { $Percentage = ($PageSize / $TotalUsers.UserCount) * 100 If ($HideProgressBar) { $Percentage = [math]::Round($Percentage,1) Write-Host "Retrieving $Percentage`% of $($TotalUsers.UserCount) users on tenant $TenantName..." } Else { Write-Progress -Activity $Message -PercentComplete $Percentage -Status 'Retrieving...' } $Stopwatch.Reset() $Stopwatch.Start() } } } Clear-Variable -Name AuthToken } Catch { Write-Error "Could not get user data." Write-Error $_ Clear-Variable -Name AuthToken } } } } Function Get-MSAzureUserGroupMembership { <# .SYNOPSIS Return the groups that a user is a member of from Azure AD. .DESCRIPTION Return the groups that a user is a member of from Azure AD. .PARAMETER ID The Azure AD GUID of the user .PARAMETER Transitive Show groups where the user is a member of a group that is a member of another group .PARAMETER Properties A comma-delimited list of properties to return in the results Format as per https://docs.microsoft.com/en-us/graph/query-parameters?view=graph-rest-1.0#select-parameter Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties .PARAMETER Filter Add filter parameters as per https://docs.microsoft.com/en-us/graph/query-parameters?context=graph%2Fapi%2F1.0&view=graph-rest-1.0#filter-parameter Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER AuthToken The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken .PARAMETER TotalCount Only return a total count of objects .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-MSAzureUserGroupMembership -ID abcdefab-1234-1234-1234-abcdabcdabcd Returns the groups that the selected user is a member of .EXAMPLE Get-MSAzureUsers -Properties 'id,userPrincipalName' -AuthToken $AuthToken Returns a list of Azure users' ID and userPrincipalNames using a previously-obtained authtoken .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$ID, [Parameter(Mandatory=$False)] [switch]$Transitive, [string]$Properties, [Parameter(Mandatory=$False)] [string]$Filter, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [switch]$TotalCount, [Parameter(Mandatory=$False)] [switch]$HideProgressBar, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AuthToken, [Parameter(Mandatory=$False)] [ValidateRange(1,500000)] [int]$ResultSize = 1000, [Parameter(Mandatory=$False)] [ValidateRange(1,99999)] [int]$ProgressUpdateFreq = 1 ) Begin { If ($Transitive) { $MemberOfScope = 'transitiveMemberOf' } Else { $MemberOfScope = 'MemberOf' } } Process { $Body = @{} $Params = @{} Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If (!$AuthToken) { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken } } Catch { Write-Error "Could not obtain authorization token." Get-JSONErrorStream -JSONResponse $_ } If ($AuthToken) { Try { $Headers = @{ Authorization = "Bearer $AuthToken" } If ($Properties) { $Body.Add('$select',$Properties) $Params.Add('Properties',$Properties) } If ($Filter) { $Body.Add('$filter',$Filter) $Params.Add('Filter',$Filter) } If ($TotalCount) { $Body.Add('ConsistencyLevel','eventual') $URI = "https://graph.microsoft.com/v1.0/users/$ID/$MemberOfScope/`$count" Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $GroupCount = New-Object PsObject $GroupCount | Add-Member -NotePropertyName 'UserCount' -NotePropertyValue $JSON If ($TenantName) { $GroupCount | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $TenantName } Clear-Variable -Name AuthToken Return $GroupCount } Else { $URI = "https://graph.microsoft.com/v1.0/users/$ID/$MemberOfScope" Write-Verbose $URI $Params = @{} If ($AuthToken) { $Params.Add('AuthToken',$AuthToken) } If ($TenantName) { $Params.Add('Tenant',$TenantName) } $TotalUsers = Get-MSAzureUsers @Params -TotalCount $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $JSON.value $PageSize = $JSON.value.count While (($JSON.'@odata.nextLink') -And ($PageSize -lt $ResultSize)) { $NextURI = $JSON.'@odata.nextLink' $JSON = Invoke-RestMethod -Method GET -uri $NextURI -Headers $Headers $JSON.value $PageSize += $JSON.value.count } } Clear-Variable -Name AuthToken } Catch { Write-Error "Could not get group membership data." Write-Error $_ Clear-Variable -Name AuthToken } } } } Function Get-MSAzureGroup { <# .SYNOPSIS Return a list of groups from Azure AD. .DESCRIPTION Return a list of groups from Azure AD. .PARAMETER Properties A comma-delimited list of properties to return in the results Format as per https://docs.microsoft.com/en-us/graph/query-parameters?view=graph-rest-1.0#select-parameter Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties .PARAMETER Filter Add filter parameters as per https://docs.microsoft.com/en-us/graph/query-parameters?context=graph%2Fapi%2F1.0&view=graph-rest-1.0#filter-parameter Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER HideProgressBar Don't show the progress bar. Cleans up logs when running on Docker. .PARAMETER AuthToken The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken .PARAMETER TotalCount Only return a total count of objects .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-MSAzureGroup Returns all MS Azure groups .EXAMPLE Get-MSAzureGroup -Properties 'id,DisplayName' -AuthToken $AuthToken Returns a list of Azure group ID and display names using a previously-obtained authtoken .NOTES Version 1.0 #> Param ( [Parameter(Mandatory=$False)] [string]$Properties, [Parameter(Mandatory=$False)] [string]$Filter, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [switch]$TotalCount, [Parameter(Mandatory=$False)] [switch]$HideProgressBar, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AuthToken, [Parameter(Mandatory=$False)] [ValidateRange(1,500000)] [int]$ResultSize, [Parameter(Mandatory=$False)] [ValidateRange(1,99999)] [int]$ProgressUpdateFreq = 1 ) Process { $Body = @{} $Params = @{} Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If (!$AuthToken) { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken } } Catch { Write-Error "Could not obtain authorization token." Get-JSONErrorStream -JSONResponse $_ } If ($AuthToken) { Try { $Headers = @{ Authorization = "Bearer $AuthToken" } If ($Properties) { $Body.Add('$select',$Properties) $Params.Add('Properties',$Properties) } If ($Filter) { $Body.Add('$filter',$Filter) $Params.Add('Filter',$Filter) } If ($TotalCount) { $Body.Add('ConsistencyLevel','eventual') $URI = 'https://graph.microsoft.com/v1.0/groups/$count' $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $GroupCount = New-Object PsObject $GroupCount | Add-Member -NotePropertyName 'GroupCount' -NotePropertyValue $JSON If ($TenantName) { $GroupCount | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $TenantName } Clear-Variable -Name AuthToken Return $GroupCount } Else { $URI = "https://graph.microsoft.com/v1.0/groups" Write-Verbose $URI $Params = @{} If ($AuthToken) { $Params.Add('AuthToken',$AuthToken) } If ($TenantName) { $Params.Add('Tenant',$TenantName) } $TotalGroups = Get-MSAzureGroup @Params -TotalCount $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $JSON.value $PageSize = $JSON.value.count $Message = "Getting Azure AD groups for tenant $TenantName." If ($HideProgressBar) { Write-Host $Message } $GroupCount = 0 $LastCount = 0 $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew() While (($JSON.'@odata.nextLink') -And ($PageSize -lt $ResultSize)) { $NextURI = $JSON.'@odata.nextLink' $JSON = Invoke-RestMethod -Method GET -uri $NextURI -Headers $Headers $JSON.value $PageSize += $JSON.value.count If ($Stopwatch.Elapsed.TotalSeconds -ge $ProgressUpdateFreq) { $Percentage = ($PageSize / $TotalGroups.GroupCount) * 100 If ($HideProgressBar) { $Percentage = [math]::Round($Percentage,1) Write-Host "Retrieving $Percentage`% of $($TotalGroups.GroupCount) users on tenant $TenantName..." } Else { Write-Progress -Activity $Message -PercentComplete $Percentage -Status 'Retrieving...' } $Stopwatch.Reset() $Stopwatch.Start() } } } Clear-Variable -Name AuthToken } Catch { Write-Error "Could not get group data." Write-Error $_ Clear-Variable -Name AuthToken } } } } Function Get-MSAzureGroupMembers { <# .SYNOPSIS Return a list of group members from Azure AD. .DESCRIPTION Return a list of group members from Azure AD. .PARAMETER ID The GUID of the group to return membership info .PARAMETER Transitive Show members where the members are members of a group that is a member of another group .PARAMETER Properties A comma-delimited list of properties to return in the results Format as per https://docs.microsoft.com/en-us/graph/query-parameters?view=graph-rest-1.0#select-parameter Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties .PARAMETER Filter Add filter parameters as per https://docs.microsoft.com/en-us/graph/query-parameters?context=graph%2Fapi%2F1.0&view=graph-rest-1.0#filter-parameter Available properties can be found at https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER AuthToken The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-MSAzureGroup Returns all MS Azure user accounts .EXAMPLE Get-MSAzureGroup -Properties 'id,userPrincipalName' -AuthToken $AuthToken Returns a list of Azure users' ID and userPrincipalNames using a previously-obtained authtoken .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$ID, [Parameter(Mandatory=$False)] [switch]$Transitive, [Parameter(Mandatory=$False)] [string]$Properties, [Parameter(Mandatory=$False)] [string]$Filter, [Parameter(Mandatory=$False)] [switch]$TotalCount, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AuthToken, [Parameter(Mandatory=$False)] [ValidateRange(1,500000)] [int]$ResultSize = 1000 ) Begin { If ($Transitive) { $MemberScope = 'transitiveMembers' } Else { $MemberScope = 'members' } } Process { $Body = @{} $Params = @{} Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If (!$AuthToken) { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken } } Catch { Write-Error "Could not obtain authorization token." Get-JSONErrorStream -JSONResponse $_ } If ($AuthToken) { Try { $Headers = @{ Authorization = "Bearer $AuthToken" } If ($Properties) { $Body.Add('$select',$Properties) $Params.Add('Properties',$Properties) } If ($Filter) { $Body.Add('$filter',$Filter) $Params.Add('Filter',$Filter) } If ($TotalCount) { $Body.Add('ConsistencyLevel','eventual') $URI = "https://graph.microsoft.com/v1.0/groups/$ID/$MemberScope/`$count" $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $MemberCount = New-Object PsObject $MemberCount | Add-Member -NotePropertyName 'MemberCount' -NotePropertyValue $JSON If ($TenantName) { $MemberCount | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $TenantName } Clear-Variable -Name AuthToken Return $MemberCount } Else { $URI = "https://graph.microsoft.com/v1.0/groups/$ID/$MemberScope" Write-Verbose $URI $Params = @{} If ($AuthToken) { $Params.Add('AuthToken',$AuthToken) } If ($TenantName) { $Params.Add('Tenant',$TenantName) } $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $JSON.value $PageSize = $JSON.value.count While (($JSON.'@odata.nextLink') -And ($PageSize -lt $ResultSize)) { $NextURI = $JSON.'@odata.nextLink' $JSON = Invoke-RestMethod -Method GET -uri $NextURI -Headers $Headers $JSON.value $PageSize += $JSON.value.count } } Clear-Variable -Name AuthToken } Catch { Write-Error "Could not get group member data." Write-Error $_ Clear-Variable -Name AuthToken } } } } Function Get-MSAzurePhoto { <# .SYNOPSIS Return users who have a photo in MS Azure. .DESCRIPTION Return users who have a photo in MS Azure. Useful for troubleshooting .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER HideProgressBar Don't show the progress bar. Cleans up logs when running on Docker. .PARAMETER ProgressUpdateFreq How frequently to show progress updates, in seconds. Defaults to 1 second .PARAMETER UserList Use a previously obtained list of Azure user IDs. If this isn't specified, then a list will be downloaded using Get-MSAzureUsers .PARAMETER MSClientID The MS client ID for the application granted access to Azure AD. .PARAMETER MSClientSecret The MS client secret for the application granted access to Azure AD. .PARAMETER MSTenantID The MS tenant ID for the O365 customer granted access to Azure AD. .EXAMPLE Get-MSAzurePhoto -TenantName contoso .NOTES Version 1.1 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [switch]$HideProgressBar, [Parameter(Mandatory=$False)] [ValidateRange(1,99999)] [int]$ProgressUpdateFreq = 1, [Parameter(Mandatory=$False)] $UserList, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientSecret, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSTenantID ) Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($MSTenantID) { $AuthToken = Get-MSGraphAccessToken -MSClientID $MSClientID -MSClientSecret $MSClientSecret -MSTenantID $MSTenantID } Else { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken } } Catch { Write-Error "Could not obtain authorization token for tenant $TenantName." Get-JSONErrorStream -JSONResponse $_ } If ($AuthToken) { $Headers = @{ Authorization = "Bearer $AuthToken" } $Params = @{ TenantName = $TenantName Properties = 'id' Filter = 'accountEnabled eq true' AuthToken = $AuthToken ResultSize = 200000 } If ($HideProgressBar) { $Params.Add('HideProgressBar', $HideProgressBar) } If ($ProgressUpdateFreq) { $Params.Add('ProgressUpdateFreq', $ProgressUpdateFreq) } If (!$UserList) { $UserList = Get-MSAzureUsers @Params } $TotalUsers = $UserList.Count $UserCount = 0 $PhotoCount = 0 $LastCount = 0 $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew() ForEach ($User in $UserList) { $Retry = $FALSE [int]$RetryCount = 0 Do { # The AuthToken will expire after an hour or so. This Try/Catch block will get a new AuthToken when an error occurs Try { $URI = "https://graph.microsoft.com/v1.0/users/$($User.id)/photo/" Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers $PhotoCount++ $Retry = $FALSE } Catch { # If ($RetryCount -gt 1) { # Write-Error "Could not get user data for tenant $TenantName." # Write-Error $_ # $Retry = $FALSE # } # Else { # If ($MSTenantID) { # $AuthToken = Get-MSGraphAccessToken -MSClientID $MSClientID -MSClientSecret $MSClientSecret -MSTenantID $MSTenantID # } # Else { # $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken # } # $Headers = @{ # Authorization = "Bearer $AuthToken" # } # $RetryCount++ # } } } While ($Retry -eq $TRUE) If ($Stopwatch.Elapsed.TotalSeconds -ge $ProgressUpdateFreq) { $Percentage = $UserCount / $TotalUsers $Rate = ($UserCount - $LastCount) / $ProgressUpdateFreq $LastCount = $UserCount $Message = "Checking Azure photo for user {0} of {1} on tenant {2} at a rate of {3}/sec." -f $UserCount, $TotalUsers, $TenantName, $Rate If ($HideProgressBar) { Write-Host $Message } Else { Write-Progress -Activity $Message -PercentComplete ($Percentage * 100) -Status 'Calculating...' } $Stopwatch.Reset() $Stopwatch.Start() } $UserCount++ } $AzurePhoto = New-Object PsObject $AzurePhoto | Add-Member -NotePropertyName 'PhotoCount' -NotePropertyValue $PhotoCount If ($TenantName) { $AzurePhoto | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $TenantName } Clear-Variable -Name AuthToken Return $AzurePhoto } } } Function Get-MSTeamsServiceStatus { <# .SYNOPSIS Return the current MS Teams service status from Office 365 .DESCRIPTION Return the current MS Teams service status from Office 365 .PARAMETER CallRecordID The MS call record ID .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-MSTeamsServiceStatus .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [switch]$TextOutput ) Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $TenantInfo = Get-NectarMSTeamsSubscription -TenantName $TenantName $TenantID = $TenantInfo.msTenantID $AuthToken = $TenantInfo | Get-MSOfficeAccessToken } Catch { Write-Error "Could not obtain authorization token." Get-JSONErrorStream -JSONResponse $_ } If ($AuthToken) { Try { $Headers = @{ Authorization = "Bearer $AuthToken" } $URI = "https://manage.office.com/api/v1.0/$($TenantID)/ServiceComms/CurrentStatus?`$filter=Workload eq 'microsoftteams'" Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers If ($TextOutput) { $JSON = $JSON | ConvertTo-Json -Depth 8 } Clear-Variable -Name AuthToken Return $JSON.value } Catch { Write-Error $_.Exception.Response.StatusDescription Clear-Variable -Name AuthToken } } } } Function Get-MSTeamsServiceMessages { <# .SYNOPSIS Return any current service status messages regarding MS Teams cloud issues .DESCRIPTION Return any current service status messages regarding MS Teams cloud issues .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-MSTeamsServiceMessages .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [switch]$Current, [Parameter(Mandatory=$False)] [switch]$TextOutput ) Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $TenantInfo = Get-NectarMSTeamsSubscription -TenantName $TenantName $TenantID = $TenantInfo.msTenantID $AuthToken = $TenantInfo | Get-MSOfficeAccessToken } Catch { Write-Error "Could not obtain authorization token." Get-JSONErrorStream -JSONResponse $_ } If ($AuthToken) { Try { $Headers = @{ Authorization = "Bearer $AuthToken" } $URI = "https://manage.office.com/api/v1.0/$($TenantID)/ServiceComms/Messages?`$filter=Workload eq 'microsoftteams'" If ($Current) { $URI = $URI + " and EndTime eq null" } Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers If ($TextOutput) { $JSON = $JSON | ConvertTo-Json -Depth 8 } Clear-Variable -Name AuthToken Return $JSON.value } Catch { Write-Error $_.Exception.Response.StatusDescription Clear-Variable -Name AuthToken } } } } Function Get-MSTeamsLicenseStatus { <# .SYNOPSIS Return the total number of SKUs for a company that have Teams in it .DESCRIPTION Return the total number of SKUs for a company that have Teams in it. Requires Organization.Read.All permission on graph.microsoft.com .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER AuthToken The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken .EXAMPLE Get-MSTeamsLicenseStatus .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AuthToken ) Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If (!$AuthToken) { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken } } Catch { Write-Error "Could not obtain authorization token for tenant $TenantName." Get-JSONErrorStream -JSONResponse $_ } If ($AuthToken) { Try { $Headers = @{ Authorization = "Bearer $AuthToken" } $URI = "https://graph.microsoft.com/v1.0/subscribedSkus" Write-Verbose $URI $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers $TotalSKUs = $JSON.value $LicenseCount = $TotalSKUs | Where {$_.servicePlans.ServicePlanName -eq 'TEAMS1'} | Select-Object skuPartNumber, ConsumedUnits If ($TotalOnly) { $LicenseCount = $LicenseCount | Measure-Object -Property consumedUnits -Sum | Select-Object Sum } If ($TenantName) { $LicenseCount | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $TenantName } Clear-Variable -Name AuthToken Return $LicenseCount } Catch { Write-Error "Could not get license data for tenant $TenantName." Write-Error $_ Clear-Variable -Name AuthToken } } } } Function Get-MSTeamsUserLicenseCount { <# .SYNOPSIS Return the total number of Teams licensed users for a given tenant. .DESCRIPTION Return the total number of Teams licensed users for a given tenant. Excludes disabled accounts. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER HideProgressBar Don't show the progress bar. Cleans up logs when running on Docker. .PARAMETER ProgressUpdateFreq How frequently to show progress updates, in seconds. Defaults to 1 second .PARAMETER UserList Use a previously obtained list of Azure user IDs. If this isn't specified, then a list will be downloaded using Get-MSAzureUsers .PARAMETER MSClientID The MS client ID for the application granted access to Azure AD. .PARAMETER MSClientSecret The MS client secret for the application granted access to Azure AD. .PARAMETER MSTenantID The MS tenant ID for the O365 customer granted access to Azure AD. .PARAMETER AuthToken The authorization token used for this request. Normally obtained via Get-MSGraphAccessToken .PARAMETER TestMode Sets the mode to run the tests under. Choose from Basic or Extended. Defaults to Basic mode Extended mode checks user O365 Service Plans for both Teams and Enterprise Voice functionality. Most accurate, but slower because it requires pulling down about 10x the data. The process ends up taking about twice as long as Basic mode. Basic mode looks at the assigned license SKUs, and also checks to see if TEAMS1 has been disabled. Faster because it requires about 10x less data than Extended mode, but can't obtain information about Enterprise Voice functionality. It's twice as fast as Extended mode, but only returns a total Teams count. It doesn't return the Enterprise Voice users. .EXAMPLE Get-MSTeamsUserLicenseCount -TenantName contoso .NOTES Version 1.2 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [switch]$HideProgressBar, [Parameter(Mandatory=$False)] [ValidateRange(1,99999)] [int]$ProgressUpdateFreq = 1, [Parameter(Mandatory=$False)] $UserList, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSClientSecret, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$MSTenantID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AuthToken, [Parameter(Mandatory=$False)] [ValidateSet('Basic','Extended', IgnoreCase=$True)] [string]$TestMode = 'Basic', [Parameter(Mandatory=$False)] [string]$ExportCSV ) Begin { $TeamsSKUList = '4b590615-0888-425a-a965-b3bf7789848d', # Microsoft 365 Education A3 for Faculty (M365EDU_A3_FACULTY) '7cfd9a2b-e110-4c39-bf20-c6a3f36a3121', # Microsoft 365 Education A3 for Students (M365EDU_A3_STUDENT) 'e97c048c-37a4-45fb-ab50-922fbf07a370', # Microsoft 365 Education A5 for Faculty (M365EDU_A5_FACULTY) '46c119d4-0379-4a9d-85e4-97c66d3f909e', # Microsoft 365 Education A5 for Students (M365EDU_A5_STUDENT) '3b555118-da6a-4418-894f-7df1e2096870', # Microsoft 365 Business Basic (O365_BUSINESS_ESSENTIALS) 'dab7782a-93b1-4074-8bb1-0e61318bea0b', # Microsoft 365 Business Basic (SMB_BUSINESS_ESSENTIALS) 'f245ecc8-75af-4f8e-b61f-27d8114de5f3', # Microsoft 365 Business Standard (O365_BUSINESS_PREMIUM) 'ac5cef5d-921b-4f97-9ef3-c99076e5470f', # Microsoft 365 Business Standard (SMB_BUSINESS_PREMIUM) 'cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46', # Microsoft 365 Business Premium (SPB) '05e9a617-0261-4cee-bb44-138d3ef5d965', # Microsoft 365 E3 (SPB_E3) '06ebc4ee-1bb5-47dd-8120-11324bc54e06', # Microsoft 365 E5 (SPB_E5) '44575883-256e-4a79-9da4-ebe9acabe2b2', # Microsoft 365 F1 (M365_F1) '66b55226-6b4f-492c-910c-a3b7a3c9d993', # Microsoft 365 F3 (SPE_F1) 'a4585165-0533-458a-97e3-c400570268c4', # Office 365 A5 for Faculty (ENTERPRISEPREMIUM_FACULTY) 'ee656612-49fa-43e5-b67e-cb1fdf7699df', # Office 365 A5 for Students (ENTERPRISEPREMIUM_STUDENT) '18181a46-0d4e-45cd-891e-60aabd171b4e', # Office 365 E1 (STANDARDPACK) '6634e0ce-1a9f-428c-a498-f84ec7b8aa2e', # Office 365 E2 (STANDARDWOFFPACK) '6fd2c87f-b296-42f0-b197-1e91e994b900', # Office 365 E3 (ENTERPRISEPACK) '189a915c-fe4f-4ffa-bde4-85b9628d07a0', # Office 365 Developer (DEVELOPERPACK) '1392051d-0cb9-4b7a-88d5-621fee5e8711', # Office 365 E4 (ENTERPRISEWITHSCAL) 'c7df2760-2c81-4ef7-b578-5b5392b571df', # Office 365 E5 (ENTERPRISEPREMIUM) '26d45bd9-adf1-46cd-a9e1-51e9a5524128', # Office 365 E5 without Audio Conferencing (ENTERPRISEPREMIUM_NOPSTNCONF) '4b585984-651b-448a-9e53-3b10f069cf7f', # Office 365 F1/3 (DESKLESSPACK) '6070a4c8-34c6-4937-8dfb-39bbc6397a60', # Meeting Room (MEETING_ROOM) '710779e8-3d4a-4c88-adb9-386c958d1fdf', # Teams Exploratory '7a551360-26c4-4f61-84e6-ef715673e083', # Dynamics 365 Remote Assist '50f60901-3181-4b75-8a2c-4c8e4c1d5a72', # '295a8eb0-f78d-45c7-8b5b-1eed5ed02dff' # Common Area Phones $TeamsPlanID = '57ff2da0-773e-42df-b2af-ffb7a2317929' # TEAMS1 $TeamsPhoneSystemID = '4828c8ec-dc2e-4779-b502-87ac9ce28ab7' # MCOEV $TeamsCallingPlanList = '4828c8ec-dc2e-4779-b502-87ac9ce28ab7', # MCOEV '4ed3ff63-69d7-4fb7-b984-5aec7f605ca8', # MCOPSTN1 '5a10155d-f5c1-411a-a8ec-e99aae125390', # MCOPSTN2 '54a152dc-90de-4996-93d2-bc47e670fc06' # MCOPSTN5 $UnmatchedSKUList = @() class UnmatchedSKU { [ValidateNotNullOrEmpty()][string]$SKUId [ValidateNotNullOrEmpty()][string]$UserId } } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($MSTenantID) { $AuthToken = Get-MSGraphAccessToken -MSClientID $MSClientID -MSClientSecret $MSClientSecret -MSTenantID $MSTenantID } ElseIf (!$AuthToken) { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken } } Catch { Write-Error "Could not obtain authorization token for tenant $TenantName." Get-JSONErrorStream -JSONResponse $_ } $TeamsCount = 0 $TeamsEVCount = 0 If ($AuthToken) { $Headers = @{ Authorization = "Bearer $AuthToken" } If ($TestMode -eq 'Basic') { $Properties = 'id,assignedLicenses' } Else { $Properties = 'id,assignedPlans' } If ($ExportCSV) { $Properties += ',userPrincipalName,mail,displayName' } $Params = @{ Properties = $Properties Filter = 'accountEnabled eq true' AuthToken = $AuthToken ResultSize = 200000 } If ($TenantName) { $Params.Add('TenantName',$TenantName) } If ($HideProgressBar) { $Params.Add('HideProgressBar', $HideProgressBar) } If ($ProgressUpdateFreq) { $Params.Add('ProgressUpdateFreq', $ProgressUpdateFreq) } If (!$UserList) { If ($TestMode -eq 'Basic') { $UserList = Get-MSAzureUsers @Params | Where {$_.assignedLicenses -ne ''} $TotalUsers = $UserList.Count $UserCount = 0 $LastCount = 0 $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew() ForEach ($User in $UserList) { $Retry = $TRUE [int]$RetryCount = 0 $LicenseSKUs = $User.assignedLicenses # Find all SKUs that match the reference list [string[]]$MatchingSKUs = (Compare-Object -ReferenceObject $TeamsSKUList -DifferenceObject $LicenseSKUs.skuId -IncludeEqual -ExcludeDifferent).InputObject # Check each SKU to make sure Teams isn't explicity disabled :SKULoop ForEach ($MatchSKU in $MatchingSKUs) { $TeamsSKUs = $LicenseSKUs | Where {$_.skuId -eq $MatchSKU} ForEach ($SKU in $TeamsSKUs) { If (!($SKU | Where {$_.disabledPlans -eq $TeamsPlanID})) { #Teams isn't disabled for this user. Increase the Teams user count and break out $TeamsCount++ Break SKULoop } } } If ($Stopwatch.Elapsed.TotalSeconds -ge $ProgressUpdateFreq) { $Percentage = $UserCount / $TotalUsers $Rate = ($UserCount - $LastCount) / $ProgressUpdateFreq $LastCount = $UserCount $Message = "Checking Teams license status for user {0} of {1} ({4} Teams users so far) on tenant {2} at a rate of {3}/sec." -f $UserCount, $TotalUsers, $TenantName, $Rate, $TeamsCount If ($HideProgressBar) { Write-Host $Message } Else { Write-Progress -Activity $Message -PercentComplete ($Percentage * 100) -Status 'Calculating...' } $Stopwatch.Reset() $Stopwatch.Start() } $UserCount++ } } Else { $UserList = Get-MSAzureUsers @Params | Where {$_.assignedPlans -ne ''} $TeamsCount = ($UserList | Select-Object -ExpandProperty assignedPlans | Where {$_.capabilityStatus -eq 'Enabled' -and $_.servicePlanId -eq $TeamsPlanID}).Count $TeamsEVCount = ($UserList | Select-Object -ExpandProperty assignedPlans | Where {$_.capabilityStatus -eq 'Enabled' -and $_.servicePlanId -eq $TeamsPhoneSystemID}).Count If ($ExportCSV) { $UserList | Select-Object id,userPrincipalName,mail,displayName -ExpandProperty assignedPlans | Where {$_.capabilityStatus -eq 'Enabled' -and $_.servicePlanId -eq $TeamsPlanID} | Export-Csv $ExportCSV -NoTypeInformation -Force } } } $TeamsLicense = New-Object PsObject $TeamsLicense | Add-Member -NotePropertyName 'TeamsCount' -NotePropertyValue $TeamsCount If ($TestMode -eq 'Extended') { $TeamsLicense | Add-Member -NotePropertyName 'EVCount' -NotePropertyValue $TeamsEVCount } If ($TenantName) { $TeamsLicense | Add-Member -NotePropertyName 'TenantName' -NotePropertyValue $TenantName } Clear-Variable -Name AuthToken Return $TeamsLicense } } } ################################################################################################################################################# # # # Zoom Functions # # # ################################################################################################################################################# Function Get-NectarZoomAuthURL { <# .SYNOPSIS Returns the Nectar 10 Zoom authorization URL needed for connecting Nectar 10 to Zoom .DESCRIPTION Returns the Nectar 10 Zoom authorization URL needed for connecting Nectar 10 to Zoom .EXAMPLE Get-NectarZoomAuthURL .NOTES Version 1.0 #> [Alias("gnzau")] [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { If (!$TenantName) { $TenantName = Get-NectarDefaultTenantName } $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/aapi/zoom/oauth/url?tenant=$TenantName" Return $JSON.data } Catch { Write-Error 'No tenant Zoom data found, or insufficient permissions.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-ZoomAccessToken { <# .SYNOPSIS Get a Zoom JWT access token for a given Zoom tenant. Needed to run other Zoom API queries. .DESCRIPTION Get a Zoom access token for a given Zoom tenant. Needed to run other Zoom API queries. Generates a JSON Web Ticket (JWT) .EXAMPLE $AuthToken = Get-ZoomAccessToken -APIKey yourapikey -APISecret yourapisecret .NOTES Version 1.0 #> Param( [Parameter(Mandatory = $False)] [ValidateSet('HS256', 'HS384', 'HS512')] $Algorithm = 'HS256', $type = $null, [Parameter(Mandatory = $True)] [string]$APIKey = $null, [int]$ValidforSeconds = 86400, [Parameter(Mandatory = $True)] $APISecret = $null ) $exp = [int][double]::parse((Get-Date -Date $((Get-Date).addseconds($ValidforSeconds).ToUniversalTime()) -UFormat %s)) # Grab Unix Epoch Timestamp and add desired expiration. [hashtable]$header = @{alg = $Algorithm; typ = $type} [hashtable]$payload = @{iss = $APIKey; exp = $exp} $headerjson = $header | ConvertTo-Json -Compress $payloadjson = $payload | ConvertTo-Json -Compress $headerjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($headerjson)).Split('=')[0].Replace('+', '-').Replace('/', '_') $payloadjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($payloadjson)).Split('=')[0].Replace('+', '-').Replace('/', '_') $ToBeSigned = $headerjsonbase64 + "." + $payloadjsonbase64 $SigningAlgorithm = switch ($Algorithm) { "HS256" {New-Object System.Security.Cryptography.HMACSHA256} "HS384" {New-Object System.Security.Cryptography.HMACSHA384} "HS512" {New-Object System.Security.Cryptography.HMACSHA512} } $SigningAlgorithm.Key = [System.Text.Encoding]::UTF8.GetBytes($APISecret) $Signature = [Convert]::ToBase64String($SigningAlgorithm.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($ToBeSigned))).Split('=')[0].Replace('+', '-').Replace('/', '_') $Token = "$headerjsonbase64.$payloadjsonbase64.$Signature" Return $Token } Function Get-ZoomMeeting { <# .SYNOPSIS Return a list of Zoom meetings. .DESCRIPTION Return a list of Zoom meetings. .PARAMETER MeetingID Return information about a specific meeting. Provide meeting ID or UUID. Don't use with From/To date range .PARAMETER Type Return live, past or past meetings with only one participant. Defaults to past .PARAMETER FromDateTime Start of date/time range to query. UTC, inclusive. Time range is based on the call start time. Defaults to yesterday. .PARAMETER ToDateTime End of date/time range to query. UTC, inclusive. Defaults to today .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER APIKey The Zoom API key for the application granted access to Zoom. .PARAMETER APISecret The Zoom API secret for the application granted access to Zoom. .PARAMETER AuthToken The authorization token used for this request. Normally obtained via Get-ZoomAccessToken .PARAMETER PageSize The number of results to return per page. Defaults to 30. .EXAMPLE Get-ZoomMeetings -AuthToken $AuthToken Returns all past Zoom meetings from the previous 24 hours .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("uuid")] [string]$MeetingID, [Parameter(Mandatory = $False)] [ValidateSet('past', 'pastone', 'live')] $Type = 'past', [Parameter(Mandatory=$False)] [string]$FromDateTime = ((Get-Date).AddDays(-1).ToString('yyyy-MM-dd')), [Parameter(Mandatory=$False)] [string]$ToDateTime = (Get-Date -Format 'yyyy-MM-dd'), [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$APIKey, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$APISecret, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AuthToken, [Parameter(Mandatory=$False)] [ValidateRange(1,300)] [int]$PageSize = 30 ) Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($APIKey) { $AuthToken = Get-ZoomAccessToken -APIKey $APIKey -APISecret $APISecret } ElseIf (!$AuthToken) { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken } } Catch { Write-Error "Could not obtain authorization token." Get-JSONErrorStream -JSONResponse $_ } If ($AuthToken) { $Headers = @{ Authorization = "Bearer $AuthToken" } If ($MeetingID) { $URI = "https://api.zoom.us/v2/metrics/meetings/$MeetingID" $Body = @{ type = $Type } $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $JSON } Else { $URI = "https://api.zoom.us/v2/metrics/meetings" $Body = @{ from = $FromDateTime to = $ToDateTime type = $Type page_size = $PageSize } $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $JSON.meetings # If there is more than one page, use next_page_token to iterate through the pages While ($JSON.next_page_token) { $Body.next_page_token = $JSON.next_page_token $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $JSON.meetings } } Clear-Variable -Name AuthToken } } } Function Get-ZoomMeetingParticipants { <# .SYNOPSIS Return a list of Zoom meeting participants for a given meeting and their connection details. .DESCRIPTION Return a list of Zoom meeting participants for a given meeting and their connection details. .PARAMETER MeetingID The ID of the meeting. Provide meeting ID or UUID. .PARAMETER Type Return live, past or past meetings with only one participant. Defaults to past .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER APIKey The Zoom API key for the application granted access to Zoom. .PARAMETER APISecret The Zoom API secret for the application granted access to Zoom. .PARAMETER AuthToken The authorization token used for this request. Normally obtained via Get-ZoomAccessToken .PARAMETER PageSize The number of results to return per page. Defaults to 30. .EXAMPLE Get-ZoomMeetingParticipants 928340928 -AuthToken $AuthToken Returns participant information from a specific meeting .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("uuid")] [string]$MeetingID, [Parameter(Mandatory = $False)] [ValidateSet('past', 'pastone', 'live')] [string]$Type = 'past', [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$APIKey, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$APISecret, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AuthToken, [Parameter(Mandatory=$False)] [ValidateRange(1,300)] [int]$PageSize = 30 ) Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($APIKey) { $AuthToken = Get-ZoomAccessToken -APIKey $APIKey -APISecret $APISecret } ElseIf (!$AuthToken) { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken } } Catch { Write-Error "Could not obtain authorization token." Get-JSONErrorStream -JSONResponse $_ } If ($AuthToken) { $Headers = @{ Authorization = "Bearer $AuthToken" } $URI = "https://api.zoom.us/v2/metrics/meetings/$MeetingID/participants" $Body = @{ type = $Type page_size = $PageSize } $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $JSON.participants # If there is more than one page, use next_page_token to iterate through the pages While ($JSON.next_page_token) { $Body.next_page_token = $JSON.next_page_token $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $JSON.participants } Clear-Variable -Name AuthToken } } } Function Get-ZoomMeetingParticipantQoS { <# .SYNOPSIS Return the participant QoS from a given meeting. .DESCRIPTION Return the participant QoS from a given meeting. .PARAMETER MeetingID Return information about a specific meeting. .PARAMETER ParticipantID Return information about a specific participant in a given meeting. Optional .PARAMETER Type Return live, past or past meetings with only one participant. Defaults to past .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER APIKey The Zoom API key for the application granted access to Zoom. .PARAMETER APISecret The Zoom API secret for the application granted access to Zoom. .PARAMETER AuthToken The authorization token used for this request. Normally obtained via Get-ZoomAccessToken .PARAMETER PageSize The number of results to return per page. Defaults to 30. .EXAMPLE Get-ZoomMeetingParticipantQoS 928340928 -AuthToken $AuthToken Returns all past Zoom meetings from the previous 24 hours .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("uuid")] [string]$MeetingID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$ParticipantID, [Parameter(Mandatory = $False)] [ValidateSet('past', 'pastone', 'live')] [string]$Type = 'past', [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$APIKey, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$APISecret, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$AuthToken, [Parameter(Mandatory=$False)] [ValidateRange(1,300)] [int]$PageSize = 30 ) Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } If ($APIKey) { $AuthToken = Get-ZoomAccessToken -APIKey $APIKey -APISecret $APISecret } ElseIf (!$AuthToken) { $AuthToken = Get-NectarMSTeamsSubscription -TenantName $TenantName | Get-MSGraphAccessToken } } Catch { Write-Error "Could not obtain authorization token." Get-JSONErrorStream -JSONResponse $_ } If ($AuthToken) { $Headers = @{ Authorization = "Bearer $AuthToken" } If ($ParticipantID) { $URI = "https://api.zoom.us/v2/metrics/meetings/$MeetingID/participants/$ParticipantID/qos" $Body = @{ type = $Type } $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $JSON } Else { $URI = "https://api.zoom.us/v2/metrics/meetings/$MeetingID/participants/qos" $Body = @{ type = $Type page_size = $PageSize } $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $JSON.participants # If there is more than one page, use next_page_token to iterate through the pages While ($JSON.next_page_token) { $Body.next_page_token = $JSON.next_page_token $JSON = Invoke-RestMethod -Method GET -uri $URI -Headers $Headers -Body $Body $JSON.participants } } Clear-Variable -Name AuthToken } } } ################################################################################################################################################# # # # Informational Functions # # # ################################################################################################################################################# Function Get-NectarCodecs { <# .SYNOPSIS Returns a list of Nectar 10 codecs used in calls .DESCRIPTION Returns a list of Nectar 10 codecs used in calls .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Get-NectarCodecs Returns all codecs .NOTES Version 1.1 #> [Alias("gnc")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/dapi/info/codecs?tenant=$TenantName" If (!$JSON) { Write-Error 'Codec not found.' } Else { Return $JSON } } Catch { Write-Error 'Unable to get codecs.' Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarExtCities { <# .SYNOPSIS Returns a list of cities found via IP geolocation .DESCRIPTION Most call records include the user's external IP address. Nectar 10 does a geo-IP lookup of the external IP address and stores the geographic information for later use. This command will return all the cities where Nectar 10 was able to successfully geolocate an external IP address. .PARAMETER SearchQuery The name of the city to locate. Can be a partial match, and may return more than one entry. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-NectarExtCities Returns the first 1000 cities sorted alphabetically. .EXAMPLE Get-NectarExtCities -ResultSize 5000 Returns the first 5000 cities sorted alphabetically. .EXAMPLE Get-NectarExtCities -SearchQuery Gu Returns all cities that contain the letters 'gu' .NOTES Version 1.0 #> [Alias("gneci")] Param ( [Parameter(Mandatory=$False)] [string]$SearchQuery, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize = 1000 ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $URI = "https://$Global:NectarCloud/dapi/info/external/cities" $Params = @{ 'pageSize' = $ResultSize } If ($SearchQuery) { $Params.Add('searchQuery',$SearchQuery) } If ($TenantName) { $Params.Add('Tenant',$TenantName) } $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $URI -Body $Params If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} $JSON.elements } Catch { Write-Error "No results. Try specifying a less-restrictive filter" Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarExtCountries { <# .SYNOPSIS Returns a list of 2-letter country codes found via IP geolocation .DESCRIPTION Most call records include the user's external IP address. Nectar 10 does a geo-IP lookup of the external IP address and stores the geographic information for later use. This command will return all the countries where Nectar 10 was able to successfully geolocate an external IP address. .PARAMETER SearchQuery The 2-letter country code to locate. Can be a partial match, and may return more than one entry. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-NectarExtCountries Returns all country codes sorted alphabetically. .EXAMPLE Get-NectarExtCountries -SearchQuery US Returns all country codes that contain the letters 'US' .NOTES Version 1.0 #> [Alias("gneco")] Param ( [Parameter(Mandatory=$False)] [string]$SearchQuery, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize = 1000 ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $URI = "https://$Global:NectarCloud/dapi/info/external/countries" $Params = @{ 'pageSize' = $ResultSize } If ($SearchQuery) { $Params.Add('searchQuery',$SearchQuery) } If ($TenantName) { $Params.Add('Tenant',$TenantName) } $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $URI -Body $Params If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} $JSON.elements } Catch { Write-Error "No results. Try specifying a less-restrictive filter" Get-JSONErrorStream -JSONResponse $_ } } } Function Get-NectarExtISPs { <# .SYNOPSIS Returns a list of ISPs found via IP geolocation .DESCRIPTION Most call records include the user's external IP address. Nectar 10 does a geo-IP lookup of the external IP address and stores the geographic information for later use. This command will return all the ISPs where Nectar 10 was able to successfully geolocate an external IP address. .PARAMETER SearchQuery The name of the city to locate. Can be a partial match, and may return more than one entry. .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-NectarExtISPs Returns the first 1000 ISPs sorted alphabetically. .EXAMPLE Get-NectarExtISPs -ResultSize 5000 Returns the first 5000 ISPs sorted alphabetically. .EXAMPLE Get-NectarExtISPs -SearchQuery Be Returns all ISPs that contain the letters 'be' .NOTES Version 1.0 #> [Alias("gneci")] Param ( [Parameter(Mandatory=$False)] [string]$SearchQuery, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [Parameter(Mandatory=$False)] [ValidateRange(1,100000)] [int]$ResultSize = 1000 ) Begin { Connect-NectarCloud } Process { Try { # Use globally set tenant name, if one was set and not explicitly included in the command If ($Global:NectarTenantName -And !$TenantName) { $TenantName = $Global:NectarTenantName } $URI = "https://$Global:NectarCloud/dapi/info/external/isps" $Params = @{ 'pageSize' = $ResultSize } If ($SearchQuery) { $Params.Add('searchQuery',$SearchQuery) } If ($TenantName) { $Params.Add('Tenant',$TenantName) } $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri $URI -Body $Params If ($TenantName) {$JSON.elements | Add-Member -Name 'TenantName' -Value $TenantName -MemberType NoteProperty} $JSON.elements } Catch { Write-Error "No results. Try specifying a less-restrictive filter" Get-JSONErrorStream -JSONResponse $_ } } } ################################################################################################################################################# ################################################################################################################################################# ## ## ## Endpoint Client Functions ## ## ## ################################################################################################################################################# ################################################################################################################################################# ################################################################################################################################################# # # # Controller Connection Functions # # # ################################################################################################################################################# Function Connect-EPCController { <# .SYNOPSIS Connects to EPC Controller and store the credentials for later use. .DESCRIPTION Connects to an Endpoint Client Controller and store the credentials for later use. .PARAMETER ControllerFQDN The FQDN of the Endpoint Client Controller .PARAMETER Credential The credentials used to access the EPC Controller. .PARAMETER StoredCredentialTarget Use stored credentials saved via New-StoredCredential. Requires prior installation of CredentialManager module via Install-Module CredentialManager, and running: Get-Credential | New-StoredCredential -Target MyEPCCreds -Persist LocalMachine .PARAMETER EnvFromFile Use a CSV file called EPCEnvList.csv located in the user's default Documents folder to show a list of environments to select from. Run [Environment]::GetFolderPath("MyDocuments") to find your default document folder. This parameter is only available if EPCEnvList.csv is found in the user's default Documents folder (ie: C:\Users\username\Documents) Also sets the default stored credential target to use for the selected environment. Requires prior installation and configuration of CredentialManager PS add-in. EPCEnvList.csv must have a header with two columns defined as "Environment, StoredCredentialTarget". Each environment and StoredCredentialTarget (if used) should be on their own separate lines .EXAMPLE $Cred = Get-Credential Connect-EPControllerFQDN -Credential $cred -ControllerFQDN contoso.nectar.services Connects to the contoso.nectar.services EPC Controller using the credentials supplied to the Get-Credential command .EXAMPLE Connect-EPControllerFQDN -ControllerFQDN contoso.nectar.services -StoredCredentialTarget MyEPCCreds Connects to contoso.nectar.services EPC Controller using previously stored credentials called MyEPCCreds .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipeline, Mandatory=$False)] # [ValidateScript ({ # If ($_ -Match "^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$") { # $True # } # Else { # Throw "ERROR: Endpoint Client Controller name must be in FQDN format." # } # })] [string] $ControllerFQDN, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.Credential()] [PSCredential] $Credential, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$StoredCredentialTarget, [switch]$UseXML ) DynamicParam { $DefaultDocPath = [Environment]::GetFolderPath("MyDocuments") $EnvPath = "$DefaultDocPath\EPCEnvList.csv" If (Test-Path $EnvPath -PathType Leaf) { # Set the dynamic parameters' name $ParameterName = 'EnvFromFile' # Create the dictionary $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary # Create the collection of attributes $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] # Create and set the parameters' attributes $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute $ParameterAttribute.Mandatory = $False $ParameterAttribute.Position = 1 # Add the attributes to the attributes collection $AttributeCollection.Add($ParameterAttribute) # Generate and set the ValidateSet $EnvSet = Import-Csv -Path $EnvPath $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($EnvSet.Environment) # Add the ValidateSet to the attributes collection $AttributeCollection.Add($ValidateSetAttribute) # Create and return the dynamic parameter $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection) $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter) Return $RuntimeParameterDictionary } } Begin { # Bind the dynamic parameter to a friendly variable If (Test-Path $EnvPath -PathType Leaf) { If ($PsBoundParameters[$ParameterName]) { $ControllerFQDN = $PsBoundParameters[$ParameterName] Write-Verbose "ControllerFQDN: $ControllerFQDN" # Get the array position of the selected environment $EnvPos = $EnvSet.Environment.IndexOf($ControllerFQDN) # Check for stored credential target in EPCEnvList.csv and use if available $StoredCredentialTarget = $EnvSet[$EnvPos].StoredCredentialTarget Write-Verbose "StoredCredentialTarget: $StoredCredentialTarget" } } <# The New-WebServiceProxy command is not supported on PS versions higher than 5.1. On PS 6.0+, we have to use Invoke-WebRequest, which works, but is more verbose and doesn't utilize the WSDL files which creates specific objects for the results. Do a check and set a global variable which will determine if the function will use New-WebServiceProxy or Invoke-WebRequest #> If ($PSVersionTable.PSVersion.Major -gt 5) { $Global:EPC_UseWSDL = $FALSE } Else { $Global:EPC_UseWSDL = $TRUE } Write-Verbose "Setting global UseWSDL variable to $Global:EPC_UseWSDL" } Process { # Need to force TLS 1.2, if not already set If ([Net.ServicePointManager]::SecurityProtocol -ne 'Tls12') { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } # Ask for the tenant name if global EPCController tenant variable not available and not entered on command line If ((-not $Global:EPControllerFQDN) -And (-not $ControllerFQDN)) { $ControllerFQDN = Read-Host "Enter the Endpoint Client Controller FQDN" } ElseIf (($Global:EPControllerFQDN) -And (-not $ControllerFQDN)) { $ControllerFQDN = $Global:EPControllerFQDN } $RegEx = "^(?:http(s)?:\/\/)?([\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+)$" $FQDNMatch = Select-String -Pattern $Regex -InputObject $ControllerFQDN $EPControllerFQDN = $FQDNMatch.Matches.Groups[2].Value # Ask for credentials if global EPCController creds aren't available If (((-not $Global:EPControllerCred) -And (-not $Credential)) -Or (($Global:EPControllerFQDN -ne $EPControllerFQDN) -And (-Not $Credential)) -And (-Not $StoredCredentialTarget)) { $Credential = Get-Credential } ElseIf ($Global:EPControllerCred -And (-not $Credential)) { $Credential = $Global:EPControllerCred } # Pull stored credentials if specified If ($StoredCredentialTarget) { Try { $Credential = Get-StoredCredential -Target $StoredCredentialTarget } Catch { Write-Error "Cannot find stored credential for target: $StoredCredentialTarget" } } # Get the WSDL If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { Write-Verbose 'Using WSDL' If ($Global:EPCWSDL_ActiveCtrl -and $Global:EPControllerFQDN -eq $EPControllerFQDN) { Write-Verbose 'Using WSDL from global variable' $EPC_ActiveCtrl = $Global:EPCWSDL_ActiveCtrl $EPC_ResGrpMgmt = $Global:EPCWSDL_ResGrpMgmt $EPC_SvcMgmt = $Global:EPCWSDL_SvcMgmt } ElseIf ((!$Global:EPCWSDL_ActiveCtrl -and $Global:EPControllerFQDN -eq $EPControllerFQDN) -Or !$Global:EPCWSDL_ActiveCtrl) { Write-Verbose "Loading WSDL from $EPControllerFQDN" $EPC_ActiveCtrl = New-WebServiceProxy "https://$EPControllerFQDN/telchemywebservices/services/telchemyActiveCtrlService?wsdl" -Namespace EPC.ActiveCtrl -Class ActiveCtrl $EPC_ResGrpMgmt = New-WebServiceProxy "https://$EPControllerFQDN/telchemywebservices/services/telchemyRsrcGroupMgmtService?wsdl" -Namespace EPC.ResGrpMgmt -Class ResGrpMgmt $EPC_SvcMgmt = New-WebServiceProxy "https://$EPControllerFQDN/telchemywebservices/services/telchemySvcMgmtService?wsdl" -Namespace EPC.SvcMgmt -Class SvcMgmt } Else { Write-Error "There is already an active connection to $($Global:EPControllerFQDN). Please open a new PowerShell window to connect to a new controller." Return } } If ((-not $Global:EPControllerCred) -Or (-not $Global:EPControllerFQDN) -Or ($Global:EPControllerFQDN -ne $EPControllerFQDN)) { # Validate login credentials by running a simple API query If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { # Store creds in EPC credential object $EPCCred = New-Object -TypeName EPC.ActiveCtrl.CredentialsType $EPCCred.username = $Credential.UserName $EPCCred.password = $Credential.GetNetworkCredential().Password Write-Verbose 'Using WSDL to test access' $EPCAPIVersionParams = New-Object -TypeName EPC.ActiveCtrl.GetAPIVersionParametersType $EPCAPIVersionParams.credentials = $EPCCred $EPCAPIVersion = $EPC_ActiveCtrl.getAPIVersion($EPCAPIVersionParams) $LoginResult = $EPCAPIVersion.result } Else { $ProgressPreference = 'SilentlyContinue' Write-Verbose 'Using WebXML to test access' [xml]$SOAPReq = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:urn='urn:telchemyActiveCtrl'> <soapenv:Header/> <soapenv:Body> <urn:getAPIVersionParameters> <credentials> <username>$($Credential.UserName)</username> <password>$($Credential.GetNetworkCredential().Password)</password> </credentials> </urn:getAPIVersionParameters> </soapenv:Body> </soapenv:Envelope>" Write-Verbose $SOAPReq.OuterXML $SOAPFQDN = "https://$EPControllerFQDN/telchemywebservices/services/telchemyActiveCtrlService" Write-Verbose $SOAPFQDN [xml]$XMLResponse = (Invoke-WebRequest -Method POST -URI $SOAPFQDN -Body $SOAPReq -ContentType 'text/xml').Content $LoginResult = $XMLResponse.Envelope.Body.getAPIVersionResults.result } If ($LoginResult -ne 'success') { Write-Error "Could not connect to $EPControllerFQDN using $($Credential.UserName)" Throw $LoginResult } Else { Write-Host -ForegroundColor Green "Successful API connection to " -NoNewLine Write-Host -ForegroundColor Yellow "https://$EPControllerFQDN" -NoNewLine Write-Host -ForegroundColor Green " using " -NoNewLine Write-Host -ForegroundColor Yellow ($Credential).UserName $Global:EPControllerFQDN = $EPControllerFQDN $Global:EPControllerCred = $Credential $Global:EPCWSDL_ActiveCtrl = $EPC_ActiveCtrl $Global:EPCWSDL_ResGrpMgmt = $EPC_ResGrpMgmt $Global:EPCWSDL_SvcMgmt = $EPC_SvcMgmt If ($PSVersionTable.PSVersion.Major -gt 5) { # Check for the PowerHTML module and install if not present. Needed for parsing web pageSize If (!(Get-InstalledModule -Name PowerHTML)) { $Null = Install-Module -Name PowerHTML -Scope CurrentUser -Force } } Write-Verbose "Setting global UseWSDL variable to $Global:EPC_UseWSDL" } } } } Function Get-EPCConnectedController { <# .SYNOPSIS Returns the FQDN of the connected controller .DESCRIPTION Returns the FQDN of the connected controller .NOTES Version 1.0 #> If ($Global:EPControllerFQDN) { Return $Global:EPControllerFQDN } Else { Return 'Not connected to a controller' } } Function Get-EPCWebSessionCookie { <# .SYNOPSIS Creates a web session cookie for use with commands that can't use the EPC API .DESCRIPTION Connects to an Endpoint Client Controller and store the credentials for later use. Must have already successfully connected via Connect-EPCController .NOTES Version 1.0 #> [cmdletbinding()] param () Connect-EPCController $ProgressPreference = 'SilentlyContinue' # Check for existing global session cookie. Connect if not available and save the web session variable in a global variable If (!$Global:EPCSessionCookie) { Write-Verbose 'Global session cookie does not exist' $Dashboard = Invoke-WebRequest -UseBasicParsing -Uri "https://$EPControllerFQDN/dashboard.htm" -SessionVariable 'EPCSession' $SecurityCheck = Invoke-WebRequest -Uri "https://$EPControllerFQDN/j_security_check" -Method POST -Body "j_username=$($Global:EPControllerCred.UserName)&j_password=$($Global:EPControllerCred.GetNetworkCredential().Password)" -WebSession $EPCSession # Verify that session is active. We do this by looking for the existence of a single link on the dashboard page. If there is, this means the session expired and the base login page is being shown. # If there are numerous links, this indicates a successful login. $Dashboard = Invoke-WebRequest -UseBasicParsing -Uri "https://$EPControllerFQDN/dashboard.htm" -WebSession $EPCSession If ($Dashboard.Links.Count -gt 1) { Write-Host -ForegroundColor Green "Successful web session connection to " -NoNewLine Write-Host -ForegroundColor Yellow "https://$EPControllerFQDN" -NoNewLine Write-Host -ForegroundColor Green " using " -NoNewLine Write-Host -ForegroundColor Yellow ($Global:EPControllerCred).UserName $Global:EPCSessionCookie = $EPCSession } Else { Throw "Could not connect to $EPControllerFQDN web session using $($Credential.UserName)" } } Else { # Verify that session is active. We do this by looking for the existence of a single link on the dashboard page. If there is, this means the session expired and the base login page is being shown. # If there are numerous links, this indicates the session is still active $Dashboard = Invoke-WebRequest -UseBasicParsing -Uri "https://$EPControllerFQDN/dashboard.htm" -WebSession $Global:EPCSessionCookie Write-Verbose "Session verify against $EPControllerFQDN" If ($Dashboard.Links.Count -eq 1) { # Re-authenticate and update global session cookie Write-Verbose 'Global session cookie expired' $Dashboard = Invoke-WebRequest -UseBasicParsing -Uri "https://$EPControllerFQDN/dashboard.htm" -SessionVariable 'EPCSession' $SecurityCheck = Invoke-WebRequest -Uri "https://$EPControllerFQDN/j_security_check" -Method POST -Body "j_username=$($Global:EPControllerCred.UserName)&j_password=$($Global:EPControllerCred.GetNetworkCredential().Password)" -WebSession $EPCSession $Dashboard = Invoke-WebRequest -UseBasicParsing -Uri "https://$EPControllerFQDN/dashboard.htm" -WebSession $EPCSession If ($Dashboard.Links.Count -gt 1) { Write-Verbose "Successful auth cookie regeneration against $EPControllerFQDN" $Global:EPCSessionCookie = $EPCSession } Else { Throw "Could not regenerate auth cookie against $EPControllerFQDN using $($Credential.UserName)" } } Else { Write-Verbose "Session verified against $EPControllerFQDN" } } } Function Disconnect-EPCController { <# .SYNOPSIS Disconnects from any active Endpoint Client Controller .DESCRIPTION Essentially deletes any stored credentials and FQDN from global variables. If PS version is less than 6, this doesn't currently work because there apparently isn't a way to remove the WSDL namespace from memory. Thiss means that connecting to a new EPC controller will still try to use the WSDL associated with the original controller. Any API commands will fail. Therefore, there is no useful reason to disconnect. .EXAMPLE Disconnect-EPCController Disconnects from all active connections to an EPC Controller .NOTES Version 1.0 #> [cmdletbinding()] param () $VariableNames = 'EPControllerCred','EPControllerFQDN','EPCSessionCookie','EPCWSDL_ActiveCtrl','EPCWSDL_ResGrpMgmt','EPCWSDL_SvcMgmt', 'EPC_UseWSDL' ForEach ($Variable in $VariableNames) { Remove-Variable $Variable -Scope Global -ErrorAction:SilentlyContinue } } ################################################################################################################################################# # # # EPC Test Point Functions # # # ################################################################################################################################################# Function Get-EPCTestPoint { <# .SYNOPSIS Return information about a given EPC test point .DESCRIPTION Return information about a given EPC test point .PARAMETER Name The name of a specific test point to retrieve information about. Will do partial matches. .PARAMETER Status Only return information about test points with a specified status .PARAMETER OrgID Only return information about test points within a specified organization .PARAMETER Version Only return information about test points that match the given version .PARAMETER RGName Only return information about test points that are within a given resource group Can specify either RG name or ID .PARAMETER RGID Only return information about test points that are within a given resource group Can specify either RG name or ID .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-EPCTestPoint Returns information about the first 1000 test points .EXAMPLE Get-EPCTestPoint -Name 'TFerguson Laptop' Returns information about a specific test point using the test point name .EXAMPLE Get-EPCTestPoint -Status Connected -OrgID Contoso Returns information about connected test points in the Contoso organization .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Name, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [ValidateSet('Connected','NotConnected','Reachable','Unknown','Unlicensed', IgnoreCase=$True)] [string]$Status, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$OrgID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$RGName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [int[]]$RGID, [Parameter(Mandatory=$False)] [int]$ResultSize = 1000, [switch]$UseXML ) Begin { Connect-EPCController If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCCred = New-Object -TypeName EPC.ActiveCtrl.CredentialsType $EPCCred.username = $Global:EPControllerCred.UserName $EPCCred.password = $Global:EPControllerCred.GetNetworkCredential().Password } Else { $ProgressPreference = 'SilentlyContinue' } } Process { If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { Write-Verbose 'Using WSDL' $EPCListTestPointParams = New-Object -TypeName EPC.ActiveCtrl.ListTestPointsParametersType $EPCListTestPointParams.credentials = $EPCCred $EPCListTestPointParams.maxNumber = $ResultSize # If any of the below parameters are specified, apply a filter If ($Name -Or $Status -Or $OrgID -Or $Version -Or $RGName -Or $RGID) { $EPCListTestPointFilterParams = New-Object -TypeName EPC.ActiveCtrl.ListTestPointsParametersTypeFilter If ($Name) { $EPCListTestPointFilterParams.namesubstring = $Name } If ($OrgID) { $EPCListTestPointFilterParams.orgID = $OrgID } If ($Version) { $EPCListTestPointFilterParams.version = $Version } If ($Status) { $EPCListTestPointFilterParams.status = $Status; $EPCListTestPointFilterParams.statusSpecified = $TRUE } If ($RGName) { $RGID = (Get-EPCResourceGroup | Where {$_.RGName -eq $RGName}).RGID } If ($RGID) { $EPCListTestPointFilterParams.RGList = $RGID } $EPCListTestPointParams.Filter = $EPCListTestPointFilterParams } $TestPointResults = $Global:EPCWSDL_ActiveCtrl.listTestPoints($EPCListTestPointParams) Write-Verbose $TestPointResults.result If ($TestPointResults.result -eq 'success') { Return $TestPointResults.testptList } Else { Throw $TestPointResults.result } } Else { Write-Verbose 'Using WebXML' [xml]$SOAPReq = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:urn='urn:telchemyActiveCtrl'> <soapenv:Header/> <soapenv:Body> <urn:listTestPointsParameters> <credentials> <username>$($Global:EPControllerCred.UserName)</username> <password>$($Global:EPControllerCred.GetNetworkCredential().Password)</password> </credentials> <maxNumber>$ResultSize</maxNumber> </urn:listTestPointsParameters> </soapenv:Body> </soapenv:Envelope>" # If any of the below parameters are specified, apply a filter If ($Name -Or $Status -Or $OrgID -Or $Version -Or $OSArch -Or $RGName -Or $RGID) { $FilterXMLElement = $SOAPReq.Envelope.Body.listTestPointsParameters.AppendChild($SOAPReq.CreateElement('filter')) If ($Name) { $NewXMLElement = $FilterXMLElement.AppendChild($SOAPReq.CreateElement('name-substring')) $NULL = $NewXMLElement.AppendChild($SOAPReq.CreateTextNode($Name)) } If ($Status) { If ($Status -eq 'NotConnected') { $StatusFormatted = 'not connected' } Else { $StatusFormatted = $Status } $NewXMLElement = $FilterXMLElement.AppendChild($SOAPReq.CreateElement('status')) $NULL = $NewXMLElement.AppendChild($SOAPReq.CreateTextNode($StatusFormatted.ToLower())) } If ($OrgID) { $NewXMLElement = $FilterXMLElement.AppendChild($SOAPReq.CreateElement('orgID')) $NULL = $NewXMLElement.AppendChild($SOAPReq.CreateTextNode($OrgID.ToLower())) } If ($Version) { $NewXMLElement = $FilterXMLElement.AppendChild($SOAPReq.CreateElement('version')) $NULL = $NewXMLElement.AppendChild($SOAPReq.CreateTextNode($Version)) } If ($RGName) { $RGID = (Get-EPCResourceGroup | Where {$_.RGName -eq $RGName}).RGID } If ($RGID) { $RGXMLElement = $FilterXMLElement.AppendChild($SOAPReq.CreateElement('RGList')) $NewXMLElement = $RGXMLElement.AppendChild($SOAPReq.CreateElement('rg')) $NULL = $NewXMLElement.AppendChild($SOAPReq.CreateTextNode($RGID)) } } Write-Verbose $SOAPReq.OuterXML $SOAPFQDN = "https://$Global:EPControllerFQDN/telchemywebservices/services/telchemyActiveCtrlService" Write-Verbose $SOAPFQDN [xml]$XMLResponse = (Invoke-WebRequest -Method POST -URI $SOAPFQDN -Body $SOAPReq -ContentType 'text/xml').Content $TestPointResults = $XMLResponse.Envelope.Body.listTestPointsResults If ($TestPointResults.result -eq 'success') { Return $TestPointResults.testptList.testpt } Else { Throw $TestPointResults.result } } } } Function Set-EPCTestPoint { <# .SYNOPSIS Update the name and/or description for a given EPC test point .DESCRIPTION Update the name and/or description for a given EPC test point .PARAMETER UUID The UUID of the test point to update .PARAMETER Name The display name to set on the test point .PARAMETER Description The description to set on the test point .EXAMPLE Set-EPCTestPoint -UUID d4a1437f-1f18-11ec-cf33-0daedf04882a -Name 'New Name' -Description 'Updated description' Updates the selected test point's name and description .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("ID")] [string]$UUID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [Alias("UserDisplayName")] [string]$DisplayName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Description ) Begin { Get-EPCWebSessionCookie $ProgressPreference = 'SilentlyContinue' } Process { $TPFQDN = "https://$EPControllerFQDN/test/testPoint.do?action=2" # If name isn't set, pull the name from the existing record, otherwise it will replace the name with its IP address If (!$DisplayName) { $DisplayName = (Get-EPCTestPoint -UUID $UUID).Name } $TPBody = @{ tptype = 1 uuid = $UUID name = $DisplayName } If ($Description) { $TPBody.Add('description',$Description -Replace "[^\sa-zA-Z0-9.-]") } Write-Verbose "UUID: $UUID, Name: $DisplayName" $TPResult = Invoke-WebRequest -Method POST -Uri $TPFQDN -Body $TPBody -WebSession $Global:EPCSessionCookie } } Function Get-EPCTestPointInterface { <# .SYNOPSIS Return network interface inforemation for a given EPC test point .DESCRIPTION Return network interface inforemation for a given EPC test point .PARAMETER ID The ID of the test point to retrieve information about .EXAMPLE Get-EPCTestPointInterface -ID d4a1437f-1f18-11ec-cf33-0daedf04882a Returns network interface information about a specific test point .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias("testptID")] [string]$UUID, [switch]$UseXML ) Begin { Connect-EPCController If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCCred = New-Object -TypeName EPC.ActiveCtrl.CredentialsType $EPCCred.username = $Global:EPControllerCred.UserName $EPCCred.password = $Global:EPControllerCred.GetNetworkCredential().Password } Else { $ProgressPreference = 'SilentlyContinue' } } Process { If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { Write-Verbose 'Using WSDL to pull interface list' $EPCListTestPointInterfaceParams = New-Object -TypeName EPC.ActiveCtrl.ListTestPointInterfaceParametersType $EPCListTestPointInterfaceParams.credentials = $EPCCred $EPCListTestPointInterfaceParams.testptID = $UUID $TestPointInterfaceResults = $Global:EPCWSDL_ActiveCtrl.listTestPointInterfaces($EPCListTestPointInterfaceParams) Write-Verbose $TestPointInterfaceResults.result If ($TestPointInterfaceResults.result -eq 'success') { Return $TestPointInterfaceResults.InterfaceList } Else { Throw $TestPointInterfaceResults.result } } Else { Write-Verbose 'Using XML to pull interface list' [xml]$SOAPReq = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:urn='urn:telchemyActiveCtrl'> <soapenv:Header/> <soapenv:Body> <urn:listTestPointInterfaceParameters> <credentials> <username>$($Global:EPControllerCred.UserName)</username> <password>$($Global:EPControllerCred.GetNetworkCredential().Password)</password> </credentials> <testptID>$UUID</testptID> </urn:listTestPointInterfaceParameters> </soapenv:Body> </soapenv:Envelope>" $SOAPFQDN = "https://$Global:EPControllerFQDN/telchemywebservices/services/telchemyActiveCtrlService" [xml]$XMLResponse = (Invoke-WebRequest -Method POST -URI $SOAPFQDN -Body $SOAPReq -ContentType 'text/xml').Content $TestPointInterfaceResults = $XMLResponse.Envelope.Body.listTestPointInterfaceResults If ($TestPointInterfaceResults.result -eq 'success') { [psobject]$TestPointInterfaces = $TestPointInterfaceResults.InterfaceList.interface | ConvertFrom-XMLElement Return $TestPointInterfaces } Else { Throw $TestPointInterfaceResults.result } } } } ################################################################################################################################################# # # # EPC Test Group Functions # # # ################################################################################################################################################# Function Get-EPCTestGroups { <# .SYNOPSIS Returns a list of EPC test groups and their associated IDs .DESCRIPTION Returns a list of EPC test groups and their associated IDs .PARAMETER ResultSize The number of results to return. Defaults to 1000. .EXAMPLE Get-EPCTestGroups .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(Mandatory=$False)] [int]$ResultSize = 1000, [switch]$UseXML ) Begin { Get-EPCWebSessionCookie $ProgressPreference = 'SilentlyContinue' } Process { $TGFQDN = "https://$Global:EPControllerFQDN/test/testGroup.do?action=14&numRecs=$ResultSize" $TGResult = Invoke-WebRequest -Method GET -Uri $TGFQDN -WebSession $Global:EPCSessionCookie # Parse out tables and focus on the 4th table which contains a list of all the test groups If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $Tables = $TGResult.ParsedHtml.body.getElementsByTagName('Table') $TestGroupRows = $Tables[3].rows $RowCount = $TestGroupRows.length - 1 $RowStart = 3 } Else { # PS v.6+ doesn't have ParsedHtml in Invoke-WebRequest, so we have to use different method $Tables = $TGResult.Content | ConvertFrom-Html $Tables = $Tables.SelectNodes("//table") $TestGroupRows = $Tables[3].SelectNodes("//tr") $RowCount = $TestGroupRows.Count - 1 $RowStart = 4 } $TestGroups = @() $RegEx = "viewtestgroup.htm\?tgid\=(\d+)" For ($RowNum=$RowStart; $RowNum -lt $RowCount; $RowNum++) { # Parse the Test Group ID out of the InnerHTML Write-Verbose "TestGroup: $($TestGroupRows.Item($RowNum).InnerText)" $FQDNMatch = Select-String -Pattern $Regex -InputObject $TestGroupRows.Item($RowNum).InnerHTML $TGID = $FQDNMatch.Matches.Groups[1].Value $Item = [PSCustomObject][Ordered]@{ Name = $TestGroupRows.Item($RowNum).InnerText TGID = $TGID } $TestGroups += $Item } If ($TestGroups) { Return $TestGroups } Else { Write-Error "Could not find any test groups" } } } Function Get-EPCTestGroupType { <# .SYNOPSIS Returns the group type and group type ID of a test group .DESCRIPTION Returns the group type and group type ID of a test group .PARAMETER Name The name of the test group to return the type and ID .EXAMPLE Get-EPCTestGroupType -Name 'TG1' Returns the group type and ID of the TG1 test group .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$Name, [switch]$UseXML ) Begin { Get-EPCWebSessionCookie $ProgressPreference = 'SilentlyContinue' } Process { $TGID = (Get-EPCTestGroups | Where {$_.Name -eq $Name}).TGID If (!$TGID) { Throw "Cound not find a test group with name $Name" } $TGFQDN = "https://$Global:EPControllerFQDN/test/edittestgroup.htm?testgroupid=$TGID" $TGResult = Invoke-WebRequest -Method GET -Uri $TGFQDN -WebSession $Global:EPCSessionCookie # Find the row where Group Type is mentioned and get the group type If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $Tables = $TGResult.ParsedHtml.body.getElementsByTagName('Table') $TGType = ($Tables[0].Rows | Where {$_.innerText -like 'Group Type*'}).InnerText } Else { # PS v.6+ doesn't have ParsedHtml in Invoke-WebRequest, so we have to use different method $Tables = $TGResult.Content | ConvertFrom-Html $Tables = $Tables.SelectNodes("//table") $TGType = ($Tables[0].SelectNodes("//tr") | Where {$_.innerText -like '*Group Type*'}).InnerText } $RegEx = "Group Type:\W*(\w*)" $FQDNMatch = Select-String -Pattern $RegEx -InputObject $TGType $TGType = $FQDNMatch.Matches.Groups[1].Value Switch ($TGType) { 'Network Entity' { $TGTypeID = 0 } 'Agent' { $TGTypeID = 1 } 'DHCP' { $TGTypeID = 2 } 'DNS' { $TGTypeID = 3 } 'HTTP' { $TGTypeID = 4 } 'POP3' { $TGTypeID = 5 } 'SIP Endpoint' { $TGTypeID = 6 } 'SMTP' { $TGTypeID = 7 } } If ($TGTypeID) { $TGTypeInfo = [PSCustomObject][Ordered]@{ ID = $TGTypeID Type = $TGType } Return $TGTypeInfo } Else { Throw "Could not find a test group type with name $TGType" } } } Function Set-EPCTestGroup { <# .SYNOPSIS Update the name of an existing test group .DESCRIPTION Update the name of an existing test group .PARAMETER Name The name of the test group to update .PARAMETER NewName The new name of the test group .PARAMETER TGID The ID of a test group. If both Name and TGID are specified, Name will take priority. .EXAMPLE Set-EPCTestGroup -Name 'Existing Name' -NewName 'New Name' .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Name, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$NewName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [int]$TGID ) Begin { Get-EPCWebSessionCookie $ProgressPreference = 'SilentlyContinue' } Process { If ($Name) { $TGID = (Get-EPCTestGroups | Where {$_.Name -eq $Name}).TGID } $TGFQDN = "https://$Global:EPControllerFQDN/test/testGroup.do?action=4" $TGBody = @{ testgroupid = $TGID testgroupname = $NewName } $TGResult = Invoke-WebRequest -Method POST -Uri $TGFQDN -Body $TGBody -WebSession $Global:EPCSessionCookie } } Function New-EPCTestGroup { <# .SYNOPSIS Create a new EPC test group .DESCRIPTION Create a new EPC test group .PARAMETER Name The name of the test group .PARAMETER Type The type to assign to the test group .EXAMPLE New-EPCTestGroup -Name MyGroup -Type Agent Creates an agent test group called MyGroup .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$Name, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [ValidateSet('Agent','Network Entity','DHCP','DNS','HTTP','POP3','SIP Endpoint','SMTP', IgnoreCase=$True)] [string]$Type = 'Agent' ) Begin { Get-EPCWebSessionCookie $ProgressPreference = 'SilentlyContinue' } Process { $TGFQDN = "https://$Global:EPControllerFQDN/test/testGroup.do?action=3" Switch ($Type) { 'Network Entity' { $TGTypeID = 0 } 'Agent' { $TGTypeID = 1 } 'DHCP' { $TGTypeID = 2 } 'DNS' { $TGTypeID = 3 } 'HTTP' { $TGTypeID = 4 } 'POP3' { $TGTypeID = 5 } 'SIP Endpoint' { $TGTypeID = 6 } 'SMTP' { $TGTypeID = 7 } } $TGBody = @{ testgroupname = $Name testgrouptype = $TGTypeID } $TGResult = Invoke-WebRequest -Method POST -Uri $TGFQDN -Body $TGBody -WebSession $Global:EPCSessionCookie } } Function Remove-EPCTestGroup { <# .SYNOPSIS Removes an existing EPC test group .DESCRIPTION Removes an existing EPC test group .EXAMPLE Remove-EPCTestGroup -Name 'My Test Group' .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Name, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [int]$TGID ) Begin { Get-EPCWebSessionCookie $ProgressPreference = 'SilentlyContinue' } Process { If ($Name) { $TGID = (Get-EPCTestGroups | Where {$_.Name -eq $Name}).TGID } $TGFQDN = "https://$Global:EPControllerFQDN/test/testGroup.do?action=6&groupid=$TGID" $TGResult = Invoke-WebRequest -Method GET -Uri $TGFQDN -WebSession $Global:EPCSessionCookie } } Function Get-EPCTestGroupMembers { <# .SYNOPSIS Returns a list of test points associated with a given test group .DESCRIPTION Returns a list of test points associated with a given test group .EXAMPLE Get-EPCTestGroupMembers -ID 4 .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$Name, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [int]$TGID, [switch]$UseXML ) Begin { Get-EPCWebSessionCookie $ProgressPreference = 'SilentlyContinue' } Process { If ($Name) { $TGID = (Get-EPCTestGroups | Where {$_.Name -eq $Name}).TGID } If ($TGID) { $Name = (Get-EPCTestGroups | Where {$_.TGID -eq $TGID}).Name } [System.Collections.ArrayList]$TGMembers = @() $TGFQDN = "https://$Global:EPControllerFQDN/test/viewtestgroup.htm?tgid=$TGID" $TGResult = Invoke-WebRequest -Method GET -Uri $TGFQDN -WebSession $Global:EPCSessionCookie # Parse out tables and focus on the 2nd table which contains a list of all the test group members If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { Write-Verbose 'Using WSDL method' $Tables = $TGResult.ParsedHtml.body.getElementsByTagName('Table') $TGMemberRows = $Tables[1].rows $RowCount = $TGMemberRows.length - 1 $RowStart = 2 For ($RowNum = $RowStart; $RowNum -lt $RowCount; $RowNum++) { $Item = [PSCustomObject][Ordered]@{ testPointName = $TGMemberRows.Item($RowNum).cells[0].InnerText groupName = $TGMemberRows.Item($RowNum).cells[1].InnerText Type = $TGMemberRows.Item($RowNum).cells[2].InnerText testInterfaceName = $TGMemberRows.Item($RowNum).cells[3].InnerText tpIp = $TGMemberRows.Item($RowNum).cells[4].InnerText TestGroupName = $Name } $TGMembers += $Item } } Else { # PS v.6+ doesn't have ParsedHtml in Invoke-WebRequest, so we have to use different method $Tables = $TGResult.Content | ConvertFrom-Html $Tables = $Tables.SelectNodes("//table") $TGMemberRows = $Tables[1].SelectNodes("//tr") $RowCount = $TGMemberRows.Count $RowStart = 4 For ($RowNum = $RowStart; $RowNum -lt $RowCount; $RowNum++) { $Item = [PSCustomObject][Ordered]@{ testPointName = $TGMemberRows.Item($RowNum).SelectNodes("/tr|td")[0].InnerText groupName = $TGMemberRows.Item($RowNum).SelectNodes("/tr|td")[1].InnerText -Replace '/', '/' Type = $TGMemberRows.Item($RowNum).SelectNodes("/tr|td")[2].InnerText testInterfaceName = $TGMemberRows.Item($RowNum).SelectNodes("/tr|td")[3].InnerText tpIp = $TGMemberRows.Item($RowNum).SelectNodes("/tr|td")[4].InnerText TestGroupName = $Name } $TGMembers += $Item } } If ($TGMembers) { Return $TGMembers } Else { Throw "Could not find any test group members" } } } Function Add-EPCTestGroupMember { <# .SYNOPSIS Adds an EPC test point to an EPC test group .DESCRIPTION Adds an EPC test point to an EPC test group .PARAMETER TestGroupName The name of the test group to add the member to .PARAMETER ResourceGroupName The name of the resource group associated with this test group .PARAMETER TestPointName The name of the test point to add to the test group .PARAMETER InterfaceID The interface ID of the test point .PARAMETER IPAddress The IP address associated with the given interface .EXAMPLE Add-EPCTestGroupMember -TestGroupName MyTestGroup -ResourceGroupName MyRG -TestPointName 'TFerguson laptop' -InterfaceID 'Primary Network Adapter' -IPAddress '192.168.1.20' Adds the given test point to the MyTestGroup test group .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$TestGroupName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$ResourceGroupName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [Alias('name')] [string]$TestPointName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$InterfaceID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$IPAddress ) Begin { Get-EPCWebSessionCookie $ProgressPreference = 'SilentlyContinue' # Get a list of all test points and resource groups for returning IDs $TPList = Get-EPCTestPoint -ResultSize 10000 $RGList = Get-EPCResourceGroup } Process { # Get the test group ID [int]$TestGroupID = (Get-EPCTestGroups | Where {$_.Name -eq $TestGroupName}).TGID If (!$TestGroupID) { Throw "Could not find a test group with name $TestGroupName" } # Get the resource group ID $TargetRG = $RGList | Where {$_.RGName -eq $ResourceGroupName} $RGID = $TargetRG.RGID [string]$RGName = $TargetRG.RGName [int]$RGParentID = $TargetRG.RGParentID While ($RGParentID -ne 0) { $NewParent = $RGList | Where {$_.RGID -eq $RGParentID} $RGName = "$($NewParent.RGName) / $RGName" Write-Verbose $RGName [int]$RGParentID = $NewParent.RGParentID } Write-Verbose "RGName: $RGName" Write-Verbose "RGID: $RGID" # Obtain list of existing group members [System.Collections.ArrayList]$TGMembers = @(Get-EPCTestGroupMembers -Name $TestGroupName | Select-Object TestPointName, GroupName, TestInterfaceName, TPIP) # Add fields for resource group and test point IDs $TGMembers | Add-Member -NotePropertyName 'resgroupid' -NotePropertyValue $NULL $TGMembers | Add-Member -NotePropertyName 'testPointId' -NotePropertyValue $NULL #Add the RGID and TPID for each entry ForEach ($Member in $TGMembers) { $Member.TestPointID = ($TPList | Where {$_.Name -eq $Member.TestPointName}).TestPtID # Parse the RG Name which may be in parent / child format $RegEx = "/?(\w+)$" $FQDNMatch = Select-String -Pattern $Regex -InputObject $Member.GroupName $EPCGroupName = $FQDNMatch.Matches.Groups[1].Value $Member.ResGroupID = ($RGList | Where {$_.RGName -eq $EPCGroupName}).RGID } $NewItem = [PSCustomObject][Ordered]@{ testPointName = $TestPointName groupName = $ResourceGroupName testInterfaceName = $InterfaceID tpIp = $IPAddress resgroupid = $RGID testPointId = ($TPList | Where {$_.Name -eq $TestPointName}).TestPtID } $TGMembers += $NewItem } End { # Convert test point array to XML $TGMemberXML = New-EPCTestPointXML -Data $TGMembers $TGFQDN = "https://$Global:EPControllerFQDN/test/testGroup.do?action=5" [int]$TestGroupType = (Get-EPCTestGroupType -Name $TestGroupName).ID Write-Verbose "XML" Write-Verbose $TGMemberXML $TGBody = @{ testpointlist = $TGMemberXML testgroupid = $TestGroupID testgrouptype = $TestGroupType dstgrouptype = $TestGroupType resgrp1 = $RGID } Write-Verbose 'Writing changes to controller database' $TGResult = Invoke-WebRequest -Method POST -Uri $TGFQDN -Body $TGBody -WebSession $Global:EPCSessionCookie } } Function Remove-EPCTestGroupMember { <# .SYNOPSIS Removes an EPC test point from an EPC test group .DESCRIPTION Removes an EPC test point from an EPC test group .PARAMETER TestGroupName The name of the test group to remove the member from .PARAMETER TestPointName The name of the test point to remove from the test group .EXAMPLE Remove-EPCTestGroupMember -TestGroupName MyGroup -TestPointName TPTest Removes TPTest from the MyGroup group .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$TestGroupName, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$TestPointName ) Begin { Get-EPCWebSessionCookie $ProgressPreference = 'SilentlyContinue' # Get a list of all test points and resource groups for returning IDs $TPList = Get-EPCTestPoint -ResultSize 10000 $RGList = Get-EPCResourceGroup } Process { # Get the test group ID [int]$TestGroupID = (Get-EPCTestGroups | Where {$_.Name -eq $TestGroupName}).TGID If (!$TestGroupID) { Throw "Could not find a test group with name $TestGroupName" } # Get the resource group ID $RGID = ($RGList | Where {$_.RGName -eq $ResourceGroupName}).RGID # Obtain list of existing group members [System.Collections.ArrayList]$TGMembers = Get-EPCTestGroupMembers -Name $TestGroupName | Select-Object TestPointName, GroupName, TestInterfaceName, TPIP # Delete the selected test point from the table of existing members $MemberDelete = $TGMembers | Where {$_.TestPointName -eq $TestPointName} If ($MemberDelete) { $TGMembers.Remove($MemberDelete) } Else { Throw "Could not find $TestPointName in $TestGroupName" } # Add fields for resource group and test point IDs $TGMembers | Add-Member -NotePropertyName 'resgroupid' -NotePropertyValue $NULL $TGMembers | Add-Member -NotePropertyName 'testPointId' -NotePropertyValue $NULL #Add the RGID and TPID for each entry ForEach ($Member in $TGMembers) { $Member.TestPointID = ($TPList | Where {$_.Name -eq $Member.TestPointName}).TestPtID $Member.ResGroupID = ($RGList | Where {$_.RGName -eq $Member.GroupName}).RGID } } End { # Convert test point array to XML $TGMemberXML = New-EPCTestPointXML -Data $TGMembers $TGFQDN = "https://$Global:EPControllerFQDN/test/testGroup.do?action=5" [int]$TestGroupType = (Get-EPCTestGroupType -Name $TestGroupName).ID $TGBody = @{ testpointlist = $TGMemberXML testgroupid = $TestGroupID testgrouptype = $TestGroupType dstgrouptype = $TestGroupType resgrp1 = $RGID } Write-Verbose 'Writing to controller database' $TGResult = Invoke-WebRequest -Method POST -Uri $TGFQDN -Body $TGBody -WebSession $Global:EPCSessionCookie } } ################################################################################################################################################# # # # EPC Test Plan Functions # # # ################################################################################################################################################# Function Get-EPCTestPlan { <# .SYNOPSIS Return a list of EPC test plans .DESCRIPTION Return a list of EPC test plans .EXAMPLE Get-EPCTestPlan .NOTES Version 1.0 #> [cmdletbinding()] Param ( [switch]$UseXML ) Begin { Connect-EPCController If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCCred = New-Object -TypeName EPC.ActiveCtrl.CredentialsType $EPCCred.username = $Global:EPControllerCred.UserName $EPCCred.password = $Global:EPControllerCred.GetNetworkCredential().Password } Else { $ProgressPreference = 'SilentlyContinue' } } Process { If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCListTestPlansParams = New-Object -TypeName EPC.ActiveCtrl.ListTestPlansParametersType $EPCListTestPlansParams.credentials = $EPCCred $TestPlanResults = $Global:EPCWSDL_ActiveCtrl.listTestPlans($EPCListTestPlansParams) Write-Verbose $TestPlanResults.result If ($TestPlanResults.result -eq 'success') { Return $TestPlanResults.TestPlanList } Else { Throw $TestPlanResults.result } } Else { [xml]$SOAPReq = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:urn='urn:telchemyActiveCtrl'> <soapenv:Header/> <soapenv:Body> <urn:listTestPlansParameters> <credentials> <username>$($Global:EPControllerCred.UserName)</username> <password>$($Global:EPControllerCred.GetNetworkCredential().Password)</password> </credentials> </urn:listTestPlansParameters> </soapenv:Body> </soapenv:Envelope>" $SOAPFQDN = "https://$Global:EPControllerFQDN/telchemywebservices/services/telchemyActiveCtrlService" [xml]$XMLResponse = (Invoke-WebRequest -Method POST -URI $SOAPFQDN -Body $SOAPReq -ContentType 'text/xml').Content $TestPlanResults = $XMLResponse.Envelope.Body.listTestPlansResults If ($TestPlanResults.result -eq 'success') { [psobject]$TestPlans = $TestPlanResults.TestPlanList.testplan | ConvertFrom-XMLElement Return $TestPlans } Else { Throw $TestPlanResults.result } } } } ################################################################################################################################################# # # # EPC Resource Group Functions # # # ################################################################################################################################################# Function Get-EPCResourceGroup { <# .SYNOPSIS Return list of EPC resource groups .DESCRIPTION Return list of EPC resource groups .PARAMETER ServiceClass Limit results to only a specific service class .EXAMPLE Get-EPCService -ServiceClass VOIP Returns service information about all VOIP services .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(Mandatory=$False)] [int]$ParentRGID, [Parameter(Mandatory=$False)] [int]$ResultSize = 1000, [switch]$UseXML ) Begin { Connect-EPCController If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCCred = New-Object -TypeName EPC.ResGrpMgmt.CredentialsType $EPCCred.username = $Global:EPControllerCred.UserName $EPCCred.password = $Global:EPControllerCred.GetNetworkCredential().Password } Else { $ProgressPreference = 'SilentlyContinue' } } Process { If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCListResourceGroupParams = New-Object -TypeName EPC.ResGrpMgmt.ListResourceGroupParametersType $EPCListResourceGroupParams.credentials = $EPCCred $EPCListResourceGroupParams.maxNumber = $ResultSize If ($ParentRGID) { $RGRelation = New-Object -TypeName EPC.ResGrpMgmt.ResourceGroupRelationshipMapType $RGRelation.RGID = $ParentRGID $FilterType = New-Object -TypeName EPC.ResGrpMgmt.ResourceGroupFilterType $FilterType.ItemElementName = 'RGrelation' $FilterType.Item = $RGRelation $EPCListResourceGroupParams.filter = $FilterType } $ResourceGroupResults = $Global:EPCWSDL_ResGrpMgmt.listResourceGroups($EPCListResourceGroupParams) Write-Verbose $ResourceGroupResults.result If ($ResourceGroupResults.result -eq 'success') { Return $ResourceGroupResults.ResourceGroupList } Else { Throw $ResourceGroupResults.result } } Else { [xml]$SOAPReq = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:urn='urn:telchemyRsrcGroupMgmt'> <soapenv:Header/> <soapenv:Body> <urn:listResourceGroupParameters> <credentials> <username>$($Global:EPControllerCred.UserName)</username> <password>$($Global:EPControllerCred.GetNetworkCredential().Password)</password> </credentials> </urn:listResourceGroupParameters> </soapenv:Body> </soapenv:Envelope>" # Add filter options if entered If ($ParentRGID) { $FilterXMLElement = $SOAPReq.Envelope.Body.listResourceGroupParameters.AppendChild($SOAPReq.CreateElement('filter')) $RGRelationXMLElement = $FilterXMLElement.AppendChild($SOAPReq.CreateElement('RGrelation')) $RGRelationshipXMLElement = $RGRelationXMLElement.AppendChild($SOAPReq.CreateElement('relationship')) $NULL = $RGRelationshipXMLElement.AppendChild($SOAPReq.CreateTextNode('all-children')) $RGIDXMLElement = $RGRelationXMLElement.AppendChild($SOAPReq.CreateElement('RGID')) $NULL = $RGIDXMLElement.AppendChild($SOAPReq.CreateTextNode($ParentRGID)) } $SOAPFQDN = "https://$Global:EPControllerFQDN/telchemywebservices/services/telchemyRsrcGroupMgmtService" Write-Verbose $SOAPFQDN Write-Verbose $SOAPReq.OuterXML [xml]$XMLResponse = (Invoke-WebRequest -Method POST -URI $SOAPFQDN -Body $SOAPReq -ContentType 'text/xml').Content $ResourceGroupResults = $XMLResponse.Envelope.Body.listResourceGroupResults If ($ResourceGroupResults.result -eq 'success') { [psobject]$ResourceGroups = $ResourceGroupResults.ResourceGroupList.RG | ConvertFrom-XMLElement Return $ResourceGroups } Else { Throw $ResourceGroupResults.result } } } } ################################################################################################################################################# # # # EPC Service Functions # # # ################################################################################################################################################# Function Get-EPCService { <# .SYNOPSIS Return list of EPC services .DESCRIPTION Return list of EPC services .PARAMETER ServiceClass Limit results to only a specific service class .EXAMPLE Get-EPCService -ServiceClass VOIP Returns service information about all VOIP services .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(Mandatory=$False)] [ValidateSet('Composite','Interface','IPSec','IPTV','NetApp','NetSVC','NetTrans','Network','Other','System','TCP','VidConf','VOIP', IgnoreCase=$True)] [string]$ServiceClass, [switch]$UseXML ) Begin { Connect-EPCController If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCCred = New-Object -TypeName EPC.SvcMgmt.CredentialsType $EPCCred.username = $Global:EPControllerCred.UserName $EPCCred.password = $Global:EPControllerCred.GetNetworkCredential().Password } Else { $ProgressPreference = 'SilentlyContinue' } } Process { If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCListServicesParams = New-Object -TypeName EPC.SvcMgmt.ListServicesParametersType $EPCListServicesParams.credentials = $EPCCred If ($ServiceClass) { $EPCListServicesParams.serviceClass = $ServiceClass $EPCListServicesParams.serviceClassSpecified = $True } $ServiceResults = $Global:EPCWSDL_SvcMgmt.listServices($EPCListServicesParams) Write-Verbose $ServiceResults.result If ($ServiceResults.result -eq 'success') { Return $ServiceResults.ServiceList } Else { Throw $ServiceResults.result } } Else { [xml]$SOAPReq = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:urn='urn:telchemySvcMgmt'> <soapenv:Header/> <soapenv:Body> <urn:listServicesParameters> <credentials> <username>$($Global:EPControllerCred.UserName)</username> <password>$($Global:EPControllerCred.GetNetworkCredential().Password)</password> </credentials> </urn:listServicesParameters> </soapenv:Body> </soapenv:Envelope>" # Add the testpoint ID to the search if entered If ($ServiceClass) { $NewXMLElement = $SOAPReq.Envelope.Body.listServicesParameters.AppendChild($SOAPReq.CreateElement('serviceClass')) $NULL = $NewXMLElement.AppendChild($SOAPReq.CreateTextNode($ServiceClass.ToLower())) } $SOAPFQDN = "https://$Global:EPControllerFQDN/telchemywebservices/services/telchemySvcMgmtService" [xml]$XMLResponse = (Invoke-WebRequest -Method POST -URI $SOAPFQDN -Body $SOAPReq -ContentType 'text/xml').Content $ServiceResults = $XMLResponse.Envelope.Body.listServicesResults If ($ServiceResults.result -eq 'success') { [psobject]$Services = $ServiceResults.ServiceList.service | ConvertFrom-XMLElement Return $Services } Else { Throw $ServiceResults.result } } } } ################################################################################################################################################# # # # EPC Test Instance Functions # # # ################################################################################################################################################# Function Get-EPCTestPointTestInstance { <# .SYNOPSIS Returns information about test point tests .DESCRIPTION Returns information about test point tests. Can be used to show any running tests .PARAMETER UUID The UUID of the test point to retrieve test information about .PARAMETER ExecMode Filter results by either normal or ad hoc tests .PARAMETER Status Filter results by the given status of a test. .EXAMPLE Get-EPCTestPointTestInstance -UUID d4a1437f-1f18-11ec-cf33-0daedf04882a -Status Running Shows running tests associated with the given test point UUID .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$UUID, [Parameter(Mandatory=$False)] [ValidateSet('Normal','AdHoc', IgnoreCase=$True)] [string]$ExecMode, [Parameter(Mandatory=$False)] [ValidateSet('Aborted','Cancelling','Cancelled','Completed','Pending','Running','Unknown', IgnoreCase=$True)] [string]$Status, [switch]$UseXML ) Begin { Connect-EPCController If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCCred = New-Object -TypeName EPC.ActiveCtrl.CredentialsType $EPCCred.username = $Global:EPControllerCred.UserName $EPCCred.password = $Global:EPControllerCred.GetNetworkCredential().Password } Else { $ProgressPreference = 'SilentlyContinue' } } Process { If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCListTestPointInstancesParams = New-Object -TypeName EPC.ActiveCtrl.ListTestPointTestInstancesParametersType $EPCListTestPointInstancesParams.credentials = $EPCCred If ($UUID) { $EPCListTestPointInstancesParams.TestPTID = $UUID } If ($ExecMode) { $EPCListTestPointInstancesParams.ExecModeFilter = $ExecMode $EPCListTestPointInstancesParams.ExecModeFilterSpecified = $True } If ($Status) { $EPCListTestPointInstancesParams.StatusFilter = $Status $EPCListTestPointInstancesParams.StatusFilterSpecified = $True } $TestPointInstancesResults = $Global:EPCWSDL_ActiveCtrl.listTestPointTestInstances($EPCListTestPointInstancesParams) Write-Verbose $TestPointInstancesResults.result If ($TestPointInstancesResults.result -eq 'success') { Return $TestPointInstancesResults.TestInstanceList } Else { Throw $TestPointInstancesResults.result } } Else { [xml]$SOAPReq = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:urn='urn:telchemyActiveCtrl'> <soapenv:Header/> <soapenv:Body> <urn:listTestPointTestInstancesParameters> <credentials> <username>$($Global:EPControllerCred.UserName)</username> <password>$($Global:EPControllerCred.GetNetworkCredential().Password)</password> </credentials> </urn:listTestPointTestInstancesParameters> </soapenv:Body> </soapenv:Envelope>" # Add the testpoint ID to the search if entered If ($UUID) { $NewXMLElement = $SOAPReq.Envelope.Body.listTestPointTestInstancesParameters.AppendChild($SOAPReq.CreateElement('testptID')) $NULL = $NewXMLElement.AppendChild($SOAPReq.CreateTextNode($UUID)) } If ($ExecMode) { # WSDL uses adhoc, XML uses ad-hoc If ($ExecMode -eq 'AdHoc') { $ExecModeLower = 'ad-hoc' } Else { $ExecModeLower = 'normal' } $NewXMLElement = $SOAPReq.Envelope.Body.listTestPointTestInstancesParameters.AppendChild($SOAPReq.CreateElement('execModeFilter')) $NULL = $NewXMLElement.AppendChild($SOAPReq.CreateTextNode($ExecModeLower)) } If ($Status) { $NewXMLElement = $SOAPReq.Envelope.Body.listTestPointTestInstancesParameters.AppendChild($SOAPReq.CreateElement('statusFilter')) $NULL = $NewXMLElement.AppendChild($SOAPReq.CreateTextNode($Status.ToLower())) } $SOAPFQDN = "https://$Global:EPControllerFQDN/telchemywebservices/services/telchemyActiveCtrlService" [xml]$XMLResponse = (Invoke-WebRequest -Method POST -URI $SOAPFQDN -Body $SOAPReq -ContentType 'text/xml').Content $TPTestResults = $XMLResponse.Envelope.Body.listTestPointTestInstancesResults If ($TPTestResults.result -eq 'success') { Return $TPTestResults.TestInstanceList.testInstance } Else { Throw $TPTestResults.result } } } } Function Start-EPCTestInstance { <# .SYNOPSIS Starts an EPC test .DESCRIPTION Starts an EPC test .PARAMETER SrcTestPointID The UUID of the originating test point .PARAMETER SrcInterface The name of the originating network interface .PARAMETER SrcIPAddress The orginating test point's IP address .PARAMETER SrcResourceGroup The originating test point's resource group .PARAMETER DstTestPointID The UUID of the target test point .PARAMETER DstInterface The name of the target network interface .PARAMETER DstIPAddress The orginating test point's IP address .PARAMETER DstResourceGroup The target test point's resource group .PARAMETER Service The service name that contains the test to run .PARAMETER TestPlan The test plan name of the test to run .EXAMPLE Start-EPCTestInstance -SrcTestPointID d4a1437f-1f18-11ec-cf33-0daedf04882a -SrcInterface Ethernet -SrcIPAddress 192.168.1.100 -DstTestPointID b4a4437f-4f48-440c-cf33-0ba0bf04882a -DstInterface Wifi -DstIPAddress 192.168.0.43 -Service Contoso -TestPlan P2P_short Stops the given EPC test .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$SrcTestPointID, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$DstTestPointID, [switch]$UseXML ) DynamicParam { # Create the dictionary $RuntimeParamDict = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $ParamList = @() $Params = [pscustomobject][ordered]@{'ParamName' = 'SrcInterface'; 'Prefix' = 'SIF'; 'ParamRequired' = $FALSE; 'Position' = 2} $ParamList += $Params $Params = [pscustomobject][ordered]@{'ParamName' = 'SrcIPAddress'; 'Prefix' = 'SIP'; 'ParamRequired' = $FALSE; 'Position' = 3} $ParamList += $Params $Params = [pscustomobject][ordered]@{'ParamName' = 'SrcResourceGroup'; 'Prefix' = 'SRG'; 'ParamRequired' = $FALSE; 'Position' = 4} $ParamList += $Params $Params = [pscustomobject][ordered]@{'ParamName' = 'DstInterface'; 'Prefix' = 'DIF'; 'ParamRequired' = $FALSE; 'Position' = 5} $ParamList += $Params $Params = [pscustomobject][ordered]@{'ParamName' = 'DstIPAddress'; 'Prefix' = 'DIP'; 'ParamRequired' = $FALSE; 'Position' = 6} $ParamList += $Params $Params = [pscustomobject][ordered]@{'ParamName' = 'DstResourceGroup'; 'Prefix' = 'DRG'; 'ParamRequired' = $FALSE; 'Position' = 7} $ParamList += $Params $Params = [pscustomobject][ordered]@{'ParamName' = 'TestPlan'; 'Prefix' = 'TP'; 'ParamRequired' = $FALSE; 'Position' = 8} $ParamList += $Params $Params = [pscustomobject][ordered]@{'ParamName' = 'Service'; 'Prefix' = 'SRV'; 'ParamRequired' = $FALSE; 'Position' = 9} $ParamList += $Params # Initialize parameter list ForEach ($Param In $ParamList) { New-DynamicParam -ParamName $Param.ParamName -Prefix $Param.Prefix -ParamRequired $Param.ParamRequired -Position $Param.Position } If ($SrcTestPointID) { # Generate and set the ValidateSet $SrcTestPointIFList = Get-EPCTestPointInterface -UUID $SrcTestPointID | Where {$_.Status -Contains 'ready' -And ($_.Status -Contains 'hasroute' -Or $_.Status -Contains 'has route')} $ValidateSetAttrib_SIF = New-Object System.Management.Automation.ValidateSetAttribute($SrcTestPointIFList.name) If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $SrcTestPointIPList = Get-EPCTestPointInterface -UUID $SrcTestPointID | Where {$_.Status -Contains 'ready' -And ($_.Status -Contains 'hasroute' -Or $_.Status -Contains 'has route')} | ForEach { $_.IFDetail | Select-Object -ExpandProperty Item } } Else { $SrcTestPointIPList = Get-EPCTestPointInterface -UUID $SrcTestPointID | Where {$_.Status -Contains 'ready' -And ($_.Status -Contains 'hasroute' -Or $_.Status -Contains 'has route')} | ForEach { ($_.IFDetail | Select-Object IPAddress).IPAddress } } $ValidateSetAttrib_SIP = New-Object System.Management.Automation.ValidateSetAttribute($SrcTestPointIPList) $SrcTestPointRGList = (Get-EPCTestPoint | Where {$_.testptID -eq $SrcTestPointID} | Select-Object RGList).RGList If ($SrcTestPointRGList.GetType().FullName -eq 'System.Xml.XmlElement') { $SrcTestPointRGList = $SrcTestPointRGList.rg } # Account for differences in XML vs WSDL $ValidateSetAttrib_SRG = New-Object System.Management.Automation.ValidateSetAttribute($SrcTestPointRGList) } If ($DstTestPointID) { # Generate and set the ValidateSet $DstTestPointIFList = Get-EPCTestPointInterface -UUID $DstTestPointID | Where {$_.Status -Contains 'ready' -And ($_.Status -Contains 'hasroute' -Or $_.Status -Contains 'has route')} $ValidateSetAttrib_DIF = New-Object System.Management.Automation.ValidateSetAttribute($DstTestPointIFList.name) If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $DstTestPointIPList = Get-EPCTestPointInterface -UUID $DstTestPointID | Where {$_.Status -Contains 'ready' -And ($_.Status -Contains 'hasroute' -Or $_.Status -Contains 'has route')} | ForEach { $_.IFDetail | Select-Object -ExpandProperty Item } } Else { $DstTestPointIPList = Get-EPCTestPointInterface -UUID $DstTestPointID | Where {$_.Status -Contains 'ready' -And ($_.Status -Contains 'hasroute' -Or $_.Status -Contains 'has route')} | ForEach { ($_.IFDetail | Select-Object IPAddress).IPAddress } } $ValidateSetAttrib_DIP = New-Object System.Management.Automation.ValidateSetAttribute($DstTestPointIPList) $DstTestPointRGList = (Get-EPCTestPoint | Where {$_.testptID -eq $DstTestPointID} | Select-Object RGList).RGList If ($DstTestPointRGList.GetType().FullName -eq 'System.Xml.XmlElement') { $DstTestPointRGList = $DstTestPointRGList.rg } # Account for differences in XML vs WSDL $ValidateSetAttrib_DRG = New-Object System.Management.Automation.ValidateSetAttribute($DstTestPointRGList) $TestPlanList = Get-EPCTestPlan $ValidateSetAttrib_PT = New-Object System.Management.Automation.ValidateSetAttribute($TestPlanList.name) $ServiceList = Get-EPCService $ValidateSetAttrib_SRV = New-Object System.Management.Automation.ValidateSetAttribute($ServiceList.name) } # Add the ValidateSet to the attributes collection $AttribColl_SIF.Add($ValidateSetAttrib_SIF) $AttribColl_SIP.Add($ValidateSetAttrib_SIP) $AttribColl_SRG.Add($ValidateSetAttrib_SRG) $AttribColl_DIF.Add($ValidateSetAttrib_DIF) $AttribColl_DIP.Add($ValidateSetAttrib_DIP) $AttribColl_DRG.Add($ValidateSetAttrib_DRG) $AttribColl_TP.Add($ValidateSetAttrib_PT) $AttribColl_SRV.Add($ValidateSetAttrib_SRV) # Create and return the dynamic parameter $RunTimeParam_SIF = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_SIF, [string], $AttribColl_SIF) $RunTimeParam_SIP = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_SIP, [string], $AttribColl_SIP) $RunTimeParam_SRG = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_SRG, [string], $AttribColl_SRG) $RunTimeParam_DIF = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_DIF, [string], $AttribColl_DIF) $RunTimeParam_DIP = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_DIP, [string], $AttribColl_DIP) $RunTimeParam_DRG = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_DRG, [string], $AttribColl_DRG) $RunTimeParam_TP = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_TP, [string], $AttribColl_TP) $RunTimeParam_SRV = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_SRV, [string], $AttribColl_SRV) $RuntimeParamDict.Add($ParamName_SIF, $RunTimeParam_SIF) $RuntimeParamDict.Add($ParamName_SIP, $RunTimeParam_SIP) $RuntimeParamDict.Add($ParamName_SRG, $RunTimeParam_SRG) $RuntimeParamDict.Add($ParamName_DIF, $RunTimeParam_DIF) $RuntimeParamDict.Add($ParamName_DIP, $RunTimeParam_DIP) $RuntimeParamDict.Add($ParamName_DRG, $RunTimeParam_DRG) $RuntimeParamDict.Add($ParamName_TP, $RunTimeParam_TP) $RuntimeParamDict.Add($ParamName_SRV, $RunTimeParam_SRV) Return $RuntimeParamDict } Begin { Connect-EPCController # If no parameters were entered, then bring up the UI If ($PSBoundParameters.Count -eq 0) { [bool]$Script:SubmitClicked = $False $Script:SrcIPAddress = $NULL $Script:DstIPAddress = $NULL # Check to see if the module was installed to determine the location of the gui.xaml file $N10Path = (Get-Module -ListAvailable -Name Nectar10 -ErrorAction:SilentlyContinue).Path If ($N10Path) { $N10Path = (Get-Item $N10Path).DirectoryName } Else { $N10Path = '.' } $inputXML = Get-Content "$N10Path\epc_starttest_gui.xaml" $inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window' [void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework') [xml]$XAML = $inputXML #Read XAML $Reader=(New-Object System.Xml.XmlNodeReader $xaml) Try { $Form=[Windows.Markup.XamlReader]::Load( $Reader ) } Catch { Throw "Unable to parse XML, with error: $($Error[0])`n Ensure that there are NO SelectionChanged or TextChanged properties in your textboxes (PowerShell cannot process them)" } #=========================================================================== # Load XAML Objects In PowerShell #=========================================================================== $xaml.SelectNodes("//*[@Name]") | %{ Try { Set-Variable -Name "$($_.Name)" -Value $Form.FindName($_.Name) -ErrorAction Stop } Catch{ Throw } } # Populate the Test Plan List $TestPlanList = Get-EPCTestPlan | Select-Object Name, testPlanID | Sort-Object Name ForEach ($TestPlan in $TestPlanList) { [void] $TP_DropBox.Items.Add($TestPlan.name) } # Grab the default value $Script:TestPlanID = $TestPlanList[0].testPlanID # Populate the Resource Group List Try { $RGList = Get-EPCResourceGroup | Select-Object RGName, RGID | Sort-Object RGName ForEach ($RG in $RGList) { [void] $RG_DropBox.Items.Add($RG.RGName) } } Catch { # Hide the RG list dropdown if user doesn't have permission to view $RG_Label.Visibility = 'Hidden' $RG_DropBox.Visibility = 'Hidden' } # Grab the default value $Script:TestPlanID = $TestPlanList[0].testPlanID $IPRegEx = "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" # Populate the Testpoint list $Script:TPList = Get-EPCTestPoint | Where {$_.status -eq 'Connected'} | Select-Object Name, testptID, RGList | Sort-Object -Property Name ForEach ($TestPoint in $Script:TPList) { [void] $SrcEndpoint_ListBox.Items.Add($TestPoint.name) [void] $DstEndpoint_ListBox.Items.Add($TestPoint.name) } ######################## #Add Event Handlers ######################## $TP_DropBox.add_SelectionChanged({ # Get the selected Test Plan $SelectedTPIndex = $TP_DropBox.SelectedIndex $Script:TestPlanID = $TestPlanList[$SelectedTPIndex].testPlanID }) $RG_DropBox.add_SelectionChanged({ # Get the selected Resource Group and set the test point list $SelectedRGIndex = $RG_DropBox.SelectedIndex $Script:ResourceGroupID = $RGList[$SelectedRGIndex].RGID $Script:TPList = Get-EPCTestPoint -RGID $Script:ResourceGroupID | Where {$_.status -eq 'Connected'} | Select-Object Name, testptID, RGList | Sort-Object -Property Name $SrcEndpoint_ListBox.Items.Clear() $DstEndpoint_ListBox.Items.Clear() $SrcInterface_ListBox.Items.Clear() $DstInterface_ListBox.Items.Clear() $SrcIPAddress_ListBox.Items.Clear() $DstIPAddress_ListBox.Items.Clear() ForEach ($TestPoint in $Script:TPList) { [void] $SrcEndpoint_ListBox.Items.Add($TestPoint.name) [void] $DstEndpoint_ListBox.Items.Add($TestPoint.name) } }) $SrcEndpoint_ListBox.add_SelectionChanged({ # Get the selected endpoint $SelectedEPIndex = $SrcEndpoint_ListBox.SelectedIndex $Script:SrcTestPointID = $Script:TPList[$SelectedEPIndex].testPtID $Script:SrcResourceGroupList = $Script:TPList[$SelectedEPIndex].RGList # Ensure RGList object is a PSObject instead of XML If ($Global:EPC_UseWSDL -eq $False -Or $UseXML) { $Script:SrcResourceGroupList = ForEach($Item in $Script:SrcResourceGroupList.RG) { $Item } } Try { # Populate interface list $SrcInterface_ListBox.Items.Clear() $SrcIPAddress_ListBox.Items.Clear() $SrcInterface_ListBox.IsEnabled = $True $Script:SrcTestPointIFList = Get-EPCTestPointInterface -UUID $Script:SrcTestPointID | Where {$_.Status -Contains 'ready' -And ($_.Status -Contains 'hasroute' -Or $_.Status -Contains 'has route') -And $_.ifID -notlike 'Teredo*'} ForEach ($Interface in $Script:SrcTestPointIFList) { $SrcInterface_ListBox.Items.Add($Interface.name) } # Auto-select the interface if there is only one If (($Script:SrcTestPointIFList | Measure-Object).Count -eq 1) { $SrcInterface_ListBox.SelectedIndex = 0 } } Catch { $SrcInterface_ListBox.Items.Clear() $SrcIPAddress_ListBox.Items.Clear() $SrcInterface_ListBox.Items.Add("ERROR CONNECTING TO ENDPOINT") $SrcInterface_ListBox.IsEnabled = $False } }) $SrcInterface_ListBox.add_SelectionChanged({ # Get the selected interface $SelectedIFIndex = $SrcInterface_ListBox.SelectedIndex If ($SelectedIFIndex -ge 0) { $Script:SrcInterfaceID = $Script:SrcTestPointIFList[$SelectedIFIndex].IFID # Populate the IP address list If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $SrcTestPointIPList = Get-EPCTestPointInterface -UUID $Script:SrcTestPointID | Where {$_.IFID -eq $Script:SrcInterfaceID} | ForEach { $_.IFDetail } | Select-Object Item | Where {$_.Item -match $IPRegEx} } Else { $SrcTestPointIPList = Get-EPCTestPointInterface -UUID $Script:SrcTestPointID | Where {$_.IFID -eq $Script:SrcInterfaceID} | ForEach { $_.IFDetail } | Select-Object @{Name='Item';Expression={$_.IPAddress}} | Where {$_.Item -match $IPRegEx} } $SrcIPAddress_ListBox.Items.Clear() ForEach ($IPAddress in $SrcTestPointIPList) { $SrcIPAddress_ListBox.Items.Add($IPAddress.Item) } # Auto-select the IP address if there is only one (usual case) If (($SrcTestPointIPList | Measure-Object).Count -eq 1) { $SrcIPAddress_ListBox.SelectedIndex = 0 } } }) $SrcIPAddress_ListBox.add_SelectionChanged({ # Get the selected IP address $Script:SrcIPAddress = $SrcIPAddress_ListBox.SelectedValue If ($Script:SrcIPAddress -and $Script:DstIPAddress) { $bSubmit.IsEnabled = $True } Else { $bSubmit.IsEnabled = $False } }) $DstEndpoint_ListBox.add_SelectionChanged({ # Get the selected endpoint $SelectedEPIndex = $DstEndpoint_ListBox.SelectedIndex $Script:DstTestPointID = $Script:TPList[$SelectedEPIndex].testPtID $Script:DstResourceGroupList = $Script:TPList[$SelectedEPIndex].RGList # Ensure RGList object is a PSObject instead of XML If ($Global:EPC_UseWSDL -eq $False -Or $UseXML) { $Script:DstResourceGroupList = ForEach($Item in $Script:DstResourceGroupList.RG) { $Item } } # Populate interface list Try { $DstInterface_ListBox.Items.Clear() $DstIPAddress_ListBox.Items.Clear() $DstInterface_ListBox.IsEnabled = $True $Script:DstTestPointIFList = Get-EPCTestPointInterface -UUID $Script:DstTestPointID | Where {$_.Status -Contains 'ready' -And ($_.Status -Contains 'hasroute' -Or $_.Status -Contains 'has route') -And $_.ifID -notlike 'Teredo*'} ForEach ($Interface in $Script:DstTestPointIFList) { $DstInterface_ListBox.Items.Add($Interface.name) } # Auto-select the interface if there is only one If (($Script:DstTestPointIFList | Measure-Object).Count -eq 1) { $DstInterface_ListBox.SelectedIndex = 0 } } Catch { $DstInterface_ListBox.Items.Clear() $DstIPAddress_ListBox.Items.Clear() $DstInterface_ListBox.Items.Add("ERROR CONNECTING TO ENDPOINT") $DstInterface_ListBox.IsEnabled = $False } }) $DstInterface_ListBox.add_SelectionChanged({ # Get the selected interface $SelectedIFIndex = $DstInterface_ListBox.SelectedIndex If ($SelectedIFIndex -ge 0) { $Script:DstInterfaceID = $Script:DstTestPointIFList[$SelectedIFIndex].IFID #Populate the IP address list If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $DstTestPointIPList = Get-EPCTestPointInterface -UUID $Script:DstTestPointID | Where {$_.IFID -eq $Script:DstInterfaceID} | ForEach { $_.IFDetail } | Select-Object Item | Where {$_.Item -match $IPRegEx} } Else { $DstTestPointIPList = Get-EPCTestPointInterface -UUID $Script:DstTestPointID | Where {$_.IFID -eq $Script:DstInterfaceID} | ForEach { $_.IFDetail } | Select-Object @{Name='Item';Expression={$_.IPAddress}} | Where {$_.Item -match $IPRegEx} } $DstIPAddress_ListBox.Items.Clear() ForEach ($IPAddress in $DstTestPointIPList) { $DstIPAddress_ListBox.Items.Add($IPAddress.Item) } # Auto-select the IP address if there is only one (usual case) If (($DstTestPointIPList | Measure-Object).Count -eq 1) { $DstIPAddress_ListBox.SelectedIndex = 0 } } }) $DstIPAddress_ListBox.add_SelectionChanged({ # Get the selected IP address $Script:DstIPAddress = $DstIPAddress_ListBox.SelectedValue If ($Script:DstIPAddress -and $Script:SrcIPAddress) { $bSubmit.IsEnabled = $True $bSubmit.Background = 'PaleGreen' } Else { $bSubmit.IsEnabled = $False } }) $bSubmit.Add_Click({ [bool]$Script:SubmitClicked = $True $form.Close() }) #Show the Form $form.ShowDialog() | Out-Null If ($Script:SubmitClicked) { $SrcTestPointID = $Script:SrcTestPointID $DstTestPointID = $Script:DstTestPointID # Set the Resource Group for source and target if it was specified If ($Script:ResourceGroupID) { $SrcRG = $Script:ResourceGroupID $DstRG = $Script:ResourceGroupID } Else { # Find a common resource group and use that for the test $CommonRG = Compare-Object -IncludeEqual -ExcludeDifferent $Script:SrcResourceGroupList $Script:DstResourceGroupList $SrcRG = $CommonRG[0].InputObject $DstRG = $CommonRG[0].InputObject } } Else { Break } } # Write-Host "TestPlanID: $Script:TestPlanID" # Write-Host "SrcTPID: $Script:SrcTestPointID" # Write-Host "SrcIFID: $Script:SrcInterfaceID" # Write-Host "SrcIPID: $Script:SrcIPAddress" # Write-Host "DstTPID: $Script:DstTestPointID" # Write-Host "DstIFID: $Script:DstInterfaceID" # Write-Host "DstIPID: $Script:DstIPAddress" # Write-Host "SrcRG: $SrcRG" # Write-Host "DstRG: $DstRG" If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCCred = New-Object -TypeName EPC.ActiveCtrl.CredentialsType $EPCCred.username = $Global:EPControllerCred.UserName $EPCCred.password = $Global:EPControllerCred.GetNetworkCredential().Password } Else { $ProgressPreference = 'SilentlyContinue' } If ($PSBoundParameters.Count -gt 0) { # Get the array position of the selected environment $SrcIFPos = $SrcTestPointIFList.Name.IndexOf($PsBoundParameters[$ParamName_SIF]) $DstIFPos = $DstTestPointIFList.Name.IndexOf($PsBoundParameters[$ParamName_DIF]) $TPPos = $TestPlanList.Name.IndexOf($PsBoundParameters[$ParamName_TP]) $SRVPos = $ServiceList.Name.IndexOf($PsBoundParameters[$ParamName_SRV]) $SrcInterfaceID = $SrcTestPointIFList[$SrcIFPos].IFID Write-Verbose "Src InterfaceID: $SrcInterfaceID" $SrcIPAddress = $PsBoundParameters[$ParamName_SIP] Write-Verbose "Src IPAddress: $SrcIPAddress" $SrcRG = $PsBoundParameters[$ParamName_SRG] Write-Verbose "Src RG: $SrcRG" $DstInterfaceID = $DstTestPointIFList[$DstIFPos].IFID Write-Verbose "Dst InterfaceID: $DstInterfaceID" $DstIPAddress = $PsBoundParameters[$ParamName_DIP] Write-Verbose "Dst IPAddress: $DstIPAddress" $DstRG = $PsBoundParameters[$ParamName_DRG] Write-Verbose "Dst RG: $DstRG" $TestPlanID = $TestPlanList[$TPPos].TestPlanID Write-Verbose "TestPlanID: $TestPlanID" $ServiceID = $ServiceList[$SRVPos].ServiceID Write-Verbose "ServiceID: $ServiceID" } } Process { If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCSource = New-Object -TypeName EPC.ActiveCtrl.TestPointTargetType $EPCTarget = New-Object -TypeName EPC.ActiveCtrl.TestPointTargetType $EPCSource.TestPointID = $SrcTestPointID $EPCSource.ifID = $SrcInterfaceID $EPCSource.IPAddress = $SrcIPAddress $EPCSource.RG = $SrcRG $EPCTarget.TestPointID = $DstTestPointID $EPCTarget.ifID = $DstInterfaceID $EPCTarget.IPAddress = $DstIPAddress $EPCTarget.RG = $DstRG $EPCStartTestPlanTPtoTPParams = New-Object -TypeName EPC.ActiveCtrl.startTestPlanParamsTPtoTPType $EPCStartTestPlanTPtoTPParams.TestPlanID = $TestPlanID $EPCStartTestPlanTPtoTPParams.Originator = $EPCSource $EPCStartTestPlanTPtoTPParams.TPTarget = $EPCTarget If ($Service) { Write-Verbose "Selected Service: $ServiceID"; $EPCStartTestPlanTPtoTPParams.ServiceID = $ServiceID } $EPCStartTestPlanParams = New-Object -TypeName EPC.ActiveCtrl.startTestPlanParametersType $EPCStartTestPlanParams.credentials = $EPCCred $EPCStartTestPlanParams.Items = $EPCStartTestPlanTPtoTPParams $EPCStartTestPlanParams.ItemsElementName = 'tp2tptest' $StartTestPlanResults = $Global:EPCWSDL_ActiveCtrl.startTestPlan($EPCStartTestPlanParams) Write-Verbose $StartTestPlanResults.result } Else { [xml]$SOAPReq = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:urn='urn:telchemyActiveCtrl'> <soapenv:Header/> <soapenv:Body> <urn:startTestPlanParameters> <credentials> <username>$($Global:EPControllerCred.UserName)</username> <password>$($Global:EPControllerCred.GetNetworkCredential().Password)</password> </credentials> <tp2tpTest> <testplanID>$TestPlanID</testplanID> <originator> <testpointID>$SrcTestPointID</testpointID> <ifID>$SrcInterfaceID</ifID> <ipAddress>$SrcIPAddress</ipAddress> <RG>$SrcRG</RG> </originator> <tpTarget> <testpointID>$DstTestPointID</testpointID> <ifID>$DstInterfaceID</ifID> <ipAddress>$DstIPAddress</ipAddress> <RG>$DstRG</RG> </tpTarget> </tp2tpTest> </urn:startTestPlanParameters> </soapenv:Body> </soapenv:Envelope>" Write-Verbose $SOAPReq.OuterXML $SOAPFQDN = "https://$Global:EPControllerFQDN/telchemywebservices/services/telchemyActiveCtrlService" Write-Verbose $SOAPFQDN [xml]$XMLResponse = (Invoke-WebRequest -Method POST -URI $SOAPFQDN -Body $SOAPReq -ContentType 'text/xml').Content $StartTestPlanResults = $XMLResponse.Envelope.Body.StartTestPlanResults } If ($StartTestPlanResults.result -eq 'success') { Return "Successfully started test`n`rInstance ID: $($StartTestPlanResults.instanceID)" } Else { Return $StartTestPlanResults.result } } } Function Stop-EPCTestInstance { <# .SYNOPSIS Stops a running EPC test .DESCRIPTION Stops a running EPC test .PARAMETER InstanceID The InstanceID of the test to stop .EXAMPLE Stop-EPCTestInstance -InstanceID MDNkMGQxZGQ4YzVjY2VlYmRkZTBlYjA2MWRlLTg3MjI=< Stops the given EPC test .NOTES Version 1.0 #> [cmdletbinding()] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$True)] [string]$InstanceID, [switch]$UseXML ) Begin { Connect-EPCController If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCCred = New-Object -TypeName EPC.ActiveCtrl.CredentialsType $EPCCred.username = $Global:EPControllerCred.UserName $EPCCred.password = $Global:EPControllerCred.GetNetworkCredential().Password } Else { $ProgressPreference = 'SilentlyContinue' } } Process { If ($Global:EPC_UseWSDL -And $UseXML -eq $False) { $EPCStopTestPlanParams = New-Object -TypeName EPC.ActiveCtrl.StopTestPlanParametersType $EPCStopTestPlanParams.credentials = $EPCCred $EPCStopTestPlanParams.instanceID = $InstanceID $StopTestPlanResults = $Global:EPCWSDL_ActiveCtrl.stopTestPlan($EPCStopTestPlanParams) } Else { [xml]$SOAPReq = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:urn='urn:telchemyActiveCtrl'> <soapenv:Header/> <soapenv:Body> <urn:stopTestPlanParameters> <credentials> <username>$($Global:EPControllerCred.UserName)</username> <password>$($Global:EPControllerCred.GetNetworkCredential().Password)</password> </credentials> <instanceID>$InstanceID</instanceID> </urn:stopTestPlanParameters> </soapenv:Body> </soapenv:Envelope>" Write-Verbose $SOAPReq.OuterXML $SOAPFQDN = "https://$Global:EPControllerFQDN/telchemywebservices/services/telchemyActiveCtrlService" Write-Verbose $SOAPFQDN [xml]$XMLResponse = (Invoke-WebRequest -Method POST -URI $SOAPFQDN -Body $SOAPReq -ContentType 'text/xml').Content $StopTestPlanResults = $XMLResponse.Envelope.Body.stopTestPlanResults } If ($StopTestPlanResult.result -eq 'success') { Return 'Test stopped successfully' } Else { Throw $StopTestPlanResult.result } } } ################################################################################################################################################# # # # Supporting Functions # # # ################################################################################################################################################# Function Convert-NectarNumToTelURI { <# .SYNOPSIS Converts a Nectar formatted number "+12223334444 x200" into a valid TEL uri "+12223334444;ext=200" .DESCRIPTION Converts a Nectar formatted number "+12223334444 x200" into a valid TEL uri "+12223334444;ext=200" .PARAMETER PhoneNumber The phone number to convert to a TEL URI .PARAMETER TenantName The name of the Nectar 10 tenant. Used in multi-tenant configurations. .EXAMPLE Convert-NectarNumToTelsURI "+12224243344 x3344" Converts the above number to a TEL URI .EXAMPLE Get-NectarUnallocatedNumber -LocationName Jericho | Convert-NectarNumToTelURI Returns the next available phone number in the Jericho location in Tel URI format .NOTES Version 1.1 #> Param ( [Parameter(ValueFromPipeline,ValueFromPipelineByPropertyName, Mandatory=$true)] [Alias("number")] [string]$PhoneNumber ) $PhoneNumber = "tel:" + $PhoneNumber.Replace(" x", ";ext=") Return $PhoneNumber } Function Get-NectarDefaultTenantName { <# .SYNOPSIS Set the default tenant name for use with other commands. .DESCRIPTION Set the default tenant name for use with other commands. .NOTES Version 1.0 #> [Alias("stne")] Param () If ($Global:NectarTenantName) { # Use globally set tenant name, if one was set and not explicitly included in the command Return $Global:NectarTenantName } ElseIf (!$TenantName -And !$Global:NectarTenantName) { # If a tenant name wasn't set (normal for most connections, set the TenantName variable if only one available $TenantList = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/aapi/tenant" If ($TenantList.Count -eq 1) { Return $TenantList } Else { $TenantList | %{$TList += ($(if($TList){", "}) + $_)} Write-Error "TenantName was not specified. Select one of $TList" $PSCmdlet.ThrowTerminatingError() Return } } } Function Get-LatLong { <# .SYNOPSIS Returns the geographical coordinates for an address. .DESCRIPTION Returns the geographical coordinates for an address. .PARAMETER Address The address of the location to return information on. Include as much detail as possible. .EXAMPLE Get-LatLong -Address "33 Main Street, Jonestown, NY, USA" Retrieves the latitude/longitude for the selected location .NOTES Version 1.0 #> Param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] [String]$Address ) Begin { $GoogleGeoAPIKey = [System.Environment]::GetEnvironmentVariable('GoogleGeocode_API_Key','user') If(!$GoogleGeoAPIKey) { Write-Host -ForegroundColor Red "You need to register for an API key and save it as persistent environment variable called GoogleGeocode_API_Key on this machine. Follow this link to get an API Key - https://developers.google.com/maps/documentation/geocoding/get-api-key " $GoogleGeoAPIKey = Read-Host "Enter a valid Google API key to be saved as environment variable GoogleGeocode_API_Key" [System.Environment]::SetEnvironmentVariable('GoogleGeocode_API_Key', $GoogleGeoAPIKey,[System.EnvironmentVariableTarget]::User) } } Process { Try { $JSON = Invoke-RestMethod -Method GET -Uri "https://maps.googleapis.com/maps/api/geocode/json?address=$Address&key=$GoogleGeoAPIKey" -ErrorVariable EV $Status = $JSON.Status [double]$Lat = $JSON.results.geometry.location.lat [double]$Lng = $JSON.results.geometry.location.lng $LatLong = New-Object PSObject If($Status -eq "OK") { $LatLong | Add-Member -type NoteProperty -Name 'Latitude' -Value $Lat $LatLong | Add-Member -type NoteProperty -Name 'Longitude' -Value $Lng } ElseIf ($Status -eq 'ZERO_RESULTS') { $LatLong | Add-Member -type NoteProperty -Name 'Latitude' -Value 0 $LatLong | Add-Member -type NoteProperty -Name 'Longitude' -Value 0 } Else { $ErrorMessage = $JSON.error_message Write-Host -ForegroundColor Yellow "WARNING: Address geolocation failed for the following reason: $Status - $ErrorMessage" $LatLong | Add-Member -type NoteProperty -Name 'Latitude' -Value 0 $LatLong | Add-Member -type NoteProperty -Name 'Longitude' -Value 0 } } Catch { "Something went wrong. Please try again." $ev.message } Return $LatLong } } Function New-DynamicParam { [cmdletbinding()] Param ( [Parameter(Mandatory=$True)] [string]$ParamName, [Parameter(Mandatory=$True)] [string]$Prefix, [Parameter(Mandatory=$True)] [bool]$ParamRequired, [Parameter(Mandatory=$True)] [int]$Position ) Process { # Set the dynamic parameters' name New-Variable -Name "ParamName_$Prefix" -Value $ParamName -Scope Script -Force # Create the collection of attributes $AttribColl_XXX = New-Object System.Collections.ObjectModel.Collection[System.Attribute] # Create and set the parameters' attributes $ParamAttrib_XXX = New-Object System.Management.Automation.ParameterAttribute $ParamAttrib_XXX.Mandatory = $ParamRequired $ParamAttrib_XXX.Position = $Position # Add the attributes to the attributes collection $AttribColl_XXX.Add($ParamAttrib_XXX) New-Variable -Name "AttribColl_$Prefix" -Value $AttribColl_XXX -Scope Script -Force } } Function ConvertFrom-XMLElement { <# .Synopsis Converts named nodes of an element to properties of a PSObject, recursively. .Parameter Element The element to convert to a PSObject. .Parameter SelectXmlInfo Output from the Select-Xml cmdlet. .Inputs Microsoft.PowerShell.Commands.SelectXmlInfo output from Select-Xml. .Outputs System.Management.Automation.PSCustomObject object created from selected XML. .Link Select-Xml .Example Select-Xml /configuration/appSettings/add web.config |ConvertFrom-XmlElement key value --- ----- webPages:Enabled false #> #Requires -Version 3 [CmdletBinding()][OutputType([psobject])] Param( [Parameter(ParameterSetName='Element',Position=0,Mandatory=$true,ValueFromPipeline=$true)][Xml.XmlElement] $Element, [Parameter(ParameterSetName='SelectXmlInfo',Position=0,Mandatory=$true,ValueFromPipeline=$true)] [Microsoft.PowerShell.Commands.SelectXmlInfo]$SelectXmlInfo ) Process { switch($PSCmdlet.ParameterSetName) { SelectXmlInfo { @($SelectXmlInfo |% {[Xml.XmlElement]$_.Node} | ConvertFrom-XmlElement) } Element { if(($Element.SelectNodes('*') |group Name |measure).Count -eq 1) { @($Element.SelectNodes('*') |ConvertFrom-XmlElement) } else { $properties = @{} $Element.Attributes |% {[void]$properties.Add($_.Name,$_.Value)} foreach($node in $Element.ChildNodes |? {$_.Name -and $_.Name -ne '#whitespace'}) { $subelements = $node.SelectNodes('*') |group Name $value = if($node.InnerText -and !$subelements) { $node.InnerText } elseif(($subelements |measure).Count -eq 1) { @($node.SelectNodes('*') | ConvertFrom-XmlElement) } else { ConvertFrom-XmlElement $node } if(!$properties.Contains($node.Name)) { # new property [void]$properties.Add($node.Name,$value) } else { # property name collision! if($properties[$node.Name] -isnot [Collections.Generic.List[object]]) { $properties[$node.Name] = ([Collections.Generic.List[object]]@($properties[$node.Name],$value)) } else { $properties[$node.Name].Add($value) } } } New-Object PSObject -Property $properties } } } } } Function New-EPCTestPointXML { [cmdletbinding()] Param ( [Parameter(Mandatory=$True)] $Data ) $xmlData = '<testPointList>' ForEach ($Obj in $Data) { $Properties = $Obj | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name $xmlData += '<testPoint>' foreach ($Property in $Properties) { $xmlData += "<$Property>$($Obj.$Property)</$Property>" } $xmlData += '</testPoint>' } $xmlData += '</testPointList>' Return $xmlData } Function Show-GroupAndStats { <# .SYNOPSIS Groups a set of data by one parameter, and shows sum, average, min, max for another numeric parameter (such as duration) .DESCRIPTION Groups a set of data by one parameter, and shows sum, average, min, max for another numeric parameter (such as duration) .PARAMETER Input The data to group and sum. Can be pipelined .PARAMETER GroupBy The parameter to group on .PARAMETER SumBy The parameter to calculate numerical statistics. Must be a numeric field .PARAMETER ShowSumByAsTimeFormat If the SumBy parameter is in seconds (Duration is an example), format the output as dd.hh:mm:ss instead of seconds .EXAMPLE Get-NectarSessions | Show-GroupAndStats -GroupBy CallerLocation -SumBy Duration Will group all calls by caller location and show sum, avg, min, max for the Duration column .NOTES Version 1.0 #> Param ( [Parameter(ValueFromPipeline, Mandatory=$True)] [pscustomobject[]]$Input, [Parameter(Mandatory=$True)] [string]$GroupBy, [Parameter(Mandatory=$True)] [string]$SumBy, [switch]$ShowSumByAsTimeFormat ) # Validate the parameters exist and are the proper format If (($Input | Get-Member $GroupBy) -eq $NULL) { Write-Error "$GroupBy is not a valid parameter for the source data" Break } If (($Input | Get-Member $SumBy) -eq $NULL) { Write-Error "$SumBy is not a valid parameter for the source data" Break } ElseIf (($Input | Get-Member $SumBy).Definition -NotMatch 'int|float|double') { Write-Error "$SumBy is not a numeric field" Break } Try { If ($ShowSumByAsTimeFormat) { $Input | Group-Object $GroupBy | Sort-Object Count -Descending | %{ [pscustomobject][ordered] @{ $GroupBy = $_.Name 'Count' = $_.Count "SUM_$SumBy" = [timespan]::FromSeconds(($_.Group | Measure-Object $SumBy -Sum).Sum) "AVG_$SumBy" = [timespan]::FromSeconds([math]::Round(($_.Group | Measure-Object $SumBy -Average).Average)) "MIN_$SumBy" = [timespan]::FromSeconds(($_.Group | Measure-Object $SumBy -Minimum).Minimum) "MAX_$SumBy" = [timespan]::FromSeconds(($_.Group | Measure-Object $SumBy -Maximum).Maximum) } } } Else { $Input | Group-Object $GroupBy | Sort-Object Count -Descending | %{ [pscustomobject][ordered] @{ $GroupBy = $_.Name 'Count' = $_.Count "SUM_$SumBy" = ($_.Group | Measure-Object $SumBy -Sum).Sum "AVG_$SumBy" = [math]::Round(($_.Group | Measure-Object $SumBy -Average).Average,2) "MIN_$SumBy" = ($_.Group | Measure-Object $SumBy -Minimum).Minimum "MAX_$SumBy" = ($_.Group | Measure-Object $SumBy -Maximum).Maximum } } } } Catch { Write-Error "Error showing output." } } Function Get-JSONErrorStream { <# .SYNOPSIS Returns the error text of a JSON stream .DESCRIPTION Returns the error text of a JSON stream .PARAMETER JSONResponse The error response .EXAMPLE Get-JSONErrorStream $_ Returns the error message from a JSON stream that errored out. .NOTES Version 1.0 #> Param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] $JSONResponse ) $ResponseStream = $JSONResponse.Exception.Response.GetResponseStream() $Reader = New-Object System.IO.StreamReader($ResponseStream) $ResponseBody = $Reader.ReadToEnd() | ConvertFrom-Json Write-Host -ForegroundColor Red -BackgroundColor Black $ResponseBody.errorMessage Write-Host Write-Host } Function New-JWT { <# .SYNOPSIS Generates a JSON Web Ticket (JWT) for authenticating with services like Zoom .DESCRIPTION Generates a JSON Web Ticket (JWT) for authenticating with services like Zoom .PARAMETER JSONResponse The error response .EXAMPLE Get-JSONErrorStream $_ Returns the error message from a JSON stream that errored out. .NOTES Version 1.0 #> Param( [Parameter(Mandatory = $True)] [ValidateSet("HS256", "HS384", "HS512")] $Algorithm = $null, $type = $null, [Parameter(Mandatory = $True)] [string]$Issuer = $null, [int]$ValidforSeconds = $null, [Parameter(Mandatory = $True)] $SecretKey = $null ) $exp = [int][double]::parse((Get-Date -Date $((Get-Date).addseconds($ValidforSeconds).ToUniversalTime()) -UFormat %s)) # Grab Unix Epoch Timestamp and add desired expiration. [hashtable]$header = @{alg = $Algorithm; typ = $type} [hashtable]$payload = @{iss = $Issuer; exp = $exp} $headerjson = $header | ConvertTo-Json -Compress $payloadjson = $payload | ConvertTo-Json -Compress $headerjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($headerjson)).Split('=')[0].Replace('+', '-').Replace('/', '_') $payloadjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($payloadjson)).Split('=')[0].Replace('+', '-').Replace('/', '_') $ToBeSigned = $headerjsonbase64 + "." + $payloadjsonbase64 $SigningAlgorithm = switch ($Algorithm) { "HS256" {New-Object System.Security.Cryptography.HMACSHA256} "HS384" {New-Object System.Security.Cryptography.HMACSHA384} "HS512" {New-Object System.Security.Cryptography.HMACSHA512} } $SigningAlgorithm.Key = [System.Text.Encoding]::UTF8.GetBytes($SecretKey) $Signature = [Convert]::ToBase64String($SigningAlgorithm.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($ToBeSigned))).Split('=')[0].Replace('+', '-').Replace('/', '_') $token = "$headerjsonbase64.$payloadjsonbase64.$Signature" $token } Function ParseBool { [CmdletBinding()] param( [Parameter(Position=0)] [System.String]$inputVal ) switch -regex ($inputVal.Trim()) { "^(1|true|yes|on|enabled)$" { Return $True } default { Return $False } } } Export-ModuleMember -Alias * -Function * # SIG # Begin signature block # MIINEQYJKoZIhvcNAQcCoIINAjCCDP4CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUw/ro9BvziYVzL+mSk09I2gdC # brygggpTMIIFGzCCBAOgAwIBAgIQCVcmswfJ1koJSkb+PKEcYzANBgkqhkiG9w0B # AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz # c3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTIwMDIxMzAwMDAwMFoXDTIzMDIx # NzEyMDAwMFowWDELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8xDzANBgNV # BAcTBkd1ZWxwaDESMBAGA1UEChMJS2VuIExhc2tvMRIwEAYDVQQDEwlLZW4gTGFz # a28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsCGnJWjBqNX+R8Pyv # 24IX7EnQkYm8i/VOd5dpxUMKOdnq+oEi2fr+tkWHtSgggbjTdcXP4l6fBxBzuGB2 # q12eLaa136Um4KAmYRnuqJ2IXfdEyW8/Zib7FVzUV41dwRBVH/VF+QZOHxwcL0MJ # 5OwiRSLiMWYqWk7c+8UIFpDe17Pjevy8g2o0RcTAhyDeEZ1FPAIFk/nkirB5psMz # mC5TfCKkuxQWOg3/F78KnvBxuVl7q9QcS2BeJXrospvQ130qRMOjrcO6suuRjtrT # iuMt3CjKtStnqKAY/2yPV1Gvlg4itoO1quANvoNgYB66B3zQZMBGicdwnq0nkG7B # vPENAgMBAAGjggHFMIIBwTAfBgNVHSMEGDAWgBRaxLl7KgqjpepxA8Bg+S32ZXUO # WDAdBgNVHQ4EFgQUHAoKnWsI9RGj62kGJANJvR36UXYwDgYDVR0PAQH/BAQDAgeA # MBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGA1UdHwRwMG4wNaAzoDGGL2h0dHA6Ly9j # cmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMDWgM6Axhi9o # dHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDBM # BgNVHSAERTBDMDcGCWCGSAGG/WwDATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3 # dy5kaWdpY2VydC5jb20vQ1BTMAgGBmeBDAEEATCBhAYIKwYBBQUHAQEEeDB2MCQG # CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wTgYIKwYBBQUHMAKG # Qmh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJBc3N1cmVk # SURDb2RlU2lnbmluZ0NBLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUA # A4IBAQAVlWAZaJ+IYUINcKhTdojQ3ViHdfcuzgTMECn1bCbr4DxH/SDmNr1n3Nm9 # ZIodfPE5fjFuqaYqQvfzFutqBTiX5hStT284WpGjrPwSuiJDRoI3iBTjyy5OABpi # kpgdncJeyNvEO2ermcBrVw4t4AUZKfYsyjxfXaX8INcvHdNGhTiN5x4SjGSXxSvx # hr7F9aLeE0mG+5yDlr5HbfPbyqLWdvLP4UcQ9WrJOmN0wa7qanrErr3ZeuDZQebL # zEesJy1VCY2bqTEI8/fyTqnlLjut7i9dvp84zKomX30lqy9R81WUas9XruMLfgVR # 3BVuBoyVtdx4AmgVzHoznDWs/vh/MIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1 # b5VQCDANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGln # aUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtE # aWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgx # MDIyMTIwMDAwWjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j # MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBT # SEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEF # AAOCAQ8AMIIBCgKCAQEA+NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLX # cep2nQUut4/6kkPApfmJ1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSR # I5aQd4L5oYQjZhJUM1B0sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXi # TWAYvqrEsq5wMWYzcT6scKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5 # Ng2Q7+S1TqSp6moKq4TzrGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8 # vYWxYoNzQYIH5DiLanMg0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYD # VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYB # BQUHAwMweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5k # aWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4 # oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv # b3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy # dEFzc3VyZWRJRFJvb3RDQS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCow # KAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZI # AYb9bAMwHQYDVR0OBBYEFFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaA # FEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPz # ItEVyCx8JSl2qB1dHC06GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRu # pY5a4l4kgU4QpO4/cY5jDhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKN # JK4kxscnKqEpKBo6cSgCPC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmif # z0DLQESlE/DmZAwlCEIysjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN # 3fYBIM6ZMWM9CBoYs4GbT8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKy # ZqHnGKSaZFHvMYICKDCCAiQCAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoT # DERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UE # AxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQCVcm # swfJ1koJSkb+PKEcYzAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAA # oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w # DAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUkkCkQ++dAuzY/ScTjRMEjQUX # KQ0wDQYJKoZIhvcNAQEBBQAEggEAln+6T6MOZitAQwPKrpdehsfVWUm0F9skfhEt # 3SgcdqRAVP2ybPViFy1hX1in327/Mhg3VS+ThXcPM1D9z/nGbe4rcphIPNjn4L5k # gMQYA0LcjsLgIKuOMspsXU4dw6XoZD+l72yCtNT0rHDxxriKqL+Snq1dnvaLq9Hj # 6+RorRQVW128LGyBQ9SNDUC6A70DcvdSHdqEzeLWW+Z2TxsQS9qO3f2muctP68KE # m4PCq6L0wsDULvtTPD0Nwx6T99NKmlv6EIVpPCm0tmv2nNtC3P1GvKdNXYscteZ3 # TrMpCwr6A8ccp2TA1FzGs24PBYz3un9+PgVgFst8sRqKNhv1nA== # SIG # End signature block |