Beaver.psm1

#Requires -Version 5.0


function Get-StringHash
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $text,

        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateSet('MD5','RIPEMD160','SHA1','SHA256','SHA384','SHA512')] 
        [ValidateNotNullorEmpty()] 
        [string] $hashAlgorithm
        
    )

    process {
        
        $strBuilder = New-Object System.Text.StringBuilder
        
        [System.Security.Cryptography.HashAlgorithm]::Create($hashAlgorithm).ComputeHash(`
            [System.Text.Encoding]::UTF8.GetBytes($text)) | ForEach-Object { 
                [void]$strBuilder.Append($_.ToString("x2")) 
            }
        
        return $strBuilder.ToString() 
    }
}


function Test-CurrentUserInGroup
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $groupName
    )

    process {
        
        return ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).`
            IsInRole($groupName)

    }
}


function Get-MaskLength
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNull()]
        [Net.IPAddress] $mask
    )

    process { 
        
        $bits = "$($mask.GetAddressBytes() | ForEach-Object { [Convert]::ToString($_, 2)})" -replace '[\s0]'
        return $bits.Length
    }
    
}


function Test-IPInSubnet
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [ValidateScript({$_ -like '*.*.*.*/*'})] 
        [string] $cidr,

        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()]
        [ValidateScript({$_ -like '*.*.*.*'})] 
        [string] $ipAddress
    )

    process {
        
        $network, [int]$subnetlen = $cidr.Split('/')
        
        $a = [uint32[]]$network.Split('.')
        [uint32] $unetwork = ($a[0] -shl 24) + ($a[1] -shl 16) + ($a[2] -shl 8) + $a[3]

        $mask = (-bnot [uint32]0) -shl (32 - $subnetlen)
        $a = [uint32[]]$ipAddress.split('.')

        [uint32] $uip = ($a[0] -shl 24) + ($a[1] -shl 16) + ($a[2] -shl 8) + $a[3]
        
        return ($unetwork -eq ($mask -band $uip))
    }
}


function Add-SignatureToFile
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $fileMask
    )

    begin {
        [System.Security.Cryptography.X509Certificates.X509Certificate2] $signingCert = $null
    }
    
    process {
    
        try {
            
            Write-Verbose 'Finding signing certificate'
            $signingCert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Sort-Object NotAfter | Select-Object -Last 1 
            
            if ($signingCert) {
                
                Write-Verbose ('Found signing certificate: subjectCert = {0} | issuedBy = {1}'`
                     -f $signingCert.Subject, $signingCert.Issuer)

                $chooseFiles = Get-ChildItem $fileMask

                foreach ($oneFile in $chooseFiles) {
            
                    if ((Get-AuthenticodeSignature $oneFile).Status -ne 'Valid') {
    
                        Write-Verbose ("Sign file: {0}" -f $oneFile)

                        [void] (Set-AuthenticodeSignature $oneFile -Certificate $signingCert `
                            -TimestampServer http://timestamp.verisign.com/scripts/timstamp.dll)
                    }
                }

            } else {
        
                throw 'Error: no signing certificate found'    
            
            }

        } catch { throw; }
    }
}


function Send-WakeOnLan
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [ValidateScript({$_ -like '*.*.*.*'})] 
        [string] $broadcastAddress,

        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [ValidateScript({$_ -like '*-*-*-*-*-*'})] 
        [string] $macAddress
    )
 
    begin {
        
        try {
        
            Write-Verbose ('Loading assembly "System.Net"')
            [void][System.Reflection.Assembly]::LoadWithPartialName("System.Net")

            Write-Verbose ('Loading assembly "System.Net.Sockets"')
            [void][System.Reflection.Assembly]::LoadWithPartialName("System.Net.Sockets")

            Write-Verbose ('Creating UDP socket')
            $udpClient = New-Object System.Net.Sockets.UdpClient
            $endPoint = New-Object System.Net.IPEndPoint $([System.Net.IPAddress]::Parse($broadCastAddress)),10000

        } catch { throw; }
    }

    process {
        
        if($udpClient -and $endPoint) {
            
            try {
            
                [byte[]]$macBytes = $macAddress.split("-") | ForEach-Object { [byte]"0x$_" }
                [byte[]]$bytes = New-Object "byte[]" $(6 + 16 * $($macBytes.length))

                for ($i = 0; $i -lt 6; $i++) { $bytes[$i] = [byte] 0xff }

                for ($i = 6; $i -lt $bytes.length; $i += $macBytes.length) {
                    for($j = 0; $j -lt $macBytes.length; $j++) { $bytes[$i + $j] = $macBytes[$j] } 
                }

                Write-Verbose ('Connect to endpoint')
                $udpClient.connect($endPoint)

                Write-Verbose ('Sending magic packet')
                [void]$udpClient.Send($bytes, $bytes.length)

                Write-Verbose ('Magic packet was sent')
                $udpClient.Close()

            } catch { throw; }
        }

    }
}


function Get-ExpireCertificate
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNull()]
        [ValidateRange(1, 365)] 
        [int] $expireInDays
    ) 
    
    process {
        
        return (
            Get-Childitem cert: -Recurse | `
            Where-Object { ($_.NotAfter -gt (Get-Date).AddDays(-1)) -and ($_.NotAfter -lt (Get-Date).AddDays($expireInDays)) } | `
            Select-Object Subject,Thumbprint,@{Name="Expires in (Days)";`
                Expression={($_.NotAfter).subtract([DateTime]::Now).days}}, `
                PSParentPath,SerialNumber,HasPrivateKey,Issuer,Archived,`
                EnhancedKeyUsageList,NotAfter,NotBefore,DnsNameList | `
            Sort-Object "Expires in (Days)" | Format-List
        )

    }
}


function New-Password
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $false)] 
        [ValidateNotNull()]
        [ValidateRange(1, 128)] 
        [int] $passwordLength,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNull()]
        [ValidateRange(1, 128)] 
        [int] $numbersNonAlphaNumeric
    ) 

    process {
        
        try {

            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Generate random new password with length $passwordLength")) {
            
                Write-Verbose ('Loading assembly "System.Web"')
                Add-Type -AssemblyName System.Web

                Write-Verbose ('Generate random passwords with length {0}' -f $passwordLength)
                return [System.Web.Security.Membership]::GeneratePassword($passwordLength, $numbersNonAlphaNumeric) 
            
            }

        } catch { throw; }
    }
}


function Send-FileOnFTP
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $url,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [ValidateScript({ Test-Path $_ })]
        [string] $localFileName,
        [Parameter(Mandatory = $true, Position = 2, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [Management.Automation.PSCredential] $credentials,
        [Parameter(Mandatory = $false, Position = 3, ValueFromPipeline = $false)] 
        [bool] $enableTls = $true
    )

    begin {

        $fileWithoutPath = Split-Path -Leaf $localFileName
        $remotePath = $url + '/' + $fileWithoutPath

    }
    
    process {
        
        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        Write-Verbose ('Create FtpWebRequest object')

        try {

            $ftpWebRequest = [System.Net.FtpWebRequest]::Create($remotePath) 
            $ftpWebRequest = [System.Net.FtpWebRequest]$ftpWebRequest 
            $ftpWebRequest.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile 

            $ftpWebRequest.UseBinary = $true
            $ftpWebRequest.UsePassive = $true
            $ftpWebRequest.EnableSsl = $enableTls
            $ftpWebRequest.KeepAlive = $false

            if($ftpWebRequest) {
                
                Write-Verbose ('Add client credentials')
                $ftpWebRequest.Credentials = $credentials.GetNetworkCredential() #New-Object System.Net.NetworkCredential($username, $password)

                Write-Verbose ('Copy file to request stream')
                $fileContents = [System.IO.File]::ReadAllBytes($localFileName) 

                $ftpWebRequest.ContentLength = $fileContents.Length

                Write-Verbose ('Transfer file: {0}' -f $localFileName)

                $reqStream = $ftpWebRequest.GetRequestStream()
                $reqStream.Write($fileContents, 0, $fileContents.Length) 
                $reqStream.Close()
                
                $response = $ftpWebRequest.GetResponse() 
                Write-Verbose ('Transfer status: {0}' -f $response.StatusDescription)
    
                $response.Close()
            } 
                    
        } 
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }
}


function Uninstall-MSIPackage 
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $fqdn,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $appName,
        [Parameter(Mandatory = $false, Position = 2, ValueFromPipeline = $false)] 
        [Management.Automation.PSCredential] $credentials
    )

    begin { 

        $wmiApp = $null 

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }
    
    process {

        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }
        
            Write-Verbose ('WMI Query: {0}' -f $fqdn)

            if($credentials) {

                $wmiApp = Get-WmiObject -ComputerName $fqdn -Query `
                    "SELECT * FROM Win32_Product WHERE Name LIKE '%$appName%'" -Credential $credentials

            } else {

                $wmiApp = Get-WmiObject -ComputerName $fqdn -Query `
                    "SELECT * FROM Win32_Product WHERE Name LIKE '%$appName%'"

            }

            if($wmiApp) {
                
                Write-Verbose ('WMI uninstalling: {0} | {1}' -f $wmiApp.Name, $wmiApp.Version)
                $wmiApp.Uninstall() 

            } else {

                throw "WMI finding $appName on $fqdn failed"

            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    }
}


