Public/New-ServiceNowSession.ps1

<#
.SYNOPSIS
Create a new ServiceNow session

.DESCRIPTION
Create a new ServiceNow session via credentials, OAuth, or access token.
This session will be used by default for all future calls.
Optionally, you can specify the api version you'd like to use; the default is the latest.
To use OAuth, ensure you've set it up, https://docs.servicenow.com/bundle/quebec-platform-administration/page/administer/security/task/t_SettingUpOAuth.html.

.PARAMETER Url
Base domain for your ServiceNow instance, eg. tenant.domain.com

.PARAMETER Credential
Username and password to connect. This can be used standalone to use basic authentication or in conjunction with ClientCredential for OAuth.

.PARAMETER ClientCredential
Required for OAuth. Credential where the username is the Client ID and the password is the Secret.

.PARAMETER AccessToken
Provide the access token directly if obtained outside of this module.

.PARAMETER Proxy
Use a proxy server for the request, rather than connecting directly. Provide the full url.

.PARAMETER ProxyCredential
Credential of user who can access Proxy. If not provided, the current user will be used.

.PARAMETER ApiVersion
Specific API version to use. The default is the latest.

.PARAMETER GetAllTable
Populate $ServiceNowTable with data from all tables the user has access to

.PARAMETER PassThru
Provide the resulting session object to the pipeline as opposed to setting as a script scoped variable to be used by default for other calls.
This is useful if you want to have multiple sessions with different api versions, credentials, etc.

.EXAMPLE
New-ServiceNowSession -Url tenant.domain.com -Credential $mycred
Create a session using basic authentication and save it to a script-scoped variable

.EXAMPLE
New-ServiceNowSession -Url tenant.domain.com -Credential $mycred -ClientCredential $myClientCred
Create a session using OAuth and save it to a script-scoped variable

.EXAMPLE
New-ServiceNowSession -Url tenant.domain.com -AccessToken 'asdfasd9f87adsfkksk3nsnd87g6s'
Create a session with an existing access token and save it to a script-scoped variable

.EXAMPLE
$session = New-ServiceNowSession -Url tenant.domain.com -Credential $mycred -ClientCredential $myClientCred -PassThru
Create a session using OAuth and save it as a local variable to be provided to functions directly

.EXAMPLE
New-ServiceNowSession -Url tenant.domain.com -Credential $mycred -Proxy http://1.2.3.4
Create a session utilizing a proxy to connect

.INPUTS
None

.OUTPUTS
Hashtable if -PassThru provided

.LINK
https://docs.servicenow.com/bundle/quebec-platform-administration/page/administer/security/reference/r_OAuthAPIRequestParameters.html
#>

