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 {
        
        Write-Verbose ('WMI Query: {0}' -f $fqdn)

        try {

            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 = 'meduna@pors-sw.cz'
    )
    
    begin {

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

    process {

        try {

            $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 {

        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 ($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 {

            $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 'SQL Anywhere installation directory not found'
    
                }

            } else {
                
                throw '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 }

# if ($outDE) {

# $outDE | Add-Member -MemberType NoteProperty -Name BeaverDEUserName -Value $userName -Force
# $outDE | Add-Member -MemberType NoteProperty -Name BeaverDEPassword -Value $password -Force
# }

        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 {

            $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 {

            $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 ($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 {

            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 ($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 {

            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 {

            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 {

            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 {

            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)]
    param()
 
    begin {

        $eaPreferencesBackup = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'

    }
    
    process {

        try {
        
            $currentPrincipal = [Security.Principal.WindowsIdentity]::GetCurrent($false)
            
            Write-Output ('Running under: {0} = {1}' -f $currentPrincipal.Name, `
                $currentPrincipal.User.Value)

            [Security.Principal.WindowsIdentity] $threadIdentityOnlyIfImpersonating = $null
            $threadIdentityOnlyIfImpersonating = [Security.Principal.WindowsIdentity]::GetCurrent($true)
            Write-Output ('This thread is impersonating: {0}' -f ([bool] $threadIdentityOnlyIfImpersonating))


            $runningIdentity = [Security.Principal.WindowsIdentity]::GetCurrent($false)
            Write-Output ('Running identity: name = {0} | system = {1} | SID = {2}{3}' -f `
                $runningIdentity.Name, $runningIdentity.IsSystem, $runningIdentity.User, [Environment]::NewLine)
            
            foreach ($oneSID in ($runningIdentity.Groups | Select-Object -Expand Value )) {
            
                Write-Output ('>> SID: {0} | {1}' -f $oneSID, (Convert-SidToUserName $oneSid))

            }

            $threadIdentity = [System.Threading.Thread]::CurrentPrincipal.Identity
            
            Write-Output ('{3}Current thread principal type: {0} | isAuthenticated = {1} | name = {2}' -f `
                $threadIdentity.GetType().Name, $threadIdentity.IsAuthenticated, $threadIdentity.Name, `
                    [Environment]::NewLine)
            
        }
        catch { throw; }
        finally { $ErrorActionPreference = $eaPreferencesBackup }
    }
}


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 {

            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 {
            
            $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
            }

            if ($enableHistory) {
            
                Write-Verbose ('Enabling the windows event log for the task scheduler')

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

    }
}


function Set-WindowsEventLogForTaskScheduler
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false)] 
        [ValidateNotNull()]
        [bool] $enable
    )
    
    process {
        
        try{
            
            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 }

    }
}


# SIG # Begin signature block
# MIITBQYJKoZIhvcNAQcCoIIS9jCCEvICAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUviEAXDShScjNGoqflxwAam7z
# GWaggg5rMIID7jCCA1egAwIBAgIQfpPr+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
# ggXOMIIEtqADAgECAgocYLQHAAAAAAMMMA0GCSqGSIb3DQEBCwUAMEYxFTATBgoJ
# kiaJk/IsZAEZFgVsb2NhbDEUMBIGCgmSJomT8ixkARkWBHBvcnMxFzAVBgNVBAMT
# DnBvcnMtREItU1JWLUNBMB4XDTE2MDQyNjExNTUwOVoXDTE4MDQyNjExNTUwOVow
# gY8xFTATBgoJkiaJk/IsZAEZFgVsb2NhbDEUMBIGCgmSJomT8ixkARkWBHBvcnMx
# DjAMBgNVBAsTBUZpcm1hMRQwEgYDVQQLEwtaYW1lc3RuYW5jaTEYMBYGA1UEAxMP
# TWlyb3NsYXYgTWVkdW5hMSAwHgYJKoZIhvcNAQkBFhFtZWR1bmFAcG9ycy1zdy5j
# ejCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALetZGc/1Qa91Sj5NcbI
# FjpqMfJMwEMnvmOzFWqA6+6K9X0732GcgKtyXqw0LitY1XZDEppxBTlivPHXVVsb
# Y1cLqWsdICAklMdq9JWybVDSUiPhog5QIm107hx/xBOqdAGoksToFoUOOkrwB6yt
# OZXbpiGSs4j7n64Z2zn5dZU5IafaZ4Z2OizLnjaNS+44h3v1FWKnEAVweynu0KSq
# DtOi5cXEvbvFIK+/Ey0dzcZXY2vK0INxOlVvRtmJOv+0bRRFv6/Z1GHD8rHsL0oJ
# yP5nquvHD3o0ikNpBcAXqEMl1emNxY0Qg4EvZLgOwKbxuE4P2dFulD56pgIISsC1
# XLMCAwEAAaOCAnIwggJuMD0GCSsGAQQBgjcVBwQwMC4GJisGAQQBgjcVCIOEoS6C
# 59UEgbWVNoTN0CWG6fw/WYTdhUuHtfBuAgFkAgEEMBMGA1UdJQQMMAoGCCsGAQUF
# BwMDMA4GA1UdDwEB/wQEAwIHgDAbBgkrBgEEAYI3FQoEDjAMMAoGCCsGAQUFBwMD
# MB0GA1UdDgQWBBTvT3cMkc1mrLf8dyTScnKZkUbvAjAcBgNVHREEFTATgRFtZWR1
# bmFAcG9ycy1zdy5jejAfBgNVHSMEGDAWgBRbhvm5YOeWkaF1Awimwcg3cfuI/zCB
# ygYDVR0fBIHCMIG/MIG8oIG5oIG2hoGzbGRhcDovLy9DTj1wb3JzLURCLVNSVi1D
# QSxDTj1EQi1TUlYsQ049Q0RQLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENO
# PVNlcnZpY2VzLENOPUNvbmZpZ3VyYXRpb24sREM9cG9ycyxEQz1sb2NhbD9jZXJ0
# aWZpY2F0ZVJldm9jYXRpb25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJp
# YnV0aW9uUG9pbnQwgb8GCCsGAQUFBwEBBIGyMIGvMIGsBggrBgEFBQcwAoaBn2xk
# YXA6Ly8vQ049cG9ycy1EQi1TUlYtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUy
# MFNlcnZpY2VzLENOPVNlcnZpY2VzLENOPUNvbmZpZ3VyYXRpb24sREM9cG9ycyxE
# Qz1sb2NhbD9jQUNlcnRpZmljYXRlP2Jhc2U/b2JqZWN0Q2xhc3M9Y2VydGlmaWNh
# dGlvbkF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAQEAl1Q1umCNlSbkV6pRaweZ
# OBYZT2h6chfXaXtNeaELolOmXjzupXeGkykqmuytvKMC7TOZqdT/O1eccwoiBUlj
# TTLK7ZKFXSI3ECwAYBpxCqW1pk88pK29jg7htvmPcOWETGWceTASlVnSD8s1bs/9
# 8GYaTqUlLMas47pARTekGdOZV3pSTLJzqC88/W+J3NOQiVTq88vpCcEoobsLYC4x
# 2crHSxJCS3dYSsFV6l/Vs0ncOpLxAS3AJQ4yPKNu/EC2mXAnWJ/8DaJ8rtadBALL
# UrCP/dfKEHbPwXWjxgU+t/LKzy9nf4ixXubmg0QR1z7eHgqQiR4AWkKVV6GFQIIf
# fjGCBAQwggQAAgEBMFQwRjEVMBMGCgmSJomT8ixkARkWBWxvY2FsMRQwEgYKCZIm
# iZPyLGQBGRYEcG9yczEXMBUGA1UEAxMOcG9ycy1EQi1TUlYtQ0ECChxgtAcAAAAA
# AwwwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZI
# hvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcC
# ARUwIwYJKoZIhvcNAQkEMRYEFMK/ZbCVg1S40UUMHFsFfS6tDO6pMA0GCSqGSIb3
# DQEBAQUABIIBAIDt57bXOuw8jqc2aE6W2dyi2I30fTDeTYJd9Uw4DdxxB4nJ3eIs
# Z912FutBb7nK4jiwSQq9EZZRVmAxU+aHA0/DKL/H03/Gb0Co0zMP6ZZvH8GtcnjQ
# hC82n1H3uieWWu6AmieHc5L5OsHqQpDWibdO4JyHNDxc1XyWsR2pIpyMNXzWJuuh
# Q6/h3G3nxEMhknqnCBIBYKXlSRFtErly/iynShmjW8ME8fwJUJb3VIPqm64P0l2o
# CRJH76UyVDGAphxm+pHljSJ+r3/L+jgBzjSzSCghIVz3ct17VvI7bvLcHoE57ra1
# SRj/E/OnMEDJ/5IwiNGB3U4CR40wiHQtr5ihggILMIICBwYJKoZIhvcNAQkGMYIB
# +DCCAfQCAQEwcjBeMQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29y
# cG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFudGVjIFRpbWUgU3RhbXBpbmcgU2Vydmlj
# ZXMgQ0EgLSBHMgIQDs/0OMj+vzVuBNhqmBsaUDAJBgUrDgMCGgUAoF0wGAYJKoZI
# hvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTYwNTA2MTEzMDU3
# WjAjBgkqhkiG9w0BCQQxFgQUkez09DgjYvhG0RX1SAB5RGL5RMcwDQYJKoZIhvcN
# AQEBBQAEggEAkBN72Fari7NCLhPQeHi0YaC+K5ANKl4PvtarDKg0B6mO41PFkxsa
# DktNnL7fuGzfSEas7AcsvhNqLqY+S9bA0KoqnBLHhyJ3jDBL3XLwLG7A5agNewY2
# yf8xH9v1VfIYfy8NNi9/t2QksVK+A4i1ZQnaYRz+HtH4get4q8Xl8DKPbDu8tudL
# Qrzyay+CAwJN3et8z1Mfv4KTbSkxfvRXRNQ9GEJk2DgT9D6BJyMCuLQQ4BIH33JD
# JYBtcy4HVTinQe5XS3RxmfHAepp3LHLQddsGP6ifJeS2ZjjIsAAueJrO9ApiCCD5
# PBtFh7KSggrp2LfS+XjFjJbw7n1z/8+GYg==
# SIG # End signature block