function Save-PptAsFormat 
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([System.String])] 
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [ValidateScript({ Test-Path $_ })] 
        [string] $pptFile,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [ValidateSet('PDF','XPS','PNG','JPG','HTML','WMV')] 
        [string] $saveAs
    )

    begin { 

        [string] $outFile = '' 
    
        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
    }
    
    process {
        
        try {

            Write-Verbose ('Open PowerPoint application')

            $ppt = New-Object -ComObject PowerPoint.Application 
            $withWindow = [Microsoft.Office.Core.MsoTriState]::msoFalse

            Write-Verbose ('Get the slide deck: {0}' -f $pptFile)
            $slideDeck = $ppt.Presentations.Open($pptFile, [Microsoft.Office.Core.MsoTriState]::msoTrue, `
                [Microsoft.Office.Core.MsoTriState]::msoTrue, $withWindow)

            Write-Verbose ('Presentation has slides: {0}' -f $slideDeck.Slides.Count)

            Add-Type -AssemblyName Microsoft.Office.Interop.PowerPoint
            $saveOptions = [Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType]::"ppSaveAs$saveAs"

            $outFile = [System.IO.Path]::ChangeExtension($pptFile, $saveAs.ToLower())
            if (Test-Path $outFile) { Remove-Item $outFile -Force }

            Write-Verbose ('Save the file: {0}' -f $outFile)
            $slideDeck.SaveAs($outFile, $saveOptions)

            Write-Verbose ('Close the slide deck')
            $slideDeck.Close()
            $slideDeck = $null

            Write-Verbose ('Quit the PowerPoint application')
            $ppt.Quit()
            $ppt = $null
            
            [GC]::Collect();
            [GC]::WaitForPendingFinalizers(); 


        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return $outFile

    }
}


function Save-DocAsPdf
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([System.String])] 
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()]
        [ValidateScript({ Test-Path $_ })] 
        [string] $docFile
    )

    begin { 

        [string] $outFile = '' 
    
        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
    }
    
    process {
        
        try {

            Write-Verbose ('Open Word application')
            $word = New-Object -ComObject Word.Application 

            Write-Verbose ('Open Word document')
            $doc = $word.Documents.Open($docFile)

            $outFile = [System.IO.Path]::ChangeExtension($docFile, 'pdf')
            if (Test-Path $outFile) { Remove-Item $outFile -Force }
        
            Write-Verbose ('Save the file: {0}' -f $outFile)
            $doc.SaveAs($outFile, 17)

            Write-Verbose ('Close Word document')
            $doc.Close()
            $doc = $null

            Write-Verbose ('Quit the Word application')
            $word.Quit()
            $word = $null
            
            [GC]::Collect();
            [GC]::WaitForPendingFinalizers(); 

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return $outFile

    }
}


function Add-SignatureInISE 
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()]
        [string] $signingCertSubject
    )
    
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
        
        [System.Security.Cryptography.X509Certificates.X509Certificate2] $signingCert = $null
    }

    process {

        try {

            if([string]::IsNullOrEmpty($signingCertSubject)) {

                $signingCert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Sort-Object NotAfter | Select-Object -Last 1

            } else {

                $signingCert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Where-Object { $_.Subject -like "*E=$signingCertSubject*" | `
                    Sort-Object NotAfter | Select-Object -Last 1 }

            }
            
            Write-Verbose ('Found certificate: {0} | {1}' -f `
                $signingCert.FriendlyName, $signingCert.SerialNumber)
            
            if($signingCert) {

                $openFiles = $psISE.CurrentPowerShellTab | ForEach-Object { $_.Files }

                foreach ($oneOpenFile in $openFiles) {
 
                    $oneFilePath = $oneOpenFile.FullPath
                    $oneFileLine = $oneOpenFile.Editor.CaretLine
                    $oneFileColumn = $oneOpenFile.Editor.CaretColumn
 
                    if (-not $oneOpenFile.IsSaved) {

                        [void] $oneOpenFile.Save()
                    }


                    if ((@('.ps1','.psm1','.psd1') -contains [System.IO.Path]::GetExtension($oneFilePath)) -and `
                        ((Get-AuthenticodeSignature $oneFilePath).Status -ne 'Valid')) {

                        [void] $psISE.CurrentPowerShellTab.Files.Remove($oneOpenFile)
                
                        Write-Verbose ('Sign file: {0}' -f $oneFilePath)

                        [void] (Set-AuthenticodeSignature $oneFilePath -Certificate $signingCert `
                            -TimestampServer http://timestamp.verisign.com/scripts/timstamp.dll)

                        $reloadedFile = $psISE.CurrentPowerShellTab.Files.Add($oneFilePath)
                        $reloadedFile.Editor.SetCaretPosition($oneFileLine, $oneFileColumn)
                    }
                }

            } else {

                throw 'Error: no signing certificate found'

            }
        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }
}


function Clear-ISEHistory
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param()

    process {

        $count = $psIse.Options.MruCount
        $psIse.Options.MruCount = 0
        $psIse.Options.MruCount = $count

    }
}


function Clear-EventLog
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param()

    process {

        if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
            throw 'Error: You must run as administrator !'

        } else {
        
            Write-Verbose ('WMI Query: {0}' -f $fqdn)

            Get-WinEvent -ListLog * | ForEach-Object { 
    
                Write-Verbose ('Clear windows event log: {0}' -f $_.LogName)
                Clear-EventLog -LogName $_.LogName -ErrorAction SilentlyContinue

            }
        }

    }
}


function Set-RegistryValue
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $keyPath,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $name,
        [Parameter(Mandatory = $true, Position = 2, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $value,
        [Parameter(Mandatory = $true, Position = 3, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [ValidateSet('string','expandstring','multistring','dword')] 
        [string] $type
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }
 
    process {
        
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }
        
            Write-Verbose ('WMI Query: {0}' -f $fqdn)
            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Set registry key $keyPath\$name to $value")) {
            
                Write-Verbose ("Ensure the key exists")

                if (Test-Path $keyPath) {
  
                    Write-Verbose ("Key already exists: {0}" -f $keyPath)
    
                } else {
  
                    Write-Verbose ("Creating the whole registry path: {0}" -f $keyPath)
                    [void] (New-Item -Path $keyPath -ItemType Registry -Force)
                }

                Write-Verbose ("Set the value")
                Set-ItemProperty -Path $keyPath -Name $name -Value $value -Type $type -Force
            
            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    }
}


function Add-SybaseODBCProfile
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $name,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [Management.Automation.PSCredential] $credentials,
        [Parameter(Mandatory = $true, Position = 2, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $srvName,
        [Parameter(Mandatory = $true, Position = 3, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $dbName,
        [Parameter(Mandatory = $true, Position = 4, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()]
        [ValidateSet(12, 16, 17)] 
        [int] $version
    )
    
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
        
        $odbcRootKey = 'HKCU:\SOFTWARE\ODBC\ODBC.INI'
    }     
    
    process {
        
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }
        
            Write-Verbose ('WMI Query: {0}' -f $fqdn)

            $sqlAnyLocation = ('HKLM:\SOFTWARE\Sybase\SQL Anywhere\{0}.0' -f $version)
            Write-Verbose ('Read SQL Anywhere registry key: {0}' -f $sqlAnyLocation)

            if (Test-Path $sqlAnyLocation) {
            
                $installDir = (Get-ItemProperty -Path $sqlAnyLocation -Name Location).Location

                if (Test-Path $installDir) {
            
                    Write-Verbose ('Found SQL Anywhere instalation folder: {0}' -f $installDir)
                    Write-Verbose ('Creating ODBC registry values')

                    Set-RegistryValue -KeyPath ('{0}\{1}' -f $odbcRootKey, $name) -Name CommLinks -Value 'TCPIP{}' -Type string -Verbose:$VerbosePreference
                    Set-RegistryValue -KeyPath ('{0}\{1}' -f $odbcRootKey, $name) -Name DatabaseName -Value $dbName -Type string -Verbose:$VerbosePreference
                    Set-RegistryValue -KeyPath ('{0}\{1}' -f $odbcRootKey, $name) -Name Driver -Value ('{0}\Bin32\dbodbc{1}.dll' -f $installDir, $version) -Type string -Verbose:$VerbosePreference
                    Set-RegistryValue -KeyPath ('{0}\{1}' -f $odbcRootKey, $name) -Name Integrated -Value 'NO' -Type string -Verbose:$VerbosePreference
                    Set-RegistryValue -KeyPath ('{0}\{1}' -f $odbcRootKey, $name) -Name PWD -Value ($credentials.GetNetworkCredential().Password) -Type string -Verbose:$VerbosePreference
                    Set-RegistryValue -KeyPath ('{0}\{1}' -f $odbcRootKey, $name) -Name ServerName -Value $srvName -Type string -Verbose:$VerbosePreference
                    Set-RegistryValue -KeyPath ('{0}\{1}' -f $odbcRootKey, $name) -Name UID -Value $credentials.UserName -Type string -Verbose:$VerbosePreference
                    Set-RegistryValue -KeyPath ('{0}\ODBC Data Sources' -f $odbcRootKey) -Name $name -Value ('SQL Anywhere {0}' -f $version) -Type String -Verbose:$VerbosePreference

                    Write-Verbose ('Creating ODBC registry values done')

                } else {
                
                    throw 'Eror: SQL Anywhere installation directory not found'
    
                }

            } else {
                
                throw 'Error: SQL Anywhere installation not found'

            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
            
    }
}


function Get-DirectoryEntry
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $distinguishedName,
        [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [Management.Automation.PSCredential] $credentials
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
        
        $outDE = $null
    }     
  
    process {
        
        try {
            
            if ($distinguishedName -like 'ldap://*') {
    
                Write-Verbose ('Correct invalid DE: {0}' -f $distinguishedName)
                $distinguishedName = 'LDAP{0}' -f $distinguishedName.Substring(4)

            } elseif ($distinguishedName -like 'gc://*') {
        
                Write-Verbose ('Correct invalid DE: {0}' -f $distinguishedName)
                $distinguishedName = 'GC{0}' -f $distinguishedName.Substring(2)
            }

            if ([string]::IsNullOrEmpty($userName)) {
      
                Write-Verbose ('ADSI connecting')
                $outDE = [ADSI] $distinguishedName

            } else {
        
                Write-Verbose ('System.DirectoryServices.DirectoryEntry connecting')
                $outDE = New-Object System.DirectoryServices.DirectoryEntry ($distinguishedName, $credentials.UserName, ($credentials.GetNetworkCredential().Password))
        
            }

            if ([string]::IsNullOrEmpty($outDE.Guid)) { 
      
                if ($outDE) {
          
                    Write-Verbose ('Disposing invalid DE')

                    $outDE.Close()

                    [GC]::Collect()
                    [GC]::WaitForPendingFinalizers()
                }

                $outDE = $null
            }


        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return $outDE
    }
}


function Get-LocalUser
{
   [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $computerName = $env:COMPUTERNAME
    )
    
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
        
        $psObj = $null
    }
    
    process {
        
        try {

            $adsiObj = Get-DirectoryEntry "WinNT://$computerName" -Verbose:$VerbosePreference

            Write-Verbose ('Local account ADSI path opened: {0}' -f $adsiObj.Path)

            $psObj = $adsiObj.Children | Where-Object {$_.SchemaClassName -eq 'user'} | ForEach-Object {
                $groups = $_.Groups() | ForEach-Object {
                    $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
                }
                $_ | Select-Object @{n = 'UserName';e = {$_.Name}},@{n = 'Groups';e = {$groups -join ';'}}
            }

            $adsiObj.Close()

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
        
        return $psObj
    }
}


function Enable-LocalUser
{
   [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $name,
        [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $computerName = $env:COMPUTERNAME
    )
 
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {
        
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }
        
            Write-Verbose ('WMI Query: {0}' -f $fqdn)

            $adsiObjPath = 'WinNT://{0}/{1},user' -f $computerName, $name
            Write-Verbose ('Open local account with ADSI: {0}' -f $adsiObjPath)

            $adsiObj = $null
            $adsiObj = Get-DirectoryEntry $adsiObjPath -Verbose:$VerbosePreference
        
            Write-Verbose ('Local account ADSI path opened: {0}' -f $adsiObj.Path)
            Write-Verbose ('Enable local account {0}.' -f $name)

            $originalUAC = [int] $adsiObj.UserFlags.Value
            $newUAC = [int] ($originalUAC -band (-bnot ([int] 2)))
            $adsiObj.UserFlags.Value = $newUAC
            $adsiRs = $adsiObj.SetInfo()
            
            $adsiObj.Close()

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }
}


function Disable-LocalUser
{
   [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $name,
        [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $computerName = $env:COMPUTERNAME
    )
 
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {
        
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }

            $adsiObjPath = "WinNT://$computerName/{0},user" -f $name
            Write-Verbose ('Open local account with ADSI: {0}' -f $adsiObjPath)

            $adsiObj = $null
            $adsiObj = Get-DirectoryEntry $adsiObjPath -Verbose:$VerbosePreference

            Write-Verbose ('Local account ADSI path opened: {0}' -f $adsiObj.Path)
            Write-Verbose ('Disable local account {0}.' -f $name)

            $originalUAC = [int] $adsiObj.UserFlags.Value
            $adsiObj.UserFlags.Value = ($originalUAC -bor 2)
            $adsiRs = $adsiObj.SetInfo()
        
            $adsiObj.Close()
    
        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }
}    


function Get-PasswordFromSecureString
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [System.Security.SecureString] $secureString
    )

    process {
        
        return ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto(`
            [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)))

    }
}


