CredentialRetriever.psm1
|
Function Invoke-AIMClient { <# .SYNOPSIS Defines specified CLIPasswordSDK command and arguments .DESCRIPTION Defines a CLIPasswordSDK process object with arguments required for specific command. .PARAMETER ClientPath The Path to CLIPasswordSDK.exe. Defaults to value of $Script:AIM.ClientPath, which is set during module import or via Set-AIMConfiguration. .PARAMETER Command The CLIPasswordSDK command to execute. Defaults to GetPassword. .PARAMETER CommandParameters The CLIPasswordSDK command parameters .PARAMETER Options Additional command options. .EXAMPLE Invoke-AIMClient -CommandParameters "/p AppDescs.AppID=TestApp /p RequiredProps=UserName,Address /p Query="Safe=TestSafe;Folder=Root;UserName=TestUser1" /o PassProps.UserName,PassProps.Address,Password,PasswordChangeInProcess"" Invokes the GetPassword action using the provided arguments. .NOTES AUTHOR: Pete Maan #> [CmdLetBinding(SupportsShouldProcess)] param( [Parameter( Mandatory = $False, ValueFromPipelineByPropertyName = $True )] [string]$ClientPath = $Script:AIM.ClientPath, [Parameter( Mandatory = $False, ValueFromPipelineByPropertyName = $True )] [string]$Command = 'GetPassword', [Parameter( Mandatory = $True, ValueFromPipelineByPropertyName = $True )] [string]$CommandParameters, [Parameter(Mandatory = $False, ValueFromPipelineByPropertyName = $True )] [string]$Options ) Begin { #Create process $Process = New-Object System.Diagnostics.Process } Process { #Check we have the path to the required client executable if (-not $ClientPath) { throw "CLIPasswordSDK path not set `nRun Set-AIMConfiguration to set path to CLIPasswordSDK" } elseif (-not (Test-Path -LiteralPath $ClientPath -PathType Leaf)) { throw "CLIPasswordSDK not found at '$ClientPath' `nRun Set-AIMConfiguration to set path to CLIPasswordSDK" } if ($PSCmdlet.ShouldProcess($ClientPath, "$CommandParameters")) { Write-Debug "Command Arguments: $Command $Options $CommandParameters" #Assign process parameters $Process.StartInfo.WorkingDirectory = "$(Split-Path $ClientPath -Parent)" $Process.StartInfo.Filename = $ClientPath $Process.StartInfo.Arguments = "$Command $Options $CommandParameters" $Process.StartInfo.RedirectStandardOutput = $True $Process.StartInfo.RedirectStandardError = $True $Process.StartInfo.UseShellExecute = $False $Process.StartInfo.CreateNoWindow = $True $Process.StartInfo.WindowStyle = 'hidden' #Start Process $Result = Start-AIMClientProcess -Process $Process -ErrorAction Stop #Return Error or Result if ($Result.StdErr -match '((?:^[A-Z]{5}[0-9]{3}[A-Z])|(?:ERROR \(\d+\)))(?::)? (.+)$') { #APPAP008E Problem occurred while trying to use user in the Vault Write-Debug "ErrorId: $($Matches[1])" Write-Debug "Message: $($Matches[2])" Write-Error -Message $Matches[2] -ErrorId $Matches[1] } ElseIf ($Result.ExitCode) { #Process failed without a recognised error message (e.g. crash or missing dependency) $Message = 'CLIPasswordSDK exited with code 0x{0:X8}' -f $Result.ExitCode if ($Result.StdErr) { $Message = "$Message`: $(([string]$Result.StdErr).Trim())" } Write-Error -Message $Message -ErrorId 'AIMClientExitCode' } Else { $Result } } } End { $Process.Dispose() } } Function Skip-CertificateCheck { <# .SYNOPSIS Bypass SSL Validation .DESCRIPTION Sets a certificate policy which skips ssl certificate validation for Windows PowerShell web requests. The certificate policy type is compiled once per session. Outputs the previously configured certificate policy, which should be restored once requests complete. .EXAMPLE $CertificatePolicy = Skip-CertificateCheck Skips certificate validation, saving the previous certificate policy to $CertificatePolicy. #> if ($PSEdition -ne 'Core') { if (-not ('CredentialRetriever.TrustAllCertificatePolicy' -as [type])) { Add-Type -TypeDefinition @' namespace CredentialRetriever { public class TrustAllCertificatePolicy : System.Net.ICertificatePolicy { public bool CheckValidationResult(System.Net.ServicePoint sp, System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem) { return true; } } } '@ } [System.Net.ServicePointManager]::CertificatePolicy [System.Net.ServicePointManager]::CertificatePolicy = New-Object -TypeName CredentialRetriever.TrustAllCertificatePolicy } } Function Start-AIMClientProcess { <# .SYNOPSIS Starts AIM CLI process .DESCRIPTION Designed to receive AIMClient process object from Invoke-AIMClient. Returns Object containing ExitCode, StdOut & StdErr .PARAMETER Process System.Diagnostics.Process object containing CLIPasswordSDK parameters .EXAMPLE Start-AIMClientProcess -Process $Process Invokes the Start method on the $Process object .NOTES AUTHOR: Pete Maan #> [CmdLetBinding(SupportsShouldProcess)] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'ShouldProcess handling is in Invoke-AIMClient')] param( [Parameter( Mandatory = $True, ValueFromPipelineByPropertyName = $True )] [System.Diagnostics.Process]$Process ) Begin { } Process { #Start Process $Process.start() | Out-Null #Read Output Stream First $StdOut = $Process.StandardOutput.ReadToEnd() $StdErr = $Process.StandardError.ReadToEnd() #If you wait for the process to exit before reading StandardOutput #the process can block trying to write to it, so the process never ends. $Process.WaitForExit() Write-Debug "Exit Code: $($Process.ExitCode)" [PSCustomObject] @{ 'ExitCode' = $Process.ExitCode 'StdOut' = $StdOut 'StdErr' = $StdErr } } End { $Process.Dispose() } } # .ExternalHelp CredentialRetriever-help.xml Function Get-AIMConfiguration { [CmdletBinding()] Param() Get-Variable -Name AIM -Scope Script -ValueOnly -ErrorAction SilentlyContinue } # .ExternalHelp CredentialRetriever-help.xml Function Get-AIMCredential { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'Suppress alert from ToSecureString ScriptMethod')] [CmdletBinding(DefaultParameterSetName = 'Default')] Param( # Unique ID of the application [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true )] [string] $AppID, # Safe name [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [ValidatePattern('^[^;"]*$')] [string] $Safe, # Folder name [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [ValidatePattern('^[^;"]*$')] [string] $Folder, # Object name [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [ValidatePattern('^[^;"]*$')] [string] $Object, # Search username [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [ValidatePattern('^[^;"]*$')] [string] $UserName, # Search address [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [ValidatePattern('^[^;"]*$')] [string] $Address, # Search database [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [ValidatePattern('^[^;"]*$')] [string] $Database, # Set PolicyID [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [ValidatePattern('^[^;"]*$')] [string] $PolicyID, # Free query of account properties [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Query' )] [ValidatePattern('^[^"]*$')] [string] $Query, # Set QueryFormat [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true )] [ValidateSet('exact', 'regexp')] [string] $QueryFormat, # Required Properties [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true )] [string[]] $RequiredProps, # Reason to record in audit log [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true )] [ValidatePattern('^[^"]*$')] [string] $Reason, # Port for communication with the provider [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true )] [int] $Port, # Number of seconds to try [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true )] [int] $Timeout, # Return an error if a password change is in progress [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true )] [switch] $FailRequestOnPasswordChange, # Output PSCredential object [Parameter(Mandatory = $false)] [switch] $AsCredential, # Output password as SecureString [Parameter(Mandatory = $false)] [switch] $AsSecureString ) Begin { #Function Parameters which will form any query string $QueryParameters = @( 'Safe', 'Folder', 'Object', 'UserName', 'Address', 'Database' 'PolicyID' ) $ConnectionParms = @( 'Port', 'Timeout' ) #Delimiter for separating the output fields $Separator = '#_-_#' #CLIPasswordSDK argument prefix: / on Windows, - on Linux $Prefix = if ($IsWindows -eq $false) { '-' } else { '/' } if ($AsCredential -and $AsSecureString) { throw 'AsCredential and AsSecureString cannot be used together.' } } Process { #Array to hold the Properties to return [array]$ReturnProps = @() #Hashtable to hold the Results to Output [hashtable]$Output = @{ } #Initial Command String $Command = "${Prefix}p AppDescs.AppID=`"$AppID`"" If ($PSCmdlet.ParameterSetName -eq 'Query') { $QueryString = $Query } Else { #Build query string from search parameters #"Property=Value;Property=Value;Property=Value" $QueryString = ($QueryParameters | Where-Object { $PSBoundParameters.ContainsKey($_) } | ForEach-Object { "$_=$($PSBoundParameters[$_])" }) -join ';' } If ($QueryString) { #Add Query to Command String $Command = "$Command ${Prefix}p Query=""$QueryString""" } #Build Command String switch ( $PSBoundParameters.Keys ) { 'QueryFormat' { #Add QueryFormat Command String $Command = "$Command ${Prefix}p QueryFormat=`"$QueryFormat`"" } 'Reason' { #Add Reason to Command String $Command = "$Command ${Prefix}p Reason=`"$Reason`"" } 'FailRequestOnPasswordChange' { #Add FailRequestOnPasswordChange to Command String $Command = "$Command ${Prefix}p FailRequestOnPasswordChange=$("$($FailRequestOnPasswordChange.IsPresent)".ToLower())" } { $ConnectionParms -contains $PSItem } { #Add ConnectionParms to Command String $Command = "$Command ${Prefix}p ConnectionParms.$_=$($PSBoundParameters[$_])" } } #UserName is required for PSCredential output $Props = @($RequiredProps | Where-Object { $_ }) If ($AsCredential -and ($Props -notcontains 'UserName')) { $Props += 'UserName' } If ($Props.Count -gt 0) { #Add RequiredProps to Command String $Props | ForEach-Object { $ReturnProps += "PassProps.$_" } $Command = "$Command ${Prefix}p RequiredProps=$($Props -join ',')" } #Add Password & PasswordChangeInProcess to output fields $ReturnProps += 'Password' $ReturnProps += 'PasswordChangeInProcess' #Create Output fields string PropX,PropY,PropZ, Password, PasswordChangeInProcess $ReturnProps = $ReturnProps -join ',' #Build Command String $Command = "$Command ${Prefix}o $ReturnProps ${Prefix}d $Separator" #Invoke Credential Provider $Result = Invoke-AIMClient -CommandParameters $Command #Output on StdOut If ($null -ne $Result.StdOut) { #split returned results at Separator $Results = ($Result.StdOut) -Split $Separator #use $returnProps to determine propertynames $ReturnProps = $ReturnProps.Split(',') For ($i = 0 ; $i -lt $ReturnProps.length ; $i++) { #PropertyName=PropertyValue $Value = ($Results[$i]).trim() #<na> (property does not exist) & <null> (property has no value) are output as $null If (($ReturnProps[$i] -like 'PassProps.*') -and ($Value -in '<na>', '<null>')) { $Value = $null } $Output[$(($ReturnProps[$i]) -replace 'PassProps.', '')] = $Value } #Create Output Object with Property Values $OutputObject = New-Object -TypeName PSObject -Property $Output #Add ScriptMethod to output object to convert password to Secure String $OutputObject | Add-Member -MemberType ScriptMethod -Name ToSecureString -Value { $this.Password | ConvertTo-SecureString -AsPlainText -Force } -Force #Add ScriptMethod to output object to convert username & password to Credential Object $OutputObject | Add-Member -MemberType ScriptMethod -Name ToCredential -Value { New-Object System.Management.Automation.PSCredential($this.UserName, $this.ToSecureString()) } -Force #Return the result from AIM CP if ($AsCredential) { $OutputObject.ToCredential() } elseif ($AsSecureString) { $OutputObject.ToSecureString() } else { $OutputObject } } } } # .ExternalHelp CredentialRetriever-help.xml function Get-CCPCredential { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'Suppress alert from ToSecureString ScriptMethod')] [CmdletBinding(DefaultParameterSetName = 'Default')] Param( # Unique ID of the application [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Query' )] [string] $AppID, # Safe name [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [string] $Safe, # Folder name [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [string] $Folder, # Object name [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [string] $Object, # Search username [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [string] $UserName, # Search address [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [string] $Address, # Search database [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [string] $Database, # Search PolicyID [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [string] $PolicyID, # Reason to record in audit log [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Query' )] [string] $Reason, # Free query of account properties [parameter( Mandatory = $true, ValueFromPipelinebyPropertyName = $true, ParameterSetName = 'Query' )] [string] $Query, # Format of free query [parameter( Mandatory = $false, ValueFromPipelinebyPropertyName = $true, ParameterSetName = 'Query' )] [ValidateSet('Exact', 'Regexp')] [string] $QueryFormat, # Number of seconds to try [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Query' )] [int] $ConnectionTimeout, # Return an error if a password change is in progress [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Query' )] [switch] $FailRequestOnPasswordChange, # Credentials to send in request to CCP [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Query' )] [ValidateNotNullOrEmpty()] [PSCredential] $Credential, # Use current system credentials for request to CCP [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Query' )] [Switch] $UseDefaultCredentials, # Use certificate to authenticate to CCP [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Query' )] [X509Certificate] $Certificate, # Use certificate to authenticate to CCP [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Query' )] [string] $CertificateThumbPrint, # Unique ID of the CCP webservice in IIS [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Query' )] [string] $WebServiceName = 'AIMWebService', # CCP URL [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Default' )] [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Query' )] [string] $URL, [parameter( Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelinebyPropertyName = $true, ParameterSetName = 'Default' )] [parameter( Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelinebyPropertyName = $true, ParameterSetName = 'Query' )] [switch] $SkipCertificateCheck, # HTTP method for request to CCP [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $false, ParameterSetName = 'Default' )] [Parameter( Mandatory = $false, ValueFromPipelineByPropertyName = $false, ParameterSetName = 'Query' )] [ValidateSet('GET', 'POST')] [string] $Method = 'GET', # Output PSCredential object [Parameter(Mandatory = $false)] [switch] $AsCredential, # Output password as SecureString [Parameter(Mandatory = $false)] [switch] $AsSecureString ) Begin { #Collection of parameters which are to be excluded from the request [array]$ExcludedParameters += [System.Management.Automation.PSCmdlet]::CommonParameters [array]$ExcludedParameters += [System.Management.Automation.PSCmdlet]::OptionalCommonParameters [array]$ExcludedParameters += 'URL', 'WebServiceName', 'Credential', 'UseDefaultCredentials', 'CertificateThumbPrint', 'Certificate', 'SkipCertificateCheck', 'Method', 'AsCredential', 'AsSecureString' if ($AsCredential -and $AsSecureString) { throw 'AsCredential and AsSecureString cannot be used together.' } if ($PSEdition -ne 'Core') { #A SecurityProtocol of SystemDefault (0) lets Schannel negotiate the strongest protocol #both ends support, and is left untouched. Only a process pinned to explicit legacy #protocols needs TLS 1.2 adding, and it is combined with the protocols already permitted. $SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol if (([int]$SecurityProtocol -ne 0) -and ([Net.SecurityProtocolType].GetEnumNames() -contains 'Tls12') -and (-not ($SecurityProtocol.HasFlag([Net.SecurityProtocolType]::Tls12)))) { Write-Verbose 'Adding TLS12 to Security Protocol' [Net.ServicePointManager]::SecurityProtocol = $SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } } } Process { #Collect bound request parameters, converting switches to boolean values $RequestParams = [ordered]@{ } $PSBoundParameters.keys | Where-Object { $ExcludedParameters -notcontains $_ } | ForEach-Object { $RequestParams[$_] = if ($PSBoundParameters[$_] -is [switch]) { $PSBoundParameters[$_].IsPresent } else { $PSBoundParameters[$_] } } $Request = @{ 'URI' = "$($URL.TrimEnd('/'))/$WebServiceName/api/Accounts" 'Method' = $Method 'ContentType' = 'application/json' 'ErrorAction' = 'Stop' 'UseBasicParsing' = $true } Switch ($Method) { 'GET' { #Request parameters sent in URL query string $QueryString = ($RequestParams.Keys | ForEach-Object { "$_=$([System.Uri]::EscapeDataString($RequestParams[$_]))" }) -join '&' $Request['URI'] += "?$QueryString" } 'POST' { #Request parameters sent in JSON body $Request['Body'] = $RequestParams | ConvertTo-Json } } # Add authentication parameters to request Switch ($($PSBoundParameters.keys)) { { $PSItem -contains 'Credential' } { $Request['Credential'] = $Credential } { $PSItem -contains 'UseDefaultCredentials' } { $Request['UseDefaultCredentials'] = $true } { $PSItem -contains 'CertificateThumbPrint' } { $Request['CertificateThumbPrint'] = $CertificateThumbPrint } { $PSItem -contains 'Certificate' } { $Request['Certificate'] = $Certificate } } $RestoreCertificatePolicy = $false #in PSCore use SkipCertificateCheck parameter if ($PSEdition -eq 'Core') { $Request.Add('SkipCertificateCheck', $SkipCertificateCheck.IsPresent) } elseif ($SkipCertificateCheck) { #Skip SSL Validation, saving previous certificate policy $CertificatePolicy = Skip-CertificateCheck $RestoreCertificatePolicy = $true } $result = $null Try { #send request $result = Invoke-RestMethod @Request } Catch { $ErrorRecord = $PSItem $ErrorMessage = $ErrorRecord.Exception.Message $ErrorID = $ErrorRecord.FullyQualifiedErrorId $err = $null #Only parse responses that look like JSON if ("$ErrorRecord".TrimStart().StartsWith('{')) { try { $err = $ErrorRecord | ConvertFrom-Json -ErrorAction Stop } catch { #Response is not valid JSON, keep original exception details $err = $null } } #CCP errors use ErrorMsg/ErrorCode, IIS/ASP.NET errors use Message if ($err.ErrorMsg) { $ErrorMessage = $err.ErrorMsg $ErrorID = $err.ErrorCode } elseif ($err.Message) { $ErrorMessage = $err.Message } #report the error and continue with any further pipeline input $PSCmdlet.WriteError( [System.Management.Automation.ErrorRecord]::new( $ErrorMessage, $ErrorID, [System.Management.Automation.ErrorCategory]::NotSpecified, $ErrorRecord ) ) } Finally { #Restore previous certificate policy if ($RestoreCertificatePolicy) { [System.Net.ServicePointManager]::CertificatePolicy = $CertificatePolicy } } if ($null -ne $result) { #Add ScriptMethod to output object to convert password to Secure String $result | Add-Member -MemberType ScriptMethod -Name ToSecureString -Value { $this.Content | ConvertTo-SecureString -AsPlainText -Force } -Force #Add ScriptMethod to output object to convert username & password to Credential Object $result | Add-Member -MemberType ScriptMethod -Name ToCredential -Value { New-Object System.Management.Automation.PSCredential($this.UserName, $this.ToSecureString()) } -Force #Return the result from CCP if ($AsCredential) { $result.ToCredential() } elseif ($AsSecureString) { $result.ToSecureString() } else { $result } } } End { } } # .ExternalHelp CredentialRetriever-help.xml Function Set-AIMConfiguration { [CmdletBinding(SupportsShouldProcess)] Param( [Parameter( Mandatory = $true, ValueFromPipelineByPropertyName = $true )] [ValidateScript( { Test-Path $_ -PathType Leaf })] [ValidateNotNullOrEmpty()] [string]$ClientPath ) Process { $ConfigFile = Join-Path -Path $HOME -ChildPath 'AIMConfiguration.xml' if ($PSCmdlet.ShouldProcess($ConfigFile, "Set ClientPath to $ClientPath")) { Set-Variable -Name AIM -Value ([pscustomobject]@{ ClientPath = $ClientPath }) -Scope Script $Script:AIM | Export-Clixml -Path $ConfigFile -Force } } } #Read config and make available in script scope $ConfigFile = Join-Path -Path $HOME -ChildPath 'AIMConfiguration.xml' If (Test-Path $ConfigFile) { Write-Verbose "Importing Settings: $ConfigFile" $config = Import-Clixml -Path $ConfigFile Set-Variable -Name AIM -Value $config -Scope Script } Else { #Use CLIPasswordSDK default install location, if present $ClientPath = @( "$env:ProgramFiles\CyberArk\ApplicationPasswordSdk\CLIPasswordSDK.exe", '/opt/CARKaim/sdk/clipasswordsdk' ) | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1 If ($ClientPath) { Write-Verbose "Using CLIPasswordSDK: $ClientPath" Set-Variable -Name AIM -Value ([pscustomobject]@{ ClientPath = $ClientPath }) -Scope Script } } |