PowerShell Gallery | HelperFunctions.ps1 6.0.19 (2024)

$sslCallbackCode=@"
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

public static class SslVerification
{
public static bool DisabledServerCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; }
public static void Disable() { System.Net.ServicePointManager.ServerCertificateValidationCallback = DisabledServerCertificateValidationCallback; }
public static void Enable() { System.Net.ServicePointManager.ServerCertificateValidationCallback = null; }
public static void DisableSsl(System.Net.Http.HttpClientHandler handler) { handler.ServerCertificateCustomValidationCallback = DisabledServerCertificateValidationCallback; }
}
"@

if(-not([System.Management.Automation.PSTypeName]"SslVerification").Type){
if($isPsCore){
Add-Type-TypeDefinition$sslCallbackCode-LanguageCSharp-WarningActionSilentlyContinue|Out-Null
}
else{
Add-Type-TypeDefinition$sslCallbackCode-LanguageCSharp-ReferencedAssemblies@('System.Net.Http')-WarningActionSilentlyContinue|Out-Null
}
}

functionGet-DefaultCredential{
Param(
[string]$Message,
[string]$DefaultUserName,
[switch]$doNotAskForCredential
)

if(Test-Path"$($bcContainerHelperConfig.hostHelperFolder)\settings.ps1"){
."$($bcContainerHelperConfig.hostHelperFolder)\settings.ps1"
if(Test-Path"$($bcContainerHelperConfig.hostHelperFolder)\aes.key"){
$key=Get-Content-Path"$($bcContainerHelperConfig.hostHelperFolder)\aes.key"
New-ObjectSystem.Management.Automation.PSCredential($DefaultUserName,(ConvertTo-SecureString-String$adminPassword-Key$key))
}
else{
New-ObjectSystem.Management.Automation.PSCredential($DefaultUserName,(ConvertTo-SecureString-String$adminPassword))
}
}
else{
if(!$doNotAskForCredential){
Get-Credential-username$DefaultUserName-Message$Message
}
}
}

functionGet-DefaultSqlCredential{
Param(
[Parameter(Mandatory=$true)]
[string]$containerName,
[System.Management.Automation.PSCredential]$sqlCredential=$null,
[switch]$doNotAskForCredential
)

if($sqlCredential-eq$null){
$containerAuth=Get-NavContainerAuth-containerName$containerName
if($containerAuth-ne"Windows"){
$sqlCredential=Get-DefaultCredential-DefaultUserName""-Message"Please enter the SQL Server System Admin credentials for container $containerName"-doNotAskForCredential:$doNotAskForCredential
}
}
$sqlCredential
}

functionCmdDo{
Param(
[string]$command="",
[string]$arguments="",
[switch]$silent,
[switch]$returnValue,
[string]$inputStr="",
[string]$messageIfCmdNotFound=""
)

$oldNoColor="$env:NO_COLOR"
$env:NO_COLOR="Y"
$oldEncoding=[Console]::OutputEncoding
try{[Console]::OutputEncoding=[System.Text.Encoding]::UTF8}catch{}
try{
$pinfo=New-ObjectSystem.Diagnostics.ProcessStartInfo
$pinfo.FileName=$command
$pinfo.RedirectStandardError=$true
$pinfo.RedirectStandardOutput=$true
if($inputStr){
$pinfo.RedirectStandardInput=$true
}
$pinfo.WorkingDirectory=Get-Location
$pinfo.UseShellExecute=$false
$pinfo.Arguments=$arguments
$pinfo.WindowStyle=[System.Diagnostics.ProcessWindowStyle]::Minimized

$p=New-ObjectSystem.Diagnostics.Process
$p.StartInfo=$pinfo
$p.Start()|Out-Null
if($inputStr){
$p.StandardInput.WriteLine($inputStr)
$p.StandardInput.Close()
}
$outtask=$p.StandardOutput.ReadToEndAsync()
$errtask=$p.StandardError.ReadToEndAsync()
$p.WaitForExit();

$message=$outtask.Result
$err=$errtask.Result

if("$err"-ne""){
$message+="$err"
}

$message=$message.Trim()

if($p.ExitCode-eq0){
if(!$silent){
Write-Host$message
}
if($returnValue){
$message.Replace("`r","").Split("`n")
}
}
else{
$message+="`n`nExitCode: "+$p.ExitCode+"`nCommandline: $command $arguments"
throw$message
}
}
catch[System.ComponentModel.Win32Exception]{
if($_.Exception.NativeErrorCode-eq2){
if($messageIfCmdNotFound){
throw$messageIfCmdNotFound
}
else{
throw"Command $command not found, you might need to install that command."
}
}
else{
throw
}
}
finally{
try{[Console]::OutputEncoding=$oldEncoding}catch{}
$env:NO_COLOR=$oldNoColor
}
}

functionDockerDo{
Param(
[Parameter(Mandatory=$true)]
[string]$imageName,
[ValidateSet('run','start','pull','restart','stop','rmi','build')]
[string]$command="run",
[switch]$accept_eula,
[switch]$accept_outdated,
[switch]$detach,
[switch]$silent,
[string[]]$parameters=@()
)

if($accept_eula){
$parameters+="--env accept_eula=Y"
}
if($accept_outdated){
$parameters+="--env accept_outdated=Y"
}
if($detach){
$parameters+="--detach"
}

$result=$true
$arguments=("$command "+[string]::Join(" ",$parameters)+" $imageName")
$pinfo=New-ObjectSystem.Diagnostics.ProcessStartInfo
$pinfo.FileName="docker.exe"
$pinfo.RedirectStandardError=$true
$pinfo.RedirectStandardOutput=$true
$pinfo.UseShellExecute=$false
$pinfo.Arguments=$arguments
$p=New-ObjectSystem.Diagnostics.Process
$p.StartInfo=$pinfo
$p.Start()|Out-Null

$outtask=$null
$errtask=$p.StandardError.ReadToEndAsync()
$out=""
$err=""

do{
if($outtask-eq$null){
$outtask=$p.StandardOutput.ReadLineAsync()
}
$outtask.Wait(100)|Out-Null
if($outtask.IsCompleted){
$outStr=$outtask.Result
if($outStr-eq$null){
break
}
if(!$silent){
Write-Host$outStr
}
$out+=$outStr
$outtask=$null
if($outStr.StartsWith("Please login")){
$registry=$imageName.Split("/")[0]
if($registry-eq"bcinsider.azurecr.io"-or$registry-eq"bcprivate.azurecr.io"){
throw"You need to login to $registry prior to pulling images. Get credentials through the ReadyToGo program on Microsoft Collaborate."
}
else{
throw"You need to login to $registry prior to pulling images."
}
}
}
elseif($outtask.IsCanceled){
break
}
elseif($outtask.IsFaulted){
break
}
}while(!($p.HasExited))

$err=$errtask.Result
$p.WaitForExit();

if($p.ExitCode-ne0){
$result=$false
if(!$silent){
$out=$out.Trim()
$err=$err.Trim()
if($command-eq"run"-and"$out"-ne""){
Dockerrm$out-f
}
$errorMessage=""
if("$err"-ne""){
$errorMessage+="$err`r`n"
if($err.Contains("authentication required")){
$registry=$imageName.Split("/")[0]
if($registry-eq"bcinsider.azurecr.io"-or$registry-eq"bcprivate.azurecr.io"){
$errorMessage+="You need to login to $registry prior to pulling images. Get credentials through the ReadyToGo program on Microsoft Collaborate.`r`n"
}
else{
$errorMessage+="You need to login to $registry prior to pulling images.`r`n"
}
}
}
$errorMessage+="ExitCode: "+$p.ExitCode+"`r`nCommandline: docker $arguments"
Write-Error-Message$errorMessage
}
}
$result
}

functionGet-NavContainerAuth{
Param
(
[string]$containerName
)

Invoke-ScriptInNavContainer-containerName$containerName-ScriptBlock{
$customConfigFile=Join-Path(Get-Item"C:\Program Files\Microsoft Dynamics NAV\*\Service").FullName"CustomSettings.config"
[xml]$customConfig=[System.IO.File]::ReadAllText($customConfigFile)
$customConfig.SelectSingleNode("//appSettings/add[@key='ClientServicesCredentialType']").Value
}
}