function Set-LocalUserPassword
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $name,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [System.Security.SecureString] $oldPassword,
        [Parameter(Mandatory = $true, Position = 2, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [System.Security.SecureString] $newPassword,
        [Parameter(Mandatory = $false, Position = 3, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $computerName = $env:COMPUTERNAME
    )
 
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {
        
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }
            
            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Change user passwords for $name")) {

                $adsiObjPath = 'WinNT://{0}/{1},user' -f $computerName, $name
                Write-Verbose ('Open local user with ADSI: {0}' -f $adsiObjPath)

                $adsiObj = $null
                $adsiObj = Get-DirectoryEntry $adsiObjPath -Verbose:$VerbosePreference
 
                Write-Verbose ('Local user ADSI path opened: {0}' -f $adsiObj.Path)
                Write-Verbose ('Change local user password.')

                $adsiRs = $adsiObj.ChangePassword((Get-PasswordFromSecureString -secureString $oldPassword), `
                    (Get-PasswordFromSecureString -secureString $newPassword))

                $adsiObj.Close()

            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }
}


function Add-MemberLocalGroup
{
   [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $memberLogin,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $localGroup,
        [Parameter(Mandatory = $true, Position = 2, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [ValidateSet('user','group')]
        [string] $localObjType,
        [Parameter(Mandatory = $false, Position = 3, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $computerName = $env:COMPUTERNAME
    )
 
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {
        
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }

            Write-Verbose ("Going to obtain local group: {0}" -f $localGroup)
    
            $localGroupDE = $null
            $localGroupDE = [ADSI] ('WinNT://{0}/{1},Group' -f $computerName, $localGroup)

            if($localGroupDE) {
    
                $memberADSIPath = 'WinNT://{0}/{1}' -f $computerName, $memberLogin
                $memberADSIPath = '{0},{1}' -f $memberADSIPath, $localObjType

                Write-Verbose ("Adding: {0} | {1}" -f $memberLogin, $memberADSIPath)
            
                $adsiRs = $null
                $adsiRs = $localGroupDE.Add($memberADSIPath)
            
                $localGroupDE.Close()
            
            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    }
}


function New-LocalObject
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $name,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [System.Security.SecureString] $password,
        [Parameter(Mandatory = $true, Position = 2, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()]
        [ValidateSet('user','group')] 
        [string] $type,
        [Parameter(Mandatory = $false, Position = 3, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $computerName = $env:COMPUTERNAME,
        [Parameter(Mandatory = $false, Position = 4, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $groups
    )
 
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {
        
        try {
        
            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }

           if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Create local $type $name")) {

                Write-Verbose ("Going to obtain local SAM handle.")
    
                $localSAM = $null
                $localSAM = Get-DirectoryEntry ('WinNT://{0}' -f $computerName) -Verbose:$VerbosePreference
            
                Write-Verbose ("Going to create or open the new object: {0} = {1}" -f $type, $name)
                Write-Verbose ("The object does not exist, going to create it.")

                $newObj = $null
                $newObj = $localSAM.Create($type, $name)

                if (-not ([string]::IsNullOrEmpty($newObj.Path))) {

                    if ($type -eq 'user') {
        
                        if (-not ([string]::IsNullOrEmpty((Get-PasswordFromSecureString -secureString $password)))) {

                            Write-Verbose ("Setting password.")

                            $adsiRs = $null
                            $adsiRs = $newObj.SetPassword((Get-PasswordFromSecureString -secureString $password))
                        }

                    }

                    Write-Verbose ("Saving the newly created object.")

                    $adsiRs = $null
                    $adsiRs = $newObj.SetInfo()

                    if (-not ([string]::IsNullOrEmpty($groups))) {

                        $groupList = $groups.Split(',', [System.StringSplitOptions]::RemoveEmptyEntries)
                            
                        Write-Verbose ("Adding the new object to groups: {0}x" -f $groupList.Count)
          
                        $groupList | ForEach-Object { 
                                
                            $oneGroup = $_

                            Add-MemberLocalGroup -memberLogin $name -localGroup $oneGroup `
                                -localObjType $type -Verbose:$VerbosePreference

                        }
                    }

                }

                $localSAM.Close()

            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }
}


function Remove-LocalObject
{
   [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $name,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [ValidateSet('user','group')]
        [string] $type,
        [Parameter(Mandatory = $false, Position = 2, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $computerName = $env:COMPUTERNAME
    )
 
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {
        
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }

            Write-Verbose ("Going to obtain local SAM handle.")
    
            $localSAM = $null
            $localSAM = Get-DirectoryEntry ('WinNT://{0}' -f $computerName) -Verbose:$VerbosePreference
            
            Write-Verbose ("Going to delete object: {0} = {1}" -f $type, $name)

            $adsiRs = $localSAM.Delete($type, $name)
            $localSAM.Close()

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    }
}


function Test-LocalUserEnabled
{
   [CmdletBinding(SupportsShouldProcess = $false)]
   [OutputType([System.Boolean])] 
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $name,
        [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $computerName = $env:COMPUTERNAME
    )
 
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'SilentContinue'
    
        [bool] $isEnabled = $false

    }

    process {
        
        try {

            Write-Verbose ("Going to obtain local SAM handle.")
    
            $localSAM = $null
            $localSAM = Get-DirectoryEntry ('WinNT://{0}/{1},user' -f $computerName, $name) -Verbose:$VerbosePreference

            Write-Verbose ('Local account ADSI path opened: {0}' -f $localSAM.Path)
    
            $isEnabled = -not ($originalUAC -band 2)
            
            $localSAM.Close()

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
        
        return $isEnabled
    }
}


function Rename-LocalObject
{
   [CmdletBinding(SupportsShouldProcess = $false)]
   param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $oldName,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $newName,
        [Parameter(Mandatory = $true, Position = 2, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [ValidateSet('user','group')]
        [string] $type,
        [Parameter(Mandatory = $false, Position = 3, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $computerName = $env:COMPUTERNAME
    )
 
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {
        
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }

            Write-Verbose ("Going to obtain local SAM handle.")
    
            $localSAM = $null
            $localSAM = Get-DirectoryEntry ('WinNT://{0}/{1},user' -f $computerName, $oldName) -Verbose:$VerbosePreference

            Write-Verbose ('Local account ADSI path opened: {0}' -f $localSAM.Path)
            Write-Verbose ('Rename local object: from = {0} | to = {1}' -f $oldName, $newName)

            $localSAM.psbase.rename($newName)
            $localSAM.Close()
        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    }
} 


function Protect-HmacSha256
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([System.String])] 
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [Security.SecureString] $password,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNull()] 
        [byte[]] $bytesToHmac
    ) 
    
    begin {

        [string] $outHmac = ''
        [IntPtr] $unmanagedString = [IntPtr]::Zero

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'SilentContinue'

    }
    
    process {
        
        try {
        
            Write-Verbose ('Signing bytes: {0}' -f $bytesToHmac.Count)
        
            $unmanagedString = [Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($password)
            $outHmac = [BitConverter]::ToString(
                (New-Object System.Security.Cryptography.HMACSHA256 @(,`
                    ([Text.Encoding]::UTF8.GetBytes([Runtime.InteropServices.Marshal]::PtrToStringUni($unmanagedString)) * `
                        [Math]::Ceiling((64 / $password.Length))))).ComputeHash($bytesToHmac)
            )

            [Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($unmanagedString)

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    
        return $outHmac
    } 
}


function Convert-UserNameToSid
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $userName,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $domain
    ) 
    
    process {
        
        return ((New-Object System.Security.Principal.NTAccount($domain, $userName)).`
            Translate([System.Security.Principal.SecurityIdentifier])).Value
    
    }
}


function Convert-SidToUserName
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $sidString
    )
    
    process {
        
        return ((New-Object System.Security.Principal.SecurityIdentifier($sidString)).`
                Translate( [System.Security.Principal.NTAccount])).Value
    }
}


function Compress-GZip
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([System.String])] 
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $text,
        [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNull()] 
        [Text.Encoding] $encoding = [Text.Encoding]::UTF8
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
    
        [byte[]] $compressed = $null
        [byte[]] $sourceToZip = $encoding.GetBytes($text)
    }
    
    process {
        
        try {

            $zippedMem = New-Object IO.MemoryStream
            $zipper = New-Object IO.Compression.GZipStream ($zippedMem, `
                [System.IO.Compression.CompressionMode]::Compress, $true)

            [void] $zipper.Write($sourceToZip, 0, $sourceToZip.Length)

            $zipper.Close()
            $zipper.Dispose()

            $compressed = $zippedMem.ToArray()
        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return [Convert]::ToBase64String($compressed)
    }
}


function Expand-GZip
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $compressedInBase64,
        [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNull()] 
        [Text.Encoding] $encoding = [Text.Encoding]::UTF8
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
    
        [byte[]] $sourceToUnzip = [Convert]::FromBase64String($compressedInBase64)
        [byte[]] $decompressed = $null
    }
    
    process {

        try {

            $zippedMem = New-Object IO.MemoryStream
            [void] $zippedMem.Write($sourceToUnzip, 0, $sourceToUnzip.Length)
            [void] $zippedMem.Seek(0, 'Begin')

            $unzip = New-Object IO.Compression.GZipStream ($zippedMem, `
                [System.IO.Compression.CompressionMode]::Decompress, $true)

            $unzippedMem = New-Object IO.MemoryStream

            [byte[]] $buffer = New-Object byte[] 64
            [int] $read = $unzip.Read($buffer, 0, $buffer.Length)

            while ($read -gt 0) { 

                [void] $unzippedMem.Write($buffer, 0, $read)
                $read = $unzip.Read($buffer, 0, $buffer.Length)
            }

            $unzip.Close()
            $unzip.Dispose()

            $decompressed = $unzippedMem.ToArray()

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return $encoding.GetString($decompressed)
    } 
}


function Compress-Deflate
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([System.String])] 
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $text,
        [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNull()] 
        [Text.Encoding] $encoding = [Text.Encoding]::UTF8
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
    
        [byte[]] $sourceToZip = $encoding.GetBytes($text)
        [byte[]] $compressed = $null
    }

    process {
        
        try {

            $zippedMem = New-Object IO.MemoryStream
            $zipper = New-Object IO.Compression.DeflateStream ($zippedMem, `
                [System.IO.Compression.CompressionMode]::Compress, $true)

            [void] $zipper.Write($sourceToZip, 0, $sourceToZip.Length)
            $zipper.Close()
            $zipper.Dispose()

            $compressed = $zippedMem.ToArray()

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return [Convert]::ToBase64String($compressed)
    }
}


function Expand-Deflate
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $compressedInBase64,
        [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNull()] 
        [Text.Encoding] $encoding = [Text.Encoding]::UTF8
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
    
        [byte[]] $sourceToUnzip = [Convert]::FromBase64String($compressedInBase64)
        [byte[]] $decompressed = $null

    }
    
    process {

        try {

            $zippedMem = New-Object IO.MemoryStream
            [void] $zippedMem.Write($sourceToUnzip, 0, $sourceToUnzip.Length)
            [void] $zippedMem.Seek(0, 'Begin')

            $unzip = New-Object IO.Compression.DeflateStream ($zippedMem, [System.IO.Compression.CompressionMode]::Decompress, $true)
            $unzippedMem = New-Object IO.MemoryStream

            [byte[]] $buffer = New-Object byte[] 64
            [int] $read = $unzip.Read($buffer, 0, $buffer.Length)

            while ($read -gt 0) { 

                [void] $unzippedMem.Write($buffer, 0, $read)
                $read = $unzip.Read($buffer, 0, $buffer.Length)
            }

            $unzip.Close()
            $unzip.Dispose()

            $decompressed = $unzippedMem.ToArray()

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
        
        return $encoding.GetString($decompressed)
    }
}


function Get-UserNameFromLogin
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([System.String])] 
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()]
        [string] $loginSamOrUpn
    )

    process {
        
        if($loginSamOrUpn -like "*\*") { 
        
            return $loginSamOrUpn.Split('\')[0]
        }
        
        elseif($loginSamOrUpn -like "*@*") { 
            
            return $loginSamOrUpn.Split('@')[0]
        }
        
        else { return $loginSamOrUpn }
    }
}


function Get-DomainFromLogin
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([System.String])] 
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()]
        [string] $loginSamOrUpn
    )

    process {
        
        if($loginSamOrUpn -like "*\*") { 
        
            return $loginSamOrUpn.Split('\')[1]
        }
        
        elseif($loginSamOrUpn -like "*@*") { 
            
            return $loginSamOrUpn.Split('@')[1]
        }
        
        else { return $null }
    }
}

function Enable-WindowsAutologon
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()]
        [Management.Automation.PSCredential] $credentials 
    )
 
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }
    
    process {

        try {
            
            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }

            Write-Verbose 'Registering autologon.'
  
            Set-RegistryValue -keyPath 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon' `
                -Name AutoAdminLogon -Value 1 -Type Dword -Verbose:$VerbosePreference

            Set-RegistryValue -keyPath 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon' `
                -Name AutoLogonCount -Value 2 -Type DWord -Verbose:$VerbosePreference

            Set-RegistryValue -keyPath 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon' `
                -Name DefaultPassword -Value ($credentials.GetNetworkCredential().Password) -Type String -Verbose:$VerbosePreference

            Set-RegistryValue -keyPath 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon' `
                -Name DefaultUserName -Value (Get-UserNameFromLogin -loginSamOrUpn $credential.UserName) -Type String -Verbose:$VerbosePreference
            
            if (-not ([string]::IsNullOrEmpty((Get-DomainFromLogin -loginSamOrUpn $domain)))) {
    
                Set-RegistryValue -keyPath 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon' `
                    -Name DefaultDomainName -Value $domain -Type String -Verbose:$VerbosePreference
    
            }

            Write-Verbose 'Autologon registered.'

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }
}


function Disable-WindowsAutologon
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param()

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {

        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }

            Write-Verbose 'Removing autologon.'

            Remove-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon' `
                AutoAdminLogon -Force

            Remove-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon' `
                AutoLogonCount -Force

            Remove-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon' `
                DefaultPassword -Force

            Remove-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon' `
                DefaultUserName -Force

            Remove-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon' `
                DefaultDomainName -Force

            Write-Verbose 'Autologon removed.'

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }
}


