ozo-ad-lab-customize-isos.ps1

#Requires -Modules @{ModuleName="OZO";ModuleVersion="1.5.1"},OZOLogger -Version 5.1

<#PSScriptInfo
    .VERSION 0.0.1
    .GUID 107291d5-0444-4f57-b173-8578b222576b
    .AUTHOR Andy Lievertz <alievertz@sonichealthcareusa.com>
    .COMPANYNAME One Zero One
    .COPYRIGHT This script is released under the terms of the GNU General Public License ("GPL") version 2.0.
    .TAGS
    .LICENSEURI https://github.com/onezeroone-dev/OZO-AD-Lab-Customize-ISOs/blob/main/LICENSE
    .PROJECTURI https://github.com/onezeroone-dev/OZO-AD-Lab-Customize-ISOs
    .ICONURI
    .EXTERNALMODULEDEPENDENCIES
    .REQUIREDSCRIPTS
    .EXTERNALSCRIPTDEPENDENCIES
    .RELEASENOTES https://github.com/onezeroone-dev/OZO-AD-Lab-Customize-ISOs/blob/main/CHANGELOG.md
#>


<#
    .SYNOPSIS
    See description.
    .DESCRIPTION
    Leverages resources from the One Zero One AD Lab release to customize AlmaLinux and Microsoft installer ISOs.
    .PARAMETER OZOADLabDir
    Location of the "ozo-ad-lab" directory. Defaults to $Env:SystemDrive\ozo-ad-lab.
    .LINK
    https://github.com/onezeroone-dev/OZO-AD-Lab-Customize-ISOs/blob/main/README.md
#>


# PARAMETERS
[CmdletBinding(SupportsShouldProcess = $true)]
param(
    [Parameter(Mandatory=$false,HelpMessage="Path to the OZO AD Lab directory")][String]$OZOADLabDir = (Join-Path -Path $Env:SystemDrive -ChildPath "ozo-ad-lab")
)

# CLASSES
Class OZOADLCIMain {
    # PROPERTIES: Strings
    [String] $downloadsDirectory = $null
    [String] $ozoADLabDirectory  = $null
    # PROPERTIES: PSCustomObjects
    [PSCustomObject] $ISO    = $null
    [PSCustomObject] $Logger = $null
    # METHODS
    # Constructor method
    OZOADLCIMain($OZOADLabDir) {
        # Set properties
        $this.downloadsDirectory = (Join-Path -Path $Env:USERPROFILE -ChildPath "Downloads")
        $this.ozoADLabDirectory  = $OZOADLabDir
        # Create a logger object
        $this.Logger = (New-OZOLogger)
        # Log a process start message
        $this.Logger.Write("Process starting.","Information")
         # Router
         $this.Logger.Write("Processing the Router ISO.","Information")
         $this.ISO = [OZOADLCIISO]::new(
             "Router", #Build
             "AlmaLinux-9-5-x86_64-dvd", #CustomISOLabel
             (Join-Path -Path $this.ozoADLabDirectory -ChildPath "Linux"), #CustomISOLinuxDir
             (Join-Path -Path $this.downloadsDirectory -ChildPath "OZO-AD-Lab-Router.iso"), #CustomISOMOvePath
             (Join-Path -Path $this.ozoADLabDirectory -ChildPath "OZO-AD-Lab-Router.iso"), #CustomISOOutputPath
             (Join-Path -Path $this.ozoADLabDirectory -ChildPath "ISO\almalinux-boot.iso") #SourceISOPath
         )
         $this.Logger.Write(("Results: " + ($this.ISO.Messages -Join("`r`n"))),"Information")
        # Client
        $this.Logger.Write("Processing the Client ISO.","Information")
        $this.ISO = [OZOADLCIISO]::new(
            "Client", #Build
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "Builds\Client"), #CustomISOBUildDir
            "OZO-AD-Lab-Client", #CustomISOLabel
            (Join-Path -Path $this.downloadsDirectory -ChildPath "OZO-AD-Lab-Client.iso"), #CustomISOMovePath
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "Mount"), #CustomISOMountDir
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "ISO\OZO-AD-Lab-Client.iso"), #CustomISOOutputPath
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "WIM\Windows 11 Enterprise"), #CustomWIMDir
            1, #SourceIndex
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "ISO\microsoft-windows-11-enterprise-evaluation.iso"), #SourceISOPath
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "ISO\microsoft-windows-11-laof.iso") #SourceLAoFISOPath
        )
        $this.Logger.Write(("Results: " + ($this.ISO.Messages -Join("`r`n"))),"Information")
        # DC
        $this.Logger.Write("Processing the DC ISO.","Information")
        $this.ISO = [OZOADLCIISO]::new(
            "DC",# Build
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "Builds\DC"), #CustomISOBuildDir
            "OZO-AD-Lab-DC", #CustomISOLabel
            (Join-Path -Path $this.downloadsDirectory -ChildPath "OZO-AD-Lab-DC.iso"), #CustomISOMovePath
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "Mount"), #CustomISOMountDir
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "ISO\OZO-AD-Lab-DC.iso"), #CustomISOOutputPath
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "WIM\Windows Server 2022"), #CustomWIMDir
            2, #SourceIndex
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "ISO\microsoft-windows-server-2022-evaluation.iso"), #SourceISOPath
            $null #SourceLAoFISOPath
        )
        $this.Logger.Write(("Results: " + ($this.ISO.Messages -Join("`r`n"))),"Information")
        # Server
        $this.Logger.Write("Processing the Server ISO.","Information")
        $this.ISO = [OZOADLCIISO]::new(
            "Server",# Build
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "Builds\Server"), #CustomISOBuildDir
            "OZO-AD-Lab-DC", #CustomISOLabel
            (Join-Path -Path $this.downloadsDirectory -ChildPath "OZO-AD-Lab-Server.iso"), #CustomISOMovePath
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "Mount"), #CustomISOMountDir
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "ISO\OZO-AD-Lab-Server.iso"), #CustomISOOutputPath
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "WIM\Windows Server 2022"), #CustomWIMDir
            2, #SourceIndex
            (Join-Path -Path $this.ozoADLabDirectory -ChildPath "ISO\microsoft-windows-server-2022-evaluation.iso"), #SourceISOPath
            $null #SourceLAoFISOPath
        )
        $this.Logger.Write(("Results: " + ($this.ISO.Messages -Join("`r`n"))),"Information")
        # Log a process end message
        $this.Logger.Write("Process complete.","Information")
    }
}