function New-ServiceNowSession {

    [CmdletBinding(DefaultParameterSetName = 'BasicAuth')]
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'api call provides in plain text')]
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'No state is actually changing')]

    param(
        [Parameter(Mandatory)]
        [Alias('ServiceNowUrl')]
        [string] $Url,

        [Parameter(Mandatory, ParameterSetName = 'BasicAuth')]
        [Parameter(Mandatory, ParameterSetName = 'OAuth')]
        [Parameter(Mandatory, ParameterSetName = 'BasicAuthProxy')]
        [Parameter(Mandatory, ParameterSetName = 'OAuthProxy')]
        [Alias('Credentials')]
        [System.Management.Automation.PSCredential] $Credential,

        [Parameter(Mandatory, ParameterSetName = 'OAuth')]
        [Parameter(Mandatory, ParameterSetName = 'OAuthProxy')]
        [System.Management.Automation.PSCredential] $ClientCredential,

        [Parameter(Mandatory, ParameterSetName = 'AccessToken')]
        [Parameter(Mandatory, ParameterSetName = 'AccessTokenProxy')]
        [string] $AccessToken,

        [Parameter(Mandatory, ParameterSetName = 'BasicAuthProxy')]
        [Parameter(Mandatory, ParameterSetName = 'OAuthProxy')]
        [Parameter(Mandatory, ParameterSetName = 'AccessTokenProxy')]
        [string] $Proxy,

        [Parameter(ParameterSetName = 'BasicAuthProxy')]
        [Parameter(ParameterSetName = 'OAuthProxy')]
        [Parameter(ParameterSetName = 'AccessTokenProxy')]
        [System.Management.Automation.PSCredential] $ProxyCredential,

        [Parameter()]
        [int] $ApiVersion,

        [Parameter()]
        [switch] $GetAllTable,

        [Parameter()]
        [switch] $PassThru
    )

    Write-Verbose $PSCmdLet.ParameterSetName

    if ( $ApiVersion -le 0 ) {
        $version = ''
    }
    else {
        $version = ('/v{0}' -f $ApiVersion)
    }

    $newSession = @{
        Domain  = $Url
        BaseUri = ('https://{0}/api/now{1}' -f $Url, $version)
    }

    if ( $PSBoundParameters.ContainsKey('Proxy') ) {
        $newSession.Add('Proxy', $Proxy)
        if ( $PSBoundParameters.ContainsKey('ProxyCredential') ) {
            $newSession.Add('ProxyCredential', $ProxyCredential)
        }
    }

    switch -Wildcard ($PSCmdLet.ParameterSetName) {
        'OAuth*' {
            $params = @{
                Uri             = 'https://{0}/oauth_token.do' -f $Url
                Body            = @{
                    'grant_type'    = 'password'
                    'client_id'     = $ClientCredential.UserName
                    'client_secret' = $ClientCredential.GetNetworkCredential().Password
                    'username'      = $Credential.UserName
                    'password'      = $Credential.GetNetworkCredential().Password
                }
                Method          = 'Post'
                UseBasicParsing = $true
            }

            # need to add this manually here, in addition to above, since we're making a rest call before our session is created
            if ( $PSBoundParameters.ContainsKey('Proxy') ) {
                $params.Add('Proxy', $Proxy)
                if ( $PSBoundParameters.ContainsKey('ProxyCredential') ) {
                    $params.Add('ProxyCredential', $ProxyCredential)
                }
                else {
                    $params.Add('ProxyUseDefaultCredentials', $true)
                }
            }

            $oldProgressPreference = $ProgressPreference
            $ProgressPreference = 'SilentlyContinue'

            $response = Invoke-WebRequest @params

            # set the progress pref back now that done with invoke-webrequest
            $ProgressPreference = $oldProgressPreference

            if ( $response.Content ) {
                $token = $response.Content | ConvertFrom-Json
                $newSession.Add('AccessToken', (New-Object System.Management.Automation.PSCredential('AccessToken', ($token.access_token | ConvertTo-SecureString -AsPlainText -Force))))
                $newSession.Add('RefreshToken', (New-Object System.Management.Automation.PSCredential('RefreshToken', ($token.refresh_token | ConvertTo-SecureString -AsPlainText -Force))))
            }
            else {
                # invoke-webrequest didn't throw an error, but we didn't get a token back either
                throw ('"{0} : {1}' -f $response.StatusCode, $response | Out-String )
            }
        }

        'AccessToken*' {
            $newSession.Add('AccessToken', (New-Object System.Management.Automation.PSCredential('AccessToken', ($AccessToken | ConvertTo-SecureString -AsPlainText -Force))))
        }

        'BasicAuth*' {
            $newSession.Add('Credential', $Credential)
        }

        Default {

        }
    }

    # Write-Verbose 'Retrieving list of classes for this instance. This will take a few seconds...'
    # $cmdbParams = @{
    # Table = 'sys_db_object'
    # # Query = 'nameSTARTSWITHcmdb_ci'
    # Properties = 'name', 'sys_id', 'label'
    # First = 100000
    # ServiceNowSession = $newSession
    # }

    # $class = Get-ServiceNowTable @cmdbParams -ErrorAction SilentlyContinue |
    # Select-Object @{
    # 'n' = 'Name'
    # 'e' = { $_.name }
    # },
    # @{
    # 'n' = 'SysId'
    # 'e' = { $_.sys_id }
    # },
    # @{
    # 'n' = 'ClassName'
    # 'e' = { $_.label }
    # }
    # if ( $class ) {
    # $newSession.Add('Classes', $class)
    # }

    Write-Verbose ($newSession | ConvertTo-Json)

    if ( $PassThru ) {
        $newSession
    }
    else {
        $Script:ServiceNowSession = $newSession
    }

    if ( $GetAllTable.IsPresent ) {
        Write-Verbose 'Getting table number prefixes'
        $defaultTable = $ServiceNowTable
        try {
            $numbers = Get-ServiceNowRecord -Table 'sys_number' -Property prefix, category -First 10000 -IncludeTotalCount
            foreach ($number in $numbers) {
                if ( $number.prefix.ToLower() -notin $defaultTable.NumberPrefix ) {
                    $ServiceNowTable.Add(
                        [pscustomobject] @{
                            "Name"             = ($number.category.link | Select-String -Pattern '^.*\?name=(.*)$').matches.groups[1].Value
                            "ClassName"        = $number.category.display_value
                            "Type"             = $null
                            "NumberPrefix"     = $number.prefix.ToLower()
                            "DescriptionField" = "short_description"
                        }
                    ) | Out-Null
                }
            }
        }
        catch {
            Write-Verbose "Session created, but failed to populate ServiceNowTable. Prefixes beyond the default won't be available. $_"
        }
    }
}