function Get-NextFreeDriveLetter
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param()

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        $drvLetter = [int][char]'C'
        $nextFDL = $null

    }

    process {

        try {

            while ((Get-PSDrive -PSProvider filesystem).Name -contains [char]$drvLetter) { $drvLetter++ }
            $nextFDL = ([char]$drvLetter)

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
        
        return $nextFDL
    }
}


function Get-CurrentIdentityInformation
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([PSObject])]
    param()
 
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        $out = New-Object PSObject

    }
    
    process {

        try {
            
            $currentPrincipal = [Security.Principal.WindowsIdentity]::GetCurrent($false)
           
            [Security.Principal.WindowsIdentity] $threadIdentityOnlyIfImpersonating = $null
            $threadIdentityOnlyIfImpersonating = [Security.Principal.WindowsIdentity]::GetCurrent($true)

            $runningIdentity = [Security.Principal.WindowsIdentity]::GetCurrent($false)

            Add-Member -InputObject $out -MemberType NoteProperty -Name 'Running under username' `
                -Value ($currentPrincipal.Name)

            Add-Member -InputObject $out -MemberType NoteProperty -Name 'Running under SID' `
                -Value ($currentPrincipal.User.Value)

            Add-Member -InputObject $out -MemberType NoteProperty -Name 'Thread IsImpersonating' `
                -Value ([bool] $threadIdentityOnlyIfImpersonating)

            Add-Member -InputObject $out -MemberType NoteProperty -Name 'RunningIdentity username' `
                -Value ($runningIdentity.Name)

            Add-Member -InputObject $out -MemberType NoteProperty -Name 'RunningIdentity SID' `
                -Value ($runningIdentity.User)

            Add-Member -InputObject $out -MemberType NoteProperty -Name 'AuthenticationType' `
                -Value ($runningIdentity.AuthenticationType)

            Add-Member -InputObject $out -MemberType NoteProperty -Name 'IsSystem' `
                -Value ($runningIdentity.IsSystem)

            Add-Member -InputObject $out -MemberType NoteProperty -Name 'IsAnonymous' `
                -Value ($runningIdentity.IsAnonymous)

            Add-Member -InputObject $out -MemberType NoteProperty -Name 'IsAuthenticated' `
                -Value ($runningIdentity.IsAuthenticated)

            Add-Member -InputObject $out -MemberType NoteProperty -Name 'IsGuest' `
                -Value ($runningIdentity.IsGuest)
                
            foreach ($oneSID in ($runningIdentity.Groups | Select-Object -Expand Value )) {
            
                Add-Member -InputObject $out -MemberType NoteProperty -Name ('In GROUP: {0}' -f (Convert-SidToUserName $oneSid)) `
                    -Value $oneSID

            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return $out
    }
}


function Find-InDirectoryService
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNull()] 
        [System.DirectoryServices.DirectoryEntry] $rootObj,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string] $filter,
        [Parameter(Mandatory = $false, Position = 2, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [string[]] $properties
    )
    
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        [ADSISearcher] $searcher = New-Object System.DirectoryServices.DirectorySearcher
        [System.DirectoryServices.SearchResultCollection] $searchRes = $null
        
        $returner = $null

    }
    
    process {

        try {
            
            Write-Verbose ('Search root DN: {0}' -f $rootObj.Path)

            $searcher.SearchRoot = $rootObj
            $searcher.CacheResults = $true
            #$searcher.PageSize = 5000
            $searcher.SearchScope = 'subTree'
            $searcher.Asynchronous = $true
            $searcher.Filter = $filter

            if ($properties) { $properties | ForEach-Object { [void] $searcher.PropertiesToLoad.Add($_) } }
            
            $searchRes = $searcher.FindAll()

            $returner = New-Object PSCustomObject
  
            $returner | Add-Member -Member NoteProperty -Name searcher -Value $searcher
            $returner | Add-Member -Member NoteProperty -Name found -Value (($searchRes) -and ($searchRes.Count -gt 0))
            $returner | Add-Member -Member NoteProperty -Name result -Value $searchRes

            Write-Verbose ('Found matching objects: {0} | {1}' -f $returner.found, $returner.result.Count)

            $searcher.Dispose()

            [GC]::Collect()
            [GC]::WaitForPendingFinalizers()

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return $returner

    }
}


function Get-DirectoryEntryObjectProperty
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNull()] 
        [System.DirectoryServices.DirectoryEntry] $de
    )

    begin {
    
        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        $propObj = $null
    }
    
    process {
    
        try {
    
            Write-Verbose ('Getting list properties: {0}' -f $de.Path)

            $props = $de | Get-Member | Where-Object { $_.MemberType -eq 'Property'} | Select-Object -ExpandProperty Name
            $propObj = New-Object PSCustomObject

            foreach ($prop in $props) {
            
                $propObj | Add-Member -MemberType NoteProperty -Name $prop -Value $de.Properties[$prop]
            }

            $de.Close()

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    
        return $propObj
    }
}


function Test-IsComputerDomainMember
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([System.Boolean])] 
    param ()
     
    begin {
    
        [bool] $isDomainMember = $false

    }
    process {

        try {

            $wmiObj = Get-WmiObject -Query 'SELECT * FROM Win32_ComputerSystem'
            if ($wmiObj.partofdomain -eq 'True') { $isDomainMember = $true } else { $isDomainMember = $false }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
        
        return $isDomainMember
    }
}


function New-EmptyFileWithSize
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()] 
        [string] $path,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNull()] 
        [int64] $sizeInBytes
    )
    
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }
    
    process {

        try {
            
            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, $path)) {

                $newFile = [System.IO.File]::Create($path)
            
                if ($newFile) {

                    $newFile.SetLength($sizeInBytes)
                    $newFile.Dispose()

                } else {
                
                    throw 'Error during creating file.'
                }
            }
        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    }     
}


function Copy-Acl
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()]
        [ValidateScript({ Test-Path $_ })] 
        [string] $sourcePath,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()] 
        [ValidateScript({ Test-Path $_ })] 
        [string] $targetPath
    )
    
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }
    
    process {

        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }

            Write-Verbose ('Get source path ACL of the source path: {0}' -f $sourcePath)
            $sourceACL = Get-Acl $sourcePath

            Write-Verbose ('Obtain new owner')
            $newOwner = New-Object System.Security.Principal.NTAccount('BUILTIN\Administrators')

            Write-Verbose ('Change owner of the ACL')
            $sourceACL.SetOwner($newOwner)

            Write-Verbose ('Disable inheritance in the ACL')
            $sourceACL.SetAccessRuleProtection($true, $true)

            Write-Verbose ('Apply the ACL to the target path: {0}' -f $targetPath)
            Set-Acl $targetPath -AclObject $sourceACL

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    }     
}


function Backup-ScheduleTask
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()]
        [ValidateScript({ Test-Path $_ })] 
        [string] $path,
        [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()]
        [string] $taskPath = '\'
    )
    
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {
        
        try {
        
            Get-ScheduledTask -TaskPath $taskPath | ForEach-Object {
                
                Write-Verbose ('Exporting task name: {0} -> {1}' -f $_.TaskName, `
                    (Join-Path $path "$($_.TaskName).xml"))

                Export-ScheduledTask -TaskName $_.TaskName -TaskPath $_.TaskPath -Verbose:$VerbosePreference |
                    Out-File (Join-Path $path "$($_.TaskName).xml")
 
            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }
}