functionCheck-BcContainerName{
Param
(
[string]$containerName=""
)

if($containerName-eq""){
throw"Container name cannot be empty"
}

if($containerName.Length-gt15){
Write-Host"WARNING: Container name should not exceed 15 characters"
}

$first=$containerName.ToLowerInvariant()[0]
if($first-lt"a"-or$first-gt"z"){
throw"Container name should start with a letter (a-z)"
}

$containerName.ToLowerInvariant().ToCharArray()|ForEach-Object{
if(($_-lt"a"-or$_-gt"z")-and($_-lt"0"-or$_-gt"9")-and($_-ne"-")){
throw"Container name contains invalid characters. Allowed characters are letters (a-z), numbers (0-9) and dashes (-)"
}
}
}

functionAssumeNavContainer{
Param
(
[string]$containerOrImageName="",
[string]$functionName=""
)

$inspect=dockerinspect$containerOrImageName|ConvertFrom-Json
if($inspect.Config.Labels.psobject.Properties.Match('maintainer').Count-eq0-or$inspect.Config.Labels.maintainer-ne"Dynamics SMB"){
throw"Container $containerOrImageName is not a Business Central container"
}
[System.Version]$version=$inspect.Config.Labels.version

if("$functionName"-eq""){
$functionName=(Get-VariableMyInvocation-Scope1).Value.MyCommand.Name
}
if($version.Major-ge15){
throw"Container $containerOrImageName does not support the function $functionName"
}
}

functionExpand-7zipArchive{
Param(
[Parameter(Mandatory=$true)]
[string]$Path,
[string]$DestinationPath,
[switch]$use7zipIfAvailable=$bcContainerHelperConfig.use7zipIfAvailable,
[switch]$silent
)

$7zipPath="$env:ProgramFiles\7-Zip\7z.exe"
$use7zip=$false
if($use7zipIfAvailable-and(Test-Path-Path$7zipPath-PathTypeLeaf)){
try{
$use7zip=[System.Diagnostics.FileVersionInfo]::GetVersionInfo($7zipPath).FileMajorPart-ge19
}
catch{
$use7zip=$false
}
}

if($use7zip){
if(!$silent){Write-Host"using 7zip"}
Set-Alias-Name7z-Value$7zipPath
$command='7z x "{0}" -o"{1}" -aoa -r'-f$Path,$DestinationPath
$global:LASTEXITCODE=0
Invoke-Expression-Command$command|Out-Null
if($LASTEXITCODE-ne0){
throw"Error $LASTEXITCODE extracting $path"
}
}
else{
if(!$silent){Write-Host"using Expand-Archive"}
if([System.IO.Path]::GetExtension($path)-eq'.zip'){
Expand-Archive-Path$Path-DestinationPath"$DestinationPath"-Force
}
else{
$tempZip=Join-Path([System.IO.Path]::GetTempPath())"$([guid]::NewGuid().ToString()).zip"
Copy-Item-Path$Path-Destination$tempZip-Force
try{
Expand-Archive-Path$tempZip-DestinationPath"$DestinationPath"-Force
}
finally{
Remove-Item-Path$tempZip-Force
}
}
}
}

functionGetTestToolkitApps{
Param(
[string]$containerName,
[switch]$includeTestLibrariesOnly,
[switch]$includeTestFrameworkOnly,
[switch]$includeTestRunnerOnly,
[switch]$includePerformanceToolkit
)

Invoke-ScriptInBCContainer-containerName$containerName-scriptblock{Param($includeTestLibrariesOnly,$includeTestFrameworkOnly,$includeTestRunnerOnly,$includePerformanceToolkit)

$version=[Version](Get-Item"C:\Program Files\Microsoft Dynamics NAV\*\Service\Microsoft.Dynamics.Nav.Server.exe").VersionInfo.FileVersion

# Add Test Framework
$apps=@()
if(($version-ge[Version]"19.0.0.0")-and(Test-Path'C:\Applications\TestFramework\TestLibraries\permissions mock')){
$apps+=@(get-childitem-Path"C:\Applications\TestFramework\TestLibraries\permissions mock\*.*"-recurse-filter"*.app")
}
$apps+=@(get-childitem-Path"C:\Applications\TestFramework\TestRunner\*.*"-recurse-filter"*.app")

if(!$includeTestRunnerOnly){
$apps+=@(get-childitem-Path"C:\Applications\TestFramework\TestLibraries\*.*"-recurse-filter"*.app")

if(!$includeTestFrameworkOnly){
# Add Test Libraries
$apps+="Microsoft_System Application Test Library.app","Microsoft_Business Foundation Test Libraries.app","Microsoft_Tests-TestLibraries.app"|ForEach-Object{
@(get-childitem-Path"C:\Applications\*.*"-recurse-filter$_)
}

if(!$includeTestLibrariesOnly){
# Add Tests
if($version-ge[Version]"18.0.0.0"){
$apps+="Microsoft_System Application Test.app","Microsoft_Business Foundation Tests.app"|ForEach-Object{
@(get-childitem-Path"C:\Applications\*.*"-recurse-filter$_)
}
}
$apps+=@(get-childitem-Path"C:\Applications\*.*"-recurse-filter"Microsoft_Tests-*.app")|Where-Object{$_-notlike"*\Microsoft_Tests-TestLibraries.app"-and($version.Major-ge17-or($_-notlike"*\Microsoft_Tests-Marketing.app"))-and$_-notlike"*\Microsoft_Tests-SINGLESERVER.app"}
}
}
}

if($includePerformanceToolkit){
$apps+=@(get-childitem-Path"C:\Applications\TestFramework\PerformanceToolkit\*.*"-recurse-filter"*Toolkit.app")
if(!$includeTestFrameworkOnly){
$apps+=@(get-childitem-Path"C:\Applications\TestFramework\PerformanceToolkit\*.*"-recurse-filter"*.app"-exclude"*Toolkit.app")
}
}

$apps|ForEach-Object{
$appFile=Get-ChildItem-path"c:\applications.*\*.*"-recurse-filter($_.Name).Replace(".app","_*.app")
if(!($appFile)){
$appFile=$_
}
$appFile.FullName
}
}-argumentList$includeTestLibrariesOnly,$includeTestFrameworkOnly,$includeTestRunnerOnly,$includePerformanceToolkit
}

functionGetExtendedErrorMessage{
Param(
$errorRecord
)

$exception=$errorRecord.Exception
$message=$exception.Message

try{
if($errorRecord.ErrorDetails){
$errorDetails=$errorRecord.ErrorDetails|ConvertFrom-Json
$message+=" $($errorDetails.error)`r`n$($errorDetails.error_description)"
}
}
catch{}
try{
if($exception-is[System.Management.Automation.MethodInvocationException]){
$exception=$exception.InnerException
}
if($exception-is[System.Net.Http.HttpRequestException]){
$message+="`r`n$($exception.Message)"
if($exception.InnerException){
if($exception.InnerException-and$exception.InnerException.Message){
$message+="`r`n$($exception.InnerException.Message)"
}
}

}
else{
$webException=[System.Net.WebException]$exception
$webResponse=$webException.Response
try{
if($webResponse.StatusDescription){
$message+="`r`n$($webResponse.StatusDescription)"
}
}
catch{}
$reqstream=$webResponse.GetResponseStream()
$sr=new-objectSystem.IO.StreamReader$reqstream
$result=$sr.ReadToEnd()
}
try{
$json=$result|ConvertFrom-Json
$message+="`r`n$($json.Message)"
}
catch{
$message+="`r`n$result"
}
try{
$correlationX=$webResponse.GetResponseHeader('ms-correlation-x')
if($correlationX){
$message+=" (ms-correlation-x = $correlationX)"
}
}
catch{}
}
catch{}
$message
}