Class OZOADLCIISO {
    # PROPERTIES: Booleans, Int16s, Strings
    [Boolean] $Proceed             = $true
    [Boolean] $Validates           = $true
    [Int16]   $sourceIndex         = $null
    [String]  $Build               = $null
    [String]  $customISOBuildDir   = $null
    [String]  $customISOLabel      = $null
    [String]  $customISOLinuxDir   = $null
    [String]  $customISOMovePath   = $null
    [String]  $customISOMountDir   = $null
    [String]  $customISOOutputPath = $null
    [String]  $customWIMDir        = $null
    [String]  $efisysBinPath       = $null
    [String]  $mountDrive          = $null
    [String]  $oscdimgExePath      = $null
    [String]  $sourceISOPath       = $null
    [String]  $sourceLAoFISOPath   = $null
    # PROPERTIES: Lists
    [System.Collections.Generic.List[String]] $Messages = @()
    # METHODS
    # Constructor method - Linux overload
    OZOADLCIISO($Build,$CustomISOLabel,$CustomISOLinuxDir,$CustomISOMovePath,$CustomISOOutputPath,$SourceISOPath) {
        # Set properties
        $this.Build               = $Build
        $this.customISOLabel      = $CustomISOLabel
        $this.customISOLinuxDir   = $CustomISOLinuxDir
        $this.customISOMovePath   = $CustomISOMovePath
        $this.customISOOutputPath = $CustomISOOutputPath
        $this.oscdimgExePath      = (Join-Path -Path ${Env:ProgramFiles(x86)} -ChildPath "Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe")
        $this.sourceISOPath       = $SourceISOPath
        # Determine if ISO validates
        If ($this.ValidateLinISO() -eq $true) {
            # ISO validates; call ProcessLinISO to create the custom ISO
            $this.ProcessLinISO()
        }
    }
    # Constructor method - Windows overload
    OZOADLCIISO($Build,$CustomISOBuildDir,$CustomISOLabel,$CustomISOMovePath,$CustomISOMountDir,$CustomISOOutputPath,$CustomWIMDir,$SourceIndex,$SourceISOPath,$SourceLAoFISOPath) {
        # Set properties
        $this.Build               = $Build
        $this.customISOBuildDir   = $CustomISOBuildDir
        $this.customISOLabel      = $CustomISOLabel
        $this.customISOMovePath   = $CustomISOMovePath
        $this.customISOMountDir   = $CustomISOMountDir
        $this.customISOOutputPath = $CustomISOOutputPath
        $this.customWIMDir        = $CustomWIMDir
        $this.efisysBinPath       = (Join-Path -Path $this.customISOBuildDir -ChildPath "efi\microsoft\boot\efisys.bin")
        $this.oscdimgExePath      = (Join-Path -Path ${Env:ProgramFiles(x86)} -ChildPath "Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe")
        $this.sourceIndex         = $SourceIndex
        $this.sourceISOPath       = $SourceISOPath
        $this.sourceLAoFISOPath   = $SourceLAoFISOPath
        # Determine if ISO validates
        IF ($this.ValidateWinISO() -eq $true) {
            # ISO validates; call ProcessWinISO to create the custom ISO
            $this.ProcessWinISO()
        }
    }
    # Validate Linux ISO method
    Hidden [Boolean] ValidateLinISO() {
        # Control variable
        [Boolean] $Return = $true
        # Check if WSL Debian is installed
        If ([Boolean](wsl -l | Where-Object {$_.Replace("`0","") -Match '^Debian'}) -eq $false) {
            # Did not find WSL Debian
            $this.Messages.Add("Missing WSL Debian. Please see https://onezeroone.dev/active-directory-lab-part-ii-customization-prerequisites/ for more information.")
            $Return = $false
        }
        # Determine if custom ISO already exists in Downloads directory
        If ((Test-Path -Path $this.customISOMovePath) -eq $true) {
            # Custom ISO already exists
            $this.Messages.Add(("Found " + $this.customISOMovePath + "; skipping."))
            $Return = $false
        }
        # Determine if custom ISO already exists in OZO AD Lab directory
        If ((Test-Path -Path $this.customISOOutputPath) -eq $true) {
            # Custom ISO already exists
            $this.Messages.Add(("Found " + $this.customISOOutputPath + "; skipping."))
            $Return = $false
        }
        # Determine that source ISO does not exist
        If ((Test-Path -Path $this.sourceISOPath) -eq $false) {
            # Source ISO does not exist
            $this.Messages.Add(("Missing " + $this.sourceISOPath + "; skipping."))
            $Return = $false
        }
        # Return
        return $Return
    }
    # Process Linux ISO method
    Hidden [Void] ProcessLinISO() {
        # Local variables
        [String] $wslResult         = $null
        [String] $wslKickstartPath  = (New-OZOWSLPathFromWindowsPath -WindowsPath (Join-Path -Path $this.customISOLinuxDir -ChildPath "ozo-ad-lab-router-ks.cfg"))
        [String] $wslSourceISOPath  = (New-OZOWSLPathFromWindowsPath -WindowsPath $this.sourceISOPath)
        [String] $wslTargetISOPath  = (New-OZOWSLPathFromWindowsPath -WindowsPath $this.customISOOutputPath)
        [String] $wslTargetISOLabel = $this.customISOLabel
        [String] $wslScriptPath     = (New-OZOWSLPathFromWindowsPath -WindowsPath (Join-Path -Path $this.customISOLinuxDir -ChildPath "ozo-create-router-iso.sh"))
        # Determine if this ISO is valid
        If ($this.Validates -eq $true) {
            # ISO is valid; use WSL Debian to call the AlmaLinux ISO customization script
            $wslResult = (& wsl --distribution "Debian" --user root KICKSTART_PATH="$wslKickstartPath" SOURCE_ISO_PATH="$wslSourceISOPath" TARGET_ISO_PATH="$wslTargetISOPath" TARGET_ISO_LABEL="$wslTargetISOLabel" $wslScriptPath)
            If ($wslResult -eq "TRUE") {
                # Move the ISO
                If ($this.MoveISO() -eq $true) {
                    # Moved ISO; report success
                    $this.Messages.Add("Success")
                }
            } Else {
                $this.Messages.Add(("Error creating " + $this.customISOOutputPath))
            }
        }
    }
    # Validate Windows ISO method
    Hidden [Boolean] ValidateWinISO() {
        # Control variable
        [Boolean] $Return = $true
        # Determine if oscdimg.exe is not present
        If ((Test-Path -Path $this.oscdimgExePath) -eq $false) {
            # Did not find oscdimg.exe; report
            $this.Logger.Write("Missing oscdimg.exe. Please see https://onezeroone.dev/active-directory-lab-part-ii-customization-prerequisites/ for more information.","Error")
            $Return = $false
        }
        # Determine if the custom ISO already exists in Downloads directory
        If ((Test-Path -Path $this.customISOMovePath) -eq $true) {
            # Custom ISO exists in the Downloads directory
            $this.Messages.Add(("Found " + $this.customISOMovePath + "; skipping."))
            $Return = $false
        }
        # Determine if the custom ISO already exists in the OZO AD Lab directory
        If ((Test-Path -Path $this.customISOOutputPath) -eq $true) {
            # Custom ISO exists in the OZO AD Lab directory
            $this.Messages.Add(("Found " + $this.customISOOutputPath + "; skipping."))
            $Return = $false
        }
        # Determine if the source ISO does not exist
        If ((Test-Path -Path $this.sourceISOPath) -eq $false) {
            # Source ISO does not exist
            $this.Messages.Add(("Missing " + $this.sourceISOPath + "; skipping."))
            $Return = $false
        }
        # Determine that the build directory exists
        If ((Test-Path -Path $this.customISOBuildDir) -eq $false) {
            $this.Messages.Add("Build directory does not exist; skipping.")
            $Return = $false
        }
        # Switch for build-specific criteria
        Switch($this.Build) {
            "Client" {
                # Determine if the Windows 11 Enterprise Languages & Optional Features ISO does not exist
                If ((Test-Path -Path $this.sourceLAoFISOPath) -eq $false) {
                    # LAoF ISO does not exist
                    $this.Messages.Add(("Missing " + $this.sourceLAoFISOPath + "; skipping."))
                    $Return = $false
                }
                break
            }
            "DC" {
                break
            }
            "Server" {
                break
            }
            default {
                break
            }
        }
        # Return
        return $Return
    }
    # Process Windows ISO method
    Hidden [Void] ProcessWinISO() {
        # Mount the source ISO
        If ($this.MountISO($this.sourceISOPath) -eq $true) {
            # Source ISO mount; copy the contents to the build directory
            If ($this.CopyISO((Join-Path -Path $this.mountDrive -ChildPath "*"),$this.customISOBuildDir) -eq $true) {
                # Contents copied; dismount the ISO
                If ($this.DismountISO($this.sourceISOPath) -eq $true) {
                    # ISO dismounted; move the install WIM
                    If ($this.MoveWIM((Join-Path -Path $this.customISOBuildDir -ChildPath "sources\install.wim"),$this.customWIMDir) -eq $true) {
                        # Moved WIM; export the desired Index back to the build directory
                        If ($this.ExportImage((Join-Path -Path $this.customWIMDir -ChildPath "install.wim"),$this.sourceIndex,(Join-Path -Path $this.customISOBuildDir -ChildPath "sources\install.wim")) -eq $true) {
                            # Exported the Index back to install.wim; switch for build-specific steps
                            Switch($this.Build) {
                                "Client" {
                                    # Mount the WIM for customization
                                    If ($this.MountWIM((Join-Path -Path $this.customISOBuildDir -ChildPath "sources\install.wim"),1,$this.customISOMountDir) -eq $true) {
                                        # Mounted the WIM for customization; mount the LAoF ISO
                                        If ($this.MountISO($this.SourceLAoFISOPath) -eq $true) {
                                            # LAoF ISO mounted; Install RSAT
                                            If ($this.InstallRSAT() -eq $true) {
                                                # RSAT installation succeeded
                                                If ($this.DismountWIM($this.customISOMountDir,$true)) {
                                                    If ($this.DismountISO($this.SourceLAoFISOPath) -eq $true) {
                                                        # Create the Windows ISO
                                                        If ($this.CreateWinISO() -eq $true) {
                                                            # Move the ISO
                                                            If ($this.MoveISO() -eq $true) {
                                                                # Moved ISO; report success
                                                                $this.Messages.Add("Success")
                                                            }
                                                        }
                                                    }
                                                }
                                            } Else {
                                                # RSAT installation failed; dismount the WIM (abandoning changes) and the ISO
                                                $this.DismountWIM($this.customISOMountDir,$false)
                                                $this.DismountISO($this.SourceLAoFISOPath)
                                            }
                                        }
                                    }
                                    break
                                }
                                "DC" {
                                    # Create the Windows ISO
                                    If ($this.CreateWinISO() -eq $true) {
                                        # Move the ISO
                                        If ($this.MoveISO() -eq $true) {
                                            # Moved ISO; report success
                                            $this.Messages.Add("Success")
                                        }
                                    }
                                    break
                                }
                                "Server" {
                                    # Create the Windows ISO
                                    If ($this.CreateWinISO() -eq $true) {
                                        # Move the ISO
                                        If ($this.MoveISO() -eq $true) {
                                            # Moved ISO; report success
                                            $this.Messages.Add("Success")
                                        }
                                    }
                                    break
                                }
                                default {
                                    $this.Messages.Add(("Unhandled build: " + $this.Build))
                                    break
                                }
                            }
                        }
                    }
                }
            } Else {
                # Contents not copied; dismount the ISO
                $this.DismountISO($this.sourceISOPath)
            }
        }
    }
    # Copy ISO method
    Hidden [Boolean] CopyISO($SourcePath,$TargetPath) {
        # Control variable
        [Boolean] $Return = $true
        # Try to
        Try {
            Copy-Item -Force -Recurse -Path $SourcePath -Destination $TargetPath -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add("Failed to copy source ISO; skipping.")
            $Return = $false
        }
        # Return
        return $Return
    }
    # Move WIM method
    Hidden [Boolean] MoveWIM($SourcePath,$TargetPath) {
        # Control variable
        [Boolean] $Return = $true
        # Try to move the WIM
        Try {
            Move-Item -Force -Path $SourcePath -Destination $TargetPath -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Failed to move " + $SourcePath + " to " + $TargetPath + "; skipping."))
            $Return = $false
        }
        # Return
        return $Return
    }
    # Export Image method
    Hidden [Boolean] ExportImage($ImagePath,$ImageIndex,$ExportedWIMPath) {
        # Control variable
        [Boolean] $Return = $true
        # Try to export the index
        Try {
            Export-WindowsImage -SourceImagePath $ImagePath -SourceIndex $ImageIndex -DestinationImagePath $ExportedWIMPath -ErrorAction Stop
            # Success;
        } Catch {
            # Failure
            $this.Messages.Add(("Failed to export Index " + $ImageIndex.ToString() + " from " + $ImagePath + "; skipping."))
            $Return = $false
        }
        # Return
        return $Return
    }
    # Install RSAT method
    Hidden [Boolean] InstallRSAT() {
        # Control variable
        [Boolean] $Return = $true
        # Try to install RSAT
        Try {
            [String] $rsatSource = (Join-Path -Path $this.mountDrive -ChildPath "LanguagesAndOptionalFeatures")
            Get-WindowsCapability -Online -Name "RSAT*" -Source $rsatSource -ErrorAction Stop | Add-WindowsCapability -Source $rsatSource -Path $this.customISOMountDir -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add("Failed to install RSAT; skipping.")
            $Return = $false
        }
        # Return
        return $Return
    }
    # Mount WIM method
    Hidden [Boolean] MountWIM($ImagePath,$ImageIndex,$MountDir) {
        # Control variable
        [Boolean] $Return = $true
        # Try to mount the WIM
        Try {
            Mount-WindowsImage -ImagePath $ImagePath -Index $ImageIndex -Path $MountDir -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Failed to mount " + $ImagePath + "; skipping."))
            $Return = $false
        }
        # Return
        return $Return
    }
    # Dismount WIM method
    Hidden [Boolean] DismountWIM($MountDir,$Save) {
        # Control variable
        [Boolean] $Return = $true
        # Try to dismount the WIM
        Try {
            # Determine if the WIM will be saved
            If ($Save -eq $true) {
                # The WIM will be saved
                Dismount-WindowsImage -Path $MountDir -Save -ErrorAction Stop
            } Else {
                # The WIM will not be saved
                Dismount-WindowsImage -Path $MountDir -Discard -ErrorAction Stop
            }
        } Catch {
            # Failure
            $this.Messages.Add(("Failed to dismount " + $MountDir + "; skipping."))
            $Return = $false
        }
        # Return
        return $Return
    }
    # Mount ISO method
    Hidden [Boolean] MountISO($ISOPath) {
        # Control variable
        [Boolean] $Return = $true
        # Try to mount the ISO
        Try {
            Mount-DiskImage -ImagePath $ISOPath -ErrorAction Stop
            # Success; get the drive letter
            $this.mountDrive = ((Get-DiskImage -ImagePath $ISOPath -ErrorAction Stop | Get-Volume -ErrorAction Stop).DriveLetter + ":")
        } Catch {
            # Failure
            $this.Messages.Add(("Failed to mount " + $ISOPath + "; skipping."))
            $Return = $false
        }
        # Return
        return $Return
    }
    # Dismount ISO method
    Hidden [Boolean] DismountISO($ISOPath) {
        # Control variable
        [Boolean] $Return = $true
        # Try to dismount the ISO
        Try {
            Dismount-DiskImage -ImagePath $ISOPath -ErrorAction Stop
            # Success;
        } Catch {
            # Failure
            $this.Messages.Add(("Failed to dismount " + $ISOPath + "; skipping."))
            $Return = $false
        }
        # Return
        return $Return
    }
    # Create Windows ISO method
    Hidden [Boolean] CreateWinISO() {
        # Control variable
        [Boolean] $Return = $true
        # Local variables
        [String] $labl = $this.customISOLabel
        [String] $esbp = $this.efisysBinPath
        [String] $cibd = $this.customISOBuildDir
        [String] $ciop = $this.customISOOutputPath
        # Try to create the ISO
        Try {
            Start-Process -Wait -NoNewWindow -FilePath $this.oscdimgExePath -ArgumentList "-u2 -udfver102 -t -l$labl -b$esbp $cibd $ciop" -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add(("Failed to create the custom ISO; skipping. Error message is: " + $_))
            $Return = $false
        }
        # Return
        return $Return
    }
    # Move ISO method
    Hidden [Boolean] MoveISO() {
        # Control variable
        [Boolean] $Return = $true
        # Try to move the ISO
        Try {
            Move-Item -Path $this.customISOOutputPath -Destination $this.customISOMovePath -ErrorAction Stop
            Unblock-File -Path $this.customISOMovePath -ErrorAction Stop
            # Success
        } Catch {
            # Failure
            $this.Messages.Add("Failed to move the custom ISO to the Downloads directory; skipping.")
            $Return = $false
        }
        # Return
        return $Return
    }
}