function Restore-ScheduleTask
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullorEmpty()]
        [ValidateScript({ Test-Path $_ })] 
        [string] $inputPath,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()]
        [Management.Automation.PSCredential] $credentials,
        [Parameter(Mandatory = $false, Position = 2, ValueFromPipeline = $false)] 
        [ValidateNotNullorEmpty()]
        [string] $destinationSchedulerFolder = '\'
    )
    
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {
        
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }
            
            $tasks = Get-ChildItem -Path $inputPath -Filter *.xml -Force
            
            Write-Verbose ('Found tasks to import: {0}' -f $tasks.Count)

            foreach ($task in $tasks)
            {
                $taskName = [System.IO.Path]::GetFileNameWithoutExtension($task)
            
                Write-Verbose ('Import task: {0} | {1}' -f $taskName, $task.FullName)
                Register-ScheduledTask -Xml (Get-Content $task.FullName | Out-String) -TaskName $taskName ï¿½User $credentials.UserName `
                    ï¿½Password ($credentials.GetNetworkCredential().Password) -TaskPath $destinationSchedulerFolder -Force -Verbose:$VerbosePreference | Out-Null

                Write-Verbose ('Disable task: {0}' -f $taskName)
                Disable-ScheduledTask -TaskName $TaskName -taskPath $destinationSchedulerFolder -Verbose:$VerbosePreference | Out-Null
            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }
}


function Set-WindowsEventLogForTaskScheduler
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $false)] 
        [ValidateNotNull()]
        [bool] $enable
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }
    
    process {
        
        try{
            
            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }

            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, $enable)) {
            
                $logName = 'Microsoft-Windows-TaskScheduler/Operational'
                
                $eventLog = New-Object System.Diagnostics.Eventing.Reader.EventLogConfiguration $logName
                $eventLog.IsEnabled = $enable
                $eventLog.SaveChanges()
                
                $eventLog.Dispose()
                                                
                [GC]::Collect()
                [GC]::WaitForPendingFinalizers() 

                Write-Verbose ('Windows event log for Task scheduler enable: {0}' -f $enable)

            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }
}


function Get-MSSQLQueryResult
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([Data.DataTable])] 
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [string] $serverPlusInstance,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullOrEmpty()]
        [string] $query,
        [Parameter(Mandatory = $false, Position = 2, ValueFromPipeline = $false)] 
        [ValidateNotNullOrEmpty()]
        [string] $database = 'Master',
        [Parameter(Mandatory = $false, Position = 3, ValueFromPipeline = $false)] 
        [ValidateNotNull()]
        [bool] $encrypt
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
        
        $dataTable = $null
    }

    process {
        
        try{
            
            Write-Verbose ('Going to connect to: SRV = {0} | DB = {1} | Encrypt = {2}' -f $serverPlusInstance, $database, $encrypt)

            $connString = 'Server={0};Database={1};Integrated Security=True;Encrypt={2};Pooling=false' -f $serverPlusInstance, $database, $encrypt
            Write-Verbose ('Connection string: {0}' -f $connString)
            
            $conn = New-Object Data.SqlClient.SqlConnection $connString
            $conn.Open()

            if ($conn) {
            
                Write-Verbose ('Server version: {0}' -f $conn.ServerVersion)
                Write-Verbose ('Workstation ID: {0}' -f $conn.WorkstationId)
                Write-Verbose ('Client connection ID: {0}' -f $conn.ClientConnectionId)
                Write-Verbose ('Connection timeout: {0} sec' -f $conn.ConnectionTimeout)

                $cmd = $conn.CreateCommand()
                $cmd.CommandText = $query

                $reader = $cmd.ExecuteReader()
            
                if ((-not $reader.IsClosed) -and $reader.HasRows) {
                
                    Write-Verbose ('Load the data table into memory')

                    $dataTable = New-Object Data.DataTable
                    $dataTable.Load($reader)
 
                    Write-Verbose ('Query returned: rows = {0}' -f $dataTable.Rows.Count)

                }
                
                $reader.Close()
                                            
            }

            $conn.Close()
            $conn.Dispose()
            
            return $dataTable

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
        
    }
}


function Import-WindowsAPI
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param ()

    begin {
        $win32api = @'
            using System;
            using System.Runtime.InteropServices;
            using System.Text;
 
            namespace Win32Api {
                 
                [StructLayout(LayoutKind.Sequential)]
                public struct LUID {
 
                    public uint LowPart;
                    public int HighPart;
                }
 
                [StructLayout(LayoutKind.Sequential)]
                public struct LUID_AND_ATTRIBUTES {
 
                    public LUID Luid;
                    public UInt32 Attributes;
                }
 
                [StructLayout(LayoutKind.Sequential)]
                public struct TOKEN_PRIVILEGES {
 
                    public UInt32 PrivilegeCount;
                    public LUID Luid;
                    public UInt32 Attributes;
                }
                 
                public class Kernel32 {
 
                    public const uint DELETE = 0x00010000;
                    public const uint READ_CONTROL = 0x00020000;
                    public const uint WRITE_DAC = 0x00040000;
                    public const uint WRITE_OWNER = 0x00080000;
                    public const uint SYNCHRONIZE = 0x00100000;
                    public const uint STANDARD_RIGHTS_ALL = (
                                                                READ_CONTROL |
                                                                WRITE_OWNER |
                                                                WRITE_DAC |
                                                                DELETE |
                                                                SYNCHRONIZE
                                                            );
                    public const uint STANDARD_RIGHTS_REQUIRED = 0x000F0000u;
                    public const uint STANDARD_RIGHTS_READ = 0x00020000u;
                     
                    public const uint PROCESS_TERMINATE = 0x0001;
                    public const uint PROCESS_CREATE_THREAD = 0x0002;
                    public const uint PROCESS_VM_OPERATION = 0x0008;
                    public const uint PROCESS_VM_READ = 0x0010;
                    public const uint PROCESS_VM_WRITE = 0x0020;
                    public const uint PROCESS_DUP_HANDLE = 0x0040;
                    public const uint PROCESS_CREATE_PROCESS = 0x0080;
                    public const uint PROCESS_SET_QUOTA = 0x0100;
                    public const uint PROCESS_SET_INFORMATION = 0x0200;
                    public const uint PROCESS_QUERY_INFORMATION = 0x0400;
                    public const uint PROCESS_SUSPEND_RESUME = 0x0800;
                    public const uint PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
                    public const uint PROCESS_ALL_ACCESS = (
                                                STANDARD_RIGHTS_ALL |
                                                PROCESS_CREATE_PROCESS |
                                                PROCESS_CREATE_THREAD |
                                                PROCESS_DUP_HANDLE |
                                                PROCESS_QUERY_INFORMATION |
                                                PROCESS_QUERY_LIMITED_INFORMATION |
                                                PROCESS_SET_INFORMATION |
                                                PROCESS_SET_QUOTA |
                                                PROCESS_SUSPEND_RESUME |
                                                PROCESS_TERMINATE |
                                                PROCESS_VM_OPERATION |
                                                PROCESS_VM_READ |
                                                PROCESS_VM_WRITE
                                                           );
 
                    [DllImport("kernel32.dll", SetLastError=true, ExactSpelling = true)]
                    public static extern IntPtr OpenProcess(uint DesiredAccess, bool InheritHandle, int ProcessId);
 
                    [DllImport("kernel32.dll", SetLastError=true, ExactSpelling = true)]
                    public static extern IntPtr GetCurrentThread();
 
                    [DllImport("Kernel32.dll", SetLastError=true)]
                    public static extern bool CloseHandle(IntPtr handle);
 
                    [DllImport("Kernel32.dll")]
                    public static extern uint GetLastError();
 
                    [DllImport("Kernel32.dll")]
                    public static extern void SetLastError(int errCode);
 
                    [DllImport("kernel32.dll", SetLastError=true, ExactSpelling = true)]
                    public static extern IntPtr GetCurrentProcess();
 
                }
 
                public class AdvAPI32 {
 
                    public const uint SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001u;
                    public const uint SE_PRIVILEGE_ENABLED = 0x00000002u;
                    public const uint SE_PRIVILEGE_REMOVED = 0x00000004u;
                    public const uint SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000u;
 
                    public const uint TOKEN_QUERY = 0x00000008;
                    public const uint TOKEN_ADJUST_PRIVILEGES = 0x00000020;
 
                    public const uint TOKEN_ASSIGN_PRIMARY = 0x00000001u;
                    public const uint TOKEN_DUPLICATE = 0x00000002u;
                    public const uint TOKEN_IMPERSONATE = 0x00000004u;
                    public const uint TOKEN_QUERY_SOURCE = 0x00000010u;
                    public const uint TOKEN_ADJUST_GROUPS = 0x00000040u;
                    public const uint TOKEN_ADJUST_DEFAULT = 0x00000080u;
                    public const uint TOKEN_ADJUST_SESSIONID = 0x00000100u;
                    public const uint TOKEN_READ = (
                                                        Win32Api.Kernel32.STANDARD_RIGHTS_READ |
                                                        TOKEN_QUERY
                                                    );
                    public const uint TOKEN_ALL_ACCESS = (
                                                            Win32Api.Kernel32.STANDARD_RIGHTS_REQUIRED |
                                                            TOKEN_ASSIGN_PRIMARY |
                                                            TOKEN_DUPLICATE |
                                                            TOKEN_IMPERSONATE |
                                                            TOKEN_QUERY |
                                                            TOKEN_QUERY_SOURCE |
                                                            TOKEN_ADJUST_PRIVILEGES |
                                                            TOKEN_ADJUST_GROUPS |
                                                            TOKEN_ADJUST_DEFAULT |
                                                            TOKEN_ADJUST_SESSIONID
                                                            );
 
                    public enum SECURITY_IMPERSONATION_LEVEL:int {
 
                        SecurityAnonymous = 0,
                        SecurityIdentification = 1,
                        SecurityImpersonation = 2,
                        SecurityDelegation = 3
                    }
 
                    [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
                    public static extern bool LookupPrivilegeValue(
                        string lpSystemName,
                        string lpName,
                        out LUID lpLuid);
 
                    [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
                    public static extern bool OpenProcessToken(
                        IntPtr ProcessHandle,
                        UInt32 DesiredAccess,
                        out IntPtr TokenHandle);
 
                    [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
                    public static extern bool OpenThreadToken(
                        IntPtr ThreadHandle,
                        UInt32 DesiredAccess,
                        bool OpenAsSelf,
                        out IntPtr TokenHandle);
 
                    [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
                    public static extern bool AdjustTokenPrivileges(
                        IntPtr TokenHandle,
                        bool DisableAllPrivileges,
                        ref TOKEN_PRIVILEGES NewState,
                        UInt32 BufferLengthInBytes,
                        IntPtr PreviousStateNull,
                        IntPtr ReturnLengthInBytesNull);
 
                    [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
                    public static extern bool DuplicateToken(
                        IntPtr ExistingTokenHandle,
                        int ImpersonationLevel,
                        out IntPtr DuplicateTokenHandle);
                 
                    [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
                    public static extern bool SetThreadToken(IntPtr Thread, IntPtr Token);
 
                    [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
                    public static extern bool RevertToSelf();
 
                    [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
                    public static extern int RegOpenKeyEx(
                        int hKey,
                        string subKey,
                        int ulOptions,
                        int samDesired,
                        out int hkResult);
 
                    [DllImport("advapi32.dll", EntryPoint = "RegEnumKeyEx")]
                    extern public static int RegEnumKeyEx(
                        int hkey,
                        int index,
                        StringBuilder
                        lpName,
                        ref int lpcbName,
                        int reserved,
                        StringBuilder lpClass,
                        ref int lpcbClass,
                        out long lpftLastWriteTime);
 
                    [DllImport("advapi32.dll", EntryPoint="RegQueryInfoKey", CallingConvention=CallingConvention.Winapi, SetLastError=true)]
                    extern public static int RegQueryInfoKey(
                        int hkey,
                        StringBuilder lpClass,
                        ref int lpcbClass,
                        int lpReserved,
                        out int lpcSubKeys,
                        out int lpcbMaxSubKeyLen,
                        out int lpcbMaxClassLen,
                        out int lpcValues,
                        out int lpcbMaxValueNameLen,
                        out int lpcbMaxValueLen,
                        out int lpcbSecurityDescriptor,
                        IntPtr lpftLastWriteTime);
 
                    [DllImport("advapi32.dll", SetLastError=true)]
                    public static extern int RegCloseKey(int hKey);
 
                }
 
                public class Shift {
                    public static int Right(int x, int count) { return x >> count; }
                    public static uint Right(uint x, int count) { return x >> count; }
                    public static long Right(long x, int count) { return x >> count; }
                    public static ulong Right(ulong x, int count) { return x >> count; }
                    public static int Left(int x, int count) { return x << count; }
                    public static uint Left(uint x, int count) { return x << count; }
                    public static long Left(long x, int count) { return x << count; }
                    public static ulong Left(ulong x, int count) { return x << count; }
                }
 
            }
'@

    }
    
    process {

        try {

            if (-not ('Win32Api.AdvAPI32' -as [type])) {

                Write-Verbose ('Define the new type.')
                [void] (Add-Type -TypeDefinition $win32api -Verbose:$VerbosePreference)
    
            } else {

                Write-Verbose ('The type already exists. Skipping.')
            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }
}

function Convert-PrivilegeNameToInt
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([int])] 
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [string] $privilegeName
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        [int] $result = -1

    }

    process {
            
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }
            
            Import-WindowsAPI -Verbose:$VerbosePreference

            [Win32Api.LUID] $privilegeValue = New-Object Win32Api.LUID
            
            $resBool = [Win32Api.AdvAPI32]::LookupPrivilegeValue($null, $privilegeName, [ref] $privilegeValue)
            Write-Verbose ('WIN32API Result: {0}'-f $resBool)

            if ($resBool) {

                Write-Verbose ('Privilege value: {0} = {1}, {2}' -f $privilegeName, $privilegeValue.LowPart, `
                    $privilegeValue.HighPart)
            
                $result = [int] $privilegeValue.LowPart
            
            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return $result

    }
}


