Autotask.psm1

<#
    .COPYRIGHT
    Copyright (c) Office Center Hønefoss AS. All rights reserved. Licensed under the MIT license.
    See https://github.com/officecenter/Autotask/blob/master/LICENSE.md for license information.
#>


[CmdletBinding()]
Param(
  [Parameter(
    Position = 0
  )]
  [pscredential]
  $Credential = $Global:AtwsCredential,
    
  [Parameter(
    Position = 1  
  )]
  [String]
  $ApiTrackingIdentifier = $Global:AtwsApiTrackingIdentifier,

  [Parameter(
    Position = 2,
    ValueFromRemainingArguments = $True
  )]
  [String[]]
  $EntityName = $Global:AtwsRefreshCachePattern
)

Write-Debug ('{0}: Start of module import' -F $MyInvocation.MyCommand.Name)


# Special consideration for -Verbose, as there is no $PSCmdLet context to check if Import-Module was called using -Verbose
# and $VerbosePreference is not inherited from Import-Module for some reason.

# Remove comments
$ParentCommand = ($MyInvocation.Line -split '#')[0]

# Store Previous preference
$OldVerbosePreference = $VerbosePreference
If ($ParentCommand -like '*-Verbose*') {
  Write-Debug ('{0}: Verbose preference detected. Verbose messages ON.' -F $MyInvocation.MyCommand.Name)
  $VerbosePreference = 'Continue'
}
$OldDebugPreference = $DebugPreference
If ($ParentCommand -like '*-Debug*') {
  Write-Debug ('{0}: Debug preference detected. Debug messages ON.' -F $MyInvocation.MyCommand.Name)
  $DebugPreference = 'Continue'
}

# Am I being loaded as the Beta version?
If ($MyInvocation.MyCommand.Name -eq 'AutotaskBeta.psm1') {
    $Script:IsBeta = $True
}

# Am I being loaded as the Beta version?
If ($MyInvocation.MyCommand.Name -eq 'AutotaskBeta.psm1') {
    $Script:IsBeta = $True
}

# Read our own manifest to access configuration data
$ManifestFileName = $MyInvocation.MyCommand.Name -replace 'pdm1$', 'psd1'
$ManifestDirectory = Split-Path $MyInvocation.MyCommand.Path -Parent

Write-Debug ('{0}: Loading Manifest file {1} from {2}' -F $MyInvocation.MyCommand.Name, $ManifestFileName, $ManifestDirectory)


Import-LocalizedData -BindingVariable My -FileName $ManifestFileName -BaseDirectory $ManifestDirectory

# Get all function files as file objects
# Private functions can only be called internally in other functions in the module

$PrivateFunction = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue ) 
Write-Debug ('{0}: Found {1} script files in {2}\Private' -F $MyInvocation.MyCommand.Name, $PrivateFunction.Count, $PSScriptRoot)

# Public functions will be exported with Prefix prepended to the Noun of the function name

$PublicFunction = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue ) 
Write-Debug ('{0}: Found {1} script files in {2}\Public' -F $MyInvocation.MyCommand.Name, $PublicFunction.Count, $PSScriptRoot)

# Static functions will be exported with Prefix prepended to the Noun of the function name

$StaticFunction = @( Get-ChildItem -Path $PSScriptRoot\Static\*.ps1 -ErrorAction SilentlyContinue ) 
Write-Debug ('{0}: Found {1} script files in {2}\Static' -F $MyInvocation.MyCommand.Name, $StaticFunction.Count, $PSScriptRoot)

# Static functions will be exported with Prefix prepended to the Noun of the function name

$DynamicFunction = @( Get-ChildItem -Path $PSScriptRoot\Dynamic\*.ps1 -ErrorAction SilentlyContinue ) 
Write-Debug ('{0}: Found {1} script files in {2}\Dynamic' -F $MyInvocation.MyCommand.Name, $DynamicFunction.Count, $PSScriptRoot)


Write-Verbose ('{0}: Importing {1} Private and {2} Public functions.' -F $MyInvocation.MyCommand.Name, $PrivateFunction.Count, $PublicFunction.Count)

