Function Connect-ISM {
  <#
  .SYNOPSIS
    Primary authentication function leveraged by the module to connect to the Ivanti Service Manager API.
  .DESCRIPTION
    rimary authentication function leveraged by the module to connect to the Ivanti Service Manager API. Supports use of an API Key or SessionID authentication.
  
    PRD = Production
    STG = Staging
    UAT = User Acceptance Testing
  .PARAMETER -Environment
      The ISM Environment you wish to connect to. This is a validate set containing "PRD", "UAT", "STG" environments.
  .PARAMETER -TenantPrefix
      The ISM URL prefix for the Tenant.
  .PARAMETER -AuthType
      Parameter that defines what type of authentication to use. APIKey requires procuring an API Key from the ITSM Admins and is primarily relegated for use by administrators and unattended scripts.
      
      SessionID will prompt the user for Spark credentials and attempt to authenticate them directly. The default Role in the module is set for "ISAnalyst" but can be adjusted in the function.
  .PARAMETER -ConnectionTest
      Switch that when included will Invoke a WebRequest against the URL of the environment chosen. StatusDescription = OK will be returned on success.
  .PARAMETER -AuthTypeCheck
    Hidden parameter primarily intended for use internally by the module. This simply returns the value of AuthType and doesn't output any headers or make any API calls at all.
  .INPUTS
    A valid string.
  .OUTPUTS
    Returns the Tenant URI and the authorization headers necessary to make REST API calls to Ivanti Service Manager.
  .NOTES
    Version:        1.2
    Author:         Sean Carlton
    Creation Date:  04/26/2023
    Purpose/Change: Added AA_API_Key variable and check to leverage Get-AutomationPSCredential when $PSPrivateMetaData is not null. This variable is populated when a script is executing from Azure Automation (Cloud and Hybrid Worker), but otherwise is null.

    Version:        1.1
    Author:         Sean Carlton
    Creation Date:  10/06/2022
    Purpose/Change: Added initial SessionID support. To leverage this option requires a local login in Spark which most users do not have. A request has been submitted with Ivanti to determine if we can leverage OIDC auth instead. If so, the code will be in place to allow use of ISMTools without an API Key, using your own credentials for authentication to the API. Added the hidden -ConfigCheck switch which will return the Connect-ISM configuration currently in use.
  
    Version:        1.0
    Author:         Sean Carlton
    Creation Date:  08/16/2022
    Purpose/Change: Initial script development
  .EXAMPLE
  The below example demonstrates how to define the ISM/Spark environment you wish to connect to upon initial import. If the -ArgumentList parameter is not provided when Import-Module is ran, then the $Environment variable configured in Connect-ISM will be used to determine which environment to connect to.

  Import-Module 'ISMTools' -ArgumentList 'PRD'

  .EXAMPLE
  
  (Connect-ISM)[0] will return the authorization headers.
  (Connect-ISM)[1] will return the Tenant URL.
  (Connect-ISM)[2] will return the Authentication Type.
  .EXAMPLE
  
  The -ConnectionTest switch will invoke a WebRequest against the Tenant URL the function is configured to connect to. It can be leveraged to check that your connection to ISM/Spark is valid. It returns all of the other properties but includes the StatusDescription of the Invoke-WebRequest call as well. 
  
  Connect-ISM -ConnectionTest 
  
  If Statement example to confirm you're connected to Spark before continuing:
  
  if ((Connect-ISM -ConnectionTest)[-1].StatusDescription -eq "OK"){
    Write-Host "Connected to Spark!"
  } else {
    Write-Warning "Failed to connect to Spark!"
    Exit
  }
  .EXAMPLE
  
  The -ConfigCheck switch will return the current configuration of the module for reference. 
  
  Connect-ISM -ConfigCheck
  
  #>
    [CmdletBinding()]
        param (
            [Parameter()]
            [ValidateSet("STG", "UAT", "PRD")]
            $Environment = $(if ($Module_Environment){
              $Module_Environment
            }else{
              ## Define the Default Environment here. This will be used when no environment is passed by Import-Module
              "PRD"
            }),
    
            [Parameter()]
            [ValidateSet("APIKey", "SessionID")]
            [string]$AuthType = "APIKey",
  
            [Parameter()]
            ## ServiceDeskAnalyst
            ## ISAnalyst
            ## Admin
            ## DesktopTechnician
            [string]$Role = "ISAnalyst",
    
            [Parameter()]
            [string]$TenantPrefix = "samaritanhealth-amc",
    
            [Parameter()]
            [switch]$ConnectionTest,
  
            [Parameter(DontShow)]
            [switch]$ConfigCheck
        )
  
  BEGIN {
    ##Initialize the Headers Hashtable.

    $Headers = @{
      "Content-Type" = "application/json"
      "Authorization" = $null
      "Accept" = "*/*"
      "Accept-Encoding" = "gzip, deflate, br"
    }

    ## If $PSPrivateMetaData is NOT null then this is running from an Azure Runbook/via Hybrid Worker. We'll attempt to pull the API key from the Credential Store of the automation account directly in that scenario.
    $Azure_Execution = ($null -ne $PSPrivateMetadata)

    if ($Azure_Execution){
      $Cred_Obj = Get-AutomationPSCredential -Name "ISM $Environment API Key"

          if ($Cred_Obj){
              $AA_API_Key = (New-Object PSCredential $Cred_Obj.Username,$Cred_Obj.Password).GetNetworkCredential().Password;
          }
    }
    ## Initialize the URL and API_Key based on $Environment selection.
    ## If you don't have acess to the API Key for the environment you need, please reach out to the ITSM Team to request access.
    Switch ($Environment){
      "STG" {
          $API_Key = if($AA_API_Key){$AA_API_Key}else{''};
          $URL = "https://$TenantPrefix-$Environment.ivanticloud.com"
      }
      "UAT" {
          $API_Key = if($AA_API_Key){$AA_API_Key}else{''};
          $URL = "https://$TenantPrefix-$Environment.ivanticloud.com"
      }
      "PRD" {
          $API_Key = if($AA_API_Key){$AA_API_Key}else{'EB68123D62F8489295C807353C92D75B'};
          $URL = "https://$TenantPrefix.ivanticloud.com"
      }
    }#END $ENVIRONMENT SWITCH
  }#END BEGIN BLOCK
  
  PROCESS {

    Switch ($AuthType){

      "APIKey" {
        ## Add the API_Key to the Headers
        $Headers["Authorization"] = "rest_api_key=$API_Key"
      }

      "SessionID" {
        ## Prompt for Network Credentials if we don't have a valid SessionID/Token already stored.
        if (!$ISMToolsModuleSessionID.SessionID){
            $SessionIDCreds = Get-Credential -User $ENV:Username -Message "Enter your network password to authenticate into Spark with the $Role role."

            $Body = @{
              "Tenant" = $URL.Split('/')[-1]
              "Username" = $SessionIDCreds.Username
              "Password" = $SessionIDCreds.GetNetworkCredential().Password
              "Role" = $Role
            }
    
            $JSONBody = $Body | ConvertTo-Json
    
            if ($SessionIDCreds.GetNetworkCredential().Password){
              try {
                $URI = "$URL/api/rest/authentication/login"
                $SessionID = Invoke-RestMethod -uri $URI -Method Post -Body $JSONBody -headers $Headers
              }
              catch {$_.Exception.Message}
            }
    
            if ($SessionID -match "#"){
              $ISMToolsModuleSessionID.SessionID = $SessionID
            } else {
              Write-Host "Failed to authenticate with role $($Body.Role) with the provided credentials! Please check the credentials and role configured in the Connect-ISM function and try again." -ForegroundColor yellow
            }

            $SessionIDCreds = $null

        }#End If No ISM_Tools_Module_Session_ID_Token

        ## Add the SessionID to the Headers
        $Headers["Authorization"] = $ISMToolsModuleSessionID.SessionID

      }

    }#END AUTHTYPE SWITCH
    
    ## Run the ConnectionTest if -ConnectionTest was passed.
    if ($ConnectionTest){
      $TestConnection = Invoke-WebRequest $URL -UseBasicParsing | Select-Object StatusDescription
    } 

  }#END PROCESS BLOCK
    
  END {
    if ($ConfigCheck){
      return $Environment, $AuthType, $Role, $TenantPrefix, $URL, $API_Key
    } else {
      return $Headers, $URL, $AuthType, $TestConnection
    }
  }#END END-BLOCK
  
}#End Connect-ISM Function