# FUNCTIONS
Function New-OZOWSLPathFromWindowsPath {
    param(
        [Parameter(Mandatory=$true,HelpMessage="The Windows path to convert")][String]$WindowsPath
    )
    # Local variables
    return ($WindowsPath.Replace("\","/")).Replace($Env:SystemDrive,("/mnt/" + ($Env:SystemDrive.Split(":"))[0].ToLower()))
}

# Create a Main object
[OZOADLCIMain]::new($OZOADLabDir) | Out-Null

# SIG # Begin signature block
# MIIfcgYJKoZIhvcNAQcCoIIfYzCCH18CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCeLmFc5O6nZ2Rd
# dh2bXxuA1AfZreMLD6gz5lhPXe+0VaCCDPgwggZyMIIEWqADAgECAghkM1HTxzif
# CDANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx
# EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8G
# A1UEAwwoU1NMLmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTAe
# Fw0xNjA2MjQyMDQ0MzBaFw0zMTA2MjQyMDQ0MzBaMHgxCzAJBgNVBAYTAlVTMQ4w
# DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjERMA8GA1UECgwIU1NMIENv
# cnAxNDAyBgNVBAMMK1NTTC5jb20gQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBD
# QSBSU0EgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCfgxNzqrDG
# bSHL24t6h3TQcdyOl3Ka5LuINLTdgAPGL0WkdJq/Hg9Q6p5tePOf+lEmqT2d0bKU
# Vz77OYkbkStW72fL5gvjDjmMxjX0jD3dJekBrBdCfVgWQNz51ShEHZVkMGE6ZPKX
# 13NMfXsjAm3zdetVPW+qLcSvvnSsXf5qtvzqXHnpD0OctVIFD+8+sbGP0EmtpuNC
# GVQ/8y8Ooct8/hP5IznaJRy4PgBKOm8yMDdkHseudQfYVdIYyQ6KvKNc8HwKp4WB
# wg6vj5lc02AlvINaaRwlE81y9eucgJvcLGfE3ckJmNVz68Qho+Uyjj4vUpjGYDdk
# jLJvSlRyGMwnh/rNdaJjIUy1PWT9K6abVa8mTGC0uVz+q0O9rdATZlAfC9KJpv/X
# gAbxwxECMzNhF/dWH44vO2jnFfF3VkopngPawismYTJboFblSSmNNqf1x1KiVgMg
# Lzh4gL32Bq5BNMuURb2bx4kYHwu6/6muakCZE93vUN8BuvIE1tAx3zQ4XldbyDge
# VtSsSKbt//m4wTvtwiS+RGCnd83VPZhZtEPqqmB9zcLlL/Hr9dQg1Zc0bl0EawUR
# 0tOSjAknRO1PNTFGfnQZBWLsiePqI3CY5NEv1IoTGEaTZeVYc9NMPSd6Ij/D+KNV
# t/nmh4LsRR7Fbjp8sU65q2j3m2PVkUG8qQIDAQABo4H7MIH4MA8GA1UdEwEB/wQF
# MAMBAf8wHwYDVR0jBBgwFoAU3QQJB6L1en1SUxKSle44gCUNplkwMAYIKwYBBQUH
# AQEEJDAiMCAGCCsGAQUFBzABhhRodHRwOi8vb2NzcHMuc3NsLmNvbTARBgNVHSAE
# CjAIMAYGBFUdIAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwOwYDVR0fBDQwMjAwoC6g
# LIYqaHR0cDovL2NybHMuc3NsLmNvbS9zc2wuY29tLXJzYS1Sb290Q0EuY3JsMB0G
# A1UdDgQWBBRUwv4QlQCTzWr158DX2bJLuI8M4zAOBgNVHQ8BAf8EBAMCAYYwDQYJ
# KoZIhvcNAQELBQADggIBAPUPJodwr5miyvXWyfCNZj05gtOII9iCv49UhCe204MH
# 154niU2EjlTRIO5gQ9tXQjzHsJX2vszqoz2OTwbGK1mGf+tzG8rlQCbgPW/M9r1x
# xs19DiBAOdYF0q+UCL9/wlG3K7V7gyHwY9rlnOFpLnUdTsthHvWlM98CnRXZ7WmT
# V7pGRS6AvGW+5xI+3kf/kJwQrfZWsqTU+tb8LryXIbN2g9KR+gZQ0bGAKID+260P
# Z+34fdzZcFt6umi1s0pmF4/n8OdX3Wn+vF7h1YyfE7uVmhX7eSuF1W0+Z0duGwdc
# +1RFDxYRLhHDsLy1bhwzV5Qe/kI0Ro4xUE7bM1eV+jjk5hLbq1guRbfZIsr0WkdJ
# LCjoT4xCPGRo6eZDrBmRqccTgl/8cQo3t51Qezxd96JSgjXktefTCm9r/o35pNfV
# HUvnfWII+NnXrJlJ27WEQRQu9i5gl1NLmv7xiHp0up516eDap8nMLDt7TAp4z5T3
# NmC2gzyKVMtODWgqlBF1JhTqIDfM63kXdlV4cW3iSTgzN9vkbFnHI2LmvM4uVEv9
# XgMqyN0eS3FE0HU+MWJliymm7STheh2ENH+kF3y0rH0/NVjLw78a3Z9UVm1F5VPz
# iIorMaPKPlDRADTsJwjDZ8Zc6Gi/zy4WZbg8Zv87spWrmo2dzJTw7XhQf+xkR6Od
# MIIGfjCCBGagAwIBAgIQZ2iSsNbwOsjnLExSAX6F6DANBgkqhkiG9w0BAQsFADB4
# MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24x
# ETAPBgNVBAoMCFNTTCBDb3JwMTQwMgYDVQQDDCtTU0wuY29tIENvZGUgU2lnbmlu
# ZyBJbnRlcm1lZGlhdGUgQ0EgUlNBIFIxMB4XDTI0MTExNjEwMzUyOFoXDTI1MTEx
# NjEwMzUyOFowZTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCENvbG9yYWRvMQ8wDQYD
# VQQHDAZEZW52ZXIxGDAWBgNVBAoMD0FuZHJldyBMaWV2ZXJ0ejEYMBYGA1UEAwwP
# QW5kcmV3IExpZXZlcnR6MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
# vIBAQzK0aahepOrPmvCEqfd6dMZC4GvV7kflKwrn4QPJGfqhFmUtadP1e3ange8O
# QZ3/w7UjOTAUNUHfhjbSgUBlKjbS6EWQKZuRFzI3SNkMJkcjTX4uS2P4QsnwM+SW
# IE5me3CTssdjtgue+Iiy53TMgW8JpoxiULVxmm3bhCRUAgxWeT6tzjytR1UyGcMc
# cm/YE6TOgsCHiZoo4X4HJD9iHDrNldArq04Jl6FsADxEswttKyfqpIRJLoAysVl1
# f8CEDBwhszJrEXBnAlWViJFfNY+dKP4jhf7lLqSvPCuADqP2jvM0Ym5I8qDGMz9j
# XPSMLF58MFB4vM4viS7nLRFJ8S1Q98vQvB8W4kk0WPuiZbZTHsROzohE1VSbLnIY
# ag5dDOWI8L6yutAsfdZFYFmSTKcMSiOj5VbK4LhAJUL2G8vPwpTGFgr+cEp0p62F
# P0WXK+/cRfGqodI5S+bg+9rQTD9zf829DwraSRAt5P5zrQk4WPst3JW/vIKNx7cV
# AgMBAAGjggGVMIIBkTAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFFTC/hCVAJPN
# avXnwNfZsku4jwzjMHoGCCsGAQUFBwEBBG4wbDBIBggrBgEFBQcwAoY8aHR0cDov
# L2NlcnQuc3NsLmNvbS9TU0xjb20tU3ViQ0EtQ29kZVNpZ25pbmctUlNBLTQwOTYt
# UjEuY2VyMCAGCCsGAQUFBzABhhRodHRwOi8vb2NzcHMuc3NsLmNvbTBRBgNVHSAE
# SjBIMAgGBmeBDAEEATA8BgwrBgEEAYKpMAEDAwEwLDAqBggrBgEFBQcCARYeaHR0
# cHM6Ly93d3cuc3NsLmNvbS9yZXBvc2l0b3J5MBMGA1UdJQQMMAoGCCsGAQUFBwMD
# ME0GA1UdHwRGMEQwQqBAoD6GPGh0dHA6Ly9jcmxzLnNzbC5jb20vU1NMY29tLVN1
# YkNBLUNvZGVTaWduaW5nLVJTQS00MDk2LVIxLmNybDAdBgNVHQ4EFgQUSj8HrSK7
# f/j+Dz31jJFhOF7rJUMwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IC
# AQBf4lcc6FUJ1W/opNz8yjS9qLUy9cQt0s35BhasB5QoTbDaW4jv9xnFGhQVg6n+
# jhL0i94Vsywd/MRBb8lYGpuBZnS/7LHuRZu7qUuud+IMDyRHIyBK6koN5bfyA5VY
# c7bFbNpbe1s1hMWke8di4qgMLZKDfyG/RtA0swf5t4UgQLPP0h+koZ8X8V5+P0V0
# 1HsdXyXd+ojo38EoZyCKfQL2aAwMPwzZfCbmI5SRXNOc6K8oqXzQcendhlKSfVBo
# Zgpi+1updqbD4jmJfYdK5AYPxJ3YH6td6ETtr8owL+bmX8lQjlXPOwVnC11rVlNB
# VjqtaJRUClLtiNiYSTKVfjdmGVJ4+sNov0dWhHc0A9o5NX/05VVYTlImuJpnG5Og
# o7w6kWRdsgE8gM58jWf7XfI6aQS0Np/z2B+ZBj0K93khEHBX7cvvORa92LCHiVeP
# km+zEAMXgxIPs/e8cmcc/o3CORgzEwxlH9Z3UOWCuXSHD3P2RPNDAY+WPdjSHm9f
# JFlGq+f9iKyedxYa/NNjNag/5EbZ+Z2NldtSMNeFdsejGJ/TJHF1PyJd4aXx9J1i
# B/IZBOoJYyh9xpQ3ljZUKE/4otPi7INpuDFwgWiUHZZJVvrGTWwxH1Yhf8P+VpFf
# aNqsBuvklUcUDs3RNE0f1qlgFfcnAepFF+RiBRqmsj29fjGCEdAwghHMAgEBMIGM
# MHgxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3Rv
# bjERMA8GA1UECgwIU1NMIENvcnAxNDAyBgNVBAMMK1NTTC5jb20gQ29kZSBTaWdu
# aW5nIEludGVybWVkaWF0ZSBDQSBSU0EgUjECEGdokrDW8DrI5yxMUgF+hegwDQYJ
# YIZIAWUDBAIBBQCgfDAQBgorBgEEAYI3AgEMMQIwADAZBgkqhkiG9w0BCQMxDAYK
# KwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG
# 9w0BCQQxIgQgiWskyV8QWhlTc4fGdWL2wO4r77v+JFlrcqXGUqBlSmgwDQYJKoZI
# hvcNAQEBBQAEggGAedWVS6WjiY+Xbtm43n7yPu9MqXuB6xNVZjIrJ+smhO4okxgU
# FgrUyO45obg7vxo1HCBiWGE2QOaq7UaPzlfoqj+dq8ZJ7OLlhAZkkTrQiaRMxGq2
# m6hXw35bLhYWwt1l/6hEqWG1XNbTIVx4Ex3GVbp61uyNXpg/YRQ7cv4pDNVX9uqq
# DhR2Fq5nO9MBNZZDyKNTMb+FBcURanJe3oprd3oJT32cPJ1+qkyAXb1zw2k8H/m1
# wcd1YDcfDdwKCdIhZKlZZHOZ2JbhCqRK/uhNBu1ciNA6REKaf/F8kgoHJ+AW7Q3t
# DB6VXU2+iYjJRxG+wq6sUgPy8kEA7yWoBAfN7JSLIvbE06Du/raFKL3zWF7a+L7E
# t09cj27yAFG6O1qSjQbBXSz8LpWwA9+LPkztPBZ1ufEgILh95fNd0PeGMNt9vp9v
# GpO5IenMiiS+5EZzZlQOP+eEAP2BAL2R6uGtWuF99TYZ3UDK4qLcZ/Vz/vhdLvHn
# /Vn2c6dxtdJNwUHuoYIPFjCCDxIGCisGAQQBgjcDAwExgg8CMIIO/gYJKoZIhvcN
# AQcCoIIO7zCCDusCAQMxDTALBglghkgBZQMEAgEwdwYLKoZIhvcNAQkQAQSgaARm
# MGQCAQEGDCsGAQQBgqkwAQMGATAxMA0GCWCGSAFlAwQCAQUABCCg24TxeTkCH7Co
# hFtDYApnZZSkEVmcdXBueVH8prBemAIIQaSn2VIf9f0YDzIwMjUwMzAzMDQzNzA2
# WjADAgEBoIIMADCCBPwwggLkoAMCAQICEFparOgaNW60YoaNV33gPccwDQYJKoZI
# hvcNAQELBQAwczELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQH
# DAdIb3VzdG9uMREwDwYDVQQKDAhTU0wgQ29ycDEvMC0GA1UEAwwmU1NMLmNvbSBU
# aW1lc3RhbXBpbmcgSXNzdWluZyBSU0EgQ0EgUjEwHhcNMjQwMjE5MTYxODE5WhcN
# MzQwMjE2MTYxODE4WjBuMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO
# BgNVBAcMB0hvdXN0b24xETAPBgNVBAoMCFNTTCBDb3JwMSowKAYDVQQDDCFTU0wu
# Y29tIFRpbWVzdGFtcGluZyBVbml0IDIwMjQgRTEwWTATBgcqhkjOPQIBBggqhkjO
# PQMBBwNCAASnYXL1MOl6xIMUlgVC49zonduUbdkyb0piy2i8t3JlQEwA74cjK8g9
# mRC8GH1cAAVMIr8M2HdZpVgkV1LXBLB8o4IBWjCCAVYwHwYDVR0jBBgwFoAUDJ0Q
# JY6apxuZh0PPCH7hvYGQ9M8wUQYIKwYBBQUHAQEERTBDMEEGCCsGAQUFBzAChjVo
# dHRwOi8vY2VydC5zc2wuY29tL1NTTC5jb20tdGltZVN0YW1waW5nLUktUlNBLVIx
# LmNlcjBRBgNVHSAESjBIMDwGDCsGAQQBgqkwAQMGATAsMCoGCCsGAQUFBwIBFh5o
# dHRwczovL3d3dy5zc2wuY29tL3JlcG9zaXRvcnkwCAYGZ4EMAQQCMBYGA1UdJQEB
# /wQMMAoGCCsGAQUFBwMIMEYGA1UdHwQ/MD0wO6A5oDeGNWh0dHA6Ly9jcmxzLnNz
# bC5jb20vU1NMLmNvbS10aW1lU3RhbXBpbmctSS1SU0EtUjEuY3JsMB0GA1UdDgQW
# BBRQTySs77U+YxMjCZIm7Lo6luRdIjAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcN
# AQELBQADggIBAJigjwMAkbyrxGRBf0Ih4r+rbCB57lTuwViC6nH2fZSciMogpqSz
# rSeVZ2eIb5vhj9rT7jqWXZn02Fncs4YTrA1QyxJW36yjC4jl5/bsFCaWuXzGXt2Y
# 6Ifp//A3Z0sNTMWTTBobmceM3sqnovdX9ToRFP+29r5yQnPcgRTI2PvrVSqLxY9E
# yk9/0cviM3W29YBl080ENblRcu3Y8RsfzRtVT/2snuDocRxvRYmd0TPaMgIj2xII
# 651QnPp1hiq9xU0AyovLzbsi5wlR5Ip4i/i8+x+HwYJNety5cYtdWJ7uQP6YaZtW
# /jNoHp76qNftq/IlSx6xEYBRjFBxHSq2fzhUQ5oBawk2OsZ2j0wOf7q7AqjCt6t/
# +fbmWjrAWYWZGj/RLjltqdFPBpIKqdhjVIxaGgzVhaE/xHKBg4k4DfFZkBYJ9BWu
# P93Tm+paWBDwXI7Fg3alGsboErWPWlvwMAmpeJUjeKLZY26JPLt9ZWceTVWuIyuj
# erqb5IMmeqLJm5iFq/Qy4YPGyPiolw5w1k9OeO4ErmS2FKvk1ejvw4SWR+S1VyWn
# ktY442WaoStxBCCVWZdMWFeB+EpL8uoQNq1MhSt/sIUjUudkyZLIbMVQjj7b6gPX
# nD6mS8FgWiCAhuM1a/hgA+6o1sJWizHdmcpYDhyNzorf9KVRE6iR7rcmMIIG/DCC
# BOSgAwIBAgIQbVIYcIfoI02FYADQgI+TVjANBgkqhkiG9w0BAQsFADB8MQswCQYD
# VQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xGDAWBgNV
# BAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBSb290IENlcnRp
# ZmljYXRpb24gQXV0aG9yaXR5IFJTQTAeFw0xOTExMTMxODUwMDVaFw0zNDExMTIx
# ODUwMDVaMHMxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwH
# SG91c3RvbjERMA8GA1UECgwIU1NMIENvcnAxLzAtBgNVBAMMJlNTTC5jb20gVGlt
# ZXN0YW1waW5nIElzc3VpbmcgUlNBIENBIFIxMIICIjANBgkqhkiG9w0BAQEFAAOC
# Ag8AMIICCgKCAgEArlEQE9L5PCCgIIXeyVAcZMnh/cXpNP8KfzFI6HJaxV6oYf3x
# h/dRXPu35tDBwhOwPsJjoqgY/Tg6yQGBqt65t94wpx0rAgTVgEGMqGri6vCI6rEt
# SZVy9vagzTDHcGfFDc0Eu71mTAyeNCUhjaYTBkyANqp9m6IRrYEXOKdd/eREsqVD
# mhryd7dBTS9wbipm+mHLTHEFBdrKqKDM3fPYdBOro3bwQ6OmcDZ1qMY+2Jn1o0l4
# N9wORrmPcpuEGTOThFYKPHm8/wfoMocgizTYYeDG/+MbwkwjFZjWKwb4hoHT2WK8
# pvGW/OE0Apkrl9CZSy2ulitWjuqpcCEm2/W1RofOunpCm5Qv10T9tIALtQo73GHI
# lIDU6xhYPH/ACYEDzgnNfwgnWiUmMISaUnYXijp0IBEoDZmGT4RTguiCmjAFF5OV
# NbY03BQoBb7wK17SuGswFlDjtWN33ZXSAS+i45My1AmCTZBV6obAVXDzLgdJ1A1r
# yyXz4prLYyfJReEuhAsVp5VouzhJVcE57dRrUanmPcnb7xi57VPhXnCuw26hw1Hd
# +ulK3jJEgbc3rwHPWqqGT541TI7xaldaWDo85k4lR2bQHPNGwHxXuSy3yczyOg57
# TcqqG6cE3r0KR6jwzfaqjTvN695GsPAPY/h2YksNgF+XBnUD9JBtL4c34AcCAwEA
# AaOCAYEwggF9MBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAU3QQJB6L1
# en1SUxKSle44gCUNplkwgYMGCCsGAQUFBwEBBHcwdTBRBggrBgEFBQcwAoZFaHR0
# cDovL3d3dy5zc2wuY29tL3JlcG9zaXRvcnkvU1NMY29tUm9vdENlcnRpZmljYXRp
# b25BdXRob3JpdHlSU0EuY3J0MCAGCCsGAQUFBzABhhRodHRwOi8vb2NzcHMuc3Ns
# LmNvbTA/BgNVHSAEODA2MDQGBFUdIAAwLDAqBggrBgEFBQcCARYeaHR0cHM6Ly93
# d3cuc3NsLmNvbS9yZXBvc2l0b3J5MBMGA1UdJQQMMAoGCCsGAQUFBwMIMDsGA1Ud
# HwQ0MDIwMKAuoCyGKmh0dHA6Ly9jcmxzLnNzbC5jb20vc3NsLmNvbS1yc2EtUm9v
# dENBLmNybDAdBgNVHQ4EFgQUDJ0QJY6apxuZh0PPCH7hvYGQ9M8wDgYDVR0PAQH/
# BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQCSGXUNplpCzxkH2fL8lPrAm/AV6USW
# Wi9xM91Q5RN7mZN3D8T7cm1Xy7qmnItFukgdtiUzLbQokDJyFTrF1pyLgGw/2hU3
# FJEywSN8crPsBGo812lyWFgAg0uOwUYw7WJQ1teICycX/Fug0KB94xwxhsvJBiRT
# pQyhu/2Kyu1Bnx7QQBA1XupcmfhbQrK5O3Q/yIi//kN0OkhQEiS0NlyPPYoRboHW
# C++wogzV6yNjBbKUBrMFxABqR7mkA0x1Kfy3Ud08qyLC5Z86C7JFBrMBfyhfPpKV
# lIiiTQuKz1rTa8ZW12ERoHRHcfEjI1EwwpZXXK5J5RcW6h7FZq/cZE9kLRZhvnRK
# tb+X7CCtLx2h61ozDJmifYvuKhiUg9LLWH0Or9D3XU+xKRsRnfOuwHWuhWch8G7k
# EmnTG9CtD9Dgtq+68KgVHtAWjKk2ui1s1iLYAYxnDm13jMZm0KpRM9mLQHBK5Gb4
# dFgAQwxOFPBslf99hXWgLyYE33vTIi9p0gYqGHv4OZh1ElgGsvyKdUUJkAr5hfbD
# X6pYScJI8v9VNYm1JEyFAV9x4MpskL6kE2Sy8rOqS9rQnVnIyPWLi8N9K4GZvPit
# /Oy+8nFL6q5kN2SZbox5d69YYFe+rN1sDD4CpNWwBBTI/q0V4pkgvhL99IV2Xasj
# HZf4peSrHdL4RjGCAlgwggJUAgEBMIGHMHMxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI
# DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjERMA8GA1UECgwIU1NMIENvcnAxLzAt
# BgNVBAMMJlNTTC5jb20gVGltZXN0YW1waW5nIElzc3VpbmcgUlNBIENBIFIxAhBa
# WqzoGjVutGKGjVd94D3HMAsGCWCGSAFlAwQCAaCCAWEwGgYJKoZIhvcNAQkDMQ0G
# CyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAzMDMwNDM3MDZaMCgGCSqG
# SIb3DQEJNDEbMBkwCwYJYIZIAWUDBAIBoQoGCCqGSM49BAMCMC8GCSqGSIb3DQEJ
# BDEiBCCCd/uqQaq2mcgFE/v8m0TmJNbeSe5jli47wUKqltIWSDCByQYLKoZIhvcN
# AQkQAi8xgbkwgbYwgbMwgbAEIJ1xf43CN2Wqzl5KsOH1ddeaF9Qc7tj9r+8D/T29
# iUfnMIGLMHekdTBzMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNV
# BAcMB0hvdXN0b24xETAPBgNVBAoMCFNTTCBDb3JwMS8wLQYDVQQDDCZTU0wuY29t
# IFRpbWVzdGFtcGluZyBJc3N1aW5nIFJTQSBDQSBSMQIQWlqs6Bo1brRiho1XfeA9
# xzAKBggqhkjOPQQDAgRHMEUCICAufNIowOikPPAYp1HO1TdTGvgpxlYonw/M9fIq
# ASO/AiEA24qlxlclUj9CC9viGEaFkH24TpqB0NrLDSg2D0U261M=
# SIG # End signature block