function Set-TokenPrivilege
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [int] $privilege,
        [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullOrEmpty()]
        [bool] $enable = $true
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {
        
        try {
            
            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }

            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Setting token privileges")) {
            
                Import-WindowsAPI -Verbose:$VerbosePreference

                [IntPtr] $accessToken = New-Object IntPtr
                Write-Verbose ('Should try OpenThreadToken() first to try if we are not impersonating')
                
                $result = [Win32Api.AdvAPI32]::OpenThreadToken(([Win32Api.Kernel32]::GetCurrentThread()), `
                    ([Win32Api.AdvAPI32]::TOKEN_ADJUST_PRIVILEGES + [Win32Api.AdvAPI32]::TOKEN_QUERY), $false, `
                        ([ref] $accessToken))
                
                $lastError = [Win32Api.Kernel32]::GetLastError()
                
                Write-Verbose ('Win32 last error: {0}' -f $lastError)
                Write-Verbose ('Thread token opened: bool = {0} | token = {1:X8}' -f $result, ([int] $accessToken))
                
                if (-not $result) {
                
                    Write-Verbose ('Must go for OpenProcessToken()')
                    
                    $result = [Win32Api.AdvAPI32]::OpenProcessToken(([Win32Api.Kernel32]::GetCurrentProcess()), `
                        ([Win32Api.AdvAPI32]::TOKEN_ADJUST_PRIVILEGES + [Win32Api.AdvAPI32]::TOKEN_QUERY), `
                            ([ref] $accessToken))
                    
                    $lastError = [Win32Api.Kernel32]::GetLastError()

                    Write-Verbose ('Win32 last error: {0}' -f $lastError)
                    Write-Verbose ('Process token opened: bool = {0} | token = {1:X8}' -f $result, ([int] $accessToken))

                } else {
            
                    Write-Verbose ('Thread token opened ok, will not try OpenProcessToken()')
                
                }


                if (($accessToken -ne [IntPtr]::Zero) -and ($accessToken -ne -1)) {
            
                    [Win32Api.TOKEN_PRIVILEGES] $tokenPrivileges = New-Object Win32Api.TOKEN_PRIVILEGES

                    $tokenPrivileges.PrivilegeCount = 1
                    $newLuid = $tokenPrivileges.Luid
                    $newLuid.LowPart = $privilege
                    $newLuid.HighPart = 0
                    $tokenPrivileges.Luid = $newLuid
      
                    if ($enable) {

                        $tokenPrivileges.Attributes = [Win32Api.AdvAPI32]::SE_PRIVILEGE_ENABLED
      
                    } else {

                        $tokenPrivileges.Attributes = 0
                    }

                    Write-Verbose ('Call AdjustTokenPrivileges()')

                    $result = [Win32Api.AdvAPI32]::AdjustTokenPrivileges($accessToken, $false, ([ref] $tokenPrivileges), `
                        ([System.Runtime.InterOpServices.Marshal]::SizeOf($tokenPrivileges)), [IntPtr]::Zero, [IntPtr]::Zero)

                    $lastError = [Win32Api.Kernel32]::GetLastError()
                
                    Write-Verbose ('WIN32API Result: {0}' -f $result)

                }

                Write-Verbose ('Disposing handle: {0}' -f $accessToken)
    
                $result = [Win32Api.Kernel32]::CloseHandle($accessToken)
                $lastError = [Win32Api.Kernel32]::GetLastError()
        
                Write-Verbose ('WIN32API Result: {0}' -f $result)
                Write-Verbose ('Win32 last error: {0}' -f $lastError)

            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }

}


function Copy-ProcessToken
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [string] $process
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        $theProcess = $null

    }

    process {
        
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }

            Import-WindowsAPI -Verbose:$VerbosePreference
            
            Write-Verbose ('Get the process: {0}' -f $process)
            $theProcess = Get-Process -Name $process

            Write-Verbose ('Will duplicate process token for ID: {0}' -f $theProcess.Id)
            Set-TokenPrivilege -privilege (Convert-PrivilegeNameToInt -privilegeName SeDebugPrivilege -Verbose:$VerbosePreference) -Verbose:$VerbosePreference
            Write-Verbose ('Open process handle')

            [IntPtr] $procHandle = [IntPtr]::Zero
            
            $procHandle = [Win32Api.Kernel32]::OpenProcess(([Win32Api.Kernel32]::PROCESS_ALL_ACCESS), $false, $theProcess.Id)
            $lastError = [Win32Api.Kernel32]::GetLastError()

            Write-Verbose ('Process handle opened: {0}' -f $procHandle)
            Write-Verbose ('Win32 last error: {0}' -f $lastError)

            if (($procHandle -ne [IntPtr]::Zero) -and ($procHandle -ne -1)) {
                
                Write-Verbose ('Open process access token with READ_CONTROL')

                [IntPtr] $procToken = [IntPtr]::Zero

                $result = [Win32Api.AdvAPI32]::OpenProcessToken($procHandle, ([Win32Api.Kernel32]::READ_CONTROL), [ref] $procToken)
                $lastError = [Win32Api.Kernel32]::GetLastError()
                
                Write-Verbose ('WIN32API Result: {0}' -f $result)
                Write-Verbose ('Win32 last error: {0}' -f $lastError)

                if (($procToken -ne [IntPtr]::Zero) -and ($procToken -ne -1)) {
                    
                    Write-Verbose ('Open process access token to DUPLICATE')

                    [IntPtr] $procToken = [IntPtr]::Zero

                    $result = [Win32Api.AdvAPI32]::OpenProcessToken($procHandle, ([Win32Api.AdvAPI32]::TOKEN_IMPERSONATE -bor `
                        [Win32Api.AdvAPI32]::TOKEN_DUPLICATE -bor [Win32Api.AdvAPI32]::TOKEN_QUERY -bor `
                            [Win32Api.AdvAPI32]::TOKEN_QUERY_SOURCE -bor [Win32Api.AdvAPI32]::TOKEN_READ), [ref] $procToken)

                    $lastError = [Win32Api.Kernel32]::GetLastError()

                    Write-Verbose ('WIN32API Result: {0}' -f $result)
                    Write-Verbose ('Win32 last error: {0}' -f $lastError)
                    Write-Verbose ('Process token opened: {0}' -f $procToken)

                    if (($procToken -ne [IntPtr]::Zero) -and ($procToken -ne -1)) {
                    
                        [IntPtr] $dulicatedProcToken = [IntPtr]::Zero

                        Write-Verbose ('Duplicate process token')

                        $result = [Win32Api.AdvAPI32]::DuplicateToken($procToken, `
                            [Win32Api.AdvAPI32+SECURITY_IMPERSONATION_LEVEL]::SecurityImpersonation, `
                                [ref] $dulicatedProcToken)

                        $lastError = [Win32Api.Kernel32]::GetLastError()
                        
                        Write-Verbose ('WIN32API Result: {0}' -f $result)
                        Write-Verbose ('Win32 last error: {0}' -f $lastError)
                        Write-Verbose ('Duplicated process token: {0}' -f $dulicatedProcToken)

                        if (($dulicatedProcToken -ne [IntPtr]::Zero) -and ($dulicatedProcToken -ne -1)) {

                            Write-Verbose ('Set current thread token to the duplicated one')

                            $result = [Win32Api.AdvAPI32]::SetThreadToken([IntPtr]::Zero, $dulicatedProcToken)
                            $lastError = [Win32Api.Kernel32]::GetLastError()
                        
                            Write-Verbose ('WIN32API Result: {0}' -f $result)
                            Write-Verbose ('Win32 last error: {0}' -f $lastError)

                            Write-Verbose ('Disposing handle: {0}' -f $dulicatedProcToken)
    
                            $result = [Win32Api.Kernel32]::CloseHandle($dulicatedProcToken)
                            $lastError = [Win32Api.Kernel32]::GetLastError()
        
                            Write-Verbose ('WIN32API Result: {0}' -f $result)
                            Write-Verbose ('Win32 last error: {0}' -f $lastError)

                            Get-CurrentIdentityInformation -Verbose:$VerbosePreference

                        }

                    }
                
                    Write-Verbose ('Disposing handle: {0}' -f $procToken)
    
                    $result = [Win32Api.Kernel32]::CloseHandle($procToken)
                    $lastError = [Win32Api.Kernel32]::GetLastError()
        
                    Write-Verbose ('WIN32API Result: {0}' -f $result)
                    Write-Verbose ('Win32 last error: {0}' -f $lastError)

                }

                Write-Verbose ('Disposing handle: {0}' -f $procHandle)
    
                $result = [Win32Api.Kernel32]::CloseHandle($procHandle)
                $lastError = [Win32Api.Kernel32]::GetLastError()
        
                Write-Verbose ('WIN32API Result: {0}' -f $result)
                Write-Verbose ('Win32 last error: {0}' -f $lastError)

            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }

}


function Restore-Self
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    param ()

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {
        
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }
            
            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Reverting to self")) {

                Import-WindowsAPI -Verbose:$VerbosePreference

                Write-Verbose ('Revert to self')
            
                $result = [Win32Api.AdvAPI32]::RevertToSelf()
                $lastError = [Win32Api.Kernel32]::GetLastError()

                Write-Verbose ('WIN32API Result: {0}' -f $result)
                Write-Verbose ('Win32 last error: {0}' -f $lastError)

                Get-CurrentIdentityInformation -Verbose:$VerbosePreference

            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    
    }
}


function Invoke-AsSystem
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [scriptblock] $code
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        $result = $null
    }

    process {
        
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }
        
            Copy-ProcessToken -process lsass -Verbose:$VerbosePreference
            
            Write-Verbose ('Invoke the code')
            $result = $code.Invoke()

            Restore-Self -Verbose:$VerbosePreference
             
        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
        
        return $result

    }
  
}


function Get-RegKeyClass
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([System.String])] 
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [ValidateSet('HKCR','HKCU','HKLM','HKU','HKCC')] 
        [string] $key,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullOrEmpty()]
        [string] $subKey
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        switch ($key) {
            
            'HKCR' { $nKey = 0x80000000} #HK Classes Root
            'HKCU' { $nKey = 0x80000001} #HK Current User
            'HKLM' { $nKey = 0x80000002} #HK Local Machine
            'HKU'  { $nKey = 0x80000003} #HK Users
            'HKCC' { $nKey = 0x80000005} #HK Current Config
        }

        $keyRead = 0x19
        $result = ''
        [int] $hKey = 0

    }

    process {
        
        try {

            if ((-not(Test-CurrentUserInGroup -groupName Administrators)) -and ($nKey -ne 0x80000001)) {
                
                throw 'Error: You must run as administrator !'

            }

            Import-WindowsAPI -Verbose:$VerbosePreference

            if (-not ([Win32Api.AdvAPI32]::RegOpenKeyEx($nKey, $subKey, 0, $keyRead, [ref] $hKey))) {

                $classVal = New-Object Text.Stringbuilder 1024
                [int] $len = 1024

                if (-not ([Win32Api.AdvAPI32]::RegQueryInfoKey($hKey, $classVal, [ref]$len, 0, [ref]$null, `
                    [ref]$null, [ref]$null, [ref]$null, [ref]$null, [ref]$null, [ref]$null, 0))) {
                    
                    $result = $classVal.ToString()
                    
                } else {
                    
                    throw 'RegQueryInfoKey() failed'

                }

                [Win32Api.AdvAPI32]::RegCloseKey($hKey) | Out-Null

            } else {
                
                throw ('Cannot open key: {0}:\{1}' -f $key, $subKey)

            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return $result
    }
}