functionCopyAppFilesToFolder{
Param(
$appFiles,
[string]$folder
)

if($appFiles-is[String]){
if(!(Test-Path$appFiles)){
$appFiles=@($appFiles.Split(',').Trim()|Where-Object{$_})
}
}
if(!(Test-Path$folder)){
New-Item-Path$folder-ItemTypeDirectory|Out-Null
}
$appFiles|Where-Object{$_}|ForEach-Object{
$appFile="$_"
if($appFile-like"http://*"-or$appFile-like"https://*"){
$appUrl=$appFile
$appFileFolder=Join-Path([System.IO.Path]::GetTempPath())([Guid]::NewGuid().ToString())
$appFile=Join-Path$appFileFolder([Uri]::UnescapeDataString([System.IO.Path]::GetFileName($appUrl.Split('?')[0])))
Download-File-sourceUrl$appUrl-destinationFile$appFile
CopyAppFilesToFolder-appFile$appFile-folder$folder
if(Test-Path$appFileFolder){
Remove-Item-Path$appFileFolder-Force-Recurse
}
}
elseif(Test-Path$appFile-PathTypeContainer){
Get-ChildItem$appFile-Recurse-File|ForEach-Object{
CopyAppFilesToFolder-appFile$_.FullName-folder$folder
}
}
elseif(Test-Path$appFile-PathTypeLeaf){
Get-ChildItem$appFile|ForEach-Object{
$appFile=$_.FullName
if($appFile-like"*.app"){
$destFileName=[System.IO.Path]::GetFileName($appFile)
$destFile=Join-Path$folder$destFileName
if(Test-Path$destFile){
Write-Host-ForegroundColorYellow"::WARNING::$destFileName already exists, it looks like you have multiple app files with the same name. App filenames must be unique."
}
Copy-Item-Path$appFile-Destination$destFile-Force
$destFile
}
elseif([string]::new([char[]](Get-Content$appFile@byteEncodingParam-TotalCount2))-eq"PK"){
$tmpFolder=Join-Path([System.IO.Path]::GetTempPath())([Guid]::NewGuid().ToString())
$copied=$false
try{
if($appFile-notlike"*.zip"){
$orgAppFile=$appFile
$appFile=Join-Path([System.IO.Path]::GetTempPath())"$([System.IO.Path]::GetFileName($orgAppFile)).zip"
Copy-Item$orgAppFile$appFile
$copied=$true
}
Expand-Archive$appfile-DestinationPath$tmpFolder-Force
CopyAppFilesToFolder-appFiles$tmpFolder-folder$folder
}
finally{
Remove-Item-Path$tmpFolder-Recurse-Force
if($copied){Remove-Item-Path$appFile-Force}
}
}
}
}
else{
Write-Host-ForegroundColorRed"::WARNING::File not found: $appFile"
}
}
}

functiongetCountryCode{
Param(
[string]$countryCode
)

$countryCodes=@{
"Afghanistan"="AF"
"Åland Islands"="AX"
"Albania"="AL"
"Algeria"="DZ"
"American Samoa"="AS"
"AndorrA"="AD"
"Angola"="AO"
"Anguilla"="AI"
"Antarctica"="AQ"
"Antigua and Barbuda"="AG"
"Argentina"="AR"
"Armenia"="AM"
"Aruba"="AW"
"Australia"="AU"
"Austria"="AT"
"Azerbaijan"="AZ"
"Bahamas"="BS"
"Bahrain"="BH"
"Bangladesh"="BD"
"Barbados"="BB"
"Belarus"="BY"
"Belgium"="BE"
"Belize"="BZ"
"Benin"="BJ"
"Bermuda"="BM"
"Bhutan"="BT"
"Bolivia"="BO"
"Bosnia and Herzegovina"="BA"
"Botswana"="BW"
"Bouvet Island"="BV"
"Brazil"="BR"
"British Indian Ocean Territory"="IO"
"Brunei Darussalam"="BN"
"Bulgaria"="BG"
"Burkina Faso"="BF"
"Burundi"="BI"
"Cambodia"="KH"
"Cameroon"="CM"
"Canada"="CA"
"Cape Verde"="CV"
"Cayman Islands"="KY"
"Central African Republic"="CF"
"Chad"="TD"
"Chile"="CL"
"China"="CN"
"Christmas Island"="CX"
"Cocos (Keeling) Islands"="CC"
"Colombia"="CO"
"Comoros"="KM"
"Congo"="CG"
"Congo, The Democratic Republic of the"="CD"
"Cook Islands"="CK"
"Costa Rica"="CR"
"Cote D'Ivoire"="CI"
"Croatia"="HR"
"Cuba"="CU"
"Cyprus"="CY"
"Czech Republic"="CZ"
"Denmark"="DK"
"Djibouti"="DJ"
"Dominica"="DM"
"Dominican Republic"="DO"
"Ecuador"="EC"
"Egypt"="EG"
"El Salvador"="SV"
"Equatorial Guinea"="GQ"
"Eritrea"="ER"
"Estonia"="EE"
"Ethiopia"="ET"
"Falkland Islands (Malvinas)"="FK"
"Faroe Islands"="FO"
"Fiji"="FJ"
"Finland"="FI"
"France"="FR"
"French Guiana"="GF"
"French Polynesia"="PF"
"French Southern Territories"="TF"
"Gabon"="GA"
"Gambia"="GM"
"Georgia"="GE"
"Germany"="DE"
"Ghana"="GH"
"Gibraltar"="GI"
"Greece"="GR"
"Greenland"="GL"
"Grenada"="GD"
"Guadeloupe"="GP"
"Guam"="GU"
"Guatemala"="GT"
"Guernsey"="GG"
"Guinea"="GN"
"Guinea-Bissau"="GW"
"Guyana"="GY"
"Haiti"="HT"
"Heard Island and Mcdonald Islands"="HM"
"Holy See (Vatican City State)"="VA"
"Honduras"="HN"
"Hong Kong"="HK"
"Hungary"="HU"
"Iceland"="IS"
"India"="IN"
"Indonesia"="ID"
"Iran, Islamic Republic Of"="IR"
"Iraq"="IQ"
"Ireland"="IE"
"Isle of Man"="IM"
"Israel"="IL"
"Italy"="IT"
"Jamaica"="JM"
"Japan"="JP"
"Jersey"="JE"
"Jordan"="JO"
"Kazakhstan"="KZ"
"Kenya"="KE"
"Kiribati"="KI"
"Korea, Democratic People's Republic of"="KP"
"Korea, Republic of"="KR"
"Kuwait"="KW"
"Kyrgyzstan"="KG"
"Lao People's Democratic Republic"="LA"
"Latvia"="LV"
"Lebanon"="LB"
"Lesotho"="LS"
"Liberia"="LR"
"Libyan Arab Jamahiriya"="LY"
"Liechtenstein"="LI"
"Lithuania"="LT"
"Luxembourg"="LU"
"Macao"="MO"
"Macedonia, The Former Yugoslav Republic of"="MK"
"Madagascar"="MG"
"Malawi"="MW"
"Malaysia"="MY"
"Maldives"="MV"
"Mali"="ML"
"Malta"="MT"
"Marshall Islands"="MH"
"Martinique"="MQ"
"Mauritania"="MR"
"Mauritius"="MU"
"Mayotte"="YT"
"Mexico"="MX"
"Micronesia, Federated States of"="FM"
"Moldova, Republic of"="MD"
"Monaco"="MC"
"Mongolia"="MN"
"Montserrat"="MS"
"Morocco"="MA"
"Mozambique"="MZ"
"Myanmar"="MM"
"Namibia"="NA"
"Nauru"="NR"
"Nepal"="NP"
"Netherlands"="NL"
"Netherlands Antilles"="AN"
"New Caledonia"="NC"
"New Zealand"="NZ"
"Nicaragua"="NI"
"Niger"="NE"
"Nigeria"="NG"
"Niue"="NU"
"Norfolk Island"="NF"
"Northern Mariana Islands"="MP"
"Norway"="NO"
"Oman"="OM"
"Pakistan"="PK"
"Palau"="PW"
"Palestinian Territory, Occupied"="PS"
"Panama"="PA"
"Papua New Guinea"="PG"
"Paraguay"="PY"
"Peru"="PE"
"Philippines"="PH"
"Pitcairn"="PN"
"Poland"="PL"
"Portugal"="PT"
"Puerto Rico"="PR"
"Qatar"="QA"
"Reunion"="RE"
"Romania"="RO"
"Russian Federation"="RU"
"RWANDA"="RW"
"Saint Helena"="SH"
"Saint Kitts and Nevis"="KN"
"Saint Lucia"="LC"
"Saint Pierre and Miquelon"="PM"
"Saint Vincent and the Grenadines"="VC"
"Samoa"="WS"
"San Marino"="SM"
"Sao Tome and Principe"="ST"
"Saudi Arabia"="SA"
"Senegal"="SN"
"Serbia and Montenegro"="CS"
"Seychelles"="SC"
"Sierra Leone"="SL"
"Singapore"="SG"
"Slovakia"="SK"
"Slovenia"="SI"
"Solomon Islands"="SB"
"Somalia"="SO"
"South Africa"="ZA"
"South Georgia and the South Sandwich Islands"="GS"
"Spain"="ES"
"Sri Lanka"="LK"
"Sudan"="SD"
"Suriname"="SR"
"Svalbard and Jan Mayen"="SJ"
"Swaziland"="SZ"
"Sweden"="SE"
"Switzerland"="CH"
"Syrian Arab Republic"="SY"
"Taiwan, Province of China"="TW"
"Tajikistan"="TJ"
"Tanzania, United Republic of"="TZ"
"Thailand"="TH"
"Timor-Leste"="TL"
"Togo"="TG"
"Tokelau"="TK"
"Tonga"="TO"
"Trinidad and Tobago"="TT"
"Tunisia"="TN"
"Turkey"="TR"
"Turkmenistan"="TM"
"Turks and Caicos Islands"="TC"
"Tuvalu"="TV"
"Uganda"="UG"
"Ukraine"="UA"
"United Arab Emirates"="AE"
"United Kingdom"="GB"
"United States"="US"
"United States Minor Outlying Islands"="UM"
"Uruguay"="UY"
"Uzbekistan"="UZ"
"Vanuatu"="VU"
"Venezuela"="VE"
"Viet Nam"="VN"
"Virgin Islands, British"="VG"
"Virgin Islands, U.S."="VI"
"Wallis and Futuna"="WF"
"Western Sahara"="EH"
"Yemen"="YE"
"Zambia"="ZM"
"Zimbabwe"="ZW"
"Hong Kong SAR"="HK"
"Serbia"="RS"
"Korea"="KR"
"Taiwan"="TW"
"Vietnam"="VN"
}

$countryCode=$countryCode.Trim()
if($countryCodes.ContainsValue($countryCode.ToUpperInvariant())){
return$countryCode.ToLowerInvariant()
}
elseif($countryCodes.ContainsKey($countryCode)){
return($countryCodes[$countryCode]).ToLowerInvariant()
}
else{
throw"Country code $countryCode is illegal"
}
}

