Public/Connect-AtwsWebAPI.ps1

<#
    .COPYRIGHT
    Copyright (c) Hugo Klemmestad. All rights reserved. Licensed under the MIT license.
    See https://github.com/ecitsolutions/Autotask/blob/master/LICENSE.md for license information.

#>


Function Connect-AtwsWebAPI {
    <#
        .SYNOPSIS
            This function connects to the Autotask Web Services API, authenticates a user and creates a
            SOAP webservices proxy object.
        .DESCRIPTION
            The function takes a credential object and uses it to authenticate and connect to the Autotask
            Web Services API. This is done by creating a webservices proxy. The proxy object imports the SOAP
            WSDL definition file, creates all entity classes in PowerShell and exposes the basic methods
            (query(), create(), update(), remove(), GetEntityInfo(), GetFieldInfo() and a few more).
        .INPUTS
            Nothing
        .OUTPUTS
            Nothing
        .EXAMPLE
            Connect-AtwsWebAPI
            If there doesn't exist any saved Default connection profile it prompts for a username and password and authenticates to Autotask. Otherwise it loads the connection profile named "Default" and connects.
        .EXAMPLE
            Connect-AtwsWebAPI -ProfileName Sandbox
            Loads the connection profile named 'Sandbox' and connects. If there are no saved connection profile called 'Sandbox' it throws an exception and exits.
        .EXAMPLE
            Connect-AtwsWebAPI -Credential $Credential -ApiTrackingIdentifier $ApiKey
            Connects to Autotask using the credentials passed as parameters
        .EXAMPLE
            New-AtwsModuleConfiguration -Credential $Credential -ApiTrackingIdentifier $ApiKey -Dateconversion Disabled | Connect-AtwsWebAPI
            Creates a new module configuration object with date conversion between EST (the Autotask API always uses EST no matter which data center you are connected to) and local time disabled.
        .NOTES
            Related commands:
            New-AtwsModuleConfiguration
            Save-AtwsModuleConfiguration
            Set-AtwsModuleConfiguration
            Get-AtwsModuleConfiguration
  #>

    
    [cmdletbinding(
        SupportsShouldProcess = $true,
        ConfirmImpact = 'Low',
        DefaultParameterSetName = 'ConfigurationFile'
    )]
    Param
    (
        [Parameter(
            Mandatory = $true,
            ParameterSetName = 'Parameters'
        )]
        [ValidateNotNullOrEmpty()]    
        [pscredential]
        # The username and password for your Autotask API user
        $Credential,
    
        [Parameter(
            Mandatory = $true,
            ParameterSetName = 'Parameters'
        )]
        [string]
        # The API tracking identifier from your Autotask API user
        $ApiTrackingIdentifier,
    
        [Parameter(
            ParameterSetName = 'Parameters'
        )]
        [Alias('Picklist', 'UsePickListLabels')]
        [switch]
        # Have the module substitute all picklist ids for their textlabel at runtime
        $ConvertPicklistIdToLabel,
    
        [Parameter(
            ParameterSetName = 'Parameters'
        )]
        [ValidateScript( {
                # It can be empty, but if it isn't it should be max 8 characters and only letters and numbers
                if ($_.length -eq 0 -or ($_ -match '[a-zA-Z0-9]' -and $_.length -gt 0 -and $_.length -le 8)) {
                    $true
                }
                else {
                    $false
                }
            })]
        [string]
        # Not used. Kept for backwards compatility. Will be removed soon.
        $Prefix,

        [Parameter(
            ParameterSetName = 'Parameters'
        )]
        [switch]
        # Not used. Kept for backwards compatility. Will be removed soon.
        $RefreshCache,

    
        [Parameter(
            ParameterSetName = 'Parameters'
        )]
        [switch]
        # Not used. Kept for backwards compatility. Will be removed soon.
        $NoDiskCache,
    
        [Parameter(
            Mandatory = $true,
            ParameterSetName = 'ConfigurationObject'
        )]
        [ValidateScript( { 
                $requiredProperties = @('Username', 'Securepassword', 'SecureTrackingIdentifier', 'ConvertPicklistIdToLabel', 'Prefix', 'RefreshCache', 'DebugPref', 'VerbosePref', 'ErrorLimit', 'DateConversion', 'PicklistExpansion', 'UdfExpansion')
                $members = Get-Member -InputObject $_ -MemberType NoteProperty
                $missingProperties = Compare-Object -ReferenceObject $requiredProperties -DifferenceObject $members.Name -PassThru -ErrorAction SilentlyContinue
                if (-not($missingProperties)) {
                    $true               
                }
                else {
                    $missingProperties | ForEach-Object {
                        Throw [System.Management.Automation.ValidationMetadataException] "Property: '$_' missing"
                    } 
                }
            })]
        [pscustomobject]
        [alias('Configuration', 'Profile')]
        # A module configuration object created with New-AtwsModuleConfiguration
        $AtwsModuleConfiguration,
    
        [Parameter(
            ParameterSetName = 'ConfigurationFile'
        )]
        [ArgumentCompleter( {
                param($Cmd, $Param, $Word, $Ast, $FakeBound)
                $(Get-ChildItem -Path $Global:AtwsModuleConfigurationPath -Filter "*.clixml").FullName
            })]
        [ValidateScript( { 
                Test-Path $_
            })]
        [Alias('Path')]
        [IO.FileInfo]
        # The path to an alternate clixml file with connection profiles
        $ProfilePath = $( 
            if ($null -eq $Global:AtwsModuleConfigurationPath ) { 
                Join-Path -Path '\' -ChildPath AtwsConfig.clixml
            }
            else {
                Join-Path -Path $Global:AtwsModuleConfigurationPath -ChildPath AtwsConfig.clixml
            }),

        # Name of the Configuration inside the Config file.
        [Parameter(
            ParameterSetName = 'ConfigurationFile'
        )]
        [ArgumentCompleter( {
                param($Cmd, $Param, $Word, $Ast, $FakeBound)
                if ($FakeBound.ProfilePath) {
                    [IO.FileInfo]$filepath = $FakeBound.ProfilePath
                }
                else {
                    [IO.FileInfo]$filepath = $(Join-Path -Path $Global:AtwsModuleConfigurationPath -ChildPath AtwsConfig.clixml)
                }
                $tempsettings = Import-Clixml -Path $filepath.Fullname
                if ($tempsettings -is [hashtable]) {
                    foreach ($item in ($tempsettings.keys | Sort-Object)) {
                        "'{0}'" -F $($item -replace "'", "''")
                    }
                }
            })]
        [alias('Name')]
        # The name for the connection profile you want to use. Default is "Default".
        [string]
        $ProfileName = 'Default'
    )
    
    begin { 
    
        # Enable modern -Debug behavior
        if ($PSCmdlet.MyInvocation.BoundParameters['Debug'].IsPresent) { $DebugPreference = 'Continue' }
    
        Write-Verbose ('{0}: Begin of function' -F $MyInvocation.MyCommand.Name)

    }
  
    process {
        # Make sure we have a valid configuration before we proceed
        # If we didn't get a prepared configuration object, create one from the parameters
        try {
            if ($PSCmdlet.ParameterSetName -eq 'Parameters') {
                $Parameters = @{
                    Credential               = $Credential
                    SecureTrackingIdentifier = ConvertTo-SecureString $ApiTrackingIdentifier -AsPlainText -Force
                    ConvertPicklistIdToLabel = $ConvertPicklistIdToLabel.IsPresent
                    Prefix                   = $Prefix
                    RefreshCache             = $RefreshCache.IsPresent
                    DebugPref                = $DebugPreference
                    VerbosePref              = $VerbosePreference
                }
                # We cannot reuse $configuration variable without triggering the validationscript
                # again
                Write-Verbose ('{0}: Calling New-AtwsModuleConfiguration with $parameters splatting' -F $MyInvocation.MyCommand.Name)
                $ConfigurationData = New-AtwsModuleConfiguration @Parameters
            }
            elseif ($ENV:FUNCTIONS_WORKER_RUNTIME) {
                # We are probably on Azure and in an azure function to boot.
                # Can be used locally, too, but that is a secret...

                try {
                    $UserName = $ENV:AtwsUserName
                    $PassWord = $ENV:AtwsPassword
                    $SecurePass = $PassWord | ConvertTo-SecureString -AsPlainText -Force
                    $Credential = [System.Management.Automation.PSCredential]::new($UserName, $SecurePass )
                    
                    $TrackingIdentifier = $ENV:AtwsTrackingIdentifier
                    $SecureTrackingIdentifier = $TrackingIdentifier | ConvertTo-SecureString -AsPlainText -Force

                }
                catch {
                    $message = 'Unable to get needed variables and convert them from Azure Function Application Settings. Fix and try again.'
                    throw (New-Object System.Configuration.Provider.ProviderException $message)
                }
                Write-Verbose ('{0}: Calling New-AtwsModuleConfiguration with variables from Azure Functions application settings.' -F $MyInvocation.MyCommand.Name)
                $ConfigurationData = New-AtwsModuleConfiguration -Credential $Credential -SecureTrackingIdentifier $SecureTrackingIdentifier 

            }
            elseif ($env:AUTOMATION_ASSET_ACCOUNTID ) {
                # We are on Azure. Try to get credentials and api key
                try {
                    $Credential = Get-AutomationPSCredential -Name 'AtwsDefaultCredential'
                }
                catch {
                    $message = "Could not find credentials with name 'AtwsDefaultCredential'. Create and run again."
                    throw (New-Object System.Configuration.Provider.ProviderException $message) 
                    return
                }
                # Now try for API key
                try {
                    $SecureIdentifier = Get-AutomationVariable -Name 'AtwsDefaultSecureIdentifier'
                    $SecureIdentifier = $SecureIdentifier | ConvertTo-SecureString -AsPlainText -Force
                }
                catch {
                    $message = "Could not a variable with name 'AtwsDefaultSecureIdentifier'. Create and run again."
                    throw (New-Object System.Configuration.Provider.ProviderException $message) 
                    return
                }
                Write-Verbose ('{0}: Calling New-AtwsModuleConfiguration with variables from Azure Automation resources.' -F $MyInvocation.MyCommand.Name)
                # There are no $DebugPreferences on Azure Automation. Set explicitly to SlientlyContinue to avoid validation error
                $ConfigurationData = New-AtwsModuleConfiguration -Credential $Credential -SecureTrackingIdentifier $SecureIdentifier -DebugPref SilentlyContinue

            }
            elseif ($PSCmdlet.ParameterSetName -eq 'ConfigurationFile') {
                # If we arrive here and $ProfilePath has no value, then use default value
                if ($null -eq $Global:AtwsModuleConfigurationPath ) { 
                    Join-Path -Path '\' -ChildPath AtwsConfig.clixml
                }
                else {
                    Join-Path -Path $Global:AtwsModuleConfigurationPath -ChildPath AtwsConfig.clixml
                }
                Write-Verbose ('{0}: Calling Get-AtwsModuleConfiguration with profilename {1} and path {2}.' -F $MyInvocation.MyCommand.Name, $ProfileName, $ProfilePath)
                           
                $ConfigurationData = Get-AtwsModuleConfiguration -Name $ProfileName -Path $ProfilePath
               
                if ($null -eq $ConfigurationData) {
                    # Create a new configuration. Prompt for credentials
                    $ConfigurationData = New-AtwsModuleConfiguration 

                    # Prepare shouldProcess comments
                    $caption = 'Save connection credentials'
                    $warning = 'Do you want to save these credentials as your Default connection profile? It will be encrypted using SecureString and encoded in CliXML. See Get-Help Set-AtwsModuleConfiguration for how to modify it.'
                    $question = 'Is it OK to save your credentials to your $Profile folder?'

                    Write-Warning $warning 
                    # Lets do it and say we didn't!
                    if ($PSCmdlet.ShouldContinue($question, $caption)) {
                        Save-AtwsModuleConfiguration -Configuration $ConfigurationData
                    }
                }
            }
            elseif (Test-AtwsModuleConfiguration -Configuration $AtwsModuleConfiguration) {
                # We got a configuration object and it passed validation
                Write-Verbose ('{0}: Calling New-AtwsModuleConfiguration with a configuration object passed on the command line.' -F $MyInvocation.MyCommand.Name)

                $ConfigurationData = $AtwsModuleConfiguration
            }
            else {
                Write-Warning ('{0}: Tried to call New-AtwsModuleConfiguration with a configuration object passed on the command line, but the configuration did not validate properly.' -F $MyInvocation.MyCommand.Name)

            }
        }
        catch {
            # Write a debug message with detailed information to developers
            $reason = ("{0}: {1}" -f $_.CategoryInfo.Category, $_.CategoryInfo.Reason)
            $message = "{2}: {0}`r`n`r`nLine:{1}`r`n`r`nScript stacktrace:`r`n{3}" -f $_.Exception.Message, $_.InvocationInfo.Line, $reason, $_.ScriptStackTrace
            Write-Debug $message

            # Pass on the error
            $PSCmdlet.ThrowTerminatingError($_)
            return
        }

        ## Connect to the API
        # or die trying
        . Connect-AtwsWebServices -Configuration $ConfigurationData -Erroraction Stop
        
    }
  
    end {
        Write-Debug ('{0}: End of function' -F $MyInvocation.MyCommand.Name)
    }
 
}