function Get-BootKey
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([System.Object[]])] 
    param ()

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {
        
        try {
        
            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }

            $s = [string]::Join('', $('JD','Skew1','GBG','Data' | ForEach-Object {
                Get-RegKeyClass -Key "HKLM" -SubKey "SYSTEM\CurrentControlSet\Control\Lsa\$_" 
            }))

            $b = New-Object byte[] $($s.Length/2)
            
            0..$($b.Length - 1) | ForEach-Object { $b[$_] = [Convert]::ToByte($s.Substring($($_*2), 2), 16) }
            
            $b2 = New-Object byte[] 16
            
            0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3, 0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7 | `
                ForEach-Object -begin { $i = 0 }{ $b2[$i] = $b[$_]; $i++}
            
            return ,$b2

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

    }
}


function Get-RC4
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([byte[]])] 
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [byte[]] $key
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }

    process {
        
        try {
            
            return New-Object Object |
                Add-Member NoteProperty key $key -PassThru |
                Add-Member NoteProperty S $null -PassThru |
                Add-Member ScriptMethod init {
                    if (-not ($this.S))
                    {
                        [byte[]]$this.S = 0..255
                        
                        0..255 | ForEach-Object -begin{ [long]$j = 0 } {
                            $j = ($j + $this.key[$($_ % $this.key.Length)] + $this.S[$_]) % $this.S.Length
                            $temp = $this.S[$_]; $this.S[$_] = $this.S[$j]; $this.S[$j] = $temp
                        }

                    }

                } -PassThru |
                Add-Member ScriptMethod "encrypt" {
                    $data = $args[0]
                    $this.init()
                    
                    $outbuf = New-Object byte[] $($data.Length)
                    $S2 = $this.S[0..$this.S.Length]
                    
                    0..$($data.Length - 1) | ForEach-Object -begin{ $i=0; $j=0; } {
                        $i = ($i+1) % $S2.Length
                        $j = ($j + $S2[$i]) % $S2.Length
                        
                        $temp = $S2[$i];$S2[$i] = $S2[$j];$S2[$j] = $temp
                        $a = $data[$_]
                        $b = $S2[ $($S2[$i]+$S2[$j]) % $S2.Length ]
                        $outbuf[$_] = ($a -bxor $b)
                    }
                    return ,$outbuf
                } -PassThru

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    
    }
}


function Get-HBootKey
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([System.Object[]])] 
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [byte[]] $bootKey
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        $qwerty = [Text.Encoding]::ASCII.GetBytes("!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%`0")
        $num = [Text.Encoding]::ASCII.GetBytes("0123456789012345678901234567890123456789`0")

    }

    process {
        
        try {

            if (-not(Test-CurrentUserInGroup -groupName Administrators)) {
                
                throw 'Error: You must run as administrator !'

            }

            Copy-ProcessToken -process lsass -Verbose:$VerbosePreference | Out-Null
            
            $key = Get-Item HKLM:\SAM\SAM\Domains\Account
            if (-not ($key)) { return $null }
            
            [byte[]]$F = $key.GetValue("F")
            if (-not ($F)) { return $null }

            $rc4 = Get-RC4 -key ([Security.Cryptography.MD5]::Create().`
                ComputeHash($F[0x70..0x7F] + $qwerty + $bootKey + $num))
            
            return ,($rc4.encrypt($F[0x80..0x9F]))

        }
        catch { throw; }
        finally { 
            
            Restore-Self -Verbose:$VerbosePreference | Out-Null
            $ErrorActionPreference = $eaPreferencesBackup 
        
        }

    }
}


function Protect-DESCipher
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([System.Object[]])] 
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [byte[]] $data,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullOrEmpty()]
        [byte[]] $key
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
        
        [byte[]] $result = $null
    }

    process {
        
        try {

            $desProv = New-Object Security.Cryptography.DESCryptoServiceProvider

            $desProv.Mode = [Security.Cryptography.CipherMode]::ECB
            $desProv.Padding = [Security.Cryptography.PaddingMode]::None
            
            $desProv.Key = $key
            $desProv.IV = $key

            $result = ($desProv.CreateEncryptor()).TransformFinalBlock($data, 0, $data.Length)

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return ,$result

    }
}


function UnProtect-DESCipher
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([System.Object[]])] 
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [byte[]] $data,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullOrEmpty()]
        [byte[]] $key
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
        
        [byte[]] $result = $null
    }

    process {
        
        try {

            $desProv = New-Object Security.Cryptography.DESCryptoServiceProvider

            $desProv.Mode = [Security.Cryptography.CipherMode]::ECB
            $desProv.Padding = [Security.Cryptography.PaddingMode]::None
            
            $desProv.Key = $key
            $desProv.IV = $key

            $result = ($desProv.CreateDecryptor()).TransformFinalBlock($data, 0, $data.Length)

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return ,$result

    }
}


function Test-TlsConnection
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([PSObject])]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [string] $hostName,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullOrEmpty()]
        [int] $port
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        $out = New-Object PSObject

    }

    process {
        
        try {

            Write-Verbose ('Going to connect to: {0} | {1}' -f $hostName, $port)
            
            [System.Net.Sockets.TcpClient] $client = New-Object System.Net.Sockets.TcpClient $hostName, $port

            if ($client) {
            
                Write-Verbose ('Connected to: {0} | {1}' -f $hostName, $port)
    
                [System.IO.Stream] $netStream = $client.GetStream()
                [System.Net.Security.SslStream] $sslStream = New-Object System.Net.Security.SslStream $netStream, $false

                
                if ($sslStream) {
                    
                    Write-Verbose ('Start SSL session: {0}' -f $hostName)
                    $sslStream.AuthenticateAsClient($hostName)

                    if ($sslStream.IsAuthenticated) {

                        $out = New-Object PSObject

                        Add-Member -InputObject $out -MemberType NoteProperty -Name 'Protocol' `
                            -Value ($sslStream.SslProtocol)

                        Add-Member -InputObject $out -MemberType NoteProperty -Name 'Cipher algorithm' `
                            -Value ($sslStream.CipherAlgorithm)

                        Add-Member -InputObject $out -MemberType NoteProperty -Name 'Cipher strength' `
                            -Value ($sslStream.CipherStrength)

                        Add-Member -InputObject $out -MemberType NoteProperty -Name 'Hash algorithm' `
                            -Value ($sslStream.HashAlgorithm)

                        Add-Member -InputObject $out -MemberType NoteProperty -Name 'Hash strength' `
                            -Value ($sslStream.HashStrength)

                        Add-Member -InputObject $out -MemberType NoteProperty -Name 'Key exchange algorithm' `
                            -Value ($sslStream.KeyExchangeAlgorithm)

                        Add-Member -InputObject $out -MemberType NoteProperty -Name 'Key exchange strength' `
                            -Value ($sslStream.KeyExchangeStrength)
                        
                        Add-Member -InputObject $out -MemberType NoteProperty -Name 'Server certificate thumbprint' `
                            -Value (($sslStream.RemoteCertificate).GetCertHashString())

                        Add-Member -InputObject $out -MemberType NoteProperty -Name 'Server certificate subject' `
                            -Value (($sslStream.RemoteCertificate).Subject)

                        Add-Member -InputObject $out -MemberType NoteProperty -Name 'Server certificate issuer' `
                            -Value (($sslStream.RemoteCertificate).Issuer)

                        Add-Member -InputObject $out -MemberType NoteProperty -Name 'Server certificate not after' `
                            -Value ([DateTime]::Parse(($sslStream.RemoteCertificate).GetExpirationDateString()))

                        Add-Member -InputObject $out -MemberType NoteProperty -Name 'Server certificate not before' `
                            -Value ([DateTime]::Parse(($sslStream.RemoteCertificate).GetEffectiveDateString()))

                    }

                    $sslStream.Close()
                    $sslStream.Dispose()
                    $netStream.Close()
                    $netStream.Dispose()

                }

                Write-Verbose 'Close TLS session.'
                
                $client.Close()

            } else {
                
                throw 'Connection failed.'

            }

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return $out
    }

}


function New-AESKey
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([string])]
    param (
        [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [string] $key,
        [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullOrEmpty()]
        [string] $initVector
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
        $aesKey = $null
    }

    process {
        
        try {

            if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Generate new AES key")) {

                $aesManagedObj = New-Object System.Security.Cryptography.AesManaged 
            
                $aesManagedObj.Mode = [System.Security.Cryptography.CipherMode]::CBC 
                $aesManagedObj.Padding = [System.Security.Cryptography.PaddingMode]::Zeros 
                $aesManagedObj.BlockSize = 128 
                $aesManagedObj.KeySize = 256 
            
                if (-not ([string]::IsNullOrEmpty($initVector))) {
    
                    $aesManagedObj.IV = [System.Convert]::FromBase64String($initVector)
    
                }
    
                if (-not ([string]::IsNullOrEmpty($key))) {
            
                    $aesManagedObj.Key = [System.Convert]::FromBase64String($key)
            
                }

                $aesManagedObj.GenerateKey() 
            
                Write-Verbose ('Use AES key: {0}' -f [System.Convert]::ToBase64String($aesManagedObj.Key))
                Write-Verbose ('Use AES initial vector: {0}' -f [System.Convert]::ToBase64String($aesManagedObj.IV))

                $aesKey = [System.Convert]::ToBase64String($aesManagedObj.Key)

            }
        
        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }

        return $aesKey
    }
} 


function Protect-AES
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([string])]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [string] $aesKey,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullOrEmpty()]
        [string] $text
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        $bytes = [System.Text.Encoding]::UTF8.GetBytes($text)
    }

    process {
        
        try {

            $aesManagedObj = New-Object System.Security.Cryptography.AesManaged 
            
            $aesManagedObj.Mode = [System.Security.Cryptography.CipherMode]::CBC 
            $aesManagedObj.Padding = [System.Security.Cryptography.PaddingMode]::Zeros 
            $aesManagedObj.BlockSize = 128 
            $aesManagedObj.KeySize = 256 
            
            $aesManagedObj.Key = [System.Convert]::FromBase64String($aesKey)
            $encryptedData = $aesManagedObj.CreateEncryptor().TransformFinalBlock($bytes, 0, $bytes.Length)
            
            [byte[]] $fullData = $aesManagedObj.IV + $encryptedData 
            $aesManagedObj.Dispose() 

            return [System.Convert]::ToBase64String($fullData)

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    
    }
}


function UnProtect-AES
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    [OutputType([string])]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] 
        [ValidateNotNullOrEmpty()]
        [string] $aesKey,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNullOrEmpty()]
        [string] $encryptText
    )

    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

        $bytes = [System.Convert]::FromBase64String($encryptText)

    }

    process {
        
        try {

            $aesManagedObj = New-Object System.Security.Cryptography.AesManaged 
            
            $aesManagedObj.Mode = [System.Security.Cryptography.CipherMode]::CBC 
            $aesManagedObj.Padding = [System.Security.Cryptography.PaddingMode]::Zeros 
            $aesManagedObj.BlockSize = 128 
            $aesManagedObj.KeySize = 256 
            
            $aesManagedObj.Key = [System.Convert]::FromBase64String($aesKey)
            $aesManagedObj.IV = $bytes[0..15]
            
            $unencryptedData = $aesManagedObj.CreateDecryptor().TransformFinalBlock($bytes, 16, $bytes.Length - 16)
            $aesManagedObj.Dispose() 

            return [System.Text.Encoding]::UTF8.GetString($unencryptedData).Trim([char]0)

        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    
    }
}



if ((-not ($psISE.CurrentPowerShellTab.AddOnsMenu.Submenus | Where-Object DisplayName -eq 'Sign open scripts')) `
    -and ($host.name -eq 'Windows PowerShell ISE Host')) {

    $psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add('Sign open scripts', `
        { Add-SignatureInISE }, 'Ctrl+Shift+S') | Out-Null 

}
if ((-not ($psISE.CurrentPowerShellTab.AddOnsMenu.Submenus | Where-Object DisplayName -eq 'Clear ISE open file history')) `
    -and ($host.name -eq 'Windows PowerShell ISE Host')) {

    $psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add('Clear ISE open file history', `
        { Clear-ISEHistory }, 'Ctrl+Shift+C') | Out-Null 

}

# SIG # Begin signature block
# MIITCAYJKoZIhvcNAQcCoIIS+TCCEvUCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUwnUdz7Va/4f5/tyg9FYeU2xV
# /Wyggg5uMIID7jCCA1egAwIBAgIQfpPr+3zGTlnqS5p31Ab8OzANBgkqhkiG9w0B
# AQUFADCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIG
# A1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhh
# d3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcg
# Q0EwHhcNMTIxMjIxMDAwMDAwWhcNMjAxMjMwMjM1OTU5WjBeMQswCQYDVQQGEwJV
# UzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFu
# dGVjIFRpbWUgU3RhbXBpbmcgU2VydmljZXMgQ0EgLSBHMjCCASIwDQYJKoZIhvcN
# AQEBBQADggEPADCCAQoCggEBALGss0lUS5ccEgrYJXmRIlcqb9y4JsRDc2vCvy5Q
# WvsUwnaOQwElQ7Sh4kX06Ld7w3TMIte0lAAC903tv7S3RCRrzV9FO9FEzkMScxeC
# i2m0K8uZHqxyGyZNcR+xMd37UWECU6aq9UksBXhFpS+JzueZ5/6M4lc/PcaS3Er4
# ezPkeQr78HWIQZz/xQNRmarXbJ+TaYdlKYOFwmAUxMjJOxTawIHwHw103pIiq8r3
# +3R8J+b3Sht/p8OeLa6K6qbmqicWfWH3mHERvOJQoUvlXfrlDqcsn6plINPYlujI
# fKVOSET/GeJEB5IL12iEgF1qeGRFzWBGflTBE3zFefHJwXECAwEAAaOB+jCB9zAd
# BgNVHQ4EFgQUX5r1blzMzHSa1N197z/b7EyALt0wMgYIKwYBBQUHAQEEJjAkMCIG
# CCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMBIGA1UdEwEB/wQIMAYB
# Af8CAQAwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NybC50aGF3dGUuY29tL1Ro
# YXd0ZVRpbWVzdGFtcGluZ0NBLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCDAOBgNV
# HQ8BAf8EBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFtcC0y
# MDQ4LTEwDQYJKoZIhvcNAQEFBQADgYEAAwmbj3nvf1kwqu9otfrjCR27T4IGXTdf
# plKfFo3qHJIJRG71betYfDDo+WmNI3MLEm9Hqa45EfgqsZuwGsOO61mWAK3ODE2y
# 0DGmCFwqevzieh1XTKhlGOl5QGIllm7HxzdqgyEIjkHq3dlXPx13SYcqFgZepjhq
# IhKjURmDfrYwggSjMIIDi6ADAgECAhAOz/Q4yP6/NW4E2GqYGxpQMA0GCSqGSIb3
# DQEBBQUAMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3Jh
# dGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2aWNlcyBD
# QSAtIEcyMB4XDTEyMTAxODAwMDAwMFoXDTIwMTIyOTIzNTk1OVowYjELMAkGA1UE
# BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTQwMgYDVQQDEytT
# eW1hbnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIFNpZ25lciAtIEc0MIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomMLOUS4uyOnREm7Dv+h8GEKU5Ow
# mNutLA9KxW7/hjxTVQ8VzgQ/K/2plpbZvmF5C1vJTIZ25eBDSyKV7sIrQ8Gf2Gi0
# jkBP7oU4uRHFI/JkWPAVMm9OV6GuiKQC1yoezUvh3WPVF4kyW7BemVqonShQDhfu
# ltthO0VRHc8SVguSR/yrrvZmPUescHLnkudfzRC5xINklBm9JYDh6NIipdC6Anqh
# d5NbZcPuF3S8QYYq3AhMjJKMkS2ed0QfaNaodHfbDlsyi1aLM73ZY8hJnTrFxeoz
# C9Lxoxv0i77Zs1eLO94Ep3oisiSuLsdwxb5OgyYI+wu9qU+ZCOEQKHKqzQIDAQAB
# o4IBVzCCAVMwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAO
# BgNVHQ8BAf8EBAMCB4AwcwYIKwYBBQUHAQEEZzBlMCoGCCsGAQUFBzABhh5odHRw
# Oi8vdHMtb2NzcC53cy5zeW1hbnRlYy5jb20wNwYIKwYBBQUHMAKGK2h0dHA6Ly90
# cy1haWEud3Muc3ltYW50ZWMuY29tL3Rzcy1jYS1nMi5jZXIwPAYDVR0fBDUwMzAx
# oC+gLYYraHR0cDovL3RzLWNybC53cy5zeW1hbnRlYy5jb20vdHNzLWNhLWcyLmNy
# bDAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtMjAdBgNV
# HQ4EFgQURsZpow5KFB7VTNpSYxc/Xja8DeYwHwYDVR0jBBgwFoAUX5r1blzMzHSa
# 1N197z/b7EyALt0wDQYJKoZIhvcNAQEFBQADggEBAHg7tJEqAEzwj2IwN3ijhCcH
# bxiy3iXcoNSUA6qGTiWfmkADHN3O43nLIWgG2rYytG2/9CwmYzPkSWRtDebDZw73
# BaQ1bHyJFsbpst+y6d0gxnEPzZV03LZc3r03H0N45ni1zSgEIKOq8UvEiCmRDoDR
# EfzdXHZuT14ORUZBbg2w6jiasTraCXEQ/Bx5tIB7rGn0/Zy2DBYr8X9bCT2bW+IW
# yhOBbQAuOA2oKY8s4bL0WqkBrxWcLC9JG9siu8P+eJRRw4axgohd8D20UaF5Mysu
# e7ncIAkTcetqGVvP6KUwVyyJST+5z3/Jvz4iaGNTmr1pdKzFHTx/kuDDvBzYBHUw
# ggXRMIIEuaADAgECAgoRT/UOAAEAAAMeMA0GCSqGSIb3DQEBCwUAMEYxFTATBgoJ
# kiaJk/IsZAEZFgVsb2NhbDEUMBIGCgmSJomT8ixkARkWBHBvcnMxFzAVBgNVBAMT
# DnBvcnMtREItU1JWLUNBMB4XDTE2MDYwODEwMjE0N1oXDTE4MDYwODEwMjE0N1ow
# gY8xFTATBgoJkiaJk/IsZAEZFgVsb2NhbDEUMBIGCgmSJomT8ixkARkWBHBvcnMx
# DjAMBgNVBAsTBUZpcm1hMRQwEgYDVQQLEwtaYW1lc3RuYW5jaTEYMBYGA1UEAxMP
# TWlyb3NsYXYgTWVkdW5hMSAwHgYJKoZIhvcNAQkBFhFtZWR1bmFAcG9ycy1zdy5j
# ejCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKAJ2SOwQqiqquu9AGF4
# tuunr08Vk6vacuefkf3q/DXd/PVqBaH6cz2c28eNhPLyBswx54cig3YgdaKLk6Tf
# BCeAPeHcI8/jssvv0oXGM9Mg3glYY52/V9wYDL9MzYAECo6YrkNlXvdzOXTcBdHb
# AR1ZrJ4lsMyMfCtS9DGbS3y86Df1OyOXtF5XH22WQav1x9G19GGc4OrAlELCw7D8
# NNOTHIaqw1dyByLaXkRHIPv22lvT6/743NyXt32Ua83NfiopNt8kB0MMpXWz1TwK
# zkYF16i9eijHomLr5P6LMGiiL3zZtOtugKG/Xv7NN7r3w2r0rUiSHoAvUU/bvL9x
# GJkCAwEAAaOCAnUwggJxMD0GCSsGAQQBgjcVBwQwMC4GJisGAQQBgjcVCIOEoS6C
# 59UEgbWVNoTN0CWG6fw/WYTdhUuHtfBuAgFkAgEEMBMGA1UdJQQMMAoGCCsGAQUF
# BwMDMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYBBQUHAwMwDgYDVR0PAQH/BAQDAgeA
# MB0GA1UdDgQWBBSSR3HS5e4hljkWy68DsF9U0PH/nzAcBgNVHREEFTATgRFtZWR1
# bmFAcG9ycy1zdy5jejAfBgNVHSMEGDAWgBSIJ2tya3r5VVeYxnIK1HfF70muPTCB
# zQYDVR0fBIHFMIHCMIG/oIG8oIG5hoG2bGRhcDovLy9DTj1wb3JzLURCLVNSVi1D
# QSgxKSxDTj1EQi1TUlYsQ049Q0RQLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2Vz
# LENOPVNlcnZpY2VzLENOPUNvbmZpZ3VyYXRpb24sREM9cG9ycyxEQz1sb2NhbD9j
# ZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlz
# dHJpYnV0aW9uUG9pbnQwgb8GCCsGAQUFBwEBBIGyMIGvMIGsBggrBgEFBQcwAoaB
# n2xkYXA6Ly8vQ049cG9ycy1EQi1TUlYtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtl
# eSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENOPUNvbmZpZ3VyYXRpb24sREM9cG9y
# cyxEQz1sb2NhbD9jQUNlcnRpZmljYXRlP2Jhc2U/b2JqZWN0Q2xhc3M9Y2VydGlm
# aWNhdGlvbkF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAQEAPpWZxhqEoS/1ben6
# 2qHCZRm9KZgcXwrgroq5KIB9pBoBdIH1NQg5sjEcA9/yjkZEFdLkrmTfjEgezzMw
# AAo7OMDZ8ILcMS5fChQSCONp0dg42WMQDKMlWb8NM8wBLFyHSMhNy+/cP1F72kCP
# 8bwv4fwSUO3KCy5XKEeZVY+56mw7ZcmsfekeYZxUkQ//KZFcb6UX5jQumB8Gh9Ts
# PIPoIqsezdmqV1niHwL4N05EwnCvYgXPnXZ0auPEXgGrfJ+E+CJ4TvX3bbxldR+9
# 1rBu80qKf8/CWSjfQSKyuBlVDvccA+buy1t4f+gSTufDxcxRoCjlD32+uUfatrfr
# iwLNijGCBAQwggQAAgEBMFQwRjEVMBMGCgmSJomT8ixkARkWBWxvY2FsMRQwEgYK
# CZImiZPyLGQBGRYEcG9yczEXMBUGA1UEAxMOcG9ycy1EQi1TUlYtQ0ECChFP9Q4A
# AQAAAx4wCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJ
# KoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQB
# gjcCARUwIwYJKoZIhvcNAQkEMRYEFG6sYkPbpBlXWxL+Si0X1seG4Q1fMA0GCSqG
# SIb3DQEBAQUABIIBAIum8VV7bYneWKfOH2d0jN4NOW+z68eVRdRgj4SbJa3Qxz4p
# 0wInBkTbeWdYjC7+2ftJgRKClELpWwquA2edWtrXs1pozzc3JuFIOyaFvYYOXCDQ
# sGsEwMo9WFI5kH1DlzF0bkYevA+cQZMuluLqGeLHXsa+BZ3qM5dlRn/y40CyKm8I
# Z9N+dGYBwWAOxcCWqfOO39VLjyl0Lu6GoSEMmUCf1GyB0e30WxOdgenOnHjMn0ER
# PA+BYb2He1ScGaatjdtdLeZP7EBTONkXL+1lnqrYMjz2V+dpWJUUrz9XQpVANCFj
# yD/N3D2s2thuXVUcfvyhJPlxp68Rdy/6VcDj5TOhggILMIICBwYJKoZIhvcNAQkG
# MYIB+DCCAfQCAQEwcjBeMQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMg
# Q29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFudGVjIFRpbWUgU3RhbXBpbmcgU2Vy
# dmljZXMgQ0EgLSBHMgIQDs/0OMj+vzVuBNhqmBsaUDAJBgUrDgMCGgUAoF0wGAYJ
# KoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTYwNjA5MTEx
# OTAwWjAjBgkqhkiG9w0BCQQxFgQUcqh4RG7APeZRZq9udzmHSZy3RYMwDQYJKoZI
# hvcNAQEBBQAEggEAPH3Q94ZZWfLbv6IcmuHTxzUA+0Gt4Qseb/0Ng1ZPezQigj5d
# AnYmfBKnDvbdBIlbPrwBPl6tZ87cLZ9xyz+G4A8b0TX2J86Z+Z8shlQaMQ7CSPtT
# t8z+fz1KcB3k+eXCEoYgix2AZYKqqBHkp/4KYSO7+FDVHGPtyl0HZkFujmLhrfKF
# t1M3hkRhHOavGKNtQQu4pGHfAioYlrxEUQnYrnHeGNq+sstrSK1iu+UzkTX75grI
# 6YdEUr1kRgA7vlv/gUgeY1gH8BntqtNTbQ1AMhmQ2wBjWqup/TsnQKKnH/aEjRXt
# wR1YxJ5vAImm+cr2yKUfcOW5NhKbh8C9JjkHqA==
# SIG # End signature block