Functions/New-IntuneWinPackage.psm1
function New-IntuneWinPackage { [cmdletbinding()] param ( [String]$SourcePath, [String]$User, [String]$GraphAPI = "https://graph.microsoft.com/beta", [Boolean]$CreateGroup = $false, [Boolean]$UserInstallation = $false, [Boolean]$ServiceUI = $false ) function WriteHost($Code, $Message) { if($code -eq 1){ Write-Host "[Pending] | " $Message -f Magenta } if($code -eq 2){ Write-Host "[Success] | " $Message -f DarkGreen } if($code -eq 3){ Write-Host "[Failed] | " $Message -f DarkRed } if($code -eq 4){ Write-Host "[Info] | " $Message -f DarkYellow } } #################################################### function Get-AuthToken { $userUpn = New-Object "System.Net.Mail.MailAddress" -ArgumentList $User $tenant = $userUpn.Host Write-Host "Checking for AzureAD module..." $AadModule = Get-Module -Name "AzureAD" -ListAvailable if ($null -eq $AadModule) { Write-Host "AzureAD PowerShell module not found, looking for AzureADPreview" $AadModule = Get-Module -Name "AzureADPreview" -ListAvailable } if ($null -eq $AadModule) { write-host write-host "AzureAD Powershell module not installed..." -f Red write-host "Install by running 'Install-Module AzureAD' or 'Install-Module AzureADPreview' from an elevated PowerShell prompt" -f Yellow write-host "Script can't continue..." -f Red write-host exit } if($AadModule.count -gt 1){ $Latest_Version = ($AadModule | Select-Object version | Sort-Object)[-1] $aadModule = $AadModule | Where-Object { $_.version -eq $Latest_Version.version } if($AadModule.count -gt 1){ $aadModule = $AadModule | Select-Object -Unique } $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll" $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll" } else { $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll" $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll" } [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null [System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null $clientId = "d1ddf0e4-d672-4dae-b554-9d5bdfd93547" $redirectUri = "urn:ietf:wg:oauth:2.0:oob" $resourceAppIdURI = "https://graph.microsoft.com" $authority = "https://login.microsoftonline.com/$Tenant" try { $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority $platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto" $userId = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier" -ArgumentList ($User, "OptionalDisplayableId") $authResult = $authContext.AcquireTokenAsync($resourceAppIdURI,$clientId,$redirectUri,$platformParameters,$userId).Result if($authResult.AccessToken){ $global:authHeader = @{ 'Content-Type'='application/json' 'Authorization'="Bearer " + $authResult.AccessToken 'ExpiresOn'=$authResult.ExpiresOn } return $authHeader } else { Write-Host Write-Host "Authorization Access Token is null, please re-run authentication..." -ForegroundColor Red Write-Host break } } catch { write-host $_.Exception.Message -f Red write-host $_.Exception.ItemName -f Red write-host break } } #################################################### function CloneObject($object){ $stream = New-Object IO.MemoryStream; $formatter = New-Object Runtime.Serialization.Formatters.Binary.BinaryFormatter; $formatter.Serialize($stream, $object); $stream.Position = 0; $formatter.Deserialize($stream); } #################################################### function WriteHeaders($authToken){ foreach ($header in $authToken.GetEnumerator()){ if ($header.Name.ToLower() -eq "authorization"){ continue; } Write-Host -ForegroundColor Gray "$($header.Name): $($header.Value)"; } } #################################################### function MakeGetRequest($collectionPath){ $uri = "$baseUrl$collectionPath"; $request = "GET $uri"; if ($logRequestUris) { Write-Host $request; } if ($logHeaders) { WriteHeaders $authToken; } try{ Test-AuthToken $response = Invoke-RestMethod $uri -Method Get -Headers $authToken; $response; } catch{ Write-Host -ForegroundColor Red $request; Write-Host -ForegroundColor Red $_.Exception.Message; throw; } } #################################################### function MakePatchRequest($collectionPath, $body){ MakeRequest "PATCH" $collectionPath $body; } #################################################### function MakePostRequest($collectionPath, $body){ MakeRequest "POST" $collectionPath $body; } #################################################### function MakeRequest($verb, $collectionPath, $body){ $uri = "$baseUrl$collectionPath"; $request = "$verb $uri"; $clonedHeaders = CloneObject $authToken; $clonedHeaders["content-length"] = $body.Length; $clonedHeaders["content-type"] = "application/json"; if ($logRequestUris) { Write-Host $request; } if ($logHeaders) { WriteHeaders $clonedHeaders; } if ($logContent) { Write-Host -ForegroundColor Gray $body; } try{ Test-AuthToken $response = Invoke-RestMethod $uri -Method $verb -Headers $clonedHeaders -Body $body; $response; } catch{ if ($_.Exception.Response.StatusCode.value__ -eq 401 -or $_.Exception.Response.StatusCode.value__ -eq 403){ Write-Host "------ 401/403 - Unauthorized ------" WriteHost 3 "Access Denied! Check your Endpoint Permissions or PIM activation" } if ($_.Exception.Response.StatusCode.value__ -eq 400){ Write-Host "------ 400 - Bad Request ------" WriteHost 3 "Check your Application.xml or Application.png!" } Write-Host -ForegroundColor Red $request; Write-Host -ForegroundColor Red $_.Exception.Message; throw; } } #################################################### function UploadAzureStorageChunk($sasUri, $id, $body){ $uri = "$sasUri&comp=block&blockid=$id"; $request = "PUT $uri"; $iso = [System.Text.Encoding]::GetEncoding("iso-8859-1"); $encodedBody = $iso.GetString($body); $headers = @{ "x-ms-blob-type" = "BlockBlob" }; if ($logRequestUris) { Write-Host $request; } if ($logHeaders) { WriteHeaders $headers; } try{ Invoke-WebRequest $uri -Method Put -Headers $headers -Body $encodedBody | Out-Null } catch{ Write-Host -ForegroundColor Red $request; Write-Host -ForegroundColor Red $_.Exception.Message; throw; } } #################################################### function FinalizeAzureStorageUpload($sasUri, $ids){ $uri = "$sasUri&comp=blocklist"; $request = "PUT $uri"; $xml = '<?xml version="1.0" encoding="utf-8"?><BlockList>'; foreach ($id in $ids){ $xml += "<Latest>$id</Latest>"; } $xml += '</BlockList>'; if ($logRequestUris) { Write-Host $request; } if ($logContent) { Write-Host -ForegroundColor Gray $xml; } try{ Invoke-RestMethod $uri -Method Put -Body $xml; } catch{ Write-Host -ForegroundColor Red $request; Write-Host -ForegroundColor Red $_.Exception.Message; throw; } } #################################################### function UploadFileToAzureStorage($sasUri, $filepath, $fileUri){ try{ $chunkSizeInBytes = 1024l * 1024l * $azureStorageUploadChunkSizeInMb; $sasRenewalTimer = [System.Diagnostics.Stopwatch]::StartNew() $fileSize = (Get-Item $filepath).length; $chunks = [Math]::Ceiling($fileSize / $chunkSizeInBytes); $reader = New-Object System.IO.BinaryReader([System.IO.File]::Open($filepath, [System.IO.FileMode]::Open)); $reader.BaseStream.Seek(0, [System.IO.SeekOrigin]::Begin); $ids = @(); for ($chunk = 0; $chunk -lt $chunks; $chunk++){ $id = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($chunk.ToString("0000"))); $ids += $id; $start = $chunk * $chunkSizeInBytes; $length = [Math]::Min($chunkSizeInBytes, $fileSize - $start); $bytes = $reader.ReadBytes($length); $currentChunk = $chunk + 1; Write-Progress -Activity "Uploading File to Azure Storage" -status "Uploading chunk $currentChunk of $chunks" ` -percentComplete ($currentChunk / $chunks*100) UploadAzureStorageChunk $sasUri $id $bytes; if ($currentChunk -lt $chunks -and $sasRenewalTimer.ElapsedMilliseconds -ge 450000){ RenewAzureStorageUpload $fileUri $sasRenewalTimer.Restart(); } } Write-Progress -Completed -Activity "Uploading File to Azure Storage" $reader.Close(); } finally { if ($null -ne $reader) { $reader.Dispose(); } } FinalizeAzureStorageUpload $sasUri $ids; } #################################################### function RenewAzureStorageUpload($fileUri){ $renewalUri = "$fileUri/renewUpload"; $actionBody = ""; MakePostRequest $renewalUri $actionBody; WaitForFileProcessing $fileUri "AzureStorageUriRenewal" $azureStorageRenewSasUriBackOffTimeInSeconds; } #################################################### function WaitForFileProcessing($fileUri, $stage){ $attempts= 600; $waitTimeInSeconds = 10; $successState = "$($stage)Success"; $pendingState = "$($stage)Pending"; $file = $null; while ($attempts -gt 0) { $file = MakeGetRequest $fileUri; if ($file.uploadState -eq $successState){ break; } elseif ($file.uploadState -ne $pendingState){ Write-Host -ForegroundColor Red $_.Exception.Message; throw "File upload state is not success: $($file.uploadState)"; } Start-Sleep $waitTimeInSeconds; $attempts--; } if ($null -eq $file -or $file.uploadState -ne $successState){ throw "File request did not complete in the allotted time."; } $file; } #################################################### function GetWin32AppBody(){ param ( [parameter(Mandatory=$true,ParameterSetName = "MSI",Position=1)] [Switch]$MSI, [parameter(Mandatory=$true,ParameterSetName = "EXE",Position=1)] [Switch]$EXE, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$displayName, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$publisher, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$description, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$filename, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$SetupFileName, [parameter(Mandatory=$true)] [ValidateSet('system','user')] $installExperience = "system", [parameter(Mandatory=$true,ParameterSetName = "EXE")] [ValidateNotNullOrEmpty()] $installCommandLine, [parameter(Mandatory=$true,ParameterSetName = "EXE")] [ValidateNotNullOrEmpty()] $uninstallCommandLine, [parameter(Mandatory=$true,ParameterSetName = "MSI")] [ValidateNotNullOrEmpty()] $MsiPackageType, [parameter(Mandatory=$true,ParameterSetName = "MSI")] [ValidateNotNullOrEmpty()] $MsiProductCode, [parameter(Mandatory=$false,ParameterSetName = "MSI")] $MsiProductName, [parameter(Mandatory=$true,ParameterSetName = "MSI")] [ValidateNotNullOrEmpty()] $MsiProductVersion, [parameter(Mandatory=$false,ParameterSetName = "MSI")] $MsiPublisher, [parameter(Mandatory=$true,ParameterSetName = "MSI")] [ValidateNotNullOrEmpty()] $MsiRequiresReboot, [parameter(Mandatory=$true,ParameterSetName = "MSI")] [ValidateNotNullOrEmpty()] $MsiUpgradeCode ) $LogoBase64 = [Convert]::ToBase64String((Get-Content -Path $SourcePath\Application.png -Encoding Byte)) if($MSI){ #löschen $body = @{ "@odata.type" = "#microsoft.graph.win32LobApp" }; $body.applicableArchitectures = "x64,x86"; $body.description = $description; $body.developer = ""; $body.displayName = $displayName; $body.fileName = $filename; $body.installCommandLine = "msiexec /i `"$SetupFileName`"" $body.installExperience = @{"runAsAccount" = "$installExperience"}; $body.informationUrl = $null; $body.isFeatured = $false; $body.minimumSupportedOperatingSystem = @{"v10_1607" = $true}; $body.msiInformation = @{ "packageType" = "$MsiPackageType"; "productCode" = "$MsiProductCode"; "productName" = "$MsiProductName"; "productVersion" = "$MsiProductVersion"; "publisher" = "$MsiPublisher"; "requiresReboot" = "$MsiRequiresReboot"; "upgradeCode" = "$MsiUpgradeCode" }; $body.notes = ""; $body.owner = ""; $body.privacyInformationUrl = $null; $body.publisher = $publisher; $body.runAs32bit = $false; $body.setupFilePath = $SetupFileName; $body.largeIcon = @{ "@odata.type" = "microsoft.graph.mimeContent"; "type" = "binary"; "value" = "$LogoBase64"; }; $body.uninstallCommandLine = "msiexec /x `"$MsiProductCode`"" } elseif($EXE){ if ($UserInstallation){ $installExperience = "user" } $date = Get-Date -Format "MM/dd/yyyy" $body = @{ "@odata.type" = "#microsoft.graph.win32LobApp" }; $body.description = "Created by: " + $XMLScriptAuthor; $body.developer = "WAGNER AG"; $body.displayName = $XMLvendor + " " + $XMLname + " " + $XMLversion + " " + $XMLlanguage + " " + $XMLRevision; $body.fileName = $filename; $body.installCommandLine = "$installCommandLine" $body.installExperience = @{"runAsAccount" = "$installExperience"}; $body.informationUrl = $null; $body.isFeatured = $false; $body.minimumSupportedOperatingSystem = @{"v10_1607" = $true}; $body.msiInformation = $null; $body.notes = $XMLsoftwaretype; $body.owner = "WAGNER AG"; $body.privacyInformationUrl = $null; $body.publisher = $publisher; $body.runAs32bit = $false; $body.setupFilePath = $SetupFileName; $body.largeIcon = @{ "@odata.type" = "microsoft.graph.mimeContent"; "type" = "binary"; "value" = "$LogoBase64"; }; $body.uninstallCommandLine = "$uninstallCommandLine" } return $body } #################################################### function GetAppFileBody($name, $size, $sizeEncrypted, $manifest){ $body = @{ "@odata.type" = "#microsoft.graph.mobileAppContentFile" }; $body.name = $name; $body.size = $size; $body.sizeEncrypted = $sizeEncrypted; $body.manifest = $manifest; $body.isDependency = $false; return $body } #################################################### function GetAppCommitBody($contentVersionId, $LobType){ $body = @{ "@odata.type" = "#$LobType" }; $body.committedContentVersion = $contentVersionId; return $body } #################################################### Function Test-SourceFile(){ param ( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $SourceFile ) try { if(!(test-path "$SourceFile")){ Write-Host Write-Host "Source File '$sourceFile' doesn't exist..." -ForegroundColor Red throw } } catch { Write-Host -ForegroundColor Red $_.Exception.Message; Write-Host break } } #################################################### Function New-DetectionRule(){ [cmdletbinding()] param ( [parameter(Mandatory=$true,ParameterSetName = "PowerShell",Position=1)] [Switch]$PowerShell, [parameter(Mandatory=$true,ParameterSetName = "MSI",Position=1)] [Switch]$MSI, [parameter(Mandatory=$true,ParameterSetName = "File",Position=1)] [Switch]$File, [parameter(Mandatory=$true,ParameterSetName = "Registry",Position=1)] [Switch]$Registry, [parameter(Mandatory=$true,ParameterSetName = "PowerShell")] [ValidateNotNullOrEmpty()] [String]$ScriptFile, [parameter(Mandatory=$true,ParameterSetName = "PowerShell")] [ValidateNotNullOrEmpty()] $enforceSignatureCheck, [parameter(Mandatory=$true,ParameterSetName = "PowerShell")] [ValidateNotNullOrEmpty()] $runAs32Bit, [parameter(Mandatory=$true,ParameterSetName = "MSI")] [ValidateNotNullOrEmpty()] [String]$MSIproductCode, [parameter(Mandatory=$true,ParameterSetName = "File")] [ValidateNotNullOrEmpty()] [String]$Path, [parameter(Mandatory=$true,ParameterSetName = "File")] [ValidateNotNullOrEmpty()] [string]$FileOrFolderName, [parameter(Mandatory=$true,ParameterSetName = "File")] [ValidateSet("notConfigured","exists","modifiedDate","createdDate","version","sizeInMB")] [string]$FileDetectionType, [parameter(Mandatory=$false,ParameterSetName = "File")] $FileDetectionValue = $null, [parameter(Mandatory=$true,ParameterSetName = "File")] [ValidateSet("True","False")] [string]$check32BitOn64System = "False", [parameter(Mandatory=$true,ParameterSetName = "Registry")] [ValidateNotNullOrEmpty()] [String]$RegistryKeyPath, [parameter(Mandatory=$true,ParameterSetName = "Registry")] [ValidateSet("notConfigured","exists","doesNotExist","string","integer","version")] [string]$RegistryDetectionType, [parameter(Mandatory=$false,ParameterSetName = "Registry")] [ValidateNotNullOrEmpty()] [String]$RegistryValue, [parameter(Mandatory=$true,ParameterSetName = "Registry")] [ValidateSet("True","False")] [string]$check32BitRegOn64System = "False" ) if($PowerShell){ if(!(Test-Path "$ScriptFile")){ Write-Host Write-Host "Could not find file '$ScriptFile'..." -ForegroundColor Red Write-Host "Script can't continue..." -ForegroundColor Red Write-Host break } $ScriptContent = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes("$ScriptFile")); $DR = @{ "@odata.type" = "#microsoft.graph.win32LobAppPowerShellScriptDetection" } $DR.enforceSignatureCheck = $false; $DR.runAs32Bit = $false; $DR.scriptContent = "$ScriptContent"; } elseif($MSI){ $DR = @{ "@odata.type" = "#microsoft.graph.win32LobAppProductCodeDetection" } $DR.productVersionOperator = "notConfigured"; $DR.productCode = "$MsiProductCode"; $DR.productVersion = $null; } elseif($File){ $DR = @{ "@odata.type" = "#microsoft.graph.win32LobAppFileSystemDetection" } $DR.check32BitOn64System = "$check32BitOn64System"; $DR.detectionType = "$FileDetectionType"; $DR.detectionValue = $FileDetectionValue; $DR.fileOrFolderName = "$FileOrFolderName"; $DR.operator = "notConfigured"; $DR.path = "$Path" } elseif($Registry){ $DR = @{ "@odata.type" = "#microsoft.graph.win32LobAppRegistryDetection" } $DR.check32BitOn64System = "$check32BitRegOn64System"; $DR.detectionType = "$RegistryDetectionType"; $DR.detectionValue = "Installed"; $DR.keyPath = "$RegistryKeyPath"; $DR.operator = "equal"; $DR.valueName = "$RegistryValue" } return $DR } #################################################### function Get-DefaultReturnCodes(){ @{"returnCode" = 0;"type" = "success"}, ` @{"returnCode" = 1707;"type" = "success"}, ` @{"returnCode" = 3010;"type" = "softReboot"}, ` @{"returnCode" = 1641;"type" = "hardReboot"}, ` @{"returnCode" = 1618;"type" = "retry"} } #################################################### function New-ReturnCode(){ param ( [parameter(Mandatory=$true)] [int]$returnCode, [parameter(Mandatory=$true)] [ValidateSet('success','softReboot','hardReboot','retry')] $type ) @{"returnCode" = $returnCode;"type" = "$type"} } #################################################### Function Get-IntuneWinXML(){ param ( [Parameter(Mandatory=$true)] $SourceFile, [Parameter(Mandatory=$true)] $fileName, [Parameter(Mandatory=$false)] [ValidateSet("false","true")] [string]$removeitem = "true" ) Test-SourceFile "$SourceFile" $Directory = [System.IO.Path]::GetDirectoryName("$SourceFile") Add-Type -Assembly System.IO.Compression.FileSystem $zip = [IO.Compression.ZipFile]::OpenRead("$SourceFile") $zip.Entries | Where-Object {$_.Name -like "$filename" } | ForEach-Object { [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$Directory\$filename", $true) } $zip.Dispose() [xml]$IntuneWinXML = Get-Content "$Directory\$filename" return $IntuneWinXML if($removeitem -eq "true"){ remove-item "$Directory\$filename" } } #################################################### Function Get-IntuneWinFile(){ param ( [Parameter(Mandatory=$true)] $SourceFile, [Parameter(Mandatory=$true)] $fileName, [Parameter(Mandatory=$false)] [string]$Folder = "win32" ) $Directory = [System.IO.Path]::GetDirectoryName("$SourceFile") if(!(Test-Path "$Directory\$folder")){ New-Item -ItemType Directory -Path "$Directory" -Name "$folder" | Out-Null } Add-Type -Assembly System.IO.Compression.FileSystem $zip = [IO.Compression.ZipFile]::OpenRead("$SourceFile") $zip.Entries | Where-Object {$_.Name -like "$filename" } | ForEach-Object { [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$Directory\$folder\$filename", $true) } $zip.Dispose() return "$Directory\$folder\$filename" if($removeitem -eq "true"){ remove-item "$Directory\$filename" } } #################################################### function Add-Win32Lob(){ [cmdletbinding()] param ( [parameter(Mandatory=$true,Position=1)] [ValidateNotNullOrEmpty()] [string]$SourceFile, [parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$displayName, [parameter(Mandatory=$true,Position=2)] [ValidateNotNullOrEmpty()] [string]$publisher, [parameter(Mandatory=$true,Position=3)] [ValidateNotNullOrEmpty()] [string]$description, [parameter(Mandatory=$true,Position=4)] [ValidateNotNullOrEmpty()] $detectionRules, [parameter(Mandatory=$true,Position=5)] [ValidateNotNullOrEmpty()] $returnCodes, [parameter(Mandatory=$false,Position=6)] [ValidateNotNullOrEmpty()] [string]$installCmdLine, [parameter(Mandatory=$false,Position=7)] [ValidateNotNullOrEmpty()] [string]$uninstallCmdLine, [parameter(Mandatory=$false,Position=8)] [ValidateSet('system','user')] $installExperience = "system" ) try { $LOBType = "microsoft.graph.win32LobApp" Write-Host "Testing if SourceFile '$SourceFile' Path is valid..." -ForegroundColor Yellow Test-SourceFile "$SourceFile" Write-Host Write-Host "Creating JSON data to pass to the service..." -ForegroundColor Yellow $DetectionXML = Get-IntuneWinXML "$SourceFile" -fileName "detection.xml" if($displayName){ $DisplayName = $displayName } else { $DisplayName = $DetectionXML.ApplicationInfo.Name } $FileName = $DetectionXML.ApplicationInfo.FileName $SetupFileName = $DetectionXML.ApplicationInfo.SetupFile $Ext = [System.IO.Path]::GetExtension($SetupFileName) if((($Ext).contains("msi") -or ($Ext).contains("Msi")) -and (!$installCmdLine -or !$uninstallCmdLine)){ $MsiExecutionContext = $DetectionXML.ApplicationInfo.MsiInfo.MsiExecutionContext $MsiPackageType = "DualPurpose"; if($MsiExecutionContext -eq "System") { $MsiPackageType = "PerMachine" } elseif($MsiExecutionContext -eq "User") { $MsiPackageType = "PerUser" } $MsiProductCode = $DetectionXML.ApplicationInfo.MsiInfo.MsiProductCode $MsiProductVersion = $DetectionXML.ApplicationInfo.MsiInfo.MsiProductVersion $MsiPublisher = $DetectionXML.ApplicationInfo.MsiInfo.MsiPublisher $MsiRequiresReboot = $DetectionXML.ApplicationInfo.MsiInfo.MsiRequiresReboot $MsiUpgradeCode = $DetectionXML.ApplicationInfo.MsiInfo.MsiUpgradeCode if($MsiRequiresReboot -eq "false"){ $MsiRequiresReboot = $false } elseif($MsiRequiresReboot -eq "true"){ $MsiRequiresReboot = $true } $mobileAppBody = GetWin32AppBody ` -MSI ` -displayName "$DisplayName" ` -publisher "$publisher" ` -description $description ` -filename $FileName ` -SetupFileName "$SetupFileName" ` -installExperience $installExperience ` -MsiPackageType $MsiPackageType ` -MsiProductCode $MsiProductCode ` -MsiProductName $displayName ` -MsiProductVersion $MsiProductVersion ` -MsiPublisher $MsiPublisher ` -MsiRequiresReboot $MsiRequiresReboot ` -MsiUpgradeCode $MsiUpgradeCode } else { $mobileAppBody = GetWin32AppBody -EXE -displayName "$DisplayName" -publisher "$publisher" ` -description $description -filename $FileName -SetupFileName "$SetupFileName" ` -installExperience $installExperience -installCommandLine $installCmdLine ` -uninstallCommandLine $uninstallcmdline } if($DetectionRules.'@odata.type' -contains "#microsoft.graph.win32LobAppPowerShellScriptDetection" -and @($DetectionRules).'@odata.type'.Count -gt 1){ Write-Host Write-Warning "A Detection Rule can either be 'Manually configure detection rules' or 'Use a custom detection script'" Write-Warning "It can't include both..." Write-Host break } else { $mobileAppBody | Add-Member -MemberType NoteProperty -Name 'detectionRules' -Value $detectionRules } if($returnCodes){ $mobileAppBody | Add-Member -MemberType NoteProperty -Name 'returnCodes' -Value @($returnCodes) } else { Write-Host Write-Warning "Intunewin file requires ReturnCodes to be specified" Write-Warning "If you want to use the default ReturnCode run 'Get-DefaultReturnCodes'" Write-Host break } Write-Host Write-Host "Creating application in Intune..." -ForegroundColor Yellow try { $mobileApp = MakePostRequest "mobileApps" ($mobileAppBody | ConvertTo-Json); } catch { WriteHost 1 "Clear Session..." powershell -nologo exit } Write-Host Write-Host "Creating Content Version in the service for the application..." -ForegroundColor Yellow $global:appId = $mobileApp.id; $contentVersionUri = "mobileApps/$appId/$LOBType/contentVersions"; $contentVersion = MakePostRequest $contentVersionUri "{}"; Write-Host Write-Host "Getting Encryption Information for '$SourceFile'..." -ForegroundColor Yellow $encryptionInfo = @{}; $encryptionInfo.encryptionKey = $DetectionXML.ApplicationInfo.EncryptionInfo.EncryptionKey $encryptionInfo.macKey = $DetectionXML.ApplicationInfo.EncryptionInfo.macKey $encryptionInfo.initializationVector = $DetectionXML.ApplicationInfo.EncryptionInfo.initializationVector $encryptionInfo.mac = $DetectionXML.ApplicationInfo.EncryptionInfo.mac $encryptionInfo.profileIdentifier = "ProfileVersion1"; $encryptionInfo.fileDigest = $DetectionXML.ApplicationInfo.EncryptionInfo.fileDigest $encryptionInfo.fileDigestAlgorithm = $DetectionXML.ApplicationInfo.EncryptionInfo.fileDigestAlgorithm $global:fileEncryptionInfo = @{}; $global:fileEncryptionInfo.fileEncryptionInfo = $encryptionInfo; $IntuneWinFile = Get-IntuneWinFile "$SourceFile" -fileName "$filename" [int64]$Size = $DetectionXML.ApplicationInfo.UnencryptedContentSize $EncrySize = (Get-Item "$IntuneWinFile").Length Write-Host Write-Host "Creating a new file entry in Azure for the upload..." -ForegroundColor Yellow $contentVersionId = $contentVersion.id; $fileBody = GetAppFileBody "$FileName" $Size $EncrySize $null; $filesUri = "mobileApps/$appId/$LOBType/contentVersions/$contentVersionId/files"; $file = MakePostRequest $filesUri ($fileBody | ConvertTo-Json); Write-Host Write-Host "Waiting for the file entry URI to be created..." -ForegroundColor Yellow $fileId = $file.id; $fileUri = "mobileApps/$appId/$LOBType/contentVersions/$contentVersionId/files/$fileId"; $file = WaitForFileProcessing $fileUri "AzureStorageUriRequest"; Write-Host Write-Host "Uploading file to Azure Storage..." -f Yellow $sasUri = $file.azureStorageUri; $global:sasUriClean = [uri]$sasUri $global:sasUriClean = "https://"+$global:sasUriClean.Host+$global:sasUriClean.AbsolutePath UploadFileToAzureStorage $file.azureStorageUri "$IntuneWinFile" $fileUri; [System.IO.Path]::GetDirectoryName("$IntuneWinFile") Remove-Item "$IntuneWinFile" -Force Write-Host Write-Host "Committing the file into Azure Storage..." -ForegroundColor Yellow $commitFileUri = "mobileApps/$appId/$LOBType/contentVersions/$contentVersionId/files/$fileId/commit"; MakePostRequest $commitFileUri ($global:fileEncryptionInfo | ConvertTo-Json); Write-Host Write-Host "Waiting for the service to process the commit file request..." -ForegroundColor Yellow $file = WaitForFileProcessing $fileUri "CommitFile"; Write-Host Write-Host "Committing the file into Azure Storage..." -ForegroundColor Yellow $commitAppUri = "mobileApps/$appId"; $commitAppBody = GetAppCommitBody $contentVersionId $LOBType; MakePatchRequest $commitAppUri ($commitAppBody | ConvertTo-Json); return $mobileApp.id } catch { Write-Host ""; Write-Host -ForegroundColor Red "Aborting with exception: $($_.Exception.ToString())"; } } #################################################### Function Test-AuthToken(){ if($global:authToken){ $DateTime = (Get-Date).ToUniversalTime() $TokenExpires = ($authToken.ExpiresOn.datetime - $DateTime).Minutes if($TokenExpires -le 0){ write-host "Authentication Token expired" $TokenExpires "minutes ago" -ForegroundColor Yellow write-host if($null -eq $User -or $User -eq ""){ $Global:User = Read-Host -Prompt "Please specify your user principal name for Azure Authentication" Write-Host } $global:authToken = Get-AuthToken -User $User } } else{ if($null -eq $User -or $User -eq ""){ $Global:User = Read-Host -Prompt "Please specify your user principal name for Azure Authentication" Write-Host } $global:authToken = Get-AuthToken -User $User } } #################################################### #---------------------------------------------------------[Get XML-File]-------------------------------------------------------- function Get-XMLFile() { WriteHost 1 "Get XML-File informations" [xml]$ApplicationXML = Get-Content $SourcePath\Application.xml $Global:XMLprefix = $ApplicationXML.Application.Prefix $Global:XMLvendor = $ApplicationXML.Application.vendor $Global:XMLname = $ApplicationXML.Application.name $Global:XMLRevision = $ApplicationXML.Application.Revision $Global:XMLversion = $ApplicationXML.Application.version $Global:XMLlanguage = $ApplicationXML.Application.language $Global:XMLsoftwaretype = $ApplicationXML.Application.SoftwareType $Global:XMLScriptAuthor = $ApplicationXML.Application.ScriptAuthor WriteHost 2 "Successfully created registry key" if ($UserInstallation){ $Global:SoftwareRegKey = "Computer\HKEY_CURRENT_USER\SOFTWARE\Wagner\SWD\"+$XMLprefix+$XMLvendor+" "+$XMLname+" "+$XMLversion+" "+$XMLlanguage+" "+$XMLRevision }else { $Global:SoftwareRegKey = "Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Wagner\SWD\"+$XMLprefix+$XMLvendor+" "+$XMLname+" "+$XMLversion+" "+$XMLlanguage+" "+$XMLRevision } Write-Host "----------------------" $Global:SoftwareRegKey Write-Host "----------------------" WriteHost 2 "Successfully created AADGroup name" $Global:AADGroupName = "SG_INTUNE_App_"+$XMLvendor+"-"+$XMLname+"-"+$XMLversion+"-"+$XMLlanguage Write-Host "----------------------" $Global:AADGroupName Write-Host "----------------------" } #---------------------------------------------------------[Create IntuneWIN App]-------------------------------------------------------- function New-IntuneWinApp() { WriteHost 1 "Create IntuneWinApp..." mkdir "$temp/$XMLname" &"$PSScriptRoot/IntuneWinAppUtil.exe" -c $SourcePath -s Deploy-Application.exe -o "$temp/$XMLname" -Force WriteHost 2 "IntuneWinApp created successfully" } #---------------------------------------------------------[Check AAD Group]-------------------------------------------------------- function Get-AADGroup() { $GroupExists = $false WriteHost 1 ("Check if "+$AADGroupName+" exists...") $GraphAPI = $GraphAPI+'/groups' $SearchAADGroup = Invoke-RestMethod -Method "GET" -Uri $GraphAPI -Headers $authHeader $SearchAADGroupNames = $SearchAADGroup.value.displayName foreach ($SearchAADGroupName in $SearchAADGroupNames) { if ($SearchAADGroupName -eq $AADGroupName) { $GroupExists = $true } } return $GroupExists } #---------------------------------------------------------[Create AAD-Group]-------------------------------------------------------- function Add-AADGroup() { WriteHost 1 ("Trying to create AAD Group "+$AADGroupName+"...") $params = @{ "description"= "Intune Software Distribution Group"; "displayName"= $AADGroupName; "mailEnabled"= $false; "securityEnabled" = $true; "mailNickname" = "Intune"; } $params = ($params|ConvertTo-Json) $GraphAPI = $GraphAPI+"/groups" $AADGroup = Invoke-RestMethod -Method "POST" -Uri $GraphAPI -Body $params -Headers $authHeader WriteHost 2 ("AAD Group "+$AADGroupName+ " createt successfully!") $AADGroupid = $AADGroup.id $GraphAPI = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$appId/assign" $JSON = @" { "mobileAppAssignments": [ { "@odata.type": "#microsoft.graph.mobileAppAssignment", "target": { "@odata.type": "#microsoft.graph.groupAssignmentTarget", "groupId": "$AADGroupid" }, "intent": "required" } ] } "@ WriteHost 1 "Create Group assignment..." $JSON Invoke-RestMethod -Method "POST" -Uri $GraphAPI -Body $JSON -Headers $authHeader WriteHost 2 "Assignment successful" } #---------------------------------------------------------[Add IntuneWin to Azure Table]-------------------------------------------------------- function AddAzureTable { $global:SourceBody = @{ 'RowKey'=($global:appId) 'PartitionKey'="IntuneWin" 'Name'=($Global:XMLname) 'Version'=($Global:XMLversion) 'Language'=($Global:XMLlanguage) 'Customer'=($Global:XMLprefix) 'Vendor'=($Global:XMLvendor) 'Revision'=($Global:XMLRevision) 'Scriptversion'=($Global:XMLscriptversion) 'UploadUser'=$User 'SoftwareType'=($Global:XMLsoftwaretype) 'IntuneWinURI'=($global:sasUriClean) 'EncryptionKey'=($global:fileEncryptionInfo.fileEncryptionInfo.encryptionKey) 'InitializationVector'=($global:fileEncryptionInfo.fileEncryptionInfo.initializationVector) } $global:sourceHeader = @{ 'Content-Type' = 'application/json' 'Accept'="*/*" } $StorageURL = "https://widstwaas001.table.core.windows.net/IntuneUploadTable"+($configPS.SASToken) Invoke-RestMethod -Method "POST" -Uri $StorageURL -Body ($global:SourceBody|ConvertTo-Json) -Header $global:sourceHeader | Out-Null } #---------------------------------------------------------[Start Script]-------------------------------------------------------- $PSScriptRoot Write-Host -NoNewLine -f Magenta "[Pending] -> Check for updates..." $version = (Get-Module -ListAvailable IntuneWinPackage) | Sort-Object Version -Descending | Select-Object Version -First 1 $psgalleryversion = Find-Module -Name IntuneWinPackage | Sort-Object Version -Descending | Select-Object Version -First 1 $stringver = $version | Select-Object @{n='ModuleVersion'; e={$_.Version -as [string]}} $a = $stringver | Select-Object Moduleversion -ExpandProperty Moduleversion $onlinever = $psgalleryversion | Select-Object @{n='OnlineVersion'; e={$_.Version -as [string]}} $b = $onlinever | Select-Object OnlineVersion -ExpandProperty OnlineVersion if ([version]"$a" -ge [version]"$b") { Write-Host -f DarkGreen "`r[Done] -> Check for updates " } else { Write-Host -f Yellow "`r[Warning] -> This PowerShell module is out of date! Please run Update-Module IntuneWinPackage in a PowerShell session with elevated rights." break } if (!$SourcePath){ $SourcePath = Read-Host "Source Path: " } WriteHost 1 "Checking parameters..." if (!$User) { WriteHost 3 "No user defined." Read-Host -Prompt "Press any key to continue" exit } if (![System.IO.File]::Exists("$SourcePath\Application.png")){ WriteHost 3 "No Icon (Application.png)" Read-Host -Prompt "Press any key to continue" exit } $temp = $env:TEMP $LocalAppdata = $env:LOCALAPPDATA Write-Host -NoNewLine -f Magenta "[Pending] -> Checking config file" if (!(Test-Path -Path "$LocalAppdata/IntuneWinPackage/config.json")){ New-Item -ItemType Directory -Force -Path "$LocalAppdat/IntuneWinPackage" | Out-Null New-Item -Path "$LocalAppdata/IntuneWinPackage" -Name "config.json" -Value '{"SASToken": ""}' -Force | Out-Null } #Config $configJSON = Get-Content -Path "$LocalAppdata/IntuneWinPackage/config.json" -raw $configPS = ConvertFrom-Json $configJSON if(!$configPS.SASToken){ Write-Host -f Yellow "`r[Warning] -> No SAS Token set! " $configPS.SASToken = Read-Host "SAS Token: " $configJSON = ConvertTo-Json -depth 32 $configPS $configJSON | Out-File "$LocalAppdata/IntuneWinPackage/config.json" } Write-Host -f DarkGreen "`r[Done] -> Checking config file " WriteHost 2 "Parameters OK!" $baseUrl = "https://graph.microsoft.com/beta/deviceAppManagement/" $logRequestUris = $true; $logHeaders = $false; $logContent = $true; $azureStorageUploadChunkSizeInMb = 6l; Test-AuthToken Get-AuthToken Get-XMLFile New-IntuneWinApp $SourceFile = "$temp/$XMLname/Deploy-Application.intunewin" $DetectionXML = Get-IntuneWinXML "$SourceFile" -fileName "detection.xml" $RegistryRule = New-DetectionRule -Registry -RegistryKeyPath "$Global:SoftwareRegKey" -RegistryValue "Status" -RegistryDetectionType string -check32BitRegOn64System False $DetectionRule = @($RegistryRule) $ReturnCodes = Get-DefaultReturnCodes $ReturnCodes += New-ReturnCode -returnCode 302 -type softReboot $ReturnCodes += New-ReturnCode -returnCode 145 -type hardReboot if ($ServiceUI){ Add-Win32Lob -SourceFile "$SourceFile" -publisher "$Global:XMLvendor" ` -description "Description" -detectionRules $DetectionRule -returnCodes $ReturnCodes ` -installCmdLine "ServiceUI.exe -Process:explorer.exe Deploy-Application.exe -DeploymentType Install" ` -uninstallCmdLine "ServiceUI.exe -Process:explorer.exe Deploy-Application.exe -DeploymentType Uninstall" }else { Add-Win32Lob -SourceFile "$SourceFile" -publisher "$Global:XMLvendor" ` -description "Description" -detectionRules $DetectionRule -returnCodes $ReturnCodes ` -installCmdLine "Deploy-Application.exe -DeploymentType Install" ` -uninstallCmdLine "Deploy-Application.exe -DeploymentType Uninstall" } if ($CreateGroup){ if (Get-AADGroup) { WriteHost 4 "There already exists a group named $AADGroupName. Should a new group be created?[y/n] (default: n)" $Answer = Read-Host if ($Answer -eq "y") { Add-AADGroup } } else { Add-AADGroup } } AddAzureTable Remove-Item -LiteralPath "$temp/$XMLname" -Force -Recurse } # SIG # Begin signature block # MIIt0wYJKoZIhvcNAQcCoIItxDCCLcACAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUSV0ru56i7StjlpMHmQzONXJS # YYiggiczMIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0B # AQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk # IElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQsw # CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu # ZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQw # ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz # 7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS # 5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7 # bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfI # SKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jH # trHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14 # Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2 # h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt # 6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPR # iQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ER # ElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4K # Jpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAd # BgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SS # y4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAk # BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAC # hjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURS # b290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRV # HSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyh # hyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO # 0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo # 8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSMb++h # UD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5x # aiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMIIFkDCCA3ig # AwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG # EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl # cnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMw # ODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UE # ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYD # VQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEppz1Y # q3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lX # FllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxe # TsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbu # yntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I # 9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmg # Z92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse # 5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKy # Ebe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwh # HbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/ # Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwID # AQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4E # FgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQADggIBALth2X2p # bL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY # ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdN # Oj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4 # i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJ # EVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9NcCOGDErcgdLM # MpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N0XWs0Mr7QbhD # parTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb/UdK # Dd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP # 0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLS # oCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9T # dSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+MIIGrjCCBJagAwIBAgIQBzY3tyRU # fNhHrP0oZipeWzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UE # ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYD # VQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcN # MzcwMzIyMjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs # IEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEy # NTYgVGltZVN0YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC # AgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+k # iPNo+n3znIkLf50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+va # PcQXf6sZKz5C3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RB # idx8ald68Dd5n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn # 7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAx # E6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB # 3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNC # aJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklS # UPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP # 015LdhJRk8mMDDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXi # YKNYCQEoAA6EVO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZ # MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCP # nshvMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQE # AwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYB # BQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0 # cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5j # cnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp # Z2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJ # YIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULh # sBguEE0TzzBTzr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAl # NDFnzbYSlm/EUExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XN # Q1/tYLaqT5Fmniye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ # 8NWKcXZl2szwcqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDn # mPv7pp1yr8THwcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsd # CEkPlM05et3/JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcm # a+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+ # 8kaddSweJywm228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6 # KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAj # fwAL5HYCJtnwZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucT # Dh3bNzgaoSv27dZ8/DCCBrAwggSYoAMCAQICEAitQLJg0pxMn17Nqb2TrtkwDQYJ # KoZIhvcNAQEMBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu # YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQg # VHJ1c3RlZCBSb290IEc0MB4XDTIxMDQyOTAwMDAwMFoXDTM2MDQyODIzNTk1OVow # aTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQD # EzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4 # NCAyMDIxIENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW0L0LQ # KK14t13VOVkbsYhC9TOM6z2Bl3DFu8SFJjCfpI5o2Fz16zQkB+FLT9N4Q/QX1x7a # +dLVZxpSTw6hV/yImcGRzIEDPk1wJGSzjeIIfTR9TIBXEmtDmpnyxTsf8u/LR1oT # pkyzASAl8xDTi7L7CPCK4J0JwGWn+piASTWHPVEZ6JAheEUuoZ8s4RjCGszF7pNJ # cEIyj/vG6hzzZWiRok1MghFIUmjeEL0UV13oGBNlxX+yT4UsSKRWhDXW+S6cqgAV # 0Tf+GgaUwnzI6hsy5srC9KejAw50pa85tqtgEuPo1rn3MeHcreQYoNjBI0dHs6EP # bqOrbZgGgxu3amct0r1EGpIQgY+wOwnXx5syWsL/amBUi0nBk+3htFzgb+sm+YzV # svk4EObqzpH1vtP7b5NhNFy8k0UogzYqZihfsHPOiyYlBrKD1Fz2FRlM7WLgXjPy # 6OjsCqewAyuRsjZ5vvetCB51pmXMu+NIUPN3kRr+21CiRshhWJj1fAIWPIMorTmG # 7NS3DVPQ+EfmdTCN7DCTdhSmW0tddGFNPxKRdt6/WMtyEClB8NXFbSZ2aBFBE1ia # 3CYrAfSJTVnbeM+BSj5AR1/JgVBzhRAjIVlgimRUwcwhGug4GXxmHM14OEUwmU// # Y09Mu6oNCFNBfFg9R7P6tuyMMgkCzGw8DFYRAgMBAAGjggFZMIIBVTASBgNVHRMB # Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBRoN+Drtjv4XxGG+/5hewiIZfROQjAfBgNV # HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD # VR0lBAwwCgYIKwYBBQUHAwMwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho # dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl # cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud # HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy # dXN0ZWRSb290RzQuY3JsMBwGA1UdIAQVMBMwBwYFZ4EMAQMwCAYGZ4EMAQQBMA0G # CSqGSIb3DQEBDAUAA4ICAQA6I0Q9jQh27o+8OpnTVuACGqX4SDTzLLbmdGb3lHKx # AMqvbDAnExKekESfS/2eo3wm1Te8Ol1IbZXVP0n0J7sWgUVQ/Zy9toXgdn43ccsi # 91qqkM/1k2rj6yDR1VB5iJqKisG2vaFIGH7c2IAaERkYzWGZgVb2yeN258TkG19D # +D6U/3Y5PZ7Umc9K3SjrXyahlVhI1Rr+1yc//ZDRdobdHLBgXPMNqO7giaG9OeE4 # Ttpuuzad++UhU1rDyulq8aI+20O4M8hPOBSSmfXdzlRt2V0CFB9AM3wD4pWywiF1 # c1LLRtjENByipUuNzW92NyyFPxrOJukYvpAHsEN/lYgggnDwzMrv/Sk1XB+JOFX3 # N4qLCaHLC+kxGv8uGVw5ceG+nKcKBtYmZ7eS5k5f3nqsSc8upHSSrds8pJyGH+PB # VhsrI/+PteqIe3Br5qC6/To/RabE6BaRUotBwEiES5ZNq0RA443wFSjO7fEYVgcq # LxDEDAhkPDOPriiMPMuPiAsNvzv0zh57ju+168u38HcT5ucoP6wSrqUvImxB+YJc # FWbMbA7KxYbD9iYzDAdLoNMHAmpqQDBISzSoUSC7rRuFCOJZDW3KBVAr6kocnqX9 # oKcfBnTn8tZSkP2vhUgh+Vc7tJwD7YZF9LRhbr9o4iZghurIr6n+lB3nYxs6hlZ4 # TjCCBsAwggSooAMCAQICEAxNaXJLlPo8Kko9KQeAPVowDQYJKoZIhvcNAQELBQAw # YzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQD # EzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGlu # ZyBDQTAeFw0yMjA5MjEwMDAwMDBaFw0zMzExMjEyMzU5NTlaMEYxCzAJBgNVBAYT # AlVTMREwDwYDVQQKEwhEaWdpQ2VydDEkMCIGA1UEAxMbRGlnaUNlcnQgVGltZXN0 # YW1wIDIwMjIgLSAyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAz+yl # JjrGqfJru43BDZrboegUhXQzGias0BxVHh42bbySVQxh9J0Jdz0Vlggva2Sk/QaD # FteRkjgcMQKW+3KxlzpVrzPsYYrppijbkGNcvYlT4DotjIdCriak5Lt4eLl6FuFW # xsC6ZFO7KhbnUEi7iGkMiMbxvuAvfTuxylONQIMe58tySSgeTIAehVbnhe3yYbyq # Ogd99qtu5Wbd4lz1L+2N1E2VhGjjgMtqedHSEJFGKes+JvK0jM1MuWbIu6pQOA3l # jJRdGVq/9XtAbm8WqJqclUeGhXk+DF5mjBoKJL6cqtKctvdPbnjEKD+jHA9QBje6 # CNk1prUe2nhYHTno+EyREJZ+TeHdwq2lfvgtGx/sK0YYoxn2Off1wU9xLokDEaJL # u5i/+k/kezbvBkTkVf826uV8MefzwlLE5hZ7Wn6lJXPbwGqZIS1j5Vn1TS+QHye3 # 0qsU5Thmh1EIa/tTQznQZPpWz+D0CuYUbWR4u5j9lMNzIfMvwi4g14Gs0/EH1OG9 # 2V1LbjGUKYvmQaRllMBY5eUuKZCmt2Fk+tkgbBhRYLqmgQ8JJVPxvzvpqwcOagc5 # YhnJ1oV/E9mNec9ixezhe7nMZxMHmsF47caIyLBuMnnHC1mDjcbu9Sx8e47LZInx # scS451NeX1XSfRkpWQNO+l3qRXMchH7XzuLUOncCAwEAAaOCAYswggGHMA4GA1Ud # DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMI # MCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATAfBgNVHSMEGDAWgBS6 # FtltTYUvcyl2mi91jGogj57IbzAdBgNVHQ4EFgQUYore0GH8jzEU7ZcLzT0qlBTf # UpwwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp # Z2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1NlRpbWVTdGFtcGluZ0NBLmNybDCB # kAYIKwYBBQUHAQEEgYMwgYAwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj # ZXJ0LmNvbTBYBggrBgEFBQcwAoZMaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t # L0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1NlRpbWVTdGFtcGluZ0NBLmNy # dDANBgkqhkiG9w0BAQsFAAOCAgEAVaoqGvNG83hXNzD8deNP1oUj8fz5lTmbJeb3 # coqYw3fUZPwV+zbCSVEseIhjVQlGOQD8adTKmyn7oz/AyQCbEx2wmIncePLNfIXN # U52vYuJhZqMUKkWHSphCK1D8G7WeCDAJ+uQt1wmJefkJ5ojOfRu4aqKbwVNgCeij # uJ3XrR8cuOyYQfD2DoD75P/fnRCn6wC6X0qPGjpStOq/CUkVNTZZmg9U0rIbf35e # Ca12VIp0bcrSBWcrduv/mLImlTgZiEQU5QpZomvnIj5EIdI/HMCb7XxIstiSDJFP # PGaUr10CU+ue4p7k0x+GAWScAMLpWnR1DT3heYi/HAGXyRkjgNc2Wl+WFrFjDMZG # QDvOXTXUWT5Dmhiuw8nLw/ubE19qtcfg8wXDWd8nYiveQclTuf80EGf2JjKYe/5c # QpSBlIKdrAqLxksVStOYkEVgM4DgI974A6T2RUflzrgDQkfoQTZxd639ouiXdE4u # 2h4djFrIHprVwvDGIqhPm73YHJpRxC+a9l+nJ5e6li6FV8Bg53hWf2rvwpWaSxEC # yIKcyRoFfLpxtU56mWz06J7UWpjIn7+NuxhcQ/XQKujiYu54BNu90ftbCqhwfvCX # hHjjCANdRyxjqCU4lwHSPzra5eX25pvcfizM/xdMTQCi2NYBDriL7ubgclWJLCcZ # YfZ3AYwwggfgMIIFyKADAgECAhAEvFAJrWGkMrU95ncQieRtMA0GCSqGSIb3DQEB # CwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8G # A1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBT # SEEzODQgMjAyMSBDQTEwHhcNMjIxMDEzMDAwMDAwWhcNMjMxMDEzMjM1OTU5WjCB # wDELMAkGA1UEBhMCQ0gxEjAQBgNVBAcTCUtpcmNoYmVyZzEuMCwGA1UEChMlV2Fn # bmVyIEFHIEluZm9ybWF0aWsgRGllbnN0bGVpc3R1bmdlbjEVMBMGA1UECxMMU21h # cnRXb3JraW5nMS4wLAYDVQQDEyVXYWduZXIgQUcgSW5mb3JtYXRpayBEaWVuc3Rs # ZWlzdHVuZ2VuMSYwJAYJKoZIhvcNAQkBFhdzaW1vbi5idWVobGVyQHdhZ25lci5j # aDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMdC5YMo70Hi3T+D+cuB # cv/hR8siaO2vzx91NoK/gjUP7XppSxmE6Ouxse5lsvWAXwVgNxYVs3JbMhjPAJqn # JvvJya9P7lEsWsHiX+R4COSOzdBzlYcxsY3lInP2vKfe7UvFxbc2tLbnrqW2AHWj # stppoKJ2aGmmXDnbR8Vs0OReM7AYItrAoFFf9wo7g6py44f5/uY2d8VXoljaeFt3 # 6kxkjgcLwj9hiQiO+BZsZG74bhm8sj/uXBmgn5h4H/gOCUfgVQIxeX+JyGLULZvX # aVCqLKC7JNoHVDSLFZhnaePN87A8u9Gacaw513IyjedBZWFkyE4U/Mz8VOBBYzNB # X43rcEaoq9wGiVb/HXIGwV/VUCg4pQ7c8FLCHMEykEEAhU2uEeS+dB5J/HrmvZbQ # rwbEGx166g/ENnXl8psBzmXSrwZHIGDmX3phtvL56Gea+Peh5lWTPVllMqw1mFck # XqU5JLbNgReal6Uy+ZNoqCrTKcYIRfdwmAdJ1Nh+UGJ0Le3/ZxqJUduQ2iFDbhw7 # oVMRqgVOA5S2k2JRLX0sCRmF9T8lw9npVlQwlRy01yYwq3T34OprOveXs12mUFys # 8VMudyk8qJ6sfg+w6NxgX1PStOgPnxSwPP1FZ9me1bwHJSImxpC6cff+2gQnafN4 # V9X2QMnkOzyDKJxTSzYFAHntAgMBAAGjggIqMIICJjAfBgNVHSMEGDAWgBRoN+Dr # tjv4XxGG+/5hewiIZfROQjAdBgNVHQ4EFgQUjA8vk7Wf1eqcSRK45LZdARisCtIw # IgYDVR0RBBswGYEXc2ltb24uYnVlaGxlckB3YWduZXIuY2gwDgYDVR0PAQH/BAQD # AgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0 # dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWdu # aW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5k # aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZT # SEEzODQyMDIxQ0ExLmNybDA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUF # BwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwgZQGCCsGAQUFBwEBBIGH # MIGEMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYB # BQUHMAKGUGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0 # ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAwGA1UdEwEB # /wQCMAAwDQYJKoZIhvcNAQELBQADggIBAL6Y+r+Pj3oQpcotIF7nDNUagOImzuAR # +WBLLwvIRG3TQQetHfCMReX1q1ytAkQEdlYYkReREkFMJniS+wxHmq5kDE2/06K/ # ujd+kt0VCBXQmIo2NXgoJEs5uD3nPYH7TLamlmdPvIoziEknzV8FlJqvy2QXCyml # xv3iDtnQnv8DZsnusktSnuUTl+KEOKmcVAPy96NMZlZ8rOuR+X8jShf4BwFRuz9e # 8mqtc7moCMZLjyBl5gJCuMSXo22ytDEKwFecbLCvxETA6fh3WJOdufAwZEmcDE79 # BZEcr5q5xnbE1GqnZkT1oJiMoyyhw6DOf3r6o2hKjeYdYSXBhAV7ZAQLz1y90LkT # 1ABkzN6W+vQL0Mv//NZ6tv7WkgLJQg+D/Widf7f5d6y9eH7QuOTJ1kP6jicXtK/l # TETOc3odIJqItrEz2YiAawRKiB6y3Ur8AqwOdb/ewwAYPsPFFv0I1p6LndmBNFET # iyEcrsH587w4xz7DslJ2l84tZocFtfXdO2HI3SpQCWB31CjqCs3iHj3dNTGQZUq0 # eetkK8bKtDLd6bZpvoH1k90rI3XRX/d7Gh4yZyHJcurTVFr37TCWWGbDlnqugpdn # w5KNIpn1wy6jR0BrzNRPucI9fxxXz1JsR4d0zqVR3Kgocrnf76bp0S0P3i1TcVNe # r/iwMkHUI17eMYIGCjCCBgYCAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMO # RGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29k # ZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExAhAEvFAJrWGkMrU95ncQ # ieRtMAkGBSsOAwIaBQCgQDAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAjBgkq # hkiG9w0BCQQxFgQUWsrntvdZEJ9QMkxBlRHPz7dyQw0wDQYJKoZIhvcNAQEBBQAE # ggIAWPtV35YM+MdvKpn4/LRPtA1aSY+ll45bPxWJmxEplzUYOMM0h4Eks3xuTgbg # blbTD/OtPLjAdw/y6QDN1BR8vtnlOQHr74dE4Jx5dO1q2HYHT/hwIQNrEp/XIUZ0 # wDFv3tPxgK8ILNpAb/nYuiD0v852odPTCzqC8WGHJqjSfh5oea8VFuCxx59P1sGz # Y+tbwDUN1R0kNK0gV4vdhJ2UNEL5aRaWhvantyJ/9+AWupvoBTTTA3dElAb1T1Xe # wnKBL2J/Uc3BX2P9FDlpgs4bRl0AOKBDRvqnU5jOsPKoCiRjya3Bqup857ctD+kw # Vr8FBH8TAaaboglcw/AlYslPerhu2aE8CArxOyQGRDvnhRj7voojGL2Y3VoX5uII # Zl+hazG1kMHLrGqOgdhkXIdcqtTKK98JzO9gb1xn4NBO4DRnwsxcVRN3DuVE2CvO # cAszkZHB+O0Jk9XPK0YKPtmUSyNdFj0kwzio49oYNQa374C2C7WXgqkH22KoQhI6 # NjlKrLAIaHVpu8faKEGYxziOKq7QyiQaPWCAcwL/eeXRIgDT3JfFtY/V+1vknVtF # 5AFhHNrrOXYCxI42qoqp9MhLnxXGjgj+Uqls+qzxP8ofmQew7lXpFl3fCmRZMpzq # clOGvfg9qU/InWC4lnEeMHH+w5pn7zXoOSfAjtlD+MMcfD6hggMgMIIDHAYJKoZI # hvcNAQkGMYIDDTCCAwkCAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln # aUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5 # NiBTSEEyNTYgVGltZVN0YW1waW5nIENBAhAMTWlyS5T6PCpKPSkHgD1aMA0GCWCG # SAFlAwQCAQUAoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0B # CQUxDxcNMjIxMjE2MDk0NDA2WjAvBgkqhkiG9w0BCQQxIgQgvsCZn3QYcs1D1TMO # RCk490cf/rNaiavl2DJ7GwBZbwUwDQYJKoZIhvcNAQEBBQAEggIAo+VREN/VR2ig # UGf78VeNcxcLK+XCUTn6ouSWaNGdhXlCKA9BQDpdJ2wblpeBvLoDJ13zYdUlEZAd # kZRk3Bi5WkoNPxLvHVgWKsVA9n2qSz82mZK829vWMaGon78RId45hsDfMC3tBbEk # iPelYGjiUIsQ0637PDZfd9OPiN8IaSAh8bAFPSJHCJPkeScBdEVgBoRjJ7Y0CxtM # byGDv/J6hh18dGZf2eMQbYY0U3eFbutMGG9NTPnrif6S79mcpHOij5gW+8sZv1z+ # ht6b983OIkW/aNpoXbUSx/FmL1cGMVV+/P5Boz3XLD94GATlqVuZ8PMe6bA0SCVF # +tdeMGMcJVZIJjIJv3oVrE5KpkLX4Mx0qzPYPMMuTyY5HLRRspglRIetkYWwqoXv # itI7Euk8W5jMZEspJyokG0UXXjQT3i+km6E39PpbEsGxReryT5IZzIN2a2nuEXk5 # vclqRQZyhNbiMeGbKe1JxcyptCr2C/x8Y7W5nMJeON2+znY67LOGOSpRVg2Cjua9 # YubKF+adLSHtijlDMis6WyT+4ojDjVF7HPj4CBA/n85dVhlNTnGaA9ljrCIWD8cU # baiioU4zTRiM8RAtVmyucU/El0rrHjUBvqdeScvCug9mecx3OC7Z8lFrSpwBCAsg # p2Ui01H2z4kd4kWmKnWUlPmEIX6PHc4= # SIG # End signature block |