functionGet-WWWRootPath{
$inetstp=Get-Item"HKLM:\SOFTWARE\Microsoft\InetStp"-ErrorActionSilentlyContinue
if($inetstp){
[System.Environment]::ExpandEnvironmentVariables($inetstp.GetValue("PathWWWRoot"))
}
else{
""
}
}

functionParse-JWTtoken([string]$token){
if($token.Contains(".")-and$token.StartsWith("eyJ")){
$tokenPayload=$token.Split(".")[1].Replace('-','+').Replace('_','/')
while($tokenPayload.Length%4){$tokenPayload+="="}
return[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($tokenPayload))|ConvertFrom-Json
}
throw"Invalid token"
}

functionTest-BcAuthContext{
Param(
$bcAuthContext
)

if(!(($bcAuthContext-is[Hashtable])-and
($bcAuthContext.ContainsKey('ClientID'))-and
($bcAuthContext.ContainsKey('Credential'))-and
($bcAuthContext.ContainsKey('authority'))-and
($bcAuthContext.ContainsKey('RefreshToken'))-and
($bcAuthContext.ContainsKey('UtcExpiresOn'))-and
($bcAuthContext.ContainsKey('tenantID'))-and
($bcAuthContext.ContainsKey('AccessToken'))-and
($bcAuthContext.ContainsKey('includeDeviceLogin'))-and
($bcAuthContext.ContainsKey('deviceLoginTimeout')))){
throw'BcAuthContext should be a HashTable created by New-BcAuthContext.'
}
}

FunctionCreatePsTestToolFolder{
Param(
[string]$containerName,
[string]$PsTestToolFolder
)

$PsTestFunctionsPath=Join-Path$PsTestToolFolder"PsTestFunctions.ps1"
$ClientContextPath=Join-Path$PsTestToolFolder"ClientContext.ps1"
$newtonSoftDllPath=Join-Path$PsTestToolFolder"NewtonSoft.json.dll"
$clientDllPath=Join-Path$PsTestToolFolder"Microsoft.Dynamics.Framework.UI.Client.dll"

if(!(Test-Path-Path$PsTestToolFolder-PathTypeContainer)){
New-Item-Path$PsTestToolFolder-ItemTypeDirectory|Out-Null
Copy-Item-Path(Join-Path$PSScriptRoot"AppHandling\PsTestFunctions.ps1")-Destination$PsTestFunctionsPath-Force
Copy-Item-Path(Join-Path$PSScriptRoot"AppHandling\ClientContext.ps1")-Destination$ClientContextPath-Force
}

Invoke-ScriptInBcContainer-containerName$containerName{Param([string]$myNewtonSoftDllPath,[string]$myClientDllPath)
if(!(Test-Path$myNewtonSoftDllPath)){
$newtonSoftDllPath="C:\Program Files\Microsoft Dynamics NAV\*\Service\Management\NewtonSoft.json.dll"
if(!(Test-Path$newtonSoftDllPath)){
$newtonSoftDllPath="C:\Program Files\Microsoft Dynamics NAV\*\Service\NewtonSoft.json.dll"
}
$newtonSoftDllPath=(Get-Item$newtonSoftDllPath).FullName
Copy-Item-Path$newtonSoftDllPath-Destination$myNewtonSoftDllPath
}
if(!(Test-Path$myClientDllPath)){
$clientDllPath="C:\Test Assemblies\Microsoft.Dynamics.Framework.UI.Client.dll"
Copy-Item-Path$clientDllPath-Destination$myClientDllPath
}
}-argumentList(Get-BcContainerPath-containerName$containerName-Path$newtonSoftDllPath),(Get-BcContainerPath-containerName$containerName-Path$clientDllPath)
}

functionRandomChar([string]$str){
$rnd=Get-Random-Maximum$str.length
[string]$str[$rnd]
}

