Nectar10.psm1
<#
These series of PowerShell functions allow administrators to automate many functions in Nectar 10 that are otherwise difficult or time-consuming to perform in the UI. To install, place the Nectar10.psm1, Nectar10.psd1 and format.ps1xml in a folder called Nectar10 in your %ProgramFiles%\WindowsPowerShell\Modules folder. To begin, start a PowerShell session and type: Connect-NectarCloud us.nectar.services (or whatever tenant you want to connect to). You will be prompted to input the creds for the tenant you want to connect to. Once you are successfully connected, you can run any number of commands. For a complete list, type Get-Command -Module Nectar10. If you want help with a particular command, type Get-Help <commandname> -Full (ie. Get-Help Connect-NectarCloud -Full). #> ############################################################## 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 .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.3 #> [Alias("cnc")] 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=$False)] [string]$TenantName, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.Credential()] [PSCredential] $Credential, [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$StoredCredentialTarget ) # 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 } # 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)) { # Attempt connection to tenant $WebRequest = Invoke-WebRequest -Uri "https://$CloudFQDN/dapi/info/network/types" -Method GET -Credential $Credential -UseBasicParsing If ($WebRequest.StatusCode -ne 200) { Write-Error "Could not connect to $CloudFQDN using $($Credential.UserName)" } Else { Write-Host -ForegroundColor Green "Successful connection to https://$CloudFQDN using" ($Credential).UserName $Global:NectarCloud = $CloudFQDN $Global:NectarCred = $Credential # If there is only one availabe 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 $TenantName. 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') { Invoke-WebRequest -Uri "https://$CloudFQDN/saml/login?domain=$DomainName" -Method POST } 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 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 -Credential $Global:NectarCred -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 = Set-NectarDefaultTenantName } $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/aapi/tenant/datasources?tenant=$TenantName" Return $JSON } 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 = Set-NectarDefaultTenantName } $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -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 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 NectarTenantName -Scope Global -ErrorAction:SilentlyContinue } ############################################################## 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. .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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 User 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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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)] [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 password = $Password isAdmin = $AdminStatus userRoleName = $Role userStatus = "ACTIVE" } $JSONBody = $Body | ConvertTo-Json Try { Write-Verbose $JSONBody $JSON = Invoke-RestMethod -Method POST -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 } $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/aapi/config/locations?pageNumber=1&tenant=$TenantName&pageSize=$ResultSize&searchQuery=$SearchQuery" 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 } $JSONBody = $Body | ConvertTo-Json Try { Write-Verbose $JSONBody $JSON = Invoke-RestMethod -Method PUT -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 Management Functions - Number Location ############################################################## 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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 Management Functions - Numbers ############################################################## 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 -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 | 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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 Devices 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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 Version 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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 $_ } } } ############################################################## AD 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 -Credential $Global:NectarCred -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(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','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','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','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(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(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName, [parameter(Mandatory=$False)] [ValidateSet('DEFAULT','USERS', 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 ($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 ($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) } # Convert date to UNIX timestamp If ($TimePeriodFrom) { $TimePeriodFrom = (Get-Date -Date $TimePeriodFrom -UFormat %s) + '000' $FilterParams.Add('StartDateFrom',$TimePeriodFrom) } If ($TimePeriodTo) { $TimePeriodTo = (Get-Date -Date $TimePeriodTo -UFormat %s) + '000' $FilterParams.Add('StartDateTo',$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 $Cookie = $FilterResults.BaseResponse.Cookies | 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 StartDateFrom 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','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','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','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 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 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")] 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','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','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','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(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)] [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,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 } 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 { $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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 StartDateFrom 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','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','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','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 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 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','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','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','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(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(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 -Credential $Global:NectarCred -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("Platforms", "callerPlatform", "calleePlatform")] [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','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")] [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','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")] [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','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")] [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','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")] [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','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")] [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','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")] [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','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")] [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','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 StartDateFrom 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','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','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','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 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 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 -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','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','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','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(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(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 -Credential $Global:NectarCred -uri $URI -Body $Params -WebSession $FilterSession ForEach ($SessionType in $JSON) { $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 StartDateFrom 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','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','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','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 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 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','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','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','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(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(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 -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/dapi/quality/locations" -Body $Params -WebSession $FilterSession $JSONGoodPoor = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -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 $_ } } } ############################################################## Events 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 StartDateFrom 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 -Credential $Global:NectarCred -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 -Credential $Global:NectarCred -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-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 StartDateFrom 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','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 StartDateFrom 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','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 StartDateFrom 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','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 StartDateFrom 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','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 StartDateFrom 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','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 $_ } } } ############################################################# 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)] [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." Get-JSONErrorStream -JSONResponse $_ } } Else { # 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 ($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 $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-MSTeamsAppInfo -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-MSTeamsAppInfo -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 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 } # 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 If (!$SkipUserCount) { Get-MSTeamsUserLicenseCount -AuthToken $AuthToken } } Catch { Write-Host 'FAIL' -ForegroundColor Red Get-JSONErrorStream -JSONResponse $_ } 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-MSTeamsAppInfo -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-MSTeamsAppInfo { <# .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-MSTeamsAppInfo .NOTES Version 1.0 #> [Alias("gmtai")] Param ( [Parameter(ValueFromPipelineByPropertyName, Mandatory=$False)] [string]$TenantName ) Begin { Connect-NectarCloud } Process { Try { If (!$TenantName) { $TenantName = Set-NectarDefaultTenantName } $JSON = Invoke-RestMethod -Method GET -Credential $Global:NectarCred -uri "https://$Global:NectarCloud/aapi/clouddatasources/configuration/msteams/subscription?tenant=$TenantName" Return $JSON.data } Catch { Write-Error "TenantName: $TenantName - No MS Teams information found, or insufficient permissions." 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.2 #> [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-MSTeamsAppInfo -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" } 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 ($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-MSTeamsAppInfo -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 .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 .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-MSTeamsAppInfo -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-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-MSTeamsAppInfo -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-MSTeamsAppInfo -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-MSTeamsAppInfo -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-MSTeamsAppInfo -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-MSTeamsAppInfo -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 Enhanced. Defaults to Enhanced mode Enhanced 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. Fast 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 Enhanced mode, but can't obtain information about Enterprise Voice functionality. It's twice as fast as Enhanced mode .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 = 'Extended' ) 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-MSTeamsAppInfo -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 'Fast') { $Properties = 'id,assignedLicenses' } Else { $Properties = 'id,assignedPlans' } $Params = @{ TenantName = $TenantName Properties = $Properties Filter = 'accountEnabled eq true' AuthToken = $AuthToken ResultSize = 200000 } If ($HideProgressBar) { $Params.Add('HideProgressBar', $HideProgressBar) } If ($ProgressUpdateFreq) { $Params.Add('ProgressUpdateFreq', $ProgressUpdateFreq) } If (!$UserList) { If ($TestMode -eq 'Fast') { $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 } } $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-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-MSTeamsAppInfo -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')] $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-MSTeamsAppInfo -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')] $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-MSTeamsAppInfo -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 $_ } } } ############################################################## 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 Set-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 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 .EXAMPLE Get-NectarSessions | Show-GroupAndStats -GroupField CallerLocation -StatsField 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 ) # 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 { $Input | Group-Object $GroupBy | %{ [pscustomobject][ordered] @{ $GroupBy = $_.Name 'Count' = $_.Count "SUM_$SumBy" = ($_.Group | Measure-Object $SumBy -Sum).Sum "AVG_$SumBy" = ($_.Group | Measure-Object $SumBy -Average).Average "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 # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUSLchfKaYSsVL13PVt0jsZZUW # hwigggpTMIIFGzCCBAOgAwIBAgIQCVcmswfJ1koJSkb+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 # DAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUgx4Dz+W/Jg8sjdFlDFnlajmz # V6MwDQYJKoZIhvcNAQEBBQAEggEAOCIoWMuzJBIPkSODQNmdrfv5KCPWAin5FqtO # 1Q2j1E9f6VzGI5cbOs4BZYNa/phPMnMZEby5a3XrGB/fRB3nf9vC0IWSf9/xbiGV # HZ0gP5Wf/KhkH0ew58LhDQpbW6UucSx21aIXaEP9SvT5IOb9ppG6cz7rF6Ds5Xrs # adLTJxyusn6KuUq8WXtKqLHcd42gzVjEgcrpF0QMg5NelbpUXJSf4U4xLqzQkVVT # 5GTeE52LlpMdEmVqF01QnOYbQK03rKZwjiuvJ4zZAp4oP1vdXO5K6BK8UHpEp5MC # /SbS8PFcO3wnyp/5Y4Udu06DyaussYkM83T3sqKF0aI8gAySVg== # SIG # End signature block |