# Loop through all script files and source them
foreach ($Import in @($PrivateFunction + $PublicFunction)) {
  Write-Debug ('{0}: Importing {1}' -F $MyInvocation.MyCommand.Name, $Import)
  try {
    . $Import.fullname
  }
  catch {
    throw "Could not import function $($Import.fullname): $_"
  }
}

# If they tried to pass any variables
If (($Credential) -or ($ApiTrackingIdentifier)) {
  Write-Verbose ('{0}: Credentials detected. Connecting to Autotask API' -F $MyInvocation.MyCommand.Name)
    
  # Remove Global variables (if used) for security
  $MyGlobalVars = Get-Variable -Scope Global -ErrorAction SilentlyContinue
  $TargetVarNames = 'AtwsCredential', 'AtwsApiTrackingIdentifier', 'AtwsRefreshCachePattern', 'AtwsUsePicklistLabels', 'AtwsNoDiskCache', 'AtwsNameSpace'
  $ConnectArgs = @{
    Credential            = $Credential
    ApiTrackingIdentifier = $ApiTrackingIdentifier
  }
  switch ($MyGlobalVars | Where-Object { $_.Name -in $TargetVarNames }) {
    { $_.Name -eq 'AtwsCredential' } {
      Write-Debug ('{0}: Credentials for {1} detected.' -F $MyInvocation.MyCommand.Name, $AtwsCredential.UserName)

      # Remove Global Object, credentials are now stored in a variable internal to the module
      
      Remove-Variable -Name $_.Name -Scope Global
    
      continue
    }
    { $_.Name -eq 'AtwsApiTrackingIdentifier' } {
      Write-Debug ('{0}: API tracking identifier for {1} detected.' -F $MyInvocation.MyCommand.Name, $AtwsCredential.UserName)

      # Remove Global Object, credentials are now stored in a variable internal to the module
    
      Remove-Variable -Name AtwsApiTrackingIdentifier -Scope Global
    
      continue
    }
    { $_.Name -eq 'AtwsRefreshCachePattern' } {
      Write-Debug ('{0}: Refreshing disk cache by force' -F $MyInvocation.MyCommand.Name)

      # Remove Global Object
    
      Remove-Variable -Name AtwsRefreshCachePattern -Scope Global
    
      continue
    }
    { $_.Name -eq 'AtwsUsePicklistLabels' } {
      Write-Debug ('{0}: Converting picklistvalues to their labels are turned ON' -F $MyInvocation.MyCommand.Name)

      $Script:UsePickListLabels = $True
    
      # Remove Global Object
    
      Remove-Variable -Name AtwsUsePicklistLabels -Scope Global

      continue
    }
    { $_.Name -eq 'AtwsNoDiskCache' } {
      Write-Debug ('{0}: Force No disk cache detected. All functions are loaded from the scripts supplied by the module.' -F $MyInvocation.MyCommand.Name)
      Write-Verbose ('{0}: Force No disk cache detected. All functions are loaded from the scripts supplied by the module.' -F $MyInvocation.MyCommand.Name)
        
      # Remove Global Object, credentials are now stored in a variable internal to the module
      Remove-Variable -Name AtwsNoDiskCache -Scope Global
  
      # Add the NoDiskCache switch to the splatted arguments we will use to connect
      $ConnectArgs['NoDiskCache'] = $true
    }
    { $_.Name -eq 'AtwsNameSpace' } {
      Write-Debug ('{0}: Module namespace has been specified. Passing it to connect function.' -F $MyInvocation.MyCommand.Name)
            
      # Add the NameSpace parameter to the splatted arguments we will use to connect
      $ConnectArgs['NameSpace'] = $Global:AtwsNameSpace

      # Remove Global Object, credentials are now stored in a variable internal to the module
      Remove-Variable -Name AtwsNameSpace -Scope Global
    }
<<<<<<< HEAD
    default { }
  }
  # Connect to the API using required, additional parameters, using internal function name
  . Connect-AtwsWebServices @ConnectArgs
  if (!$ConnectArgs['NoDiskCache']) {
    If ($IsBeta) { 
      $DynamicCache = '{0}\WindowsPowershell\Cache\{1}\Beta' -f $([environment]::GetFolderPath('MyDocuments')), $Script:Atws.CI
    }
    Else {
      $DynamicCache = '{0}\WindowsPowershell\Cache\{1}\Dynamic' -f $([environment]::GetFolderPath('MyDocuments')), $Script:Atws.CI
    }
    If (Test-Path $DynamicCache) {
      $FunctionCount = $DynamicFunction.Count
      $DynamicFunction = @( Get-ChildItem -Path $DynamicCache\*.ps1 -ErrorAction SilentlyContinue )
      Write-Debug ('{0}: Personal disk cache: Found {1} script files in {2}' -F $MyInvocation.MyCommand.Name, $DynamicFunction.Count, $DynamicCache)
=======
    # Connect to the API using required, additional parameters, using internal function name
    . Connect-AtwsWebServices @ConnectArgs
    if (!$ConnectArgs['NoDiskCache']) {
        If ($IsBeta) { 
          $DynamicCache = '{0}\WindowsPowershell\Cache\{1}\Beta' -f $([environment]::GetFolderPath('MyDocuments')), $Script:Atws.CI
        }
        Else {
          $DynamicCache = '{0}\WindowsPowershell\Cache\{1}\Dynamic' -f $([environment]::GetFolderPath('MyDocuments')), $Script:Atws.CI
        }
        If (Test-Path $DynamicCache) {
            $FunctionCount = $DynamicFunction.Count
            $DynamicFunction = @( Get-ChildItem -Path $DynamicCache\*atws*.ps1 -ErrorAction SilentlyContinue )
            Write-Debug ('{0}: Personal disk cache: Found {1} script files in {2}' -F $MyInvocation.MyCommand.Name, $DynamicFunction.Count, $DynamicCache)
>>>>>>> 3221018422d4febe633890ae9de7232fb2c6b6a2
      
      $VersionString = "#Version {0}" -F $My.ModuleVersion
      $ScriptVersion = Select-String -Pattern $VersionString -Path $DynamicFunction.FullName -ErrorAction SilentlyContinue
      If ($ScriptVersion.Count -ne $FunctionCount) {
        Write-Debug ('{0}: Personal disk cache: Wrong number of script files or scripts are not the right version in {1}, refreshing all entities.' -F $MyInvocation.MyCommand.Name, $DynamicCache)
  
        # Clear out old cache, it will be recreated
        If ($DynamicFunction.Count -gt 0) { 
          $Null = Remove-Item -Path $DynamicFunction.fullname -Force -ErrorAction SilentlyContinue
        }
  
        # Refresh ALL dynamic entities.
        $EntityName = '*' 
      }

      $OldFunctions = @(Get-ChildItem -Path $DynamicCache -Exclude *atws*.ps1 -ErrorAction SilentlyContinue)
      If ($OldFunctions.Count -gt 0) {

        Write-Debug ('{0}: Personal disk cache: Found {1} old script files in {2}. Deleting.' -F $MyInvocation.MyCommand.Name, $OldFunctions.Count, $DynamicCache)
        
        $Null = Remove-Item -Path $OldFunctions.fullname -Force -ErrorAction SilentlyContinue
      }
    }
    Else {

      Write-Debug ('{0}: Personal disk cache {1} does not exist. Forcing load of all dynamic entities.' -F $MyInvocation.MyCommand.Name, $DynamicCache)
    
      # No personal dynamic cache. Refresh ALL dynamic entities.
      $EntityName = '*'
    }

    # Refresh any entities the caller has ordered
    # We only consider entities that are dynamic
    $Entities = Get-AtwsFieldInfo -Dynamic
    $EntitiesToProcess = @()

    Write-Debug ('{0}: {1} dynamic entities are eligible for refresh.' -F $MyInvocation.MyCommand.Name, $DynamicCache)

    Foreach ($String in $EntityName) {
      Write-Debug ('{0}: Selecting entities that match pattern "{1}"' -F $MyInvocation.MyCommand.Name, $String)
      
      $EntitiesToProcess += $Entities.GetEnumerator().Where( { $_.Key -like $String })
    }
    # Prepare Index for progressbar
    $Index = 0
    $ProgressParameters = @{
      Activity = 'Updating diskcache for requested entities.'
      Id       = 10
    }

    # Make sure we only check each possible entity once
    $EntitiesToProcess = $EntitiesToProcess | Sort-Object -Property Name -Unique

    Write-Debug ('{0}: {1} entities have been selected for refresh' -F $MyInvocation.MyCommand.Name, $EntitiesToProcess.Count)
  

    Foreach ($EntityToProcess in $EntitiesToProcess) {
      $Index++
      $PercentComplete = $Index / $EntitiesToProcess.Count * 100

      # Add parameters for @splatting
      $ProgressParameters['PercentComplete'] = $PercentComplete
      $ProgressParameters['Status'] = 'Entity {0}/{1} ({2:n0}%)' -F $Index, $EntitiesToProcess.Count, $PercentComplete
      $ProgressParameters['CurrentOperation'] = 'Getting fieldinfo for {0}' -F $EntityToProcess.Name

      Write-Progress @ProgressParameters

      $null = Get-AtwsFieldInfo -Entity $EntityToProcess.Key -UpdateCache
    }

    If ($EntitiesToProcess.Count -gt 0) { 
      Write-Debug ('{0}: Calling Import-AtwsCmdLet with {1} entities to process' -F $MyInvocation.MyCommand.Name, $EntitiesToProcess.Count)
  
      # Recreate functions that have been updated
      Import-AtwsCmdLet -Entities $EntitiesToProcess

      # Re-read Dynamic functions
      $DynamicFunction = @( Get-ChildItem -Path $DynamicCache\*atws*.ps1 -ErrorAction SilentlyContinue ) 

      Write-Debug ('{0}: Personal disk cache: Found {1} script files in {2}' -F $MyInvocation.MyCommand.Name, $DynamicFunction.Count, $DynamicCache)
    }
  }

  Write-Verbose ('{0}: Importing {1} Static and {2} Dynamic functions.' -F $MyInvocation.MyCommand.Name, $StaticFunction.Count, $DynamicFunction.Count)
  
  # Loop through all script files and source them
  foreach ($Import in @($StaticFunction + $DynamicFunction)) {
    Write-Debug ('{0}: Importing {1}' -F $MyInvocation.MyCommand.Name, $Import)

    try {
      . $Import.fullname
    }
    catch {
      throw "Could not import function $($Import.fullname): $_"
    }
  }
  
  # Explicitly export public functions
  Write-Debug ('{0}: Exporting {1} Public functions.' -F $MyInvocation.MyCommand.Name, $PublicFunction.Count) 
  Export-ModuleMember -Function $PublicFunction.Basename

  # Explicitly export static functions
  Write-Debug ('{0}: Exporting {1} Static functions.' -F $MyInvocation.MyCommand.Name, $StaticFunction.Count)
  Export-ModuleMember -Function $StaticFunction.Basename

  # Explicitly export dynamic functions
  Write-Debug ('{0}: Exporting {1} Dynamic functions.' -F $MyInvocation.MyCommand.Name, $DynamicFunction.Count)
  Export-ModuleMember -Function $DynamicFunction.Basename
}
Else {
  Write-Verbose 'No Credentials were passed with -ArgumentList. Loading module without any connection to Autotask Web Services. Use Connect-AtwsWebAPI to connect.'
  Export-ModuleMember -Function 'Connect-AtwsWebAPI'
}


# Backwards compatibility since we are now trying to use consistent naming
Set-Alias -Scope Global -Name 'Connect-AutotaskWebAPI' -Value 'Connect-AtwsWebAPI'


# Restore Previous preference
If ($OldVerbosePreference -ne $VerbosePreference) {
  Write-Debug ('{0}: Restoring old Verbose preference' -F $MyInvocation.MyCommand.Name)
  $VerbosePreference = $OldVerbosePreference
}
If ($OldDebugPreference -ne $DebugPreference) {
  Write-Debug ('{0}: Restoring old Debug preference' -F $MyInvocation.MyCommand.Name)
  $DebugPreference = $OldDebugPreference
}