functionGetRandomPassword{
$cons='bcdfghjklmnpqrstvwxz'
$voc='aeiouy'
$numbers='0123456789'

((RandomChar$cons).ToUpper()+`
(RandomChar$voc)+`
(RandomChar$cons)+`
(RandomChar$voc)+`
(RandomChar$numbers)+`
(RandomChar$numbers)+`
(RandomChar$numbers)+`
(RandomChar$numbers))
}

functiongetVolumeMountParameter($volumes,$hostPath,$containerPath){
$volume=$volumes|Where-Object{$_-like"*|$hostPath"-or$_-like"$hostPath|*"}
if($volume){
$volumeName=$volume.Split('|')[1]
"--mount source=$($volumeName),target=$containerPath"
}
else{
"--volume ""$($hostPath):$($containerPath)"""
}
}

functiontestPfxCertificate([string]$pfxFile,[SecureString]$pfxPassword,[string]$certkind){
if(!(Test-Path$pfxFile)){
throw"$certkind certificate file does not exist"
}
try{
$cert=New-ObjectSystem.Security.Cryptography.X509Certificates.X509Certificate2($pfxFile,$pfxPassword)
}
catch{
throw"Unable to read $certkind certificate. Error was $($_.Exception.Message)"
}
if([DateTime]::Now-gt$cert.NotAfter){
throw"$certkind certificate expired on $($cert.GetExpirationDateString())"
}
if([DateTime]::Now-gt$cert.NotAfter.AddDays(-14)){
Write-Host-ForegroundColorYellow"$certkind certificate will expire on $($cert.GetExpirationDateString())"
}
}

functionGetHash{
param(
[string]$str
)

$stream=[IO.MemoryStream]::new([Text.Encoding]::UTF8.GetBytes($str))
(Get-FileHash-InputStream$stream-AlgorithmSHA256).Hash
}

functionDownloadFileLow{
Param(
[string]$sourceUrl,
[string]$destinationFile,
[switch]$dontOverwrite,
[switch]$useDefaultCredentials,
[switch]$skipCertificateCheck,
[hashtable]$headers=@{"UserAgent"="BcContainerHelper $bcContainerHelperVersion"},
[int]$timeout=100
)

$handler=New-ObjectSystem.Net.Http.HttpClientHandler
if($skipCertificateCheck){
Write-Host"Disabling SSL Verification on HttpClient"
[SslVerification]::DisableSsl($handler)
}
if($useDefaultCredentials){
$handler.UseDefaultCredentials=$true
}
$httpClient=New-ObjectSystem.Net.Http.HttpClient-ArgumentList$handler
$httpClient.Timeout=[Timespan]::FromSeconds($timeout)
$headers.Keys|ForEach-Object{
$httpClient.DefaultRequestHeaders.Add($_,$headers."$_")
}
$stream=$null
$fileStream=$null
if($dontOverwrite){
$fileMode=[System.IO.FileMode]::CreateNew
}
else{
$fileMode=[System.IO.FileMode]::Create
}
try{
$stream=$httpClient.GetStreamAsync($sourceUrl).GetAwaiter().GetResult()
$fileStream=New-ObjectSystem.IO.Filestream($destinationFile,$fileMode)
if(-not$stream.CopyToAsync($fileStream).Wait($timeout*1000)){
throw"Timeout downloading file"
}
}
finally{
if($fileStream){
$fileStream.Close()
$fileStream.Dispose()
}
if($stream){
$stream.Dispose()
}
}
}

functionLoadDLL{
Param(
[string]$path
)
$bytes=[System.IO.File]::ReadAllBytes($path)
[System.Reflection.Assembly]::Load($bytes)|Out-Null
}

functionGetAppInfo{
Param(
[string[]]$appFiles,
[string]$compilerFolder,
[string]$cacheAppInfoPath=''
)

$appInfoCache=$null
$cacheUpdated=$false
if($cacheAppInfoPath){
if(Test-Path$cacheAppInfoPath){
$appInfoCache=Get-Content-Path$cacheAppInfoPath-Encodingutf8|ConvertFrom-Json
}
else{
$appInfoCache=@{}
}
}
Write-Host"::group::Getting .app info $cacheAppInfoPath"
$binPath=Join-Path$compilerFolder'compiler/extension/bin'
if($isLinux){
$alcPath=Join-Path$binPath'linux'
$alToolExe=Join-Path$alcPath'altool'
Write-Host"Setting execute permissions on altool"
&/usr/bin/envsudopwsh-command"& chmod +x $alToolExe"
}
else{
$alcPath=Join-Path$binPath'win32'
$alToolExe=Join-Path$alcPath'altool.exe'
}
if(-not(Test-Path$alcPath)){
$alcPath=$binPath
}
$alToolExists=Test-Path-Path$alToolExe-PathTypeLeaf
$alcDllPath=$alcPath
if(!$isLinux-and!$isPsCore){
$alcDllPath=$binPath
}

$ErrorActionPreference="STOP"
$assembliesAdded=$false
$packageStream=$null
$package=$null
try{
foreach($pathin$appFiles){
Write-Host-NoNewline"- $([System.IO.Path]::GetFileName($path))"
if($appInfoCache-and$appInfoCache.PSObject.Properties.Name-eq$path){
$appInfo=$appInfoCache."$path"
Write-Host" (cached)"
}
else{
if($alToolExists){
$manifest=CmdDo-Command$alToolExe-arguments@('GetPackageManifest',"""$path""")-returnValue-silent|ConvertFrom-Json
$appInfo=@{
"appId"=$manifest.id
"publisher"=$manifest.publisher
"name"=$manifest.name
"version"=$manifest.version
"application"="$(if($manifest.PSObject.Properties.Name -eq 'application'){$manifest.application})"
"platform"="$(if($manifest.PSObject.Properties.Name -eq 'platform'){$manifest.Platform})"
"propagateDependencies"=($manifest.PSObject.Properties.Name-eq'PropagateDependencies')-and$manifest.PropagateDependencies
"dependencies"=@(if($manifest.PSObject.Properties.Name-eq'dependencies'){$manifest.dependencies|ForEach-Object{@{"id"=$_.id;"name"=$_.name;"publisher"=$_.publisher;"version"=$_.version}}})
}
Write-Host" (succeeded using altool)"
}
else{
if(!$assembliesAdded){
Add-Type-AssemblyNameSystem.IO.Compression.FileSystem
Add-Type-AssemblyNameSystem.Text.Encoding
LoadDLL-Path(Join-Path$alcDllPathNewtonsoft.Json.dll)
LoadDLL-Path(Join-Path$alcDllPathSystem.Collections.Immutable.dll)
if(Test-Path(Join-Path$alcDllPathSystem.IO.Packaging.dll)){
LoadDLL-Path(Join-Path$alcDllPathSystem.IO.Packaging.dll)
}
LoadDLL-Path(Join-Path$alcDllPathMicrosoft.Dynamics.Nav.CodeAnalysis.dll)
$assembliesAdded=$true
}
$packageStream=[System.IO.File]::OpenRead($path)
$package=[Microsoft.Dynamics.Nav.CodeAnalysis.Packaging.NavAppPackageReader]::Create($PackageStream,$true)
$manifest=$package.ReadNavAppManifest()
$appInfo=@{
"appId"=$manifest.AppId
"publisher"=$manifest.AppPublisher
"name"=$manifest.AppName
"version"="$($manifest.AppVersion)"
"dependencies"=@($manifest.Dependencies|ForEach-Object{@{"id"=$_.AppId;"name"=$_.Name;"publisher"=$_.Publisher;"version"="$($_.Version)"}})
"application"="$($manifest.Application)"
"platform"="$($manifest.Platform)"
"propagateDependencies"=$manifest.PropagateDependencies
}
$packageStream.Close()
Write-Host" (succeeded using codeanalysis)"
}
if($cacheAppInfoPath){
$appInfoCache|Add-Member-MemberTypeNoteProperty-Name$path-Value$appInfo
$cacheUpdated=$true
}
}
@{
"Id"=$appInfo.appId
"AppId"=$appInfo.appId
"Publisher"=$appInfo.publisher
"Name"=$appInfo.name
"Version"=[System.Version]$appInfo.version
"Dependencies"=@($appInfo.dependencies)
"Path"=$path
"Application"=$appInfo.application
"Platform"=$appInfo.platform
"PropagateDependencies"=$appInfo.propagateDependencies
}
}
if($cacheUpdated){
$appInfoCache|ConvertTo-Json-Depth99|Set-Content-Path$cacheAppInfoPath-EncodingUTF8-Force
}
}
catch[System.Reflection.ReflectionTypeLoadException]{
Write-Host" (failed)"
if($_.Exception.LoaderExceptions){
$_.Exception.LoaderExceptions|Select-Object-PropertyMessage|Select-Object-Unique|ForEach-Object{
Write-Host"LoaderException: $($_.Message)"
}
}
throw
}
finally{
if($package){
$package.Dispose()
}
if($packageStream){
$packageStream.Dispose()
}
}
Write-Host"::endgroup::"
}

functionGetLatestAlLanguageExtensionVersionAndUrl{
Param(
[switch]$allowPrerelease
)

$listing=Invoke-WebRequest-MethodPOST-UseBasicParsing`
-Urihttps://marketplace.visualstudio.com/_apis/public/gallery/extensionquery?api-version=3.0-preview.1`
-Body'{"filters":[{"criteria":[{"filterType":8,"value":"Microsoft.VisualStudio.Code"},{"filterType":12,"value":"4096"},{"filterType":7,"value":"ms-dynamics-smb.al"}],"pageNumber":1,"pageSize":50,"sortBy":0,"sortOrder":0}],"assetTypes":[],"flags":0x192}'`
-ContentTypeapplication/json|ConvertFrom-Json

$result=$listing.results|Select-Object-First1-ExpandPropertyextensions`
|Select-Object-ExpandPropertyversions`
|Where-Object{($allowPrerelease.IsPresent-or!(($_.properties.Key-eq'Microsoft.VisualStudio.Code.PreRelease')-and($_.properties|where-object{$_.Key-eq'Microsoft.VisualStudio.Code.PreRelease'}).value-eq"true"))}`
|Select-Object-First1

if($result){
$vsixUrl=$result.files|Where-Object{$_.assetType-eq"Microsoft.VisualStudio.Services.VSIXPackage"}|Select-Object-ExpandPropertysource
if($vsixUrl){
return$result.version,$vsixUrl
}
}
throw"Unable to locate latest AL Language Extension from the VS Code Marketplace"
}

functionDetermineVsixFile{
Param(
[string]$vsixFile
)

if($vsixFile-eq'default'){
return''
}
elseif($vsixFile-eq'latest'-or$vsixFile-eq'preview'){
$version,$url=GetLatestAlLanguageExtensionVersionAndUrl-allowPrerelease:($vsixFile-eq'preview')
return$url
}
else{
return$vsixFile
}
}

# Cached Path for latest and preview versions of AL Language Extension
$AlLanguageExtenssionPath=@('','')

functionDownloadLatestAlLanguageExtension{
Param(
[switch]$allowPrerelease
)

# Check if we already have the latest version downloaded and located in this session
if($script:AlLanguageExtenssionPath[$allowPrerelease.IsPresent]){
$path=$script:AlLanguageExtenssionPath[$allowPrerelease.IsPresent]
if(Test-Path$path-PathTypeContainer){
return$path
}
else{
$script:AlLanguageExtenssionPath[$allowPrerelease.IsPresent]=''
}
}

$mutexName="DownloadAlLanguageExtension"
$mutex=New-ObjectSystem.Threading.Mutex($false,$mutexName)
try{
try{
if(!$mutex.WaitOne(1000)){
Write-Host"Waiting for other process downloading AL Language Extension"
$mutex.WaitOne()|Out-Null
Write-Host"Other process completed downloading"
}
}
catch[System.Threading.AbandonedMutexException]{
Write-Host"Other process terminated abnormally"
}

$version,$url=GetLatestAlLanguageExtensionVersionAndUrl-allowPrerelease:$allowPrerelease
$path=Join-Path$bcContainerHelperConfig.hostHelperFolder"alLanguageExtension/$version"
if(!(Test-Path$path-PathTypeContainer)){
$AlLanguageExtensionsFolder=Join-Path$bcContainerHelperConfig.hostHelperFolder"alLanguageExtension"
if(!(Test-Path$AlLanguageExtensionsFolder-PathTypeContainer)){
New-Item-Path$AlLanguageExtensionsFolder-ItemTypeDirectory|Out-Null
}
$description="AL Language Extension"
if($allowPrerelease){
$description+=" (Prerelease)"
}
$zipFile="$path.zip"
Download-File-sourceUrl$url-destinationFile$zipFile-Description$description
Expand-7zipArchive-Path$zipFile-DestinationPath$path
Remove-Item-Path$zipFile-Force
}
$script:AlLanguageExtenssionPath[$allowPrerelease.IsPresent]=$path
return$path
}
finally{
$mutex.ReleaseMutex()
}
}

functionRunAlTool{
Param(
[string[]]$arguments,
[switch]$usePrereleaseAlTool=($bccontainerHelperConfig.usePrereleaseAlTool)
)
$path=DownloadLatestAlLanguageExtension-allowPrerelease:$usePrereleaseAlTool
if($isLinux){
$alToolExe=Join-Path$path'extension/bin/linux/altool'
Write-Host"Setting execute permissions on altool"
&/usr/bin/envsudopwsh-command"& chmod +x $alToolExe"
}
else{
$alToolExe=Join-Path$path'extension/bin/win32/altool.exe'
}
CmdDo-Command$alToolExe-arguments$arguments-returnValue-silent
}

functionGetApplicationDependency([string]$appFile,[string]$minVersion="0.0"){
try{
$appJson=Get-AppJsonFromAppFile-appFile$appFile
}
catch{
Write-Host-ForegroundColorRed"Unable to read app $([System.IO.Path]::GetFileName($appFile)), ignoring application dependency check"
return$minVersion
}
$version=$minVersion
if($appJson.PSObject.Properties.Name-eq"Application"){
$version=$appJson.application
}
elseif($appJson.PSObject.Properties.Name-eq"dependencies"){
$baseAppDependency=$appJson.dependencies|Where-Object{$_.Name-eq"Base Application"-and$_.Publisher-eq"Microsoft"}
if($baseAppDependency){
$version=$baseAppDependency.Version
}
}
if([System.Version]$version-lt[System.Version]$minVersion){
$version=$minVersion
}
return$version
}

functionReplaceCDN{
Param(
[string]$sourceUrl,
[switch]$useBlobUrl
)

$bcCDNs=@(
@{"oldCDN"="bcartifacts.azureedge.net";"newCDN"="bcartifacts-exdbf9fwegejdqak.b02.azurefd.net";"blobUrl"="bcartifacts.blob.core.windows.net"},
@{"oldCDN"="bcinsider.azureedge.net";"newCDN"="bcinsider-fvh2ekdjecfjd6gk.b02.azurefd.net";"blobUrl"="bcinsider.blob.core.windows.net"},
@{"oldCDN"="bcpublicpreview.azureedge.net";"newCDN"="bcpublicpreview-f2ajahg0e2cudpgh.b02.azurefd.net";"blobUrl"="bcpublicpreview.blob.core.windows.net"},
@{"oldCDN"="businesscentralapps.azureedge.net";"newCDN"="businesscentralapps-hkdrdkaeangzfydv.b02.azurefd.net";"blobUrl"="businesscentralapps.blob.core.windows.net"},
@{"oldCDN"="bcprivate.azureedge.net";"newCDN"="bcprivate-fmdwbsb3ekbkc0bt.b02.azurefd.net";"blobUrl"="bcprivate.blob.core.windows.net"}
)

foreach($cdnin$bcCDNs){
$found=$false
$cdn.blobUrl,$cdn.newCDN,$cdn.oldCDN|ForEach-Object{
if($sourceUrl.ToLowerInvariant().StartsWith("https://$_/")){
$sourceUrl="https://$(if($useBlobUrl){$cdn.blobUrl}else{$cdn.newCDN})/$($sourceUrl.Substring($_.Length+9))"
$found=$true
}
if($sourceUrl-eq$_){
$sourceUrl="$(if($useBlobUrl){$cdn.blobUrl}else{$cdn.newCDN})"
$found=$true
}
}
if($found){
break
}
}
$sourceUrl
}

# SIG # Begin signature block
# MIImbAYJKoZIhvcNAQcCoIImXTCCJlkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDAJuFhQifFIMyp
# 3qVOnASg0BaanSFUN3w9gIYe4EKp8qCCH4QwggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwggYaMIIEAqADAgECAhBiHW0MUgGeO5B5FSCJIRwKMA0GCSqG
# SIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0
# ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBSb290IFI0
# NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5NTlaMFQxCzAJBgNVBAYTAkdC
# MRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVi
# bGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
# ggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxji*ztNsfvxYB5UXeWUzCxEeAEZG
# bEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NVDgFigOMYzB2OKhdqfWGVoYW3
# haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/36F09fy1tsB8je/RV0mIk8XL/
# tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05ZwmRmTnAO5/arnY83jeNzhP06S
# hdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm+qxp4VqpB3MV/h53yl41aHU5
# pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUedyz8rNyfQJy/aOs5b4s+ac7I
# H60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz44MPZ1f9+YEQIQty/NQd/2yGg
# W+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBMdlyh2n5HirY4jKnFH/9gRvd+
# QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQYMBaAFDLrkpr/NZZILyhAQnAg
# NpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritUpimqF6TNDDAOBgNVHQ8BAf8E
# BAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNVHSUEDDAKBggrBgEFBQcDAzAb
# BgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsGA1UdHwREMEIwQKA+oDyGOmh0
# dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nUm9v
# dFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsGAQUFBzAChjpodHRwOi8vY3J0
# LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ1Jvb3RSNDYucDdj
# MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0B
# AQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURhw1aVcdGRP4Wh60BAscjW4HL9
# hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0ZdOaWTsyNyBBsMLHqafvIhrCym
# laS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajjcw5+w/KeFvPYfLF/ldYpmlG+
# vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNcWbWDRF/3sBp6fWXhz7DcML4i
# TAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalOhOfCipnx8CaLZeVme5yELg09
# Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJszkyeiaerlphwoKx1uHRzNyE6
# bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z76mKnzAfZxCl/3dq3dUNw4rg3
# sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5JKdGvspbOrTfOXyXvmPL6E52z
# 1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHHj95Ejza63zdrEcxWLDX6xWls
# /GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2Bev6SivBBOHY+uqiirZtg0y9
# ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/L9Uo2bC5a4CH2RwwggZZMIIE
# waADAgECAhANIM3qwHRbWKHw+Zq6JhzlMA0GCSqGSIb3DQEBDAUAMFQxCzAJBgNV
# BAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzApBgNVBAMTIlNlY3Rp
# Z28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwHhcNMjExMDIyMDAwMDAwWhcN
# MjQxMDIxMjM1OTU5WjBdMQswCQYDVQQGEwJESzEUMBIGA1UECAwLSG92ZWRzdGFk
# ZW4xGzAZBgNVBAoMEkZyZWRkeSBLcmlzdGlhbnNlbjEbMBkGA1UEAwwSRnJlZGR5
# IEtyaXN0aWFuc2VuMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgYC5
# tlg+VRktRRkahxxaV8+DAd6vHoDpcO6w7yT24lnSoMuA6nR7kgy90Y/sHIwKE9Ww
# t/px/GAY8eBePWjJrFpG8fBtJbXadRTVd/470Hs/q9t+kh6A/0ELj7wYsKSNOyuF
# Poy4rtClOv9ZmrRpoDVnh8Epwg2DpklX2BNzykzBQxIbkpp+xVo2mhPNWDIesntc
# 4/BnSebLGw1Vkxmu2acKkIjYrne/7lsuyL9ue0vk8TGk9JBPNPbGKJvHu9szP9oG
# oH36fU1sEZ+AacXrp+onsyPf/hkkpAMHAhzQHl+5Ikvcus/cDm06twm7VywmZcas
# 2rFAV5MyE6WMEaYAolwAHiPz9WAs2GDhFtZZg1tzbRjJIIgPpR+doTIcpcDBcHnN
# dSdgWKrTkr2f339oT5bnJfo7oVzc/2HGWvb8Fom6LQAqSC11vWmznHYsCm72g+fo
# TKqW8lLDfLF0+aFvToLosrtW9l6Z+l+RQ8MtJ9EHOm2Ny8cFLzZCDZYw32BydwcL
# V5rKdy4Ica9on5xZvyMOLiFwuL4v2V4pjEgKJaGSS/IVSMEGjrM9DHT6YS4/oq9q
# 20rQUmMZZQmGmEyyKQ8t11si8VHtScN5m0Li8peoWfCU9mRFxSESwTWow8d462+o
# 9/SzmDxCACdFwzvfKx4JqDMm55cL+beunIvc0NsCAwEAAaOCAZwwggGYMB8GA1Ud
# IwQYMBaAFA8qyyCHKLjsb0iuK1SmKaoXpM0MMB0GA1UdDgQWBBTZD6uy9ZWIIqQh
# 3srYu1FlUhdM0TAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADATBgNVHSUE
# DDAKBggrBgEFBQcDAzARBglghkgBhvhCAQEEBAMCBBAwSgYDVR0gBEMwQTA1Bgwr
# BgEEAbIxAQIBAwIwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9D
# UFMwCAYGZ4EMAQQBMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuc2VjdGln
# by5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FSMzYuY3JsMHkGCCsGAQUF
# BwEBBG0wazBEBggrBgEFBQcwAoY4aHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0
# aWdvUHVibGljQ29kZVNpZ25pbmdDQVIzNi5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6
# Ly9vY3NwLnNlY3RpZ28uY29tMA0GCSqGSIb3DQEBDAUAA4IBgQASEbZACurQeQN8
# WDTR+YyNpoQ29YAbbdBRhhzHkT/1ao7LE0QIOgGR4GwKRzufCAwu8pCBiMOUTDHT
# ezkh0rQrG6khxBX2nSTBL5i4LwKMR08HgZBsbECciABy15yexYWoB/D0H8WuGe63
# PhGWueR4IFPbIz+jEVxfW0Nyyr7bXTecpKd1iprm+TOmzc2E6ab95dkcXdJVx6Zy
# s++QrrOfQ+a57qEXkS/wnjjbN9hukL0zg+g8L4DHLKTodzfiQOampvV8QzbnB7Y8
# YjNcxR9s/nptnlQH3jorNFhktiBXvD62jc8pAIg6wyH6NxSMjtTsn7QhkIp2kusw
# IQwD8hN/fZ/m6gkXZhRJWFr2WRZOz+edZ62Jf25C/NYWscwfBwn2hzRZf1HgyxkX
# Al88dvvUA3kw1T6uo8aAB9IcL6Owiy7q4T+RLRF7oqx0vcw0193Yhq/gPOaUFlqz
# ExP6TQ5TR9XWVPQk+a1B1ATKMLi1JShO6KWTmNkFkgkgpkW69BEwggauMIIElqAD
# AgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYT
# AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy
# dC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAz
# MjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQK
# Ew5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBS
# U0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDM
# g/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOx
# s+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09ns
# ad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtA
# rF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLf*ck4fsbVYTXn+149z
# k6wsOeKlSNbwsDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6
# OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qh
# HGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1
# KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX
# 6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0
# sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQID
# AQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2F
# L3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08w
# DgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEB
# BGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsG
# AQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz
# dGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgG
# BmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+Y
# qUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjY
# C+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0
# FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6
# WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGj
# VoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzp
# SwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwd
# eDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o
# 08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n
# +2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y
# 3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIO
# K+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIGwjCCBKqgAwIBAgIQBUSv85SdCDmm
# v9s/X+VhFjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMO
# RGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNB
# NDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIzMDcxNDAwMDAwMFoXDTM0
# MTAxMzIzNTk1OVowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMzCCAiIwDQYJKoZI
# hvcNAQEBBQADggIPADCCAgoCggIBAKNTRYcdg45brD5UsyPgz5/X5dLnXaEOCdwv
# SKOXejsqnGfcYhVYwamTEafNqrJq3RApih5iY2nTWJw1cb86l+uUUI8cIOrHmjsv
# lmbjaedp/lvD1isgHMGXlLSlUIHyz8sHpjBoyoNC2vx/CSSUpIIa2mq62DvKXd4Z
# GIX7ReoNYWyd/nFexAaaPPDFLnkPG2ZS48jWPl/aQ9OE9dDH9kgtXkV1lnX+3RCh
# G4PBuOZSlbVH13gpOWvgeFmX40QrStWVzu8IF+qCZE3/I+PKhu60pCFkcOvV5aDa
# Y7Mu6QXuqvYk9R28mxyyt1/f8O52fTGZZUdVnUokL6wrl76f5P17cz4y7lI0+9S7
# 69SgLDSb495uZBkHNwGRDxy1Uc2qTGaDiGhiu7xBG3gZbeTZD+BYQfvYsSzhUa+0
# rRUGFOpiCBPTaR58ZE2dD9/O0V6MqqtQFcmzyrzXxDtoRKOlO0L9c33u3Qr/eTQQ
# fqZcClhMAD6FaXXHg2TWdc2PEnZWpST618RrIbroHzSYLzrqawGw9/sqhux7Ujip
# mAmhcbJsca8+uG+W1eEQE/5hRwqM/vC2x9XH3mwk8L9CgsqgcT2ckpMEtGlwJw1P
# t7U20clfCKRwo+wK8REuZODLIivK8SgTIUlRfgZm0zu++uuRONhRB8qUt+JQofM6
# 04qDy0B7AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIw
# ADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjAL
# BglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYD
# VR0OBBYEFKW27xPn783QZKHVVqllMaPe1eNJMFoGA1UdHwRTMFEwT6BNoEuGSWh0
# dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZT
# SEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsG
# AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0
# dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQw
# OTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAIEa
# 1t6gqbWYF7xwjU+KPGic2CX/yyzkzepdIpLsjCICqbjPgKjZ5+PF7SaCinEvGN1O
# tt5s1+FgnCvt7T1IjrhrunxdvcJhN2hJd6PrkKoS1yeF844ektrCQDifXcigLiV4
# JZ0qBXqEKZi2V3mP2yZWK7Dzp703DNiYdk9WuVLCtp04qYHnbUFcjGnRuSvExnvP
# nPp44pMadqJpddNQ5EQSviANnqlE0PjlSXcIWiHFtM+YlRpUurm8wWkZus8W8oM3
# NG6wQSbd3lqXTzON1I13fXVFoaVYJmoDRd7ZULVQjK9WvUzF4UbFKNOt50MAcN7M
# mJ4ZiQPq1JE3701S88lgIcRWR+3aEUuMMsOI5ljitts++V+wQtaP4xeR0arAVeOG
# v6wnLEHQmjNKqDbUuXKWfpd5OEhfysLcPTLfddY2Z1qJ+Panx+VPNTwAvb6cKmx5
# AdzaROY63jg7B145WPR8czFVoIARyxQMfq68/qTreWWqaNYiyjvrmoI1VygWy2ny
# Mpqy0tg6uLFGhmu6F/3Ed2wVbK6rr3M66ElGt9V/zLY4wNjsHPW2obhDLN9OTH0e
# aHDAdwrUAuBcYLso/zjlUlrWrBciI0707NMX+1Br/wd3H3GXREHJuEbTbDJ8WC9n
# R2XlG3O2mflrLAZG70Ee8PBf4NvZrZCARK+AEEGKMYIGPjCCBjoCAQEwaDBUMQsw
# CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQDEyJT
# ZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgQ0EgUjM2AhANIM3qwHRbWKHw+Zq6
# JhzlMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAw
# GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG
# AQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIF1P7wgmsCfco*kSjCCGwUBBDc17LMpFK
# bJ7v/wZ6GIQ/MA0GCSqGSIb3DQEBAQUABIICAHRUpnktaqdpDQNMkQsUYzgl8Pcy
# /DF+IosXV004P8z868e+0HsiL6GpT7ubYpdOn7P5LNpqSjvMyG4+akfOijSkExAf
# LPu/+S18wCpiuVlV+g5hQM1UMIojZi5cTeMxlMH3li0oq2ot6fjEdSvYbsrw5Jpq
# tTeLWA1KKxG4qHM9E+MKmNZME8CdmEdY7V1l1xxcYC43IC22QCURULjYs3nfDMNS
# EF8lKoDRmP/9fzEQAYMpfjfyV3O/fmrkWUn7Gz3VFHqtfOaZBWPgKxWs+BXteOUa
# rGex4U1HuiKdcstlD+tRAt4J1TTerM8LneXvXfc1vJokhmqbSe93eo9BthV0rcmV
# 1/xPinqFir7nGgBjD/7n0cTRLhYq+nhu6L7wnFI/xsj9XBS1q/D3lf7aZW5sqTZx
# 088efq9cBD4rXJ4JQ339QQVd3jhWlDLy7gBdU2ySpz3Q5/gFGGwNzkNli9PtrwFm
# O+8at3myvJNhVkC7dlk1yO/wsiq5B2A0Cbl9q/1zUKLCaYeoNeeTt2jX7TbxAza0
# Dgs8FxOo8TG7yE24LOHgmjSFTJWJTFn0JF62KpnFu7OV/0r0K+2IhaLLXaTsCe6N
# OfiC8Z8oHLUTJSqYQtDU4pyNOKoOmvovSu9ko67q00ay32gZXZHFl4Y3/rLfAMqx
# jstH+BpsS2b0OU5IoYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0wggMJAgEBMHcwYzEL
# MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJE
# aWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBD
# QQIQBUSv85SdCDmmv9s/X+VhFjANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJ
# AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI0MDYyNDE2NTI0N1owLwYJ
# KoZIhvcNAQkEMSIEIN4JTRe1rbp5rgaocSfXU8+rJ8HLXVIRmW9CjCXSRDX0MA0G
# CSqGSIb3DQEBAQUABIICAA7PwwrSWKVA/qQONfIGM1PtGGnn6mFmJRrFJ9DzYRiy
# u+XoF3WNWkDFt6gONQiqpJN58EcyyfkIcTAW7gatqLNiCAm/xZ3bBWrtahuPMcR7
# O8/W61N/XCXJ7jzW41R4hNu5zxMfVOACTPjMKXhlG4P44CSBmXqv1klWQTZnnSEl
# eFyGjTSksN0MaKchlHiaazbVNz6p4uXQcP2fXvBrTYvc85to6J+s+uIyLps9rTqn
# SoT9kJg7cbPp7xdSi/JyQenEtb8Zy0u7FYt6JJ+LBGw5OKwSIx6JQDxX5+12mxbI
# pAQU1ystZn8/Q0nI3rh8SCBluS1oV7AvvjR9qUF6W5UCYPF8eGCR8lQ5C0BsovTx
# IXSAXkjzpyF0b2G9p+nc6GAe4DIQWW162zdbXTvdiYEE10ItWOVPbdwsDtIKDXMr
# Z3wJCQOSQouBUnNX7FjB6UJL46hbHonzuMeaUYMmlOtIyra304j/uQg4s9UO9JcQ
# GhUYF3o9oBv0bzX+tFXxNnlujVeUtiUbAkzIqYOn4tQgtdpKt9gv1DPHB5HR4IvX
# JY9AMM1nkjTGjV0LPNySIo+C4Icri+0aJehEX3yO6tqFfFWK97PZcexElHcQt2k+
# EOHw6Vohj4LjuwWgLpnc20/Fdjp3xEA6UUXoSNqgmxvhQOHG+iQ9Zu1r8s5+rauJ
# SIG # End signature block

PowerShell Gallery
        | HelperFunctions.ps1 6.0.19 (2024)
Top Articles
Latest Posts
Article information

Author: Cheryll Lueilwitz

Last Updated:

Views: 5998

Rating: 4.3 / 5 (74 voted)

Reviews: 89% of readers found this page helpful

Author information

Name: Cheryll Lueilwitz

Birthday: 1997-12-23

Address: 4653 O'Kon Hill, Lake Juanstad, AR 65469

Phone: +494124489301

Job: Marketing Representative

Hobby: Reading, Ice skating, Foraging, BASE jumping, Hiking, Skateboarding, Kayaking

Introduction: My name is Cheryll Lueilwitz, I am a sparkling, clean, super, lucky, joyous, outstanding, lucky person who loves writing and wants to share my knowledge and understanding with you.