├── BitsOnNano.exe ├── ContainerImage.ps1xml ├── ContainerImage.psd1 ├── ContainerImage.psm1 ├── Json.coreclr.dll ├── LICENSE ├── README.md └── SaveHTTPItemUsingBITS.psm1 /BitsOnNano.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/ContainerProvider/266585a3de90c214276ec81dffbe89c9b050f62d/BitsOnNano.exe -------------------------------------------------------------------------------- /ContainerImage.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ContainerImage 6 | 7 | ContainerImageItem 8 | 9 | 10 | 11 | 12 | 20 13 | 14 | 15 | 23 16 | 17 | 18 | 20 19 | 20 | 21 | 35 22 | 23 | 24 | 25 | 26 | 27 | 28 | Name 29 | 30 | 31 | Version 32 | 33 | 34 | Source 35 | 36 | 37 | Description 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /ContainerImage.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/ContainerProvider/266585a3de90c214276ec81dffbe89c9b050f62d/ContainerImage.psd1 -------------------------------------------------------------------------------- /ContainerImage.psm1: -------------------------------------------------------------------------------- 1 | #region Variables 2 | $script:location_modules = "$env:LOCALAPPDATA\ContainerImage" 3 | $script:file_modules = "$script:location_modules\sources.txt" 4 | $script:ContainerSources = $null 5 | $script:wildcardOptions = [System.Management.Automation.WildcardOptions]::CultureInvariant -bor ` 6 | [System.Management.Automation.WildcardOptions]::IgnoreCase 7 | $script:ContainerImageSearchIndex = "ContainerImageSearchIndex.txt" 8 | $script:PSArtifactTypeModule = 'Module' 9 | $script:PSArtifactTypeScript = 'Script' 10 | $script:isNanoServerInitialized = $false 11 | $script:isNanoServer = $false 12 | $script:Providername = "ContainerImage" 13 | $separator = "|#|" 14 | 15 | Microsoft.PowerShell.Core\Set-StrictMode -Version Latest 16 | 17 | #endregion Variables 18 | 19 | #region One-Get Functions 20 | 21 | function Find-Package 22 | { 23 | [CmdletBinding()] 24 | param 25 | ( 26 | [string[]] 27 | $names, 28 | 29 | [string] 30 | $RequiredVersion, 31 | 32 | [string] 33 | $MinimumVersion, 34 | 35 | [string] 36 | $MaximumVersion 37 | ) 38 | 39 | Set-ModuleSourcesVariable 40 | 41 | $options = $request.Options 42 | 43 | foreach( $o in $options.Keys ) 44 | { 45 | Write-Debug ( "OPTION: {0} => {1}" -f ($o, $options[$o]) ) 46 | } 47 | 48 | $AllVersions = $null 49 | if($options.ContainsKey("AllVersions")) 50 | { 51 | $AllVersions = $options['AllVersions'] 52 | } 53 | 54 | $sources = @() 55 | if($options.ContainsKey('Source')) 56 | { 57 | $sources = $options['Source'] 58 | } 59 | 60 | $convertedRequiredVersion = Convert-Version $requiredVersion 61 | $convertedMinVersion = Convert-Version $minimumVersion 62 | $convertedMaxVersion = Convert-Version $maximumVersion 63 | 64 | if(-not (CheckVersion $convertedMinVersion $convertedMaxVersion $convertedRequiredVersion $AllVersions)) 65 | { 66 | return $null 67 | } 68 | 69 | if ($null -eq $names -or $names.Count -eq 0) 70 | { 71 | $names = @('') 72 | } 73 | 74 | $allResults = @() 75 | $allSources = Get-Sources $sources 76 | foreach($currSource in $allSources) 77 | { 78 | foreach ($singleName in $names) 79 | { 80 | if ([string]::IsNullOrWhiteSpace($singleName) -or $singleName.Trim() -eq '*') 81 | { 82 | # if no name is supplied but min or max version is supplied, error out 83 | if ($null -ne $convertedMinVersion -or $null -ne $convertedMaxVersion) 84 | { 85 | ThrowError -CallerPSCmdlet $PSCmdlet ` 86 | -ExceptionName System.Exception ` 87 | -ExceptionMessage "Name is required when either MinimumVersion or MaximumVersion parameter is used" ` 88 | -ExceptionObject $singleName ` 89 | -ErrorId NameRequiredForMinOrMaxVersion ` 90 | -ErrorCategory InvalidData 91 | } 92 | } 93 | 94 | $location = $currSource.SourceLocation 95 | $sourceName = $currSource.Name 96 | 97 | if($location.StartsWith("http://") -or $location.StartsWith("https://")) 98 | { 99 | $allResults += Find-Azure -Name $singleName ` 100 | -MinimumVersion $convertedMinVersion ` 101 | -MaximumVersion $convertedMaxVersion ` 102 | -RequiredVersion $convertedRequiredVersion ` 103 | -AllVersions:$AllVersions ` 104 | -Location $location ` 105 | -SourceName $sourceName 106 | } 107 | elseif($location.StartsWith("\\")) 108 | { 109 | $allResults += Find-UNCPath -Name $singleName ` 110 | -MinimumVersion $convertedMinVersion ` 111 | -MaximumVersion $convertedMaxVersion ` 112 | -RequiredVersion $convertedRequiredVersion ` 113 | -AllVersions:$AllVersions ` 114 | -localPath $location ` 115 | -SourceName $sourceName 116 | } 117 | else 118 | { 119 | Write-Error "Bad source '$sourceName' with location at $location" 120 | } 121 | } 122 | } 123 | 124 | if($null -eq $allResults) 125 | { 126 | return 127 | } 128 | 129 | foreach($result in $allResults) 130 | { 131 | $swid = New-SoftwareIdentityFromContainerImageItemInfo $result 132 | Write-Output $swid 133 | } 134 | } 135 | 136 | function Download-Package 137 | { 138 | [CmdletBinding()] 139 | param 140 | ( 141 | [Parameter(Mandatory=$true)] 142 | [ValidateNotNullOrEmpty()] 143 | [string] 144 | $FastPackageReference, 145 | 146 | [Parameter(Mandatory=$true)] 147 | [ValidateNotNullOrEmpty()] 148 | [string] 149 | $Location 150 | ) 151 | 152 | DownloadPackageHelper -FastPackageReference $FastPackageReference ` 153 | -Request $Request ` 154 | -Location $Location 155 | } 156 | 157 | function Install-Package 158 | { 159 | [CmdletBinding()] 160 | param 161 | ( 162 | [Parameter(Mandatory=$true)] 163 | [ValidateNotNullOrEmpty()] 164 | [string] 165 | $FastPackageReference 166 | ) 167 | 168 | [string[]] $splitterArray = @("$separator") 169 | [string[]] $resultArray = $FastPackageReference.Split($splitterArray, [System.StringSplitOptions]::None) 170 | 171 | $name = $resultArray[0] 172 | $version = $resultArray[1] 173 | $Location = $script:location_modules 174 | 175 | $Destination = GenerateFullPath -Location $Location ` 176 | -Name $name ` 177 | -Version $Version 178 | 179 | $downloadOutput = DownloadPackageHelper -FastPackageReference $FastPackageReference ` 180 | -Request $Request ` 181 | -Location $Location 182 | 183 | $startInstallTime = Get-Date 184 | 185 | if(-not (Test-Path $destination)) 186 | { 187 | Write-verbose "$Destination does not exist" 188 | } 189 | else 190 | { 191 | Write-verbose "$Destination does exist. I should install" 192 | } 193 | 194 | Write-Verbose "Trying to install the Image: $Destination" 195 | 196 | Install-ContainerOSImage -WimPath $Destination ` 197 | -Force 198 | 199 | $endInstallTime = Get-Date 200 | $differenceInstallTime = New-TimeSpan -Start $startInstallTime -End $endInstallTime 201 | $installTime = "Installed in " + $differenceInstallTime.Hours + " hours, " + $differenceInstallTime.Minutes + " minutes, " + $differenceInstallTime.Seconds + " seconds." 202 | Write-Verbose $installTime 203 | 204 | # Clean up 205 | Write-Verbose "Removing the installer: $Destination" 206 | rm $Destination 207 | 208 | Write-Output $downloadOutput 209 | } 210 | 211 | function Get-InstalledPackage 212 | { 213 | } 214 | 215 | function Initialize-Provider 216 | { 217 | write-debug "In $($script:Providername) - Initialize-Provider" 218 | } 219 | 220 | function Get-PackageProviderName 221 | { 222 | return $script:Providername 223 | } 224 | 225 | #endregion One-Get Functions 226 | 227 | #region Stand-Alone Functions 228 | 229 | function Find-ContainerImage 230 | { 231 | <# 232 | .Synopsis 233 | Finds the container image from an online gallery that match specified criteria. 234 | It can also search from other registered sources. 235 | .DESCRIPTION 236 | Find-ContainerImage finds the images from the online gallery that match specified criteria. 237 | .EXAMPLE 238 | Find-ContainerImage -Name ImageName 239 | .EXAMPLE 240 | Find-ContainerImage -MinimumVersion Version 241 | .EXAMPLE 242 | Find-ContainerImage -MaximumVersion Version 243 | .EXAMPLE 244 | Find-ContainerImage -RequiredVersion Version 245 | .EXAMPLE 246 | Find-ContainerImage -AllVersions 247 | #> 248 | 249 | [cmdletbinding()] 250 | param 251 | ( 252 | [Parameter(Mandatory=$false,Position=0)] 253 | [string[]] 254 | $Name, 255 | 256 | [System.Version] 257 | $MinimumVersion, 258 | 259 | [System.Version] 260 | $MaximumVersion, 261 | 262 | [System.Version] 263 | $RequiredVersion, 264 | 265 | [switch] 266 | $AllVersions, 267 | 268 | [System.string] 269 | $source 270 | ) 271 | 272 | Begin 273 | { 274 | } 275 | 276 | Process 277 | { 278 | $PSBoundParameters["Provider"] = $script:Providername 279 | 280 | PackageManagement\Find-Package @PSBoundParameters 281 | } 282 | } 283 | 284 | function Save-ContainerImage 285 | { 286 | <# 287 | .Synopsis 288 | Saves a container image without installing it. 289 | .DESCRIPTION 290 | The Save-ContainerImage cmdlet lets you save a container image locally without installing it. 291 | This lets you inspect the container image before you install, helping to minimize the risks 292 | of malicious code or malware on your system. 293 | As a best practice, when you have finished evaluating a container image for potential risks, 294 | and before you install the image for use, delete the image from the path to which you have saved. 295 | .EXAMPLE 296 | Save-ContainerImage -Name ImageName -Path C:\temp\ 297 | .EXAMPLE 298 | Save-ContainerImage -Name ImageName -LiteralPath C:\temp\ 299 | .EXAMPLE 300 | Save-ContainerImage -Name ImageName -MinimumVersion Version -Path .\..\ 301 | .EXAMPLE 302 | Save-ContainerImage -Name ImageName -MaximumVersion Version -LiteralPath C:\temp\ 303 | .EXAMPLE 304 | Save-ContainerImage -Name ImageName -RequiredVersion Version -Path C:\t*p\ 305 | .EXAMPLE 306 | Save-ContainerImage -Name ImageName -RequiredVersion Version -Path C:\t*p\ -Source ContainerImageGallery 307 | .EXAMPLE 308 | Find-ContainerImage -Name ImageName | Save-ContainerImage -LiteralPath C:\temp\ 309 | #> 310 | 311 | [CmdletBinding(DefaultParameterSetName='NameAndPathParameterSet', 312 | SupportsShouldProcess=$true)] 313 | Param 314 | ( 315 | [Parameter(Mandatory=$true, 316 | ValueFromPipelineByPropertyName=$true, 317 | Position=0, 318 | ParameterSetName='NameAndPathParameterSet')] 319 | [Parameter(Mandatory=$true, 320 | ValueFromPipelineByPropertyName=$true, 321 | Position=0, 322 | ParameterSetName='NameAndLiteralPathParameterSet')] 323 | [ValidateNotNullOrEmpty()] 324 | 325 | [string[]] 326 | $Name, 327 | 328 | [Parameter(Mandatory=$true, 329 | ValueFromPipeline=$true, 330 | ValueFromPipelineByPropertyName=$true, 331 | ParameterSetName='InputOjectAndPathParameterSet')] 332 | [Parameter(Mandatory=$true, 333 | ValueFromPipeline=$true, 334 | ValueFromPipelineByPropertyName=$true, 335 | ParameterSetName='InputOjectAndLiteralPathParameterSet')] 336 | [ValidateNotNull()] 337 | [PSCustomObject[]] 338 | $InputObject, 339 | 340 | [Parameter(ValueFromPipelineByPropertyName=$true, 341 | ParameterSetName='NameAndPathParameterSet')] 342 | [Parameter(ValueFromPipelineByPropertyName=$true, 343 | ParameterSetName='NameAndLiteralPathParameterSet')] 344 | [Version] 345 | $MinimumVersion, 346 | 347 | [Parameter(ValueFromPipelineByPropertyName=$true, 348 | ParameterSetName='NameAndPathParameterSet')] 349 | [Parameter(ValueFromPipelineByPropertyName=$true, 350 | ParameterSetName='NameAndLiteralPathParameterSet')] 351 | [Version] 352 | $MaximumVersion, 353 | 354 | [Parameter(ValueFromPipelineByPropertyName=$true, 355 | ParameterSetName='NameAndPathParameterSet')] 356 | [Parameter(ValueFromPipelineByPropertyName=$true, 357 | ParameterSetName='NameAndLiteralPathParameterSet')] 358 | [Alias('Version')] 359 | [Version] 360 | $RequiredVersion, 361 | 362 | [Parameter(Mandatory=$true, ParameterSetName='NameAndPathParameterSet')] 363 | [Parameter(Mandatory=$true, ParameterSetName='InputOjectAndPathParameterSet')] 364 | [string] 365 | $Path, 366 | 367 | [Parameter(Mandatory=$true, ParameterSetName='NameAndLiteralPathParameterSet')] 368 | [Parameter(Mandatory=$true, ParameterSetName='InputOjectAndLiteralPathParameterSet')] 369 | [string] 370 | $LiteralPath, 371 | 372 | [Parameter()] 373 | [switch] 374 | $Force, 375 | 376 | [Parameter()] 377 | [System.string] 378 | $source 379 | ) 380 | 381 | if($InputObject) 382 | { 383 | } 384 | else 385 | { 386 | $PSBoundParameters["Provider"] = $script:Providername 387 | } 388 | 389 | PackageManagement\Save-Package @PSBoundParameters 390 | } 391 | 392 | function Install-ContainerImage 393 | { 394 | <# 395 | .Synopsis 396 | Downloads the image from the cloud and installs them on the local computer. 397 | .DESCRIPTION 398 | The Install-ContainerImage gets the container image that meets the specified cirteria from the cloud. 399 | It saves the image locally and then installs it. 400 | .EXAMPLE 401 | Install-ContainerImage -Name ImageName 402 | .EXAMPLE 403 | Install-ContainerImage -Name ImageName -Source ContainerImageGallery 404 | .EXAMPLE 405 | Install-ContainerImage -Name ImageName -MinimumVersion Version 406 | .EXAMPLE 407 | Install-ContainerImage -Name ImageName -MaximumVersion Version 408 | .EXAMPLE 409 | Install-ContainerImage -Name ImageName -RequiredVersion Version 410 | .EXAMPLE 411 | Find-ContainerImage -Name ImageName | Install-ContainerImage 412 | #> 413 | 414 | [CmdletBinding(DefaultParameterSetName='NameAndPathParameterSet', 415 | SupportsShouldProcess=$true)] 416 | Param 417 | ( 418 | [Parameter(Mandatory=$true, 419 | ValueFromPipelineByPropertyName=$true, 420 | Position=0, 421 | ParameterSetName='NameParameterSet')] 422 | [ValidateNotNullOrEmpty()] 423 | [string[]] 424 | $Name, 425 | 426 | [Parameter(Mandatory=$true, 427 | ValueFromPipeline=$true, 428 | ValueFromPipelineByPropertyName=$true, 429 | Position=0, 430 | ParameterSetName='InputObject')] 431 | [ValidateNotNull()] 432 | [PSCustomObject[]] 433 | $InputObject, 434 | 435 | [Parameter(ValueFromPipelineByPropertyName=$true, 436 | ParameterSetName='NameParameterSet')] 437 | [Alias("Version")] 438 | [ValidateNotNull()] 439 | [Version] 440 | $MinimumVersion, 441 | 442 | [Parameter(ValueFromPipelineByPropertyName=$true, 443 | ParameterSetName='NameParameterSet')] 444 | [ValidateNotNull()] 445 | [Version] 446 | $MaximumVersion, 447 | 448 | [Parameter(ValueFromPipelineByPropertyName=$true, 449 | ParameterSetName='NameParameterSet')] 450 | [ValidateNotNull()] 451 | [Version] 452 | $RequiredVersion, 453 | 454 | [Parameter()] 455 | [switch] 456 | $Force, 457 | 458 | [Parameter()] 459 | [System.string] 460 | $Source 461 | ) 462 | 463 | if($WhatIfPreference) 464 | { 465 | $null = $PSBoundParameters.Remove("WhatIf") 466 | $findOutput = PackageManagement\Find-Package @PSBoundParameters 467 | $packageName = $findOutput.name 468 | $packageversion = $findOutput.version 469 | $packageSource = $findOutput.Source 470 | 471 | $string = '"Package ' + $packageName + ' version ' + $packageversion + ' from ' + $packageSource + '"' 472 | $messageWhatIf = 'What if: Performing the operation "Install Package" on target ' + $string + ' .' 473 | 474 | Write-Host $messageWhatIf 475 | return 476 | } 477 | 478 | if($InputObject) 479 | { 480 | } 481 | else 482 | { 483 | $PSBoundParameters["Provider"] = $script:Providername 484 | } 485 | 486 | #PackageManagement\Install-Package @PSBoundParameters 487 | 488 | $Location = $script:location_modules 489 | 490 | $PSBoundParameters["Path"] = $Location 491 | $PSBoundParameters["Force"] = $true 492 | $downloadOutput = PackageManagement\Save-Package @PSBoundParameters 493 | 494 | $Destination = GenerateFullPath -Location $Location ` 495 | -Name $downloadOutput.Name ` 496 | -Version $downloadOutput.Version 497 | 498 | $startInstallTime = Get-Date 499 | 500 | Write-Verbose "Trying to install the Image: $Destination" 501 | 502 | Install-ContainerOSImage -WimPath $Destination ` 503 | -Force 504 | 505 | $endInstallTime = Get-Date 506 | $differenceInstallTime = New-TimeSpan -Start $startInstallTime -End $endInstallTime 507 | $installTime = "Installed in " + $differenceInstallTime.Hours + " hours, " + $differenceInstallTime.Minutes + " minutes, " + $differenceInstallTime.Seconds + " seconds." 508 | Write-Verbose $installTime 509 | 510 | # Clean up 511 | Write-Verbose "Removing the installer: $Destination" 512 | rm $Destination 513 | } 514 | 515 | #endregion Stand-Alone Functions 516 | 517 | #region Helper-Functions 518 | 519 | function CheckVersion 520 | { 521 | param 522 | ( 523 | [System.Version]$MinimumVersion, 524 | [System.Version]$MaximumVersion, 525 | [System.Version]$RequiredVersion, 526 | [switch]$AllVersions 527 | ) 528 | 529 | if($AllVersions -and $RequiredVersion) 530 | { 531 | Write-Error "AllVersions and RequiredVersion cannot be used together" 532 | return $false 533 | } 534 | 535 | if($AllVersions -or $RequiredVersion) 536 | { 537 | if($MinimumVersion -or $MaximumVersion) 538 | { 539 | Write-Error "AllVersions and RequiredVersion switch cannot be used with MinimumVersion or MaximumVersion" 540 | return $false 541 | } 542 | } 543 | 544 | if($MinimumVersion -and $MaximumVersion) 545 | { 546 | if($MaximumVersion -lt $MinimumVersion) 547 | { 548 | Write-Error "Minimum Version cannot be more than Maximum Version" 549 | return $false 550 | } 551 | } 552 | 553 | return $true 554 | } 555 | 556 | function Find-Azure 557 | { 558 | param( 559 | [Parameter(Mandatory=$false,Position=0)] 560 | [string[]] 561 | $Name, 562 | 563 | [System.Version] 564 | $MinimumVersion, 565 | 566 | [System.Version] 567 | $MaximumVersion, 568 | 569 | [System.Version] 570 | $RequiredVersion, 571 | 572 | [switch] 573 | $AllVersions, 574 | 575 | [System.String] 576 | $Location, 577 | 578 | [System.String] 579 | $SourceName 580 | ) 581 | 582 | Add-Type -AssemblyName System.Net.Http 583 | 584 | $searchFile = Get-SearchIndex -fwdLink $Location ` 585 | -SourceName $SourceName 586 | 587 | $searchFileContent = Get-Content $searchFile 588 | 589 | if($null -eq $searchFileContent) 590 | { 591 | return $null 592 | } 593 | 594 | if(IsNanoServer) 595 | { 596 | $jsonDll = [Microsoft.PowerShell.CoreCLR.AssemblyExtensions]::LoadFrom($PSScriptRoot + "\Json.coreclr.dll") 597 | $jsonParser = $jsonDll.GetTypes() | Where-Object name -match jsonparser 598 | $searchContent = $jsonParser::FromJson($searchFileContent) 599 | $searchStuff = $searchContent.Get_Item("array0") 600 | $searchData = @() 601 | foreach($searchStuffEntry in $searchStuff) 602 | { 603 | $obj = New-Object PSObject 604 | $obj | Add-Member NoteProperty Name $searchStuffEntry.Name 605 | $obj | Add-Member NoteProperty Version $searchStuffEntry.Version 606 | $obj | Add-Member NoteProperty Description $searchStuffEntry.Description 607 | $obj | Add-Member NoteProperty SasToken $searchStuffEntry.SasToken 608 | $searchData += $obj 609 | } 610 | } 611 | else 612 | { 613 | $searchData = $searchFileContent | ConvertFrom-Json 614 | } 615 | 616 | # If name is null or whitespace, interpret as * 617 | if ([string]::IsNullOrWhiteSpace($Name)) 618 | { 619 | $Name = "*" 620 | } 621 | 622 | # Handle the version not given scenario 623 | if((-not ($MinimumVersion -or $MaximumVersion -or $RequiredVersion -or $AllVersions))) 624 | { 625 | $MinimumVersion = [System.Version]'0.0.0.0' 626 | } 627 | 628 | $searchResults = @() 629 | $searchDictionary = @{} 630 | 631 | foreach($entry in $searchData) 632 | { 633 | $toggle = $false 634 | 635 | # Check if the search string has * in it 636 | if ([System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($Name)) 637 | { 638 | if($entry.name -like $Name) 639 | { 640 | $toggle = $true 641 | } 642 | else 643 | { 644 | continue 645 | } 646 | } 647 | else 648 | { 649 | if($entry.name -eq $Name) 650 | { 651 | $toggle = $true 652 | } 653 | else 654 | { 655 | continue 656 | } 657 | } 658 | 659 | $thisVersion = Convert-Version $entry.version 660 | 661 | if($MinimumVersion) 662 | { 663 | $convertedMinimumVersion = Convert-Version $MinimumVersion 664 | 665 | if(($thisVersion -ge $convertedMinimumVersion)) 666 | { 667 | if($searchDictionary.ContainsKey($entry.name)) 668 | { 669 | $objEntry = $searchDictionary[$entry.name] 670 | $objVersion = Convert-Version $objEntry.Version 671 | 672 | if($thisVersion -gt $objVersion) 673 | { 674 | $toggle = $true 675 | } 676 | else 677 | { 678 | $toggle = $false 679 | } 680 | } 681 | else 682 | { 683 | $toggle = $true 684 | } 685 | } 686 | else 687 | { 688 | $toggle = $false 689 | } 690 | } 691 | 692 | if($MaximumVersion) 693 | { 694 | $convertedMaximumVersion = Convert-Version $MaximumVersion 695 | 696 | if(($thisVersion -le $convertedMaximumVersion)) 697 | { 698 | if($searchDictionary.ContainsKey($entry.name)) 699 | { 700 | $objEntry = $searchDictionary[$entry.name] 701 | $objVersion = Convert-Version $objEntry.Version 702 | 703 | if($thisVersion -gt $objVersion) 704 | { 705 | $toggle = $true 706 | } 707 | else 708 | { 709 | $toggle = $false 710 | } 711 | } 712 | else 713 | { 714 | $toggle = $true 715 | } 716 | } 717 | else 718 | { 719 | $toggle = $false 720 | } 721 | } 722 | 723 | if($RequiredVersion) 724 | { 725 | $convertedRequiredVersion = Convert-Version $RequiredVersion 726 | 727 | if(($thisVersion -eq $convertedRequiredVersion)) 728 | { 729 | $toggle = $true 730 | } 731 | else 732 | { 733 | $toggle = $false 734 | } 735 | } 736 | 737 | if($AllVersions) 738 | { 739 | if($toggle) 740 | { 741 | $searchResults += $entry 742 | } 743 | } 744 | 745 | if($toggle) 746 | { 747 | if($searchDictionary.ContainsKey($entry.name)) 748 | { 749 | $searchDictionary.Remove($entry.name) 750 | } 751 | 752 | $searchDictionary.Add($entry.name, $entry) 753 | } 754 | } 755 | 756 | if(-not $AllVersions) 757 | { 758 | $searchDictionary.Keys | ForEach-Object { 759 | $searchResults += $searchDictionary.Item($_) 760 | } 761 | } 762 | 763 | $searchEntries = @() 764 | 765 | foreach($searchEntry in $searchResults) 766 | { 767 | $EntryName = $searchEntry.Name 768 | $EntryVersion = $searchEntry.Version 769 | $EntryDescription = $searchEntry.Description 770 | $SasToken = $searchEntry.SasToken 771 | $ResultEntry = Microsoft.PowerShell.Utility\New-Object PSCustomObject -Property ([ordered]@{ 772 | Name = $EntryName 773 | Version = $EntryVersion 774 | Description = $EntryDescription 775 | SasToken = $SasToken 776 | Source = $SourceName 777 | }) 778 | 779 | $searchEntries += $ResultEntry 780 | } 781 | 782 | $searchEntries = $searchEntries | Sort-Object "Version" -Descending 783 | 784 | return $searchEntries 785 | } 786 | 787 | function Find-UNCPath 788 | { 789 | param( 790 | [Parameter(Mandatory=$false,Position=0)] 791 | [string] 792 | $Name, 793 | 794 | [System.Version] 795 | $MinimumVersion, 796 | 797 | [System.Version] 798 | $MaximumVersion, 799 | 800 | [System.Version] 801 | $RequiredVersion, 802 | 803 | [switch] 804 | $AllVersions, 805 | 806 | [System.String] 807 | $localPath, 808 | 809 | [System.String] 810 | $SourceName 811 | ) 812 | 813 | $responseArray = @() 814 | try 815 | { 816 | $nameToSearch = "" 817 | if(-not $Name) 818 | { 819 | $nameToSearch = "*.wim" 820 | } 821 | else 822 | { 823 | if(-not($name.ToLower().EndsWith(".wim"))) 824 | { 825 | $name = $name + ".wim" 826 | } 827 | 828 | $nameToSearch = $Name 829 | } 830 | 831 | $images = @() 832 | $images = Get-ChildItem -Path $localPath ` 833 | -ErrorAction SilentlyContinue ` 834 | -Filter $nameToSearch ` 835 | -Recurse ` 836 | -File ` 837 | -Depth 1 ` 838 | -Force | % { $_.FullName } 839 | 840 | $searchResults = @() 841 | $searchDictionary = @{} 842 | 843 | # Handle the version not given scenario 844 | if((-not ($MinimumVersion -or $MaximumVersion -or $RequiredVersion -or $AllVersions))) 845 | { 846 | $MinimumVersion = [System.Version]'0.0.0.0' 847 | } 848 | 849 | foreach($image in $images) 850 | { 851 | # Since the Get-ChildItem has filtered images by name 852 | # All images are potentially candidates for result 853 | $toggle = $true 854 | $thisVersion = get-Version $image 855 | $fileName = Split-Path $image -Leaf 856 | 857 | if($MinimumVersion) 858 | { 859 | $convertedMinimumVersion = Convert-Version $MinimumVersion 860 | 861 | if(($thisVersion -ge $convertedMinimumVersion)) 862 | { 863 | if($searchDictionary.ContainsKey($fileName)) 864 | { 865 | $objEntry = $searchDictionary[$fileName] 866 | $objVersion = Convert-Version $objEntry.Version 867 | 868 | if($thisVersion -gt $objVersion) 869 | { 870 | $toggle = $true 871 | } 872 | else 873 | { 874 | $toggle = $false 875 | } 876 | } 877 | else 878 | { 879 | $toggle = $true 880 | } 881 | } 882 | else 883 | { 884 | $toggle = $false 885 | } 886 | } 887 | 888 | if($MaximumVersion) 889 | { 890 | $convertedMaximumVersion = Convert-Version $MaximumVersion 891 | 892 | if(($thisVersion -le $convertedMaximumVersion)) 893 | { 894 | if($searchDictionary.ContainsKey($fileName)) 895 | { 896 | $objEntry = $searchDictionary[$fileName] 897 | $objVersion = Convert-Version $objEntry.Version 898 | 899 | if($thisVersion -gt $objVersion) 900 | { 901 | $toggle = $true 902 | } 903 | else 904 | { 905 | $toggle = $false 906 | } 907 | } 908 | else 909 | { 910 | $toggle = $true 911 | } 912 | } 913 | else 914 | { 915 | $toggle = $false 916 | } 917 | } 918 | 919 | if($RequiredVersion) 920 | { 921 | $convertedRequiredVersion = Convert-Version $RequiredVersion 922 | 923 | if(($thisVersion -eq $convertedRequiredVersion)) 924 | { 925 | $toggle = $true 926 | } 927 | else 928 | { 929 | $toggle = $false 930 | } 931 | } 932 | 933 | if($AllVersions) 934 | { 935 | if($toggle) 936 | { 937 | $searchResults += $image 938 | } 939 | } 940 | 941 | if($toggle) 942 | { 943 | if($searchDictionary.ContainsKey($fileName)) 944 | { 945 | $searchDictionary.Remove($fileName) 946 | } 947 | 948 | $searchDictionary.Add($fileName, $image) 949 | } 950 | } 951 | 952 | if(-not $AllVersions) 953 | { 954 | $searchDictionary.Keys | ForEach-Object { 955 | $searchResults += $searchDictionary.Item($_) 956 | } 957 | } 958 | 959 | $searchEntries = @() 960 | 961 | foreach($searchEntry in $searchResults) 962 | { 963 | $entryName = Split-Path $searchEntry -Leaf 964 | $entryVersion = get-Version $searchEntry 965 | $entryDesc = $entryName 966 | $path = $localPath 967 | $ResultEntry = Microsoft.PowerShell.Utility\New-Object PSCustomObject -Property ([ordered]@{ 968 | Name = $EntryName 969 | Version = $EntryVersion 970 | Description = $EntryDesc 971 | SasToken = $path 972 | Source = $SourceName 973 | }) 974 | 975 | $searchEntries += $ResultEntry 976 | } 977 | 978 | return $searchEntries 979 | } 980 | catch 981 | { 982 | Write-Error "Unable to access the sub-folders of $localPath" 983 | return 984 | } 985 | } 986 | 987 | function Convert-Version([string]$version) 988 | { 989 | if ([string]::IsNullOrWhiteSpace($version)) 990 | { 991 | return $null; 992 | } 993 | 994 | # not supporting semver here. let's try to normalize the versions 995 | if ($version.StartsWith(".")) 996 | { 997 | # add leading zeros 998 | $version = "0" + $version 999 | } 1000 | 1001 | # let's see how many parts are we given with the version 1002 | $parts = $version.Split(".").Count 1003 | 1004 | # add .0 dependending number of parts since we need 4 parts 1005 | while ($parts -lt 4) 1006 | { 1007 | $version = $version + ".0" 1008 | $parts += 1 1009 | } 1010 | 1011 | [version]$convertedVersion = $null 1012 | 1013 | # try to convert 1014 | if ([version]::TryParse($version, [ref]$convertedVersion)) 1015 | { 1016 | return $convertedVersion 1017 | } 1018 | 1019 | return $null; 1020 | } 1021 | 1022 | function IsNanoServer 1023 | { 1024 | if ($script:isNanoServerInitialized) 1025 | { 1026 | return $script:isNanoServer 1027 | } 1028 | else 1029 | { 1030 | $operatingSystem = Get-CimInstance -ClassName win32_operatingsystem 1031 | $systemSKU = $operatingSystem.OperatingSystemSKU 1032 | $script:isNanoServer = ($systemSKU -eq 109) -or ($systemSKU -eq 144) -or ($systemSKU -eq 143) 1033 | $script:isNanoServerInitialized = $true 1034 | return $script:isNanoServer 1035 | } 1036 | } 1037 | 1038 | function Resolve-FwdLink 1039 | { 1040 | param 1041 | ( 1042 | [parameter(Mandatory=$false)] 1043 | [System.String]$Uri 1044 | ) 1045 | 1046 | Add-Type -AssemblyName System.Net.Http 1047 | $httpClient = New-Object System.Net.Http.HttpClient 1048 | $response = $httpclient.GetAsync($Uri) 1049 | $link = $response.Result.RequestMessage.RequestUri 1050 | 1051 | return $link 1052 | } 1053 | 1054 | function Get-SearchIndex 1055 | { 1056 | param 1057 | ( 1058 | [Parameter(Mandatory=$true)] 1059 | [string] 1060 | $fwdLink, 1061 | 1062 | [Parameter(Mandatory=$true)] 1063 | [string] 1064 | $SourceName 1065 | ) 1066 | 1067 | $fullUrl = Resolve-FwdLink $fwdLink 1068 | $fullUrl = $fullUrl.AbsoluteUri 1069 | $searchIndex = $SourceName + "_" + $script:ContainerImageSearchIndex 1070 | $destination = Join-Path $script:location_modules $searchIndex 1071 | 1072 | if(-not(Test-Path $script:location_modules)) 1073 | { 1074 | md $script:location_modules 1075 | } 1076 | 1077 | if(Test-Path $destination) 1078 | { 1079 | Remove-Item $destination 1080 | DownloadFile -downloadURL $fullUrl ` 1081 | -destination $destination 1082 | } 1083 | else 1084 | { 1085 | DownloadFile -downloadURL $fullUrl ` 1086 | -destination $destination 1087 | } 1088 | 1089 | return $destination 1090 | } 1091 | 1092 | function DownloadFile 1093 | { 1094 | [CmdletBinding()] 1095 | param($downloadURL, $destination) 1096 | 1097 | try 1098 | { 1099 | # Download the file 1100 | if($downloadURL.StartsWith("http://") -or $downloadURL.StartsWith("https://")) 1101 | { 1102 | if(-not (CheckDiskSpace $destination $downloadURL)) 1103 | { 1104 | return 1105 | } 1106 | 1107 | Write-Verbose "Downloading $downloadUrl to $destination" 1108 | $saveItemPath = $PSScriptRoot + "\SaveHTTPItemUsingBITS.psm1" 1109 | Import-Module "$saveItemPath" 1110 | $startTime = Get-Date 1111 | Save-HTTPItemUsingBitsTransfer -Uri $downloadURL ` 1112 | -Destination $destination 1113 | 1114 | Write-Verbose "Finished downloading" 1115 | $endTime = Get-Date 1116 | $difference = New-TimeSpan -Start $startTime -End $endTime 1117 | $downloadTime = "Downloaded in " + $difference.Hours + " hours, " + $difference.Minutes + " minutes, " + $difference.Seconds + " seconds." 1118 | Write-Verbose $downloadTime 1119 | } 1120 | elseif($downloadURL.StartsWith("\\")) 1121 | { 1122 | $startTime = Get-Date 1123 | cp $downloadURL $destination 1124 | $endTime = Get-Date 1125 | $difference = New-TimeSpan -Start $startTime -End $endTime 1126 | $downloadTime = "Downloaded in " + $difference.Hours + " hours, " + $difference.Minutes + " minutes, " + $difference.Seconds + " seconds." 1127 | Write-Verbose $downloadTime 1128 | } 1129 | } 1130 | catch 1131 | { 1132 | ThrowError -CallerPSCmdlet $PSCmdlet ` 1133 | -ExceptionName $_.Exception.GetType().FullName ` 1134 | -ExceptionMessage $_.Exception.Message ` 1135 | -ExceptionObject $downloadURL ` 1136 | -ErrorId FailedToDownload ` 1137 | -ErrorCategory InvalidOperation 1138 | } 1139 | } 1140 | 1141 | function New-SoftwareIdentityFromContainerImageItemInfo 1142 | { 1143 | [Cmdletbinding()] 1144 | param( 1145 | [PSCustomObject] 1146 | $package 1147 | ) 1148 | 1149 | $fastPackageReference = $package.Name + 1150 | $separator + $package.version + 1151 | $separator + $package.Description + 1152 | $separator + $package.Source + 1153 | $separator + $package.SasToken 1154 | 1155 | $Name = [System.IO.Path]::GetFileNameWithoutExtension($package.Name) 1156 | 1157 | $params = @{ 1158 | FastPackageReference = $fastPackageReference; 1159 | Name = $Name; 1160 | Version = $package.version.ToString(); 1161 | versionScheme = "MultiPartNumeric"; 1162 | Source = $package.Source; 1163 | Summary = $package.Description; 1164 | } 1165 | New-SoftwareIdentity @params 1166 | } 1167 | 1168 | function get-Version 1169 | { 1170 | param($fullPath) 1171 | 1172 | # Throw error if the given File is folder or doesn't exist 1173 | if((Get-Item $fullPath) -is [System.IO.DirectoryInfo]) 1174 | { 1175 | Write-Error "Please enter a file name not a folder." 1176 | throw "$fullPath is a folder not file" 1177 | } 1178 | 1179 | $containerImageInfo = Get-WindowsImage -ImagePath $fullPath -Index 1 1180 | $containerImageVersion = $containerImageInfo.Version 1181 | 1182 | return $containerImageVersion 1183 | } 1184 | 1185 | # Utility to throw an errorrecord 1186 | function ThrowError 1187 | { 1188 | param 1189 | ( 1190 | [parameter(Mandatory = $true)] 1191 | [ValidateNotNullOrEmpty()] 1192 | [System.Management.Automation.PSCmdlet] 1193 | $CallerPSCmdlet, 1194 | 1195 | [parameter(Mandatory = $true)] 1196 | [ValidateNotNullOrEmpty()] 1197 | [System.String] 1198 | $ExceptionName, 1199 | 1200 | [parameter(Mandatory = $true)] 1201 | [ValidateNotNullOrEmpty()] 1202 | [System.String] 1203 | $ExceptionMessage, 1204 | 1205 | [System.Object] 1206 | $ExceptionObject, 1207 | 1208 | [parameter(Mandatory = $true)] 1209 | [ValidateNotNullOrEmpty()] 1210 | [System.String] 1211 | $ErrorId, 1212 | 1213 | [parameter(Mandatory = $true)] 1214 | [ValidateNotNull()] 1215 | [System.Management.Automation.ErrorCategory] 1216 | $ErrorCategory 1217 | ) 1218 | 1219 | $exception = New-Object $ExceptionName $ExceptionMessage; 1220 | $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $ErrorId, $ErrorCategory, $ExceptionObject 1221 | $CallerPSCmdlet.ThrowTerminatingError($errorRecord) 1222 | } 1223 | 1224 | function DownloadPackageHelper 1225 | { 1226 | [CmdletBinding()] 1227 | param 1228 | ( 1229 | [Parameter(Mandatory=$true)] 1230 | [ValidateNotNullOrEmpty()] 1231 | [string] 1232 | $FastPackageReference, 1233 | 1234 | [Parameter()] 1235 | [ValidateNotNullOrEmpty()] 1236 | [string] 1237 | $Location, 1238 | 1239 | [Parameter(Mandatory=$true)] 1240 | [ValidateNotNullOrEmpty()] 1241 | $request 1242 | ) 1243 | 1244 | [string[]] $splitterArray = @("$separator") 1245 | 1246 | [string[]] $resultArray = $fastPackageReference.Split($splitterArray, [System.StringSplitOptions]::None) 1247 | 1248 | $name = $resultArray[0] 1249 | $version = $resultArray[1] 1250 | $description = $resultArray[2] 1251 | $source = $resultArray[3] 1252 | $sasToken = $resultArray[4] 1253 | 1254 | if($sasToken.StartsWith("\\")) 1255 | { 1256 | $sasToken = Join-Path $sasToken $name 1257 | } 1258 | 1259 | $options = $request.Options 1260 | 1261 | foreach( $o in $options.Keys ) 1262 | { 1263 | Write-Debug ( "OPTION: {0} => {1}" -f ($o, $options[$o]) ) 1264 | } 1265 | 1266 | $Force = $false 1267 | if($options.ContainsKey("Force")) 1268 | { 1269 | $Force = $options['Force'] 1270 | } 1271 | 1272 | if(-not (Test-Path $Location)) 1273 | { 1274 | if($Force) 1275 | { 1276 | Write-Verbose "Creating: $Location as it doesn't exist." 1277 | mkdir $Location 1278 | } 1279 | else 1280 | { 1281 | $errorMessage = ("Cannot find the path '{0}' because it does not exist" -f $Location) 1282 | ThrowError -ExceptionName "System.ArgumentException" ` 1283 | -ExceptionMessage $errorMessage ` 1284 | -ErrorId "PathNotFound" ` 1285 | -CallerPSCmdlet $PSCmdlet ` 1286 | -ExceptionObject $Location ` 1287 | -ErrorCategory InvalidArgument 1288 | } 1289 | } 1290 | 1291 | $fullPath = GenerateFullPath -Location $Location ` 1292 | -Name $name ` 1293 | -Version $Version 1294 | 1295 | if(Test-Path $fullPath) 1296 | { 1297 | if($Force) 1298 | { 1299 | $existingFileItem = get-item $fullPath 1300 | if($existingFileItem.isreadonly) 1301 | { 1302 | throw "Cannot remove read-only file $fullPath. Remove read-only and use -Force again." 1303 | } 1304 | else 1305 | { 1306 | Remove-Item $fullPath 1307 | DownloadFile $sasToken $fullPath 1308 | } 1309 | } 1310 | else 1311 | { 1312 | Write-Verbose "$fullPath already exists. Skipping save. Use -Force to overwrite." 1313 | } 1314 | } 1315 | else 1316 | { 1317 | DownloadFile $sasToken $fullPath 1318 | } 1319 | 1320 | $savedWindowsPackageItem = Microsoft.PowerShell.Utility\New-Object PSCustomObject -Property ([ordered]@{ 1321 | Name = $name 1322 | Version = $version 1323 | Description = $description 1324 | SasToken = $sasToken 1325 | Source = $source 1326 | FullPath = $fullPath 1327 | }) 1328 | 1329 | Write-Output (New-SoftwareIdentityFromContainerImageItemInfo $savedWindowsPackageItem) 1330 | } 1331 | 1332 | function GenerateFullPath 1333 | { 1334 | param 1335 | ( 1336 | [Parameter(Mandatory=$true)] 1337 | [System.String] 1338 | $Location, 1339 | 1340 | [Parameter(Mandatory=$true)] 1341 | [System.String] 1342 | $Name, 1343 | 1344 | [Parameter(Mandatory=$true)] 1345 | [System.Version] 1346 | $Version 1347 | ) 1348 | 1349 | $fileExtension = ".wim" 1350 | 1351 | if($Name.EndsWith($fileExtension)) 1352 | { 1353 | $Name = $name.TrimEnd($fileExtension) 1354 | } 1355 | 1356 | $fileName = $name + "-" + $Version.ToString().replace('.','-') + $fileExtension 1357 | $fullPath = Join-Path $Location $fileName 1358 | return $fullPath 1359 | } 1360 | 1361 | function CheckDiskSpace 1362 | { 1363 | param($Destination, $token) 1364 | 1365 | $headers = @{'x-ms-client-request-id'=$(hostname);'x-ms-version'='2015-02-21'} 1366 | $httpresponse = Invoke-HttpClient -FullUri $token ` 1367 | -Headers $headers ` 1368 | -Method Head ` 1369 | -ea SilentlyContinue ` 1370 | -ev ev 1371 | 1372 | $contentLength = $httpresponse.Headers.ContentLength 1373 | $parent = Split-Path $Destination -Parent 1374 | $Drive = (Get-Item $parent).PSDrive.Name 1375 | $getDriveSpace = get-ciminstance win32_logicaldisk | Where-Object {$_.DeviceID -match $Drive} | % Freespace 1376 | 1377 | $contentLengthInMB = [math]::Round($contentLength/1mb, 2) 1378 | $driveSpaceInIMB = [math]::Round($getDriveSpace/1mb, 2) 1379 | 1380 | Write-Verbose "Download size: $($contentLengthInMB)MB" 1381 | Write-Verbose "Free space on the drive: $($driveSpaceInIMB)MB" 1382 | 1383 | if($contentLength -ge ($getDriveSpace * 0.95)) 1384 | { 1385 | Write-Error "Not enough space to save the file" 1386 | return $false 1387 | } 1388 | return $true 1389 | } 1390 | 1391 | function Invoke-HTTPClient 1392 | { 1393 | param( 1394 | [Uri] $FullUri, 1395 | [Hashtable] $Headers, 1396 | [ValidateSet('Get','Head')] 1397 | [string] $httpMethod, 1398 | [int] $retryCount = 0 1399 | ) 1400 | 1401 | Add-Type -AssemblyName System.Net.Http 1402 | do 1403 | { 1404 | $httpClient = [System.Net.Http.HttpClient]::new() 1405 | foreach($headerKey in $Headers.Keys) 1406 | { 1407 | $httpClient.DefaultRequestHeaders.Add($headerKey, $Headers[$headerKey]) 1408 | } 1409 | 1410 | $HttpCompletionOption = 'ResponseContentRead' 1411 | if ($httpMethod -eq 'Get') 1412 | { 1413 | $httpRequestMessage = [System.Net.Http.HttpRequestMessage]::new([System.Net.Http.HttpMethod]::Get, $fullUri) 1414 | } 1415 | else 1416 | { 1417 | $httpRequestMessage = [System.Net.Http.HttpRequestMessage]::new([System.Net.Http.HttpMethod]::Head, $fullUri) 1418 | $HttpCompletionOption = 'ResponseHeadersRead' 1419 | } 1420 | 1421 | $result = $httpClient.SendAsync($httpRequestMessage, $HttpCompletionOption) 1422 | $null = $result.AsyncWaitHandle.WaitOne() 1423 | 1424 | if ($result.Result.IsSuccessStatusCode) 1425 | { 1426 | break; 1427 | } 1428 | $retryCount--; 1429 | $msg = 'RetryCount: {0}, Http.GetAsync did not return successful status code. Status Code: {1}, {2}' -f ` 1430 | $retryCount, $result.Result.StatusCode, $result.Result.ReasonPhrase 1431 | $msg = $msg + ('Result Reason Phrase: {0}' -f $result.Result.ReasonPhrase) 1432 | } while($retryCount -gt 0) 1433 | 1434 | if (-not $result.Result.IsSuccessStatusCode) 1435 | { 1436 | $msg = 'Http.GetAsync did not return successful status code. Status Code: {0}, {1}' -f ` 1437 | $result.Result.StatusCode, $result.Result.ReasonPhrase 1438 | throw $msg 1439 | } 1440 | return $result.Result.Content 1441 | } 1442 | 1443 | #endregion Helper-Functions 1444 | 1445 | #region PackageSource Functions 1446 | 1447 | function Get-Sources 1448 | { 1449 | param($sources) 1450 | 1451 | Set-ModuleSourcesVariable 1452 | 1453 | $listOfSources = @() 1454 | 1455 | foreach($mySource in $script:ContainerSources.Values) 1456 | { 1457 | if((-not $sources) -or 1458 | (($mySource.Name -eq $sources) -or 1459 | ($mySource.SourceLocation -eq $sources))) 1460 | { 1461 | $tempHolder = @{} 1462 | 1463 | $location = $mySource."SourceLocation" 1464 | $tempHolder.Add("SourceLocation", $location) 1465 | 1466 | $packageSourceName = $mySource.Name 1467 | $tempHolder.Add("Name", $packageSourceName) 1468 | 1469 | $listOfSources += $tempHolder 1470 | } 1471 | } 1472 | 1473 | return $listOfSources 1474 | } 1475 | 1476 | function DeSerialize-PSObject 1477 | { 1478 | [CmdletBinding(PositionalBinding=$false)] 1479 | Param 1480 | ( 1481 | [Parameter(Mandatory=$true)] 1482 | $Path 1483 | ) 1484 | $filecontent = Microsoft.PowerShell.Management\Get-Content -Path $Path 1485 | [System.Management.Automation.PSSerializer]::Deserialize($filecontent) 1486 | } 1487 | 1488 | function Set-ModuleSourcesVariable 1489 | { 1490 | [CmdletBinding()] 1491 | param([switch]$Force) 1492 | 1493 | if(Microsoft.PowerShell.Management\Test-Path $script:file_modules) 1494 | { 1495 | $script:ContainerSources = DeSerialize-PSObject -Path $script:file_modules 1496 | } 1497 | else 1498 | { 1499 | $script:ContainerSources = [ordered]@{} 1500 | 1501 | $defaultModuleSource = Microsoft.PowerShell.Utility\New-Object PSCustomObject -Property ([ordered]@{ 1502 | Name = "ContainerImageGallery" 1503 | SourceLocation = "http://go.microsoft.com/fwlink/?LinkID=746630&clcid=0x409" 1504 | Trusted=$false 1505 | Registered= $true 1506 | InstallationPolicy = "Untrusted" 1507 | }) 1508 | 1509 | $script:ContainerSources.Add("ContainerImageGallery", $defaultModuleSource) 1510 | Save-ModuleSources 1511 | } 1512 | } 1513 | 1514 | function Save-ModuleSources 1515 | { 1516 | # check if exists 1517 | if(-not (Test-Path $script:location_modules)) 1518 | { 1519 | $null = md $script:location_modules 1520 | } 1521 | 1522 | # seralize module 1523 | Microsoft.PowerShell.Utility\Out-File -FilePath $script:file_modules ` 1524 | -Force ` 1525 | -InputObject ([System.Management.Automation.PSSerializer]::Serialize($script:ContainerSources)) 1526 | } 1527 | 1528 | function Get-DynamicOptions 1529 | { 1530 | param 1531 | ( 1532 | [Microsoft.PackageManagement.MetaProvider.PowerShell.OptionCategory] 1533 | $category 1534 | ) 1535 | } 1536 | 1537 | function Add-PackageSource 1538 | { 1539 | [CmdletBinding()] 1540 | param 1541 | ( 1542 | [string] 1543 | $Name, 1544 | 1545 | [string] 1546 | $Location, 1547 | 1548 | [bool] 1549 | $Trusted 1550 | ) 1551 | 1552 | Set-ModuleSourcesVariable -Force 1553 | 1554 | $Options = $request.Options 1555 | 1556 | # Add new module source 1557 | $moduleSource = Microsoft.PowerShell.Utility\New-Object PSCustomObject -Property ([ordered]@{ 1558 | Name = $Name 1559 | SourceLocation = $Location 1560 | Trusted=$Trusted 1561 | Registered= $true 1562 | InstallationPolicy = if($Trusted) {'Trusted'} else {'Untrusted'} 1563 | }) 1564 | 1565 | #TODO: Check if name already exists 1566 | $script:ContainerSources.Add($Name, $moduleSource) 1567 | 1568 | Save-ModuleSources 1569 | 1570 | Write-Output -InputObject (New-PackageSourceFromModuleSource -ModuleSource $moduleSource) 1571 | } 1572 | 1573 | function Remove-PackageSource 1574 | { 1575 | param 1576 | ( 1577 | [string] 1578 | $Name 1579 | ) 1580 | 1581 | Set-ModuleSourcesVariable -Force 1582 | 1583 | if(-not $script:ContainerSources.Contains($Name)) 1584 | { 1585 | Write-Error -Message "Package source $Name not found" ` 1586 | -ErrorId "Package source $Name not found" ` 1587 | -Category InvalidOperation ` 1588 | -TargetObject $Name 1589 | continue 1590 | } 1591 | 1592 | $script:ContainerSources.Remove($Name) 1593 | 1594 | Save-ModuleSources 1595 | 1596 | #Write-Verbose ($LocalizedData.PackageSourceUnregistered -f ($Name)) 1597 | } 1598 | 1599 | function Resolve-PackageSource 1600 | { 1601 | Set-ModuleSourcesVariable 1602 | $SourceName = $request.PackageSources 1603 | 1604 | if(-not $SourceName) 1605 | { 1606 | $SourceName = "*" 1607 | } 1608 | 1609 | foreach($moduleSourceName in $SourceName) 1610 | { 1611 | if($request.IsCanceled) 1612 | { 1613 | return 1614 | } 1615 | 1616 | $wildcardPattern = New-Object System.Management.Automation.WildcardPattern $moduleSourceName,$script:wildcardOptions 1617 | $moduleSourceFound = $false 1618 | 1619 | $script:ContainerSources.GetEnumerator() | 1620 | Microsoft.PowerShell.Core\Where-Object {$wildcardPattern.IsMatch($_.Key)} | 1621 | Microsoft.PowerShell.Core\ForEach-Object { 1622 | 1623 | $moduleSource = $script:ContainerSources[$_.Key] 1624 | 1625 | $packageSource = New-PackageSourceFromModuleSource -ModuleSource $moduleSource 1626 | 1627 | Write-Output -InputObject $packageSource 1628 | 1629 | $moduleSourceFound = $true 1630 | } 1631 | 1632 | if(-not $moduleSourceFound) 1633 | { 1634 | $sourceName = Get-SourceName -Location $moduleSourceName 1635 | 1636 | if($sourceName) 1637 | { 1638 | $moduleSource = $script:ContainerSources[$sourceName] 1639 | 1640 | $packageSource = New-PackageSourceFromModuleSource -ModuleSource $moduleSource 1641 | 1642 | Write-Output -InputObject $packageSource 1643 | } 1644 | 1645 | } 1646 | } 1647 | } 1648 | 1649 | function New-PackageSourceFromModuleSource 1650 | { 1651 | param 1652 | ( 1653 | [Parameter(Mandatory=$true)] 1654 | $ModuleSource 1655 | ) 1656 | 1657 | $packageSourceDetails = @{} 1658 | 1659 | # create a new package source 1660 | $src = New-PackageSource -Name $ModuleSource.Name ` 1661 | -Location $ModuleSource.SourceLocation ` 1662 | -Trusted $ModuleSource.Trusted ` 1663 | -Registered $ModuleSource.Registered ` 1664 | -Details $packageSourceDetails 1665 | 1666 | # return the package source object. 1667 | Write-Output -InputObject $src 1668 | } 1669 | 1670 | function Get-SourceName 1671 | { 1672 | [CmdletBinding()] 1673 | [OutputType("string")] 1674 | Param 1675 | ( 1676 | [Parameter(Mandatory=$true)] 1677 | [ValidateNotNullOrEmpty()] 1678 | [string] 1679 | $Location 1680 | ) 1681 | 1682 | Set-ModuleSourcesVariable 1683 | 1684 | foreach($psModuleSource in $script:ContainerSources.Values) 1685 | { 1686 | if(($psModuleSource.Name -eq $Location) -or 1687 | ($psModuleSource.SourceLocation -eq $Location)) 1688 | { 1689 | return $psModuleSource.Name 1690 | } 1691 | } 1692 | } 1693 | 1694 | #endregion PackageSource Functions 1695 | 1696 | #region Export 1697 | Export-ModuleMember -Function Find-Package, ` 1698 | Download-Package, ` 1699 | Install-Package, ` 1700 | #Uninstall-Package, ` 1701 | Get-InstalledPackage, ` 1702 | Add-PackageSource, ` 1703 | Remove-PackageSource, ` 1704 | Resolve-PackageSource, ` 1705 | Get-DynamicOptions, ` 1706 | Initialize-Provider, ` 1707 | Get-PackageProviderName, ` 1708 | Find-ContainerImage, ` 1709 | Save-ContainerImage, ` 1710 | Install-ContainerImage 1711 | #endregion Export 1712 | 1713 | # SIG # Begin signature block 1714 | # MIIarwYJKoZIhvcNAQcCoIIaoDCCGpwCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB 1715 | # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR 1716 | # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUhEyF30SeN3pbXMdwtp6G5j74 1717 | # O1egghWCMIIEwzCCA6ugAwIBAgITMwAAAK9TR3dsG/GjAgAAAAAArzANBgkqhkiG 1718 | # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G 1719 | # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw 1720 | # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTYwNTAzMTcxMzI1 1721 | # WhcNMTcwODAzMTcxMzI1WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp 1722 | # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw 1723 | # b3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNO 1724 | # OjMxQzUtMzBCQS03QzkxMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT 1725 | # ZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvIU6bFARsNjT 1726 | # YwC3p63ceKHrpHQuCZTbNDUUaayYlNptTs9au9YI+P9IBOKErcKjXkxftzeQaum8 1727 | # 6O7IGQlJPvqr0Cms32bitA6yECmWddujRimd4ULql8Imc452jaG1pjiPfq8uZcTg 1728 | # YHkJ5/AMQ+K2NuGTLEw4//BTmnBMOugRKUuUZcVQZG+E9wot5slnIe1p/VgYpt8D 1729 | # ejA4crXFzAeAXtj4XEY7NdE351GaIET0Y1LeKdWjnwhz2dqjhX2BJE/HDid/HYv3 1730 | # bnrgHBlHfmOTkaB799B8amERbJjNJfqrCKofWxUBWq7R1iStUCFjSSvt+Q/OS2ao 1731 | # YsLXObA2rwIDAQABo4IBCTCCAQUwHQYDVR0OBBYEFGgYUtBYbEj/U4U9IDez4/ZM 1732 | # dPF4MB8GA1UdIwQYMBaAFCM0+NlSRnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEsw 1733 | # SaBHoEWGQ2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3Rz 1734 | # L01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsG 1735 | # AQUFBzAChjxodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jv 1736 | # c29mdFRpbWVTdGFtcFBDQS5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI 1737 | # hvcNAQEFBQADggEBAIz32N/DMfk74OzCmb8uSgdkrVDlMU0+O4OWsClrjoUq0o6w 1738 | # 2qNxSX+nzxxbt7e7paBO+0pyf2m4XaGBLfuZW8lBRC2mR+U5K1wXzZTqy/3v1dIK 1739 | # yngU2cPT1L8yaC5v6FkpDljzBfslTmPvPljhN41uKTifBPqxpO+H41lCVaG/zN6H 1740 | # DovSoSt8jMOh01+9VCUsbccY6J7D9iT3erE1a0FVXy7cn9mDckXaeAOfz8cMJWlc 1741 | # NWqN1J+DjUWpArxwQjVX+gxC1CUx8Z1aA+HSBfbCXaOAtLRni3VUf1Wje/mHZevD 1742 | # fUkM2gKd9TcEu2IN1pDcWnjcSb5KLOPfSOU7Xz8wggTsMIID1KADAgECAhMzAAAB 1743 | # Cix5rtd5e6asAAEAAAEKMA0GCSqGSIb3DQEBBQUAMHkxCzAJBgNVBAYTAlVTMRMw 1744 | # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN 1745 | # aWNyb3NvZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNp 1746 | # Z25pbmcgUENBMB4XDTE1MDYwNDE3NDI0NVoXDTE2MDkwNDE3NDI0NVowgYMxCzAJ 1747 | # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k 1748 | # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIx 1749 | # HjAcBgNVBAMTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIwDQYJKoZIhvcNAQEB 1750 | # BQADggEPADCCAQoCggEBAJL8bza74QO5KNZG0aJhuqVG+2MWPi75R9LH7O3HmbEm 1751 | # UXW92swPBhQRpGwZnsBfTVSJ5E1Q2I3NoWGldxOaHKftDXT3p1Z56Cj3U9KxemPg 1752 | # 9ZSXt+zZR/hsPfMliLO8CsUEp458hUh2HGFGqhnEemKLwcI1qvtYb8VjC5NJMIEb 1753 | # e99/fE+0R21feByvtveWE1LvudFNOeVz3khOPBSqlw05zItR4VzRO/COZ+owYKlN 1754 | # Wp1DvdsjusAP10sQnZxN8FGihKrknKc91qPvChhIqPqxTqWYDku/8BTzAMiwSNZb 1755 | # /jjXiREtBbpDAk8iAJYlrX01boRoqyAYOCj+HKIQsaUCAwEAAaOCAWAwggFcMBMG 1756 | # A1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBSJ/gox6ibN5m3HkZG5lIyiGGE3 1757 | # NDBRBgNVHREESjBIpEYwRDENMAsGA1UECxMETU9QUjEzMDEGA1UEBRMqMzE1OTUr 1758 | # MDQwNzkzNTAtMTZmYS00YzYwLWI2YmYtOWQyYjFjZDA1OTg0MB8GA1UdIwQYMBaA 1759 | # FMsR6MrStBZYAck3LjMWFrlMmgofMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9j 1760 | # cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY0NvZFNpZ1BDQV8w 1761 | # OC0zMS0yMDEwLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6 1762 | # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljQ29kU2lnUENBXzA4LTMx 1763 | # LTIwMTAuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQCmqFOR3zsB/mFdBlrrZvAM2PfZ 1764 | # hNMAUQ4Q0aTRFyjnjDM4K9hDxgOLdeszkvSp4mf9AtulHU5DRV0bSePgTxbwfo/w 1765 | # iBHKgq2k+6apX/WXYMh7xL98m2ntH4LB8c2OeEti9dcNHNdTEtaWUu81vRmOoECT 1766 | # oQqlLRacwkZ0COvb9NilSTZUEhFVA7N7FvtH/vto/MBFXOI/Enkzou+Cxd5AGQfu 1767 | # FcUKm1kFQanQl56BngNb/ErjGi4FrFBHL4z6edgeIPgF+ylrGBT6cgS3C6eaZOwR 1768 | # XU9FSY0pGi370LYJU180lOAWxLnqczXoV+/h6xbDGMcGszvPYYTitkSJlKOGMIIF 1769 | # vDCCA6SgAwIBAgIKYTMmGgAAAAAAMTANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZIm 1770 | # iZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQD 1771 | # EyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTAwODMx 1772 | # MjIxOTMyWhcNMjAwODMxMjIyOTMyWjB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMK 1773 | # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 1774 | # IENvcnBvcmF0aW9uMSMwIQYDVQQDExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBD 1775 | # QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJyWVwZMGS/HZpgICBC 1776 | # mXZTbD4b1m/My/Hqa/6XFhDg3zp0gxq3L6Ay7P/ewkJOI9VyANs1VwqJyq4gSfTw 1777 | # aKxNS42lvXlLcZtHB9r9Jd+ddYjPqnNEf9eB2/O98jakyVxF3K+tPeAoaJcap6Vy 1778 | # c1bxF5Tk/TWUcqDWdl8ed0WDhTgW0HNbBbpnUo2lsmkv2hkL/pJ0KeJ2L1TdFDBZ 1779 | # +NKNYv3LyV9GMVC5JxPkQDDPcikQKCLHN049oDI9kM2hOAaFXE5WgigqBTK3S9dP 1780 | # Y+fSLWLxRT3nrAgA9kahntFbjCZT6HqqSvJGzzc8OJ60d1ylF56NyxGPVjzBrAlf 1781 | # A9MCAwEAAaOCAV4wggFaMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMsR6MrS 1782 | # tBZYAck3LjMWFrlMmgofMAsGA1UdDwQEAwIBhjASBgkrBgEEAYI3FQEEBQIDAQAB 1783 | # MCMGCSsGAQQBgjcVAgQWBBT90TFO0yaKleGYYDuoMW+mPLzYLTAZBgkrBgEEAYI3 1784 | # FAIEDB4KAFMAdQBiAEMAQTAfBgNVHSMEGDAWgBQOrIJgQFYnl+UlE/wq4QpTlVnk 1785 | # pDBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtp 1786 | # L2NybC9wcm9kdWN0cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEE 1787 | # SDBGMEQGCCsGAQUFBzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2Nl 1788 | # cnRzL01pY3Jvc29mdFJvb3RDZXJ0LmNydDANBgkqhkiG9w0BAQUFAAOCAgEAWTk+ 1789 | # fyZGr+tvQLEytWrrDi9uqEn361917Uw7LddDrQv+y+ktMaMjzHxQmIAhXaw9L0y6 1790 | # oqhWnONwu7i0+Hm1SXL3PupBf8rhDBdpy6WcIC36C1DEVs0t40rSvHDnqA2iA6VW 1791 | # 4LiKS1fylUKc8fPv7uOGHzQ8uFaa8FMjhSqkghyT4pQHHfLiTviMocroE6WRTsgb 1792 | # 0o9ylSpxbZsa+BzwU9ZnzCL/XB3Nooy9J7J5Y1ZEolHN+emjWFbdmwJFRC9f9Nqu 1793 | # 1IIybvyklRPk62nnqaIsvsgrEA5ljpnb9aL6EiYJZTiU8XofSrvR4Vbo0HiWGFzJ 1794 | # NRZf3ZMdSY4tvq00RBzuEBUaAF3dNVshzpjHCe6FDoxPbQ4TTj18KUicctHzbMrB 1795 | # 7HCjV5JXfZSNoBtIA1r3z6NnCnSlNu0tLxfI5nI3EvRvsTxngvlSso0zFmUeDord 1796 | # EN5k9G/ORtTTF+l5xAS00/ss3x+KnqwK+xMnQK3k+eGpf0a7B2BHZWBATrBC7E7t 1797 | # s3Z52Ao0CW0cgDEf4g5U3eWh++VHEK1kmP9QFi58vwUheuKVQSdpw5OPlcmN2Jsh 1798 | # rg1cnPCiroZogwxqLbt2awAdlq3yFnv2FoMkuYjPaqhHMS+a3ONxPdcAfmJH0c6I 1799 | # ybgY+g5yjcGjPa8CQGr/aZuW4hCoELQ3UAjWwz0wggYHMIID76ADAgECAgphFmg0 1800 | # AAAAAAAcMA0GCSqGSIb3DQEBBQUAMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAX 1801 | # BgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290 1802 | # IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNzA0MDMxMjUzMDlaFw0yMTA0MDMx 1803 | # MzAzMDlaMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD 1804 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xITAf 1805 | # BgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQTCCASIwDQYJKoZIhvcNAQEB 1806 | # BQADggEPADCCAQoCggEBAJ+hbLHf20iSKnxrLhnhveLjxZlRI1Ctzt0YTiQP7tGn 1807 | # 0UytdDAgEesH1VSVFUmUG0KSrphcMCbaAGvoe73siQcP9w4EmPCJzB/LMySHnfL0 1808 | # Zxws/HvniB3q506jocEjU8qN+kXPCdBer9CwQgSi+aZsk2fXKNxGU7CG0OUoRi4n 1809 | # rIZPVVIM5AMs+2qQkDBuh/NZMJ36ftaXs+ghl3740hPzCLdTbVK0RZCfSABKR2YR 1810 | # JylmqJfk0waBSqL5hKcRRxQJgp+E7VV4/gGaHVAIhQAQMEbtt94jRrvELVSfrx54 1811 | # QTF3zJvfO4OToWECtR0Nsfz3m7IBziJLVP/5BcPCIAsCAwEAAaOCAaswggGnMA8G 1812 | # A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCM0+NlSRnAK7UD7dvuzK7DDNbMPMAsG 1813 | # A1UdDwQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADCBmAYDVR0jBIGQMIGNgBQOrIJg 1814 | # QFYnl+UlE/wq4QpTlVnkpKFjpGEwXzETMBEGCgmSJomT8ixkARkWA2NvbTEZMBcG 1815 | # CgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJvb3Qg 1816 | # Q2VydGlmaWNhdGUgQXV0aG9yaXR5ghB5rRahSqClrUxzWPQHEy5lMFAGA1UdHwRJ 1817 | # MEcwRaBDoEGGP2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1 1818 | # Y3RzL21pY3Jvc29mdHJvb3RjZXJ0LmNybDBUBggrBgEFBQcBAQRIMEYwRAYIKwYB 1819 | # BQUHMAKGOGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z 1820 | # b2Z0Um9vdENlcnQuY3J0MBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEB 1821 | # BQUAA4ICAQAQl4rDXANENt3ptK132855UU0BsS50cVttDBOrzr57j7gu1BKijG1i 1822 | # uFcCy04gE1CZ3XpA4le7r1iaHOEdAYasu3jyi9DsOwHu4r6PCgXIjUji8FMV3U+r 1823 | # kuTnjWrVgMHmlPIGL4UD6ZEqJCJw+/b85HiZLg33B+JwvBhOnY5rCnKVuKE5nGct 1824 | # xVEO6mJcPxaYiyA/4gcaMvnMMUp2MT0rcgvI6nA9/4UKE9/CCmGO8Ne4F+tOi3/F 1825 | # NSteo7/rvH0LQnvUU3Ih7jDKu3hlXFsBFwoUDtLaFJj1PLlmWLMtL+f5hYbMUVbo 1826 | # nXCUbKw5TNT2eb+qGHpiKe+imyk0BncaYsk9Hm0fgvALxyy7z0Oz5fnsfbXjpKh0 1827 | # NbhOxXEjEiZ2CzxSjHFaRkMUvLOzsE1nyJ9C/4B5IYCeFTBm6EISXhrIniIh0EPp 1828 | # K+m79EjMLNTYMoBMJipIJF9a6lbvpt6Znco6b72BJ3QGEe52Ib+bgsEnVLaxaj2J 1829 | # oXZhtG6hE6a/qkfwEm/9ijJssv7fUciMI8lmvZ0dhxJkAj0tr1mPuOQh5bWwymO0 1830 | # eFQF1EEuUKyUsKV4q7OglnUa2ZKHE3UiLzKoCG6gW4wlv6DvhMoh1useT8ma7kng 1831 | # 9wFlb4kLfchpyOZu6qeXzjEp/w7FW1zYTRuh2Povnj8uVRZryROj/TGCBJcwggST 1832 | # AgEBMIGQMHkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD 1833 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xIzAh 1834 | # BgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBAhMzAAABCix5rtd5e6as 1835 | # AAEAAAEKMAkGBSsOAwIaBQCggbAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw 1836 | # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFCnf 1837 | # 8gNYPWZpakYnLulOVI6tN7xxMFAGCisGAQQBgjcCAQwxQjBAoBaAFABQAG8AdwBl 1838 | # AHIAUwBoAGUAbABsoSaAJGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9Qb3dlclNo 1839 | # ZWxsIDANBgkqhkiG9w0BAQEFAASCAQBXJZUTztaRAvpsN06XjraLCv3S5sNZaJHS 1840 | # Yu/AAy1Es98KXGMibNFdM3IPvQEiVrYKSQKMwNOZU0oeFOehDgiV1JdNNKB6so7i 1841 | # 6Z9PQwlUghF6Blx9F8aJS8prFzqgqjzS6k5Q30jyJXexty4stjr6MU4R8WHWxyyG 1842 | # 8zYMskSG647VIaCrFe7m4oEjoAuzWYwyO3QMBL9daq3RgF7sfT92Zaxkx5FPMcqa 1843 | # oSZai6YtjtdYrH/3qQb1o1O54yTGBvFGIRsfcNAi5JtIGPADYyXCWmqfsjckpk7n 1844 | # dx3HzQVfQO0UTiqcAFieYoglOTZQW222aYWzawEVbWvD1sv3qxsFoYICKDCCAiQG 1845 | # CSqGSIb3DQEJBjGCAhUwggIRAgEBMIGOMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQI 1846 | # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv 1847 | # ZnQgQ29ycG9yYXRpb24xITAfBgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD 1848 | # QQITMwAAAK9TR3dsG/GjAgAAAAAArzAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkD 1849 | # MQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTYwNjI4MTY1MzQ4WjAjBgkq 1850 | # hkiG9w0BCQQxFgQU6t6JulCaS0YjAEOQxaa0DvpMZ/AwDQYJKoZIhvcNAQEFBQAE 1851 | # ggEApEkTkl859ZLFfXAZPsxkyDdbKfpRclUFLOactrY2L5EfPmKL185mP8ZzcA/p 1852 | # lk0YKR4Dh513q7dLG/lPx2sKTh9mbOToK4qz6eeJV//Np9/+m4GJnRwydBS5JMSq 1853 | # t6f5NItj/sYV1/sMq9McfL8UhAtUzXWnEsiparth4MXEVTmRa+8EsaTn2TM0nxid 1854 | # Jg2P7V/lv6F7s2yjONAtg3kt+uGLM1ilhlClCuyOppkqLGU0oMC75yz6WCyaR2cG 1855 | # FeulvLI4Nm4BH/xSUv0SeeRJN7EUc8dDMHgQxkTox+ErgIxvuWuF0Y5k1O6weq6G 1856 | # l63xIr1phQ8YfWuubOSVPdVTsg== 1857 | # SIG # End signature block 1858 | -------------------------------------------------------------------------------- /Json.coreclr.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/ContainerProvider/266585a3de90c214276ec81dffbe89c9b050f62d/Json.coreclr.dll -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | 4 | Copyright (c) 2015 Microsoft Corporation. 5 | 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Introduction 2 | #### Install a Container image from the online Package repository 3 | 4 | The Container OS images for Nano Server and Windows Server Core are now available in an online package repository. They can be found and installed using the ContainerImage provider of PackageManagement (a.k.a. OneGet) PowerShell module. The provider needs to be installed before using it. The following PowerShell cmdlets can be used to install the provider. 5 | * Install-PackageProvider ContainerImage 6 | * Import-ContainerImage ContainerImage 7 | 8 | Once the provider is installed and imported, you can search, download, or install Container images using PowerShell cmdlets. There are two sets of cmdlets, the first set is specific for the Container OS images, including: 9 | * Find-ContainerImage 10 | * Save-ContainerImage 11 | * Install-ContainerImage 12 | 13 | The 2nd set is generic PackageManagement cmdlets, including: 14 | * Find-Package 15 | * Save-Package 16 | * Get-Package 17 | 18 | The 2nd set of cmdlets are performed the same as the 1st set, with specifying –provider ContainerImage. Without specifying the –provider parameter, it may perform slightly slower as PackageManagement will iterate all its providers. Below is the detailed usage. For a complete usage of the cmdlets, use get-help . For the general usage of the Containers, read the container MSDN doc. 19 | 20 | #### Search a Container Image 21 | Both Find-ContainerImage and Find-Package search and return a list of Container images available in the online repository. 22 | 23 | ##### Example 1: Find the latest version of all available Container images. 24 | Find-ContainerImage 25 | Find-Package –provider ContainerImage 26 | 27 | ##### Example 2: Search by the image name. The –name parameter accepts wildcard. 28 | Find-ContainerImage -Name *nano* 29 | Find-Package –provider ContainerImage -Name *nano * 30 | 31 | ##### Example 3: Search by version, according to –RequiredVersion, -MinimumVersion, and –MaximumVersion requirements. With –AllVersions parameter, all available versions of a Container image are returned. Without it, only the latest version is returned. 32 | Find-ContainerImage -Name *nano* -RequiredVersion 10.0.14300.1000 33 | Find-Package –provider ContainerImage –AllVersions -Name *nano* 34 | 35 | #### Install a Container 36 | Install-ContainerImage installs a Container image to the local machine. Both cmdlets accept pipeline result from search cmdlets. The operating system: 37 | 1.must have the Containers Package (i.e. Microsoft-NanoServer-Containers-Package Windows package) installed 38 | 2.version must match the version of Container OS image, i.e. 10.0.14300.1000 39 | Otherwise, the installation will fail. 40 | 41 | ##### Example 1: Install the latest version of a Container image to the local machine. 42 | Install-ContainerImage -Name NanoServer 43 | 44 | ##### Example 2: Install a Container image with pipeline result from the search cmdlets. 45 | Find-ContainerImage *nano* | Install-ContainerImage 46 | Find-ContainerImage -Name *windowsServercore * |Install-ContainerImage 47 | 48 | #### Download a Container image 49 | You can download and save a Container image without installation, using Save-ContainerImage or Save-Package. Both cmdlets accept pipeline result from the search cmdlets. 50 | 51 | ##### Example 1: Download and save a Container image to a directory that matches the wildcard path. The latest version will be saved if you do not specify the version requirements. 52 | Save-ContainerImage -Name NanoServer -Path C:\t*p\ 53 | Save-Package –provider ContainerImage -Name WindowsServerCore -Path .\temp -MinimumVersion 10.0.14300.1000 54 | 55 | ##### Example 2: Download and save a ContainerImage from the search cmdlets. 56 | Find-ContainerImage -Name *nano* -MaximumVersion 10.2 -MinimumVersion 1.0 | Save-ContainerImage -Path c:\ 57 | Find-Package -provider ContainerImage -Name *shield* -Culture es-es | Save-Package -Path . 58 | 59 | #### Inventory installed Container images 60 | You can inventory what Container images are installed, using PackageManagement Get-Package cmdlet. 61 | 62 | ##### Example 1: Inventory what Container images are installed in the local machine. 63 | Get-Package –provider ContainerImage 64 | 65 | ### ContainerImage cmdlets 66 | - Find-ContainerImage [[-Name] ] [-MinimumVersion ] [-MaximumVersion ] [-RequiredVersion ] [-AllVersions] [-source ] [] 67 | - Save-ContainerImage [-Name] -Path [-MinimumVersion ] [-MaximumVersion ] [-RequiredVersion ] [-Force] [-source ] [-WhatIf] [-Confirm] [] 68 | - Save-ContainerImage [-Name] -LiteralPath [-MinimumVersion ] [-MaximumVersion ] [-RequiredVersion ] [-Force] [-source ] [-WhatIf] [-Confirm] [] 69 | - Install-ContainerImage [-Force] [-Source ] [-WhatIf] [-Confirm] [] 70 | - Install-ContainerImage [-Name] [-MinimumVersion ] [-MaximumVersion ] [-RequiredVersion ] [-Force] [-Source ] [-WhatIf] [-Confirm] [] 71 | 72 | ### More examples 73 | #Finds the container image from an online gallery that match specified criteria. It can also search from other registered sources. 74 | # Find the latest version of all available container images 75 | Find-ContainerImage 76 | 77 | # Find thethe latest version of container image with the given name 78 | -Find-ContainerImage -Name ImageName 79 | 80 | # Find the latest version of all available container images that do not have version less than the given version 81 | -Find-ContainerImage -MinimumVersion Version 82 | 83 | # Find the latest version of all available container images that do not have version more than the given version 84 | -Find-ContainerImage -MaximumVersion Version 85 | 86 | # Find the latest version of all available container images that have the given version 87 | -Find-ContainerImage -RequiredVersion Version 88 | 89 | # Find the latest version of all versions of all available container images 90 | -Find-ContainerImage –AllVersions 91 | 92 | #Saves a container image without installing it. Save-ContainerImage cmdlet lets you save a container image locally without installing it. This lets you inspect the container image before you install, helping to minimize the risks of malicious code or malware on your system. 93 | 94 | # Save the latest version of the given name to the directory that matches the wildcard Path 95 | -Save-ContainerImage -Name ImageName -Path C:\t*p\ 96 | 97 | # Save the latest version of the given name to the directory that matches the LiteralPath 98 | -Save-ContainerImage -Name ImageName -LiteralPath C:\temp\ 99 | 100 | # Save the latest version no less than the minimum version of the given name to the relative directory given by Path 101 | -Save -ContainerImage -Name ImageName -MinimumVersion Version -Path .\..\ 102 | 103 | # Save the latest version no more than the maximum version of the given name to the directory that matches the LiteralPath 104 | -Save-ContainerImage -Name ImageName -MaximumVersion Version -LiteralPath C:\temp\ 105 | 106 | # Save the given version of the given name to the directory that matches the Path 107 | -Save-ContainerImage -Name ImageName -RequiredVersion Version -Path C:\t*p\ 108 | 109 | # Save the given version of the given name to the directory that matches the Path of the default Source 110 | -Save-ContainerImage -Name ImageName -RequiredVersion Version -Path C:\t*p\ -Source ContainerImageGallery 111 | 112 | # All results of the find will be saved in the given LiteralPath 113 | -Find-ContainerImage -Name ImageName | Save-ContainerImage -LiteralPath C:\temp\ 114 | 115 | #Downloads the image from the cloud and installs them on the local computer. The Install-ContainerImage gets the container image that meets the specified cirteria from the cloud. It saves the image locally and then installs it. 116 | 117 | # Installing the latest version of the given name to the local machine 118 | -Install-ContainerImage -Name ImageName 119 | 120 | # Installing the latest version of the given name from ContainerImageGallery to the local machine 121 | -Install-ContainerImage -Name ImageName -Source ContainerImageGallery 122 | 123 | # Installing the latest version greater the given version of the given name to the local machine 124 | -Install-ContainerImage -Name ImageName -MinimumVersion Version 125 | 126 | # Installing the latest version less than or equal to the given version of the given name to the local machine 127 | -Install-ContainerImage -Name ImageName -MaximumVersion Version 128 | 129 | # Installing the given version of the given name to the local machine 130 | -Install-ContainerImage -Name ImageName -RequiredVersion Version 131 | 132 | # Install all the results of find 133 | -Find-ContainerImage -Name ImageName | Install-ContainerImage 134 | 135 | ### Version 136 | 0.6.5.0 137 | 138 | ### Version History 139 | 140 | #### 0.5.2 141 | Initial public release for ContainerProvider 142 | 143 | #### 0.5.3 144 | Adding capacity to handle folders on share 145 | 146 | #### 0.6.4.0 147 | #####Revamped the provider: 148 | ######1. Renamed to ContainerImage 149 | ######2. Abides by all OneGet Provider Rules 150 | ######3. Updated the parameter Destination to Path/LiteralPath 151 | ######4. Can handle folders on share 152 | ######5. Fixed the issue of downloading large installer on Nano and remoting via BITS 153 | ######6. This version needs the Windows Server 2016 Technical Preview 5 operating system, otherwise the commands will fail 154 | 155 | ### 0.6.4.1 156 | Fixed the issue of loading System assemblies 157 | 158 | ### Dependencies 159 | This module has no dependencies -------------------------------------------------------------------------------- /SaveHTTPItemUsingBITS.psm1: -------------------------------------------------------------------------------- 1 | function Save-HTTPItemUsingBitsTransfer 2 | { 3 | [CmdletBinding()] 4 | param( 5 | [Parameter(Mandatory=$true)] 6 | $Uri, 7 | [Parameter(Mandatory=$true)] 8 | $Destination 9 | ) 10 | 11 | begin 12 | { 13 | $fullUri = [Uri]$Uri 14 | if (($fullUri.Scheme -ne 'http') -and ($fullUri.Scheme -ne 'https')) 15 | { 16 | throw "Uri: $uri is not supported. Only http or https schema are supported." 17 | } 18 | } 19 | 20 | end 21 | { 22 | $jstate = $null 23 | [bool] $isTransferCompleted = $false 24 | try 25 | { 26 | $mycurrentPath = $script:MyInvocation.MyCommand.Path 27 | $myCurrentDirectory = Split-Path $mycurrentPath 28 | $bitsCommandPath = join-path $myCurrentDirectory "BitsOnNano.exe" 29 | $jobNameTemp = "SH{0}" -f (get-date).Ticks 30 | $output = & $bitsCommandPath -Start-Transfer -DisplayName $jobNameTemp -Source $Uri -Destination $Destination 31 | $le = $lastexitcode 32 | $id = Write-Progress -ParentId 0 -Activity "Downloading from $Uri" 33 | 34 | if ($id -eq $null) { 35 | $id = 1 36 | } 37 | 38 | do 39 | { 40 | $jname,$jid,$jstate,$jbytesTransferred,$jbytesTotal,$null = $output -split ":" 41 | 42 | if ( (@("BG_JOB_STATE_ERROR", "BG_JOB_STATE_TRANSIENT_ERROR", "BG_JOB_STATE_CANCELLED") -contains $jstate) -or ($le)) 43 | { 44 | & $bitsCommandPath -Stop-Transfer -ID $jid | Out-Null 45 | 46 | throw "Save-HTTPItem: Bits Transfer failed. Job State: $jstate ExitCode = $le" 47 | } 48 | 49 | if (@("BG_JOB_STATE_TRANSFERRING") -contains $jstate) 50 | { 51 | $percentComplete = ($jbytesTransferred / $jbytesTotal) * 100 52 | $status = "Downloaded {0}MB of total {1}MB" -f ($jbytesTransferred/1mb),($jbytesTotal/1mb) 53 | $null = Write-Progress -Activity "Downloading from $Uri" -PercentComplete $percentComplete -Id $id 54 | } 55 | elseif (@("BG_JOB_STATE_TRANSFERRED") -contains $jstate) 56 | { 57 | & $bitsCommandPath -Remove-Transfer -ID $jid | Out-Null 58 | $isTransferCompleted = $true 59 | break; 60 | } 61 | elseif (@("BG_JOB_STATE_QUEUED") -contains $jstate) 62 | { 63 | $null = Write-Progress -Activity "QUEUED" -PercentComplete 0 -Id $id 64 | } 65 | elseif (@("BG_JOB_STATE_CONNECTING") -contains $jstate) 66 | { 67 | $null = Write-Progress -Activity "CONNECTING" -PercentComplete 0 -Id $id 68 | } 69 | elseif (@("BG_JOB_STATE_ACKNOWLEDGED") -contains $jstate) 70 | { 71 | $null = Write-Progress -Activity "ACKNOWLEDGED" -PercentComplete 0 -Id $id 72 | } 73 | 74 | Start-Sleep -Seconds 1 75 | $output = & $bitsCommandPath -Get-TransferStatus -ID $jid 76 | $le = $lastExitCode 77 | }while($true); 78 | } 79 | finally 80 | { 81 | #"Calling finally: jstate:$jstate isTC:$isTransferCompleted" 82 | $null = Write-Progress -Completed -Activity "Downloading from $Uri" -Id $id 83 | 84 | if ((-not $jstate) -and (-not $isTransferCompleted)) 85 | { 86 | "CleanUp:" 87 | & $bitsCommandPath -Stop-Transfer -ID $jid | Out-Null 88 | } 89 | } 90 | } 91 | } 92 | 93 | # SIG # Begin signature block 94 | # MIIarwYJKoZIhvcNAQcCoIIaoDCCGpwCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB 95 | # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR 96 | # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQULpdn3qSmV6PT6964rUllsXvf 97 | # eSSgghWCMIIEwzCCA6ugAwIBAgITMwAAAJb6gDHvN2RGRQAAAAAAljANBgkqhkiG 98 | # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G 99 | # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw 100 | # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTUxMDA3MTgxNDI0 101 | # WhcNMTcwMTA3MTgxNDI0WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp 102 | # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw 103 | # b3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNO 104 | # OkJCRUMtMzBDQS0yREJFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT 105 | # ZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm1pYSwjyVGa6 106 | # tIZe8M6+zXQQ33WKYIyKYcI3oiZcZgVcxdizVjv3hKmjqmRTC5REuLtaSYbdeCuG 107 | # bdMP2+NGWrqeWKLQIxb/Gs/BkEzrr+ewnZ+UQ7xON8jkhPhMSdT5ZiVVNdhVgo+y 108 | # 3hvrk0tk4iDpr5Xwqk5U2W5yZkXras/mIIfO54mjfS31tKQbIsxxubm8Np9ioBit 109 | # boqgiC1iwSxGh7/LGPp1NJVacuQc1JMuzkhRNXxwALbWbyrsUV8Aztz5eaUASLoF 110 | # jkK43ety0X/rV9Qlws43Q2LjKhztpEaxloEr0gioCAEmkJssDjd1qqCZ6X/bht1e 111 | # ggluXnz2tQIDAQABo4IBCTCCAQUwHQYDVR0OBBYEFMfD/XvxW9NCtvwEw94qmvuS 112 | # ht7IMB8GA1UdIwQYMBaAFCM0+NlSRnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEsw 113 | # SaBHoEWGQ2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3Rz 114 | # L01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsG 115 | # AQUFBzAChjxodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jv 116 | # c29mdFRpbWVTdGFtcFBDQS5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI 117 | # hvcNAQEFBQADggEBADQzONHGQV0X/NPCsvaZQv26Syn1rUGW85E9wUCgtf0iWG55 118 | # ntOcHryYkkVIkjB/vd9ixfzGlW2Bz08YdPHJc5he9ZNkfwhjHqW9r6ii06pa4kzE 119 | # PbgYlLwVRRvxzJwLZpSe56UceM8FmEnsRUSVKzabhLjmiIAFpnNlGgYd6g0eDvxT 120 | # FM9SOJozV4Mjyb7e+Gv//ZxUeZcTK2S/Nam+B6m/mlRVajUYotCDwziVxrm1irMt 121 | # a15M55pT3aawt+QrwXaRUMRSRmIgXTHgFWdM3AksQGA0a77rRKGYldX0iPyH2XOw 122 | # rTHQww9kEcX1r+2R+9QjmsljYc3ZPGnA+2YCADEwggTsMIID1KADAgECAhMzAAAB 123 | # Cix5rtd5e6asAAEAAAEKMA0GCSqGSIb3DQEBBQUAMHkxCzAJBgNVBAYTAlVTMRMw 124 | # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN 125 | # aWNyb3NvZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNp 126 | # Z25pbmcgUENBMB4XDTE1MDYwNDE3NDI0NVoXDTE2MDkwNDE3NDI0NVowgYMxCzAJ 127 | # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k 128 | # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIx 129 | # HjAcBgNVBAMTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIwDQYJKoZIhvcNAQEB 130 | # BQADggEPADCCAQoCggEBAJL8bza74QO5KNZG0aJhuqVG+2MWPi75R9LH7O3HmbEm 131 | # UXW92swPBhQRpGwZnsBfTVSJ5E1Q2I3NoWGldxOaHKftDXT3p1Z56Cj3U9KxemPg 132 | # 9ZSXt+zZR/hsPfMliLO8CsUEp458hUh2HGFGqhnEemKLwcI1qvtYb8VjC5NJMIEb 133 | # e99/fE+0R21feByvtveWE1LvudFNOeVz3khOPBSqlw05zItR4VzRO/COZ+owYKlN 134 | # Wp1DvdsjusAP10sQnZxN8FGihKrknKc91qPvChhIqPqxTqWYDku/8BTzAMiwSNZb 135 | # /jjXiREtBbpDAk8iAJYlrX01boRoqyAYOCj+HKIQsaUCAwEAAaOCAWAwggFcMBMG 136 | # A1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBSJ/gox6ibN5m3HkZG5lIyiGGE3 137 | # NDBRBgNVHREESjBIpEYwRDENMAsGA1UECxMETU9QUjEzMDEGA1UEBRMqMzE1OTUr 138 | # MDQwNzkzNTAtMTZmYS00YzYwLWI2YmYtOWQyYjFjZDA1OTg0MB8GA1UdIwQYMBaA 139 | # FMsR6MrStBZYAck3LjMWFrlMmgofMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9j 140 | # cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY0NvZFNpZ1BDQV8w 141 | # OC0zMS0yMDEwLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6 142 | # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljQ29kU2lnUENBXzA4LTMx 143 | # LTIwMTAuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQCmqFOR3zsB/mFdBlrrZvAM2PfZ 144 | # hNMAUQ4Q0aTRFyjnjDM4K9hDxgOLdeszkvSp4mf9AtulHU5DRV0bSePgTxbwfo/w 145 | # iBHKgq2k+6apX/WXYMh7xL98m2ntH4LB8c2OeEti9dcNHNdTEtaWUu81vRmOoECT 146 | # oQqlLRacwkZ0COvb9NilSTZUEhFVA7N7FvtH/vto/MBFXOI/Enkzou+Cxd5AGQfu 147 | # FcUKm1kFQanQl56BngNb/ErjGi4FrFBHL4z6edgeIPgF+ylrGBT6cgS3C6eaZOwR 148 | # XU9FSY0pGi370LYJU180lOAWxLnqczXoV+/h6xbDGMcGszvPYYTitkSJlKOGMIIF 149 | # vDCCA6SgAwIBAgIKYTMmGgAAAAAAMTANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZIm 150 | # iZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQD 151 | # EyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTAwODMx 152 | # MjIxOTMyWhcNMjAwODMxMjIyOTMyWjB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMK 153 | # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 154 | # IENvcnBvcmF0aW9uMSMwIQYDVQQDExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBD 155 | # QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJyWVwZMGS/HZpgICBC 156 | # mXZTbD4b1m/My/Hqa/6XFhDg3zp0gxq3L6Ay7P/ewkJOI9VyANs1VwqJyq4gSfTw 157 | # aKxNS42lvXlLcZtHB9r9Jd+ddYjPqnNEf9eB2/O98jakyVxF3K+tPeAoaJcap6Vy 158 | # c1bxF5Tk/TWUcqDWdl8ed0WDhTgW0HNbBbpnUo2lsmkv2hkL/pJ0KeJ2L1TdFDBZ 159 | # +NKNYv3LyV9GMVC5JxPkQDDPcikQKCLHN049oDI9kM2hOAaFXE5WgigqBTK3S9dP 160 | # Y+fSLWLxRT3nrAgA9kahntFbjCZT6HqqSvJGzzc8OJ60d1ylF56NyxGPVjzBrAlf 161 | # A9MCAwEAAaOCAV4wggFaMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMsR6MrS 162 | # tBZYAck3LjMWFrlMmgofMAsGA1UdDwQEAwIBhjASBgkrBgEEAYI3FQEEBQIDAQAB 163 | # MCMGCSsGAQQBgjcVAgQWBBT90TFO0yaKleGYYDuoMW+mPLzYLTAZBgkrBgEEAYI3 164 | # FAIEDB4KAFMAdQBiAEMAQTAfBgNVHSMEGDAWgBQOrIJgQFYnl+UlE/wq4QpTlVnk 165 | # pDBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtp 166 | # L2NybC9wcm9kdWN0cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEE 167 | # SDBGMEQGCCsGAQUFBzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2Nl 168 | # cnRzL01pY3Jvc29mdFJvb3RDZXJ0LmNydDANBgkqhkiG9w0BAQUFAAOCAgEAWTk+ 169 | # fyZGr+tvQLEytWrrDi9uqEn361917Uw7LddDrQv+y+ktMaMjzHxQmIAhXaw9L0y6 170 | # oqhWnONwu7i0+Hm1SXL3PupBf8rhDBdpy6WcIC36C1DEVs0t40rSvHDnqA2iA6VW 171 | # 4LiKS1fylUKc8fPv7uOGHzQ8uFaa8FMjhSqkghyT4pQHHfLiTviMocroE6WRTsgb 172 | # 0o9ylSpxbZsa+BzwU9ZnzCL/XB3Nooy9J7J5Y1ZEolHN+emjWFbdmwJFRC9f9Nqu 173 | # 1IIybvyklRPk62nnqaIsvsgrEA5ljpnb9aL6EiYJZTiU8XofSrvR4Vbo0HiWGFzJ 174 | # NRZf3ZMdSY4tvq00RBzuEBUaAF3dNVshzpjHCe6FDoxPbQ4TTj18KUicctHzbMrB 175 | # 7HCjV5JXfZSNoBtIA1r3z6NnCnSlNu0tLxfI5nI3EvRvsTxngvlSso0zFmUeDord 176 | # EN5k9G/ORtTTF+l5xAS00/ss3x+KnqwK+xMnQK3k+eGpf0a7B2BHZWBATrBC7E7t 177 | # s3Z52Ao0CW0cgDEf4g5U3eWh++VHEK1kmP9QFi58vwUheuKVQSdpw5OPlcmN2Jsh 178 | # rg1cnPCiroZogwxqLbt2awAdlq3yFnv2FoMkuYjPaqhHMS+a3ONxPdcAfmJH0c6I 179 | # ybgY+g5yjcGjPa8CQGr/aZuW4hCoELQ3UAjWwz0wggYHMIID76ADAgECAgphFmg0 180 | # AAAAAAAcMA0GCSqGSIb3DQEBBQUAMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAX 181 | # BgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290 182 | # IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNzA0MDMxMjUzMDlaFw0yMTA0MDMx 183 | # MzAzMDlaMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD 184 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xITAf 185 | # BgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQTCCASIwDQYJKoZIhvcNAQEB 186 | # BQADggEPADCCAQoCggEBAJ+hbLHf20iSKnxrLhnhveLjxZlRI1Ctzt0YTiQP7tGn 187 | # 0UytdDAgEesH1VSVFUmUG0KSrphcMCbaAGvoe73siQcP9w4EmPCJzB/LMySHnfL0 188 | # Zxws/HvniB3q506jocEjU8qN+kXPCdBer9CwQgSi+aZsk2fXKNxGU7CG0OUoRi4n 189 | # rIZPVVIM5AMs+2qQkDBuh/NZMJ36ftaXs+ghl3740hPzCLdTbVK0RZCfSABKR2YR 190 | # JylmqJfk0waBSqL5hKcRRxQJgp+E7VV4/gGaHVAIhQAQMEbtt94jRrvELVSfrx54 191 | # QTF3zJvfO4OToWECtR0Nsfz3m7IBziJLVP/5BcPCIAsCAwEAAaOCAaswggGnMA8G 192 | # A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCM0+NlSRnAK7UD7dvuzK7DDNbMPMAsG 193 | # A1UdDwQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADCBmAYDVR0jBIGQMIGNgBQOrIJg 194 | # QFYnl+UlE/wq4QpTlVnkpKFjpGEwXzETMBEGCgmSJomT8ixkARkWA2NvbTEZMBcG 195 | # CgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJvb3Qg 196 | # Q2VydGlmaWNhdGUgQXV0aG9yaXR5ghB5rRahSqClrUxzWPQHEy5lMFAGA1UdHwRJ 197 | # MEcwRaBDoEGGP2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1 198 | # Y3RzL21pY3Jvc29mdHJvb3RjZXJ0LmNybDBUBggrBgEFBQcBAQRIMEYwRAYIKwYB 199 | # BQUHMAKGOGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z 200 | # b2Z0Um9vdENlcnQuY3J0MBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEB 201 | # BQUAA4ICAQAQl4rDXANENt3ptK132855UU0BsS50cVttDBOrzr57j7gu1BKijG1i 202 | # uFcCy04gE1CZ3XpA4le7r1iaHOEdAYasu3jyi9DsOwHu4r6PCgXIjUji8FMV3U+r 203 | # kuTnjWrVgMHmlPIGL4UD6ZEqJCJw+/b85HiZLg33B+JwvBhOnY5rCnKVuKE5nGct 204 | # xVEO6mJcPxaYiyA/4gcaMvnMMUp2MT0rcgvI6nA9/4UKE9/CCmGO8Ne4F+tOi3/F 205 | # NSteo7/rvH0LQnvUU3Ih7jDKu3hlXFsBFwoUDtLaFJj1PLlmWLMtL+f5hYbMUVbo 206 | # nXCUbKw5TNT2eb+qGHpiKe+imyk0BncaYsk9Hm0fgvALxyy7z0Oz5fnsfbXjpKh0 207 | # NbhOxXEjEiZ2CzxSjHFaRkMUvLOzsE1nyJ9C/4B5IYCeFTBm6EISXhrIniIh0EPp 208 | # K+m79EjMLNTYMoBMJipIJF9a6lbvpt6Znco6b72BJ3QGEe52Ib+bgsEnVLaxaj2J 209 | # oXZhtG6hE6a/qkfwEm/9ijJssv7fUciMI8lmvZ0dhxJkAj0tr1mPuOQh5bWwymO0 210 | # eFQF1EEuUKyUsKV4q7OglnUa2ZKHE3UiLzKoCG6gW4wlv6DvhMoh1useT8ma7kng 211 | # 9wFlb4kLfchpyOZu6qeXzjEp/w7FW1zYTRuh2Povnj8uVRZryROj/TGCBJcwggST 212 | # AgEBMIGQMHkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD 213 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xIzAh 214 | # BgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBAhMzAAABCix5rtd5e6as 215 | # AAEAAAEKMAkGBSsOAwIaBQCggbAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw 216 | # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFOnw 217 | # E72BG3SgJzTuScoCSxxOruLdMFAGCisGAQQBgjcCAQwxQjBAoBaAFABQAG8AdwBl 218 | # AHIAUwBoAGUAbABsoSaAJGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9Qb3dlclNo 219 | # ZWxsIDANBgkqhkiG9w0BAQEFAASCAQByobPvDrRGQpJf/r/uVnQXjROseTdYd07U 220 | # ot8WtQlCdFkXKs/bqOlOKQtxKN2iGgnb7w2nFcL/O+faVyfh9rkpgJTLX8CGCoVg 221 | # RxWO7fUD/gb7tDRw2jpvmq7AVrC46f5jh61YDzBerV544PNNjiLBfVgyVbrbd02n 222 | # LxMfsQCHVr7twl/h8JYC0olcllrJu1rGag7AJIaXMRYB/0ConzwHyXzM7H1aNRYz 223 | # 8UIPZNdAFAmASbsXZW6t/IvBYjelpCOcqIXalYHUZTlbWmMn67FovXk946aIXU8/ 224 | # WrQM8gWG/r30hc+AuPhQrFkK5ueDgm0kAhvWc3/LOEcSYMDg/r6HoYICKDCCAiQG 225 | # CSqGSIb3DQEJBjGCAhUwggIRAgEBMIGOMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQI 226 | # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv 227 | # ZnQgQ29ycG9yYXRpb24xITAfBgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD 228 | # QQITMwAAAJb6gDHvN2RGRQAAAAAAljAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkD 229 | # MQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTYwNDE5MTgyNDI2WjAjBgkq 230 | # hkiG9w0BCQQxFgQU39hqK636kNFUIGIvYe+c6/aGwYswDQYJKoZIhvcNAQEFBQAE 231 | # ggEAj6qFQtwjSy8gv69zTQUXk4HyL+VA8cnG3RQRqpVGCF0G1pYs6kMTX0TCT7U1 232 | # IKUBJbqtBCdi0XFPWIySF0YIHRPMu6eJzJYRPe8cn4qACONE5ca3bj8oFgOTrtwY 233 | # rpsbTNjXxb35GkhdiJxLJk7MqdSnUB64rcSGsHm8/J8/kf3/rxxl1eVsOj9AZqb9 234 | # aQ5GzsuGrQ8+ZUas3lAeXA2ym/4oedbGugXt9uCwZYCt6q3hIot3stwkY0+69yc+ 235 | # MBM0LPeBoybL4poJN8r2a0j10A7jJi7GO620OcoJf2/bepykJn+CtIbybqKhNnXY 236 | # VEE5GxewUMr8EjEgAcV0m5n3EQ== 237 | # SIG # End signature block 238 | --------------------------------------------------------------------------------