├── NanoServerPackage
├── BitsOnNano.exe
├── Json.coreclr.dll
├── NanoServerPackage.Format.ps1xml
├── NanoServerPackage.psd1
├── NanoServerPackage.psm1
├── SaveHTTPItemUsingBITS.psm1
└── Test
│ ├── Comprehensive.tests.ps1
│ ├── NanoServerPackage.Find.Tests.ps1
│ ├── NanoServerPackage.Install.Tests.ps1
│ └── NanoServerPackage.Save.Tests.ps1
└── README.md
/NanoServerPackage/BitsOnNano.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OneGet/NanoServerPackage/57b99c642aa2e4d969c3d675baf9827a38b590f1/NanoServerPackage/BitsOnNano.exe
--------------------------------------------------------------------------------
/NanoServerPackage/Json.coreclr.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OneGet/NanoServerPackage/57b99c642aa2e4d969c3d675baf9827a38b590f1/NanoServerPackage/Json.coreclr.dll
--------------------------------------------------------------------------------
/NanoServerPackage/NanoServerPackage.Format.ps1xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CBSItemInfo
6 |
7 | Microsoft.PowerShell.Commands.NanoServerPackageItemInfo
8 |
9 |
10 |
11 |
12 | 50
13 |
14 |
15 | 20
16 |
17 |
18 | 16
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | Name
27 |
28 |
29 | Version
30 |
31 |
32 | Culture
33 |
34 |
35 | Description
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/NanoServerPackage/NanoServerPackage.psd1:
--------------------------------------------------------------------------------
1 | @{
2 | RootModule = 'NanoServerPackage.psm1'
3 | ModuleVersion = '1.0.1.0'
4 | GUID = '25831220-0a16-4f2e-9b66-864a4252c857'
5 | Author = 'Microsoft Corporation'
6 | Description = 'A PackageManagement provider to Discover, Save and Install Nano Server Packages on-demand'
7 | CompanyName = 'Microsoft Corporation'
8 | Copyright = '© Microsoft Corporation. All rights reserved.'
9 | PowerShellVersion = '5.0'
10 | VariablesToExport = "*"
11 | FunctionsToExport = @('Install-NanoServerPackage',
12 | 'Find-NanoServerPackage',
13 | 'Save-NanoServerPackage')
14 | FormatsToProcess = 'NanoServerPackage.Format.ps1xml'
15 | #RequiredModules = @('Dism')
16 | PrivateData = @{
17 | "PackageManagementProviders" = @(
18 | 'NanoServerPackage.psm1'
19 | )
20 |
21 | PSData = @{
22 |
23 | # Tags applied to this module. These help with module discovery in online galleries.
24 | Tags = 'Packagemanagement','Provider'
25 |
26 | # A URL to the license for this module.
27 | # LicenseUri = ''
28 |
29 | # A URL to the main website for this project.
30 | # ProjectUri = ''
31 |
32 | # A URL to an icon representing this module.
33 | # IconUri = ''
34 |
35 | # ReleaseNotes of this module
36 | ReleaseNotes = @"
37 | To use this with PowerShell PackageManagement you need to run the following commands:
38 |
39 | 1. Install-PackageProvider NanoServerPackage
40 |
41 | 2. Set-ExecutionPolicy RemoteSigned -Scope Process
42 |
43 | 3. Import-PackageProvider NanoServerPackage
44 |
45 | Now commands like Find-Package, Install-Package, Save-Package can recognize optional Nano Server Packages.
46 | "@
47 |
48 | # External dependent modules of this module
49 | # ExternalModuleDependencies = ''
50 |
51 | } # End of PSData hashtable
52 | }
53 | }
54 |
55 |
56 | # SIG # Begin signature block
57 | # MIIarwYJKoZIhvcNAQcCoIIaoDCCGpwCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
58 | # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
59 | # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU/R1pwsbIWPF4HnEpR+1Zj5lD
60 | # epKgghWCMIIEwzCCA6ugAwIBAgITMwAAAJb6gDHvN2RGRQAAAAAAljANBgkqhkiG
61 | # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
62 | # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw
63 | # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTUxMDA3MTgxNDI0
64 | # WhcNMTcwMTA3MTgxNDI0WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
65 | # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
66 | # b3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNO
67 | # OkJCRUMtMzBDQS0yREJFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
68 | # ZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm1pYSwjyVGa6
69 | # tIZe8M6+zXQQ33WKYIyKYcI3oiZcZgVcxdizVjv3hKmjqmRTC5REuLtaSYbdeCuG
70 | # bdMP2+NGWrqeWKLQIxb/Gs/BkEzrr+ewnZ+UQ7xON8jkhPhMSdT5ZiVVNdhVgo+y
71 | # 3hvrk0tk4iDpr5Xwqk5U2W5yZkXras/mIIfO54mjfS31tKQbIsxxubm8Np9ioBit
72 | # boqgiC1iwSxGh7/LGPp1NJVacuQc1JMuzkhRNXxwALbWbyrsUV8Aztz5eaUASLoF
73 | # jkK43ety0X/rV9Qlws43Q2LjKhztpEaxloEr0gioCAEmkJssDjd1qqCZ6X/bht1e
74 | # ggluXnz2tQIDAQABo4IBCTCCAQUwHQYDVR0OBBYEFMfD/XvxW9NCtvwEw94qmvuS
75 | # ht7IMB8GA1UdIwQYMBaAFCM0+NlSRnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEsw
76 | # SaBHoEWGQ2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3Rz
77 | # L01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsG
78 | # AQUFBzAChjxodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jv
79 | # c29mdFRpbWVTdGFtcFBDQS5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI
80 | # hvcNAQEFBQADggEBADQzONHGQV0X/NPCsvaZQv26Syn1rUGW85E9wUCgtf0iWG55
81 | # ntOcHryYkkVIkjB/vd9ixfzGlW2Bz08YdPHJc5he9ZNkfwhjHqW9r6ii06pa4kzE
82 | # PbgYlLwVRRvxzJwLZpSe56UceM8FmEnsRUSVKzabhLjmiIAFpnNlGgYd6g0eDvxT
83 | # FM9SOJozV4Mjyb7e+Gv//ZxUeZcTK2S/Nam+B6m/mlRVajUYotCDwziVxrm1irMt
84 | # a15M55pT3aawt+QrwXaRUMRSRmIgXTHgFWdM3AksQGA0a77rRKGYldX0iPyH2XOw
85 | # rTHQww9kEcX1r+2R+9QjmsljYc3ZPGnA+2YCADEwggTsMIID1KADAgECAhMzAAAB
86 | # Cix5rtd5e6asAAEAAAEKMA0GCSqGSIb3DQEBBQUAMHkxCzAJBgNVBAYTAlVTMRMw
87 | # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
88 | # aWNyb3NvZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNp
89 | # Z25pbmcgUENBMB4XDTE1MDYwNDE3NDI0NVoXDTE2MDkwNDE3NDI0NVowgYMxCzAJ
90 | # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
91 | # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIx
92 | # HjAcBgNVBAMTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIwDQYJKoZIhvcNAQEB
93 | # BQADggEPADCCAQoCggEBAJL8bza74QO5KNZG0aJhuqVG+2MWPi75R9LH7O3HmbEm
94 | # UXW92swPBhQRpGwZnsBfTVSJ5E1Q2I3NoWGldxOaHKftDXT3p1Z56Cj3U9KxemPg
95 | # 9ZSXt+zZR/hsPfMliLO8CsUEp458hUh2HGFGqhnEemKLwcI1qvtYb8VjC5NJMIEb
96 | # e99/fE+0R21feByvtveWE1LvudFNOeVz3khOPBSqlw05zItR4VzRO/COZ+owYKlN
97 | # Wp1DvdsjusAP10sQnZxN8FGihKrknKc91qPvChhIqPqxTqWYDku/8BTzAMiwSNZb
98 | # /jjXiREtBbpDAk8iAJYlrX01boRoqyAYOCj+HKIQsaUCAwEAAaOCAWAwggFcMBMG
99 | # A1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBSJ/gox6ibN5m3HkZG5lIyiGGE3
100 | # NDBRBgNVHREESjBIpEYwRDENMAsGA1UECxMETU9QUjEzMDEGA1UEBRMqMzE1OTUr
101 | # MDQwNzkzNTAtMTZmYS00YzYwLWI2YmYtOWQyYjFjZDA1OTg0MB8GA1UdIwQYMBaA
102 | # FMsR6MrStBZYAck3LjMWFrlMmgofMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9j
103 | # cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY0NvZFNpZ1BDQV8w
104 | # OC0zMS0yMDEwLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6
105 | # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljQ29kU2lnUENBXzA4LTMx
106 | # LTIwMTAuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQCmqFOR3zsB/mFdBlrrZvAM2PfZ
107 | # hNMAUQ4Q0aTRFyjnjDM4K9hDxgOLdeszkvSp4mf9AtulHU5DRV0bSePgTxbwfo/w
108 | # iBHKgq2k+6apX/WXYMh7xL98m2ntH4LB8c2OeEti9dcNHNdTEtaWUu81vRmOoECT
109 | # oQqlLRacwkZ0COvb9NilSTZUEhFVA7N7FvtH/vto/MBFXOI/Enkzou+Cxd5AGQfu
110 | # FcUKm1kFQanQl56BngNb/ErjGi4FrFBHL4z6edgeIPgF+ylrGBT6cgS3C6eaZOwR
111 | # XU9FSY0pGi370LYJU180lOAWxLnqczXoV+/h6xbDGMcGszvPYYTitkSJlKOGMIIF
112 | # vDCCA6SgAwIBAgIKYTMmGgAAAAAAMTANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZIm
113 | # iZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQD
114 | # EyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTAwODMx
115 | # MjIxOTMyWhcNMjAwODMxMjIyOTMyWjB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMK
116 | # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
117 | # IENvcnBvcmF0aW9uMSMwIQYDVQQDExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBD
118 | # QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJyWVwZMGS/HZpgICBC
119 | # mXZTbD4b1m/My/Hqa/6XFhDg3zp0gxq3L6Ay7P/ewkJOI9VyANs1VwqJyq4gSfTw
120 | # aKxNS42lvXlLcZtHB9r9Jd+ddYjPqnNEf9eB2/O98jakyVxF3K+tPeAoaJcap6Vy
121 | # c1bxF5Tk/TWUcqDWdl8ed0WDhTgW0HNbBbpnUo2lsmkv2hkL/pJ0KeJ2L1TdFDBZ
122 | # +NKNYv3LyV9GMVC5JxPkQDDPcikQKCLHN049oDI9kM2hOAaFXE5WgigqBTK3S9dP
123 | # Y+fSLWLxRT3nrAgA9kahntFbjCZT6HqqSvJGzzc8OJ60d1ylF56NyxGPVjzBrAlf
124 | # A9MCAwEAAaOCAV4wggFaMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMsR6MrS
125 | # tBZYAck3LjMWFrlMmgofMAsGA1UdDwQEAwIBhjASBgkrBgEEAYI3FQEEBQIDAQAB
126 | # MCMGCSsGAQQBgjcVAgQWBBT90TFO0yaKleGYYDuoMW+mPLzYLTAZBgkrBgEEAYI3
127 | # FAIEDB4KAFMAdQBiAEMAQTAfBgNVHSMEGDAWgBQOrIJgQFYnl+UlE/wq4QpTlVnk
128 | # pDBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtp
129 | # L2NybC9wcm9kdWN0cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEE
130 | # SDBGMEQGCCsGAQUFBzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2Nl
131 | # cnRzL01pY3Jvc29mdFJvb3RDZXJ0LmNydDANBgkqhkiG9w0BAQUFAAOCAgEAWTk+
132 | # fyZGr+tvQLEytWrrDi9uqEn361917Uw7LddDrQv+y+ktMaMjzHxQmIAhXaw9L0y6
133 | # oqhWnONwu7i0+Hm1SXL3PupBf8rhDBdpy6WcIC36C1DEVs0t40rSvHDnqA2iA6VW
134 | # 4LiKS1fylUKc8fPv7uOGHzQ8uFaa8FMjhSqkghyT4pQHHfLiTviMocroE6WRTsgb
135 | # 0o9ylSpxbZsa+BzwU9ZnzCL/XB3Nooy9J7J5Y1ZEolHN+emjWFbdmwJFRC9f9Nqu
136 | # 1IIybvyklRPk62nnqaIsvsgrEA5ljpnb9aL6EiYJZTiU8XofSrvR4Vbo0HiWGFzJ
137 | # NRZf3ZMdSY4tvq00RBzuEBUaAF3dNVshzpjHCe6FDoxPbQ4TTj18KUicctHzbMrB
138 | # 7HCjV5JXfZSNoBtIA1r3z6NnCnSlNu0tLxfI5nI3EvRvsTxngvlSso0zFmUeDord
139 | # EN5k9G/ORtTTF+l5xAS00/ss3x+KnqwK+xMnQK3k+eGpf0a7B2BHZWBATrBC7E7t
140 | # s3Z52Ao0CW0cgDEf4g5U3eWh++VHEK1kmP9QFi58vwUheuKVQSdpw5OPlcmN2Jsh
141 | # rg1cnPCiroZogwxqLbt2awAdlq3yFnv2FoMkuYjPaqhHMS+a3ONxPdcAfmJH0c6I
142 | # ybgY+g5yjcGjPa8CQGr/aZuW4hCoELQ3UAjWwz0wggYHMIID76ADAgECAgphFmg0
143 | # AAAAAAAcMA0GCSqGSIb3DQEBBQUAMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAX
144 | # BgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290
145 | # IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNzA0MDMxMjUzMDlaFw0yMTA0MDMx
146 | # MzAzMDlaMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD
147 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xITAf
148 | # BgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQTCCASIwDQYJKoZIhvcNAQEB
149 | # BQADggEPADCCAQoCggEBAJ+hbLHf20iSKnxrLhnhveLjxZlRI1Ctzt0YTiQP7tGn
150 | # 0UytdDAgEesH1VSVFUmUG0KSrphcMCbaAGvoe73siQcP9w4EmPCJzB/LMySHnfL0
151 | # Zxws/HvniB3q506jocEjU8qN+kXPCdBer9CwQgSi+aZsk2fXKNxGU7CG0OUoRi4n
152 | # rIZPVVIM5AMs+2qQkDBuh/NZMJ36ftaXs+ghl3740hPzCLdTbVK0RZCfSABKR2YR
153 | # JylmqJfk0waBSqL5hKcRRxQJgp+E7VV4/gGaHVAIhQAQMEbtt94jRrvELVSfrx54
154 | # QTF3zJvfO4OToWECtR0Nsfz3m7IBziJLVP/5BcPCIAsCAwEAAaOCAaswggGnMA8G
155 | # A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCM0+NlSRnAK7UD7dvuzK7DDNbMPMAsG
156 | # A1UdDwQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADCBmAYDVR0jBIGQMIGNgBQOrIJg
157 | # QFYnl+UlE/wq4QpTlVnkpKFjpGEwXzETMBEGCgmSJomT8ixkARkWA2NvbTEZMBcG
158 | # CgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJvb3Qg
159 | # Q2VydGlmaWNhdGUgQXV0aG9yaXR5ghB5rRahSqClrUxzWPQHEy5lMFAGA1UdHwRJ
160 | # MEcwRaBDoEGGP2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1
161 | # Y3RzL21pY3Jvc29mdHJvb3RjZXJ0LmNybDBUBggrBgEFBQcBAQRIMEYwRAYIKwYB
162 | # BQUHMAKGOGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z
163 | # b2Z0Um9vdENlcnQuY3J0MBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEB
164 | # BQUAA4ICAQAQl4rDXANENt3ptK132855UU0BsS50cVttDBOrzr57j7gu1BKijG1i
165 | # uFcCy04gE1CZ3XpA4le7r1iaHOEdAYasu3jyi9DsOwHu4r6PCgXIjUji8FMV3U+r
166 | # kuTnjWrVgMHmlPIGL4UD6ZEqJCJw+/b85HiZLg33B+JwvBhOnY5rCnKVuKE5nGct
167 | # xVEO6mJcPxaYiyA/4gcaMvnMMUp2MT0rcgvI6nA9/4UKE9/CCmGO8Ne4F+tOi3/F
168 | # NSteo7/rvH0LQnvUU3Ih7jDKu3hlXFsBFwoUDtLaFJj1PLlmWLMtL+f5hYbMUVbo
169 | # nXCUbKw5TNT2eb+qGHpiKe+imyk0BncaYsk9Hm0fgvALxyy7z0Oz5fnsfbXjpKh0
170 | # NbhOxXEjEiZ2CzxSjHFaRkMUvLOzsE1nyJ9C/4B5IYCeFTBm6EISXhrIniIh0EPp
171 | # K+m79EjMLNTYMoBMJipIJF9a6lbvpt6Znco6b72BJ3QGEe52Ib+bgsEnVLaxaj2J
172 | # oXZhtG6hE6a/qkfwEm/9ijJssv7fUciMI8lmvZ0dhxJkAj0tr1mPuOQh5bWwymO0
173 | # eFQF1EEuUKyUsKV4q7OglnUa2ZKHE3UiLzKoCG6gW4wlv6DvhMoh1useT8ma7kng
174 | # 9wFlb4kLfchpyOZu6qeXzjEp/w7FW1zYTRuh2Povnj8uVRZryROj/TGCBJcwggST
175 | # AgEBMIGQMHkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD
176 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xIzAh
177 | # BgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBAhMzAAABCix5rtd5e6as
178 | # AAEAAAEKMAkGBSsOAwIaBQCggbAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
179 | # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFPtx
180 | # aEnMvssm/yGMRzmZQ4WWih3uMFAGCisGAQQBgjcCAQwxQjBAoBaAFABQAG8AdwBl
181 | # AHIAUwBoAGUAbABsoSaAJGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9Qb3dlclNo
182 | # ZWxsIDANBgkqhkiG9w0BAQEFAASCAQBGfSeBs3zO1cHrliTRKFIO3/JXU9myppwe
183 | # yODuUIxytNlJBvukDFe5tu34aKXm5uAhoZlE8xzteIxnyOVYFekHLBHCpZK/qKK0
184 | # dJUdwdSaihIJyh3LXElp5QsQLpV85DHoK723v79uxc9wdtFAuN8t9lqyUax44C0g
185 | # TO3G7jwCEPlFiIEAtxrft1919ZU+1TDqo7p7NCQ5+wHQCKnthJJIyM4OxnUUAk3f
186 | # 5qsj19GZHX9SSm8OZvr9+yiNRc21qtrVUbnst63s09+5xRVrbdDvNgrxPzwGbT3v
187 | # +qW98aIjMZ/BYDqdQCvCKeLy+Ve1ytWdZseenTOcTKYKg7DYvvUYoYICKDCCAiQG
188 | # CSqGSIb3DQEJBjGCAhUwggIRAgEBMIGOMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
189 | # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
190 | # ZnQgQ29ycG9yYXRpb24xITAfBgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD
191 | # QQITMwAAAJb6gDHvN2RGRQAAAAAAljAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkD
192 | # MQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTYwNDIxMjI1MTM5WjAjBgkq
193 | # hkiG9w0BCQQxFgQUC4u+riKWZ3p1D4mJuxdrkM13XnUwDQYJKoZIhvcNAQEFBQAE
194 | # ggEAXBW0hZnEn/GKwJ99/WhNx29K3hOyNyWuqNZsVXaVZsrS0tC3rLRXriElTd64
195 | # hxYhAzIvC4mnzg2a9gCgG5Bv8GKwJP8M7h33ddw3kYqPHCMlIApwmSNgPDuFeDg5
196 | # FjGEnACprNdR6R0booQ4ut722KVsAkl3fQIMDdmL1TIk+GyzDIsp00GbWdUquHxG
197 | # 7utdzUD+Ko94+D++9q5pZTKXBNqmzgXc+wO7dzWnWYP/pLasiHAUB8ymS9CRExmi
198 | # XtLtaSzjz5UNmzN2msqlRt0yiZL7Wbkoyom/3s7p1rMX9/a1ZcQaXRTNLGjGR+tx
199 | # GkOXKcgUuFW+/7A5HbPrHomU9A==
200 | # SIG # End signature block
201 |
--------------------------------------------------------------------------------
/NanoServerPackage/NanoServerPackage.psm1:
--------------------------------------------------------------------------------
1 | #region Script variables
2 |
3 | Microsoft.PowerShell.Core\Set-StrictMode -Version Latest
4 |
5 | $script:providerName ="NanoServerPackage"
6 | $script:WindowsPackageExtension = ".cab"
7 | $script:onlinePackageCache = @{}
8 | $script:imageCultureCache = @{}
9 | $script:imagePathCache = @{}
10 | $script:wildcardOptions = [System.Management.Automation.WildcardOptions]::CultureInvariant -bor `
11 | [System.Management.Automation.WildcardOptions]::IgnoreCase
12 |
13 | $script:WindowsPackage = "$env:LOCALAPPDATA\NanoServerPackageProvider"
14 | $script:downloadedCabLocation = "$script:WindowsPackage\DownloadedCabs"
15 | $script:file_modules = "$script:WindowsPackage\sources.txt"
16 | $script:windowsPackageSources = $null
17 | $script:defaultPackageName = "NanoServerPackageSource"
18 | $script:defaultPackageLocation = "http://go.microsoft.com/fwlink/?LinkID=723027&clcid=0x409"
19 | $script:isNanoServerInitialized = $false
20 | $script:isNanoServer = $false
21 | $script:systemSKU = -1
22 | $script:systemVersion = $null
23 | $script:availablePackages = @()
24 | $separator = "|#|"
25 |
26 | #endregion Script variables
27 |
28 | #region Stand-Alone
29 |
30 | function Find-NanoServerPackage
31 | {
32 | [cmdletbinding()]
33 | param
34 | (
35 | [Parameter(Mandatory=$false,
36 | Position=0)]
37 | [ValidateNotNullOrEmpty()]
38 | [string[]]
39 | $Name,
40 |
41 | [System.Version]
42 | $MinimumVersion,
43 |
44 | [System.Version]
45 | $MaximumVersion,
46 |
47 | [System.Version]
48 | $RequiredVersion,
49 |
50 | [switch]
51 | $AllVersions,
52 |
53 | [string]
54 | $Culture
55 | )
56 |
57 | $PSBoundParameters["Provider"] = $script:providerName
58 |
59 | $packages = PackageManagement\Find-Package @PSBoundParameters
60 |
61 | foreach($package in $packages) {
62 | Microsoft.PowerShell.Utility\Add-Member -InputObject $package -MemberType NoteProperty -Name "Description" -Value $package.Summary
63 |
64 | try {
65 | if ($package.Metadata["NanoServerVersion"] -ne $null)
66 | {
67 | Microsoft.PowerShell.Utility\Add-Member -InputObject $package -MemberType NoteProperty -Name "NanoServerVersion" -Value (ConvertNanoServerVersionToString $package.Metadata["NanoServerVersion"][0])
68 | }
69 | }
70 | catch {}
71 |
72 | $package.PSTypeNames.Insert(0, "Microsoft.PowerShell.Commands.NanoServerPackageItemInfo") | Out-Null
73 | $package
74 | }
75 | }
76 |
77 | function Save-NanoServerPackage
78 | {
79 | [CmdletBinding(DefaultParameterSetName='NameAndPathParameterSet',
80 | SupportsShouldProcess=$true)]
81 | Param
82 | (
83 | [Parameter(Mandatory=$true,
84 | ValueFromPipelineByPropertyName=$true,
85 | Position=0,
86 | ParameterSetName='NameAndPathParameterSet')]
87 | [Parameter(Mandatory=$true,
88 | ValueFromPipelineByPropertyName=$true,
89 | Position=0,
90 | ParameterSetName='NameAndLiteralPathParameterSet')]
91 | [ValidateNotNullOrEmpty()]
92 |
93 | [string[]]
94 | $Name,
95 |
96 | [Parameter(Mandatory=$true,
97 | ValueFromPipeline=$true,
98 | ValueFromPipelineByPropertyName=$true,
99 | ParameterSetName='InputOjectAndPathParameterSet')]
100 | [Parameter(Mandatory=$true,
101 | ValueFromPipeline=$true,
102 | ValueFromPipelineByPropertyName=$true,
103 | ParameterSetName='InputOjectAndLiteralPathParameterSet')]
104 | [ValidateNotNull()]
105 | [PSCustomObject[]]
106 | $InputObject,
107 |
108 | [Parameter(Mandatory=$false,
109 | ValueFromPipelineByPropertyName=$true,
110 | ParameterSetName='NameAndPathParameterSet')]
111 | [Parameter(Mandatory=$false,
112 | ValueFromPipelineByPropertyName=$true,
113 | ParameterSetName='NameAndLiteralPathParameterSet')]
114 | [string]
115 | $Culture,
116 |
117 | [Parameter(ValueFromPipelineByPropertyName=$true,
118 | ParameterSetName='NameAndPathParameterSet')]
119 | [Parameter(ValueFromPipelineByPropertyName=$true,
120 | ParameterSetName='NameAndLiteralPathParameterSet')]
121 | [Version]
122 | $MinimumVersion,
123 |
124 | [Parameter(ValueFromPipelineByPropertyName=$true,
125 | ParameterSetName='NameAndPathParameterSet')]
126 | [Parameter(ValueFromPipelineByPropertyName=$true,
127 | ParameterSetName='NameAndLiteralPathParameterSet')]
128 | [Version]
129 | $MaximumVersion,
130 |
131 | [Parameter(ValueFromPipelineByPropertyName=$true,
132 | ParameterSetName='NameAndPathParameterSet')]
133 | [Parameter(ValueFromPipelineByPropertyName=$true,
134 | ParameterSetName='NameAndLiteralPathParameterSet')]
135 | [Alias('Version')]
136 | [Version]
137 | $RequiredVersion,
138 |
139 | [Parameter(Mandatory=$true, ParameterSetName='NameAndPathParameterSet')]
140 | [Parameter(Mandatory=$true, ParameterSetName='InputOjectAndPathParameterSet')]
141 | [string]
142 | $Path,
143 |
144 | [Parameter(Mandatory=$true, ParameterSetName='NameAndLiteralPathParameterSet')]
145 | [Parameter(Mandatory=$true, ParameterSetName='InputOjectAndLiteralPathParameterSet')]
146 | [string]
147 | $LiteralPath,
148 |
149 | [Parameter()]
150 | [switch]
151 | $Force
152 | )
153 |
154 | Begin
155 | {
156 | }
157 |
158 | Process
159 | {
160 | # verify name does not have wild card
161 | foreach ($packageName in $Name)
162 | {
163 | if ([System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($packageName))
164 | {
165 | ThrowError -CallerPSCmdlet $PSCmdlet `
166 | -ExceptionName System.Exception `
167 | -ExceptionMessage "Name cannot contain wildcards" `
168 | -ExceptionObject $packageName `
169 | -ErrorId WildCardCharsAreNotSupported `
170 | -ErrorCategory InvalidData
171 |
172 | return
173 | }
174 | }
175 |
176 | if($InputObject)
177 | {
178 | $Name = $InputObject.Name
179 | $RequiredVersion = $InputObject.Version
180 | $Culture = $InputObject.Culture
181 |
182 | if (-not [string]::IsNullOrWhiteSpace($Culture) -and $Culture.Contains(','))
183 | {
184 | $Culture = ''
185 | }
186 | }
187 |
188 | if($Path)
189 | {
190 | $ExceptionObject = $Path
191 | $destinationPath = Resolve-PathHelper -Path $Path `
192 | -CallerPSCmdlet $PSCmdlet | Microsoft.PowerShell.Utility\Select-Object -First 1
193 | }
194 | else
195 | {
196 | $ExceptionObject = $LiteralPath
197 | $destinationPath = Resolve-PathHelper -Path $LiteralPath `
198 | -IsLiteralPath `
199 | -CallerPSCmdlet $PSCmdlet | Microsoft.PowerShell.Utility\Select-Object -First 1
200 | }
201 |
202 | if(-not $destinationPath -or -not (Microsoft.PowerShell.Management\Test-Path -LiteralPath $destinationPath))
203 | {
204 | # When -Force is specified, Path will be created if not available.
205 | if($Force -and $destinationPath) {
206 | $null = Microsoft.PowerShell.Management\New-Item -Path $destinationPath -ItemType Directory -Force
207 | } else {
208 | $errorMessage = ("Cannot find the path '{0}' because it does not exist" -f $LiteralPath)
209 | ThrowError -ExceptionName "System.ArgumentException" `
210 | -ExceptionMessage $errorMessage `
211 | -ErrorId "PathNotFound" `
212 | -CallerPSCmdlet $PSCmdlet `
213 | -ExceptionObject $ExceptionObject `
214 | -ErrorCategory InvalidArgument
215 | }
216 | }
217 |
218 | if($Name)
219 | {
220 | # no culture given, use culture of the system
221 | if ([string]::IsNullOrWhiteSpace($Culture))
222 | {
223 | $Culture = (Get-Culture).Name
224 | }
225 |
226 | $listOfNames = @()
227 | foreach ($packageName in $Name)
228 | {
229 | $listOfNames += $packagename
230 | }
231 |
232 | $findResults = @()
233 | $findResults += (Find -Name $listOfNames `
234 | -MinimumVersion $MinimumVersion `
235 | -MaximumVersion $MaximumVersion `
236 | -RequiredVersion $RequiredVersion `
237 | -Culture $Culture `
238 | -Force:$Force)
239 |
240 | if ($findResults.Count -eq 0)
241 | {
242 | Write-Error "No results found for $listOfNames"
243 | return
244 | }
245 |
246 | foreach($findResult in $findResults)
247 | {
248 | $dependenciesToBeInstalled = [System.Collections.ArrayList]::new()
249 |
250 | if (-not (Get-DependenciesToInstall -availablePackages $script:availablePackages -culture $Culture -package $findResult -dependenciesToBeInstalled $dependenciesToBeInstalled)) {
251 | return
252 | }
253 |
254 | foreach ($result in $dependenciesToBeInstalled) {
255 | $currLang = $result.Culture
256 |
257 | $skipBase = $false
258 |
259 | # check whether base package is in list of available packages, if so, don't save
260 | foreach ($availablePackage in $script:availablePackages) {
261 | if (Test-PackageWithSearchQuery -fullyQualifiedName $availablePackage -name $result.Name -requiredVersion $result.Version -Culture "Base")
262 | {
263 | # if it is, no need to download base installer
264 | $skipBase = $true
265 | }
266 | }
267 |
268 | if (-not $skipBase) {
269 | # Base Installer
270 | $fileName_base = Get-FileName -name $result.Name `
271 | -Culture "" `
272 | -version $result.Version.ToString()
273 |
274 | $destination_base = Join-Path $destinationPath $fileName_base
275 |
276 | if($PSCmdlet.ShouldProcess($fileName_base, "Save-NanoServerPackage"))
277 | {
278 | if(Test-Path $destination_base)
279 | {
280 | if($Force)
281 | {
282 | Remove-Item $destination_base
283 |
284 | $token = $result.Locations.base
285 | DownloadFile -downloadURL $token -destination $destination_base
286 | }
287 | else
288 | {
289 | # The file exists, not downloading
290 | Write-Information "$fileName_base already existsat $destinationPath. Skipping save."
291 | }
292 | }
293 | else
294 | {
295 | $token = $result.Locations.base
296 | DownloadFile -downloadURL $token -destination $destination_base
297 | }
298 | }
299 | }
300 |
301 | # Language Installer
302 | $fileName_lang = Get-FileName -name $result.Name `
303 | -Culture $currLang `
304 | -version $result.Version.ToString()
305 |
306 | $destination_lang = Join-Path $destinationPath $fileName_lang
307 |
308 | if($PSCmdlet.ShouldProcess($fileName_lang, "Save-NanoServerPackage"))
309 | {
310 | if(Test-Path $destination_lang)
311 | {
312 | if($Force)
313 | {
314 | Remove-Item $destination_lang
315 |
316 | $token = $result.Locations.$currLang
317 | DownloadFile -downloadURL $token -destination $destination_lang
318 | }
319 | else
320 | {
321 | # The file exists, not downloading
322 | Write-Information "$fileName_lang already exists at $destinationPath. Skipping save."
323 | }
324 | }
325 | else
326 | {
327 | $token = $result.Locations.$currLang
328 | DownloadFile -downloadURL $token -destination $destination_lang
329 | }
330 | }
331 |
332 | $result
333 | }
334 | }
335 |
336 | }
337 | }
338 |
339 | End
340 | {
341 | }
342 | }
343 |
344 | function Install-NanoServerPackage
345 | {
346 | [CmdletBinding(SupportsShouldProcess=$true)]
347 | param
348 | (
349 | [parameter(Mandatory=$true,
350 | Position=0,
351 | ValueFromPipelineByPropertyName=$true,
352 | ParameterSetName='NameParameterSet')]
353 | [ValidateNotNullOrEmpty()]
354 | [System.String[]]$Name,
355 |
356 | [Parameter(ValueFromPipelineByPropertyName=$true,
357 | ParameterSetName='NameParameterSet')]
358 | [ValidateNotNull()]
359 | [Version]
360 | $MinimumVersion,
361 |
362 | [parameter(Mandatory=$false,
363 | ValueFromPipelineByPropertyName=$true,
364 | ParameterSetName='NameParameterSet')]
365 | [ValidateNotNullOrEmpty()]
366 | [System.String]$Culture,
367 |
368 | [Parameter(ValueFromPipelineByPropertyName=$true,
369 | ParameterSetName='NameParameterSet')]
370 | [Alias('Version')]
371 | [System.Version]$RequiredVersion,
372 |
373 | [Parameter(ValueFromPipelineByPropertyName=$true,
374 | ParameterSetName='NameParameterSet')]
375 | [ValidateNotNull()]
376 | [System.Version]$MaximumVersion,
377 |
378 | [ValidateNotNullOrEmpty()]
379 | [System.String]$ToVhd,
380 |
381 | [parameter()]
382 | [switch]$Force
383 | )
384 |
385 | Begin
386 | {
387 | }
388 |
389 | Process
390 | {
391 | # verify name does not have wild card
392 | foreach ($packageName in $Name)
393 | {
394 | if ([System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($packageName))
395 | {
396 | ThrowError -CallerPSCmdlet $PSCmdlet `
397 | -ExceptionName System.Exception `
398 | -ExceptionMessage "Name cannot contain wildcards" `
399 | -ExceptionObject $packageName `
400 | -ErrorId WildCardCharsAreNotSupported `
401 | -ErrorCategory InvalidData
402 |
403 | return
404 | }
405 | }
406 |
407 | # pipeline case where culture passed in is en-us, de-de, etc.
408 | if (-not [string]::IsNullOrWhiteSpace($Culture) -and $Culture.Contains(','))
409 | {
410 | $Culture = ''
411 | }
412 |
413 | $packagesToBeInstalled = @()
414 |
415 | # do a find first, if there are any errors, don't install
416 | $packagesToBeInstalled += (Find -Name $Name -MinimumVersion $MinimumVersion -MaximumVersion $MaximumVersion -RequiredVersion $RequiredVersion `
417 | -Culture $Culture -ErrorAction Stop)
418 |
419 | if ($packagesToBeInstalled.Count -eq 0)
420 | {
421 | ThrowError -CallerPSCmdlet $PSCmdlet `
422 | -ExceptionName System.InvalidOperationException `
423 | -ExceptionMessage ("Package '{0}' not found" -f $Name) `
424 | -ExceptionObject $packageName `
425 | -ErrorId PackageNotFound `
426 | -ErrorCategory InvalidOperation
427 | }
428 |
429 | $mountDrive = $null
430 |
431 | # the available packages on the system
432 | $availablePackages = $()
433 |
434 | $installedPackage = $null
435 |
436 | if (-not [string]::IsNullOrWhiteSpace($ToVhd))
437 | {
438 | if($PSCmdlet.ShouldProcess($ToVhd, "Mount-WindowsImage"))
439 | {
440 | $ToVhd = Resolve-PathHelper $ToVhd -callerPSCmdlet $PSCmdlet
441 |
442 | if (-not ([System.IO.File]::Exists($ToVhd)))
443 | {
444 | $exception = New-Object System.ArgumentException "$ToVhd does not exist"
445 | $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument
446 | $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, "InvalidVhdPath", $errorCategory, $ToVhd
447 |
448 | $PSCmdlet.ThrowTerminatingError($errorRecord)
449 | }
450 |
451 | # mount image
452 | $mountDrive = New-MountDrive
453 |
454 | Write-Verbose "Mounting $ToVhd to $mountDrive"
455 |
456 | Write-Progress -Activity "Mounting $ToVhd to $mountDrive" -PercentComplete 0
457 |
458 | $null = Mount-WindowsImage -ImagePath $ToVhd -Index 1 -Path $mountDrive
459 |
460 | $mountedVHDEdition = $null
461 |
462 | foreach ($packageToBeInstalled in $packagesToBeInstalled)
463 | {
464 | # if this package can't be install on standard, should do a check
465 | if (-not $packageToBeInstalled.Sku.Contains("144") -or (-not [string]::IsNullOrWhiteSpace($packageToBeInstalled.NanoServerVersion)))
466 | {
467 | # initialize the regkey
468 | if ($mountedVHDEdition -eq $null)
469 | {
470 | $regKey = $null
471 |
472 | $vhdNanoServerVersion = $null
473 | $mountedVHDEdition = "ERROR"
474 |
475 | try
476 | {
477 | reg load HKLM\NANOSERVERPACKAGEVHDSYS "$mountDrive\Windows\System32\config\SOFTWARE" | Out-Null
478 | $regKey = dir 'HKLM:\NANOSERVERPACKAGEVHDSYS\Microsoft\Windows NT'
479 | $mountedVHDEdition = $regKey.GetValue("EditionID")
480 | $majorVersion = $regKey.GetValue("CurrentMajorVersionNumber")
481 | $minorVersion = $regKey.GetValue("CurrentMinorVersionNumber")
482 | $buildVersion = $regKey.GetValue("CurrentBuildNumber")
483 | $vhdNanoServerVersion = [version]::new($majorVersion, $minorVersion, $buildVersion, 0)
484 | }
485 | catch
486 | {
487 | # ERROR
488 | $mountedVHDEdition = "ERROR"
489 | $vhdNanoServerVersion = $null
490 | }
491 | finally
492 | {
493 | try
494 | {
495 | if ($regKey -ne $null)
496 | {
497 | $regKey.Handle.Close()
498 | [gc]::Collect()
499 | reg unload HKLM\NANOSERVERPACKAGEVHDSYS | Out-Null
500 | }
501 | }
502 | catch { }
503 | }
504 | }
505 |
506 | if (-not [string]::IsNullOrWhiteSpace($packageToBeInstalled.NanoServerVersion) -and -not (NanoServerVersionMatched -dependencyVersionString $packageToBeInstalled.NanoServerVersion -version $vhdNanoServerVersion))
507 | {
508 | # unmount the drive
509 | if ($null -ne $mountDrive)
510 | {
511 | Write-Progress -Activity "Unmounting mount drive $mountDrive" -PercentComplete 90
512 | Write-Verbose "Unmounting mount drive $mountDrive"
513 | Remove-MountDrive $mountDrive -discard $true
514 | Write-Progress -Completed -Activity "Completed"
515 | }
516 |
517 | $exception = New-Object System.ArgumentException "The package '$name' with version $($packageToBeInstalled.Version) requires $(ConvertNanoServerVersionToString $packageToBeInstalled.NanoServerVersion). But the current Nano Server has version $vhdNanoServerVersion which is out of this range. Please see https://github.com/OneGet/NanoServerPackage for instructions." `
518 | $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidData
519 | $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, "WrongNanoServerEdition", $errorCategory, $packageToBeInstalled.Name
520 |
521 | $PSCmdlet.ThrowTerminatingError($errorRecord)
522 | }
523 |
524 | if (-not $packageToBeInstalled.Sku.Contains("144") -and $mountedVHDEdition -eq "ServerStandardNano")
525 | {
526 | # unmount the drive
527 | if ($null -ne $mountDrive)
528 | {
529 | Write-Progress -Activity "Unmounting mount drive $mountDrive" -PercentComplete 90
530 | Write-Verbose "Unmounting mount drive $mountDrive"
531 | Remove-MountDrive $mountDrive -discard $true
532 | Write-Progress -Completed -Activity "Completed"
533 | }
534 |
535 | $exception = New-Object System.ArgumentException "$($packageToBeInstalled.Name) cannot be installed on this edition of NanoServer"
536 | $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidData
537 | $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, "WrongNanoServerEdition", $errorCategory, $packageToBeInstalled.Name
538 |
539 | $PSCmdlet.ThrowTerminatingError($errorRecord)
540 | }
541 | }
542 | }
543 | }
544 | }
545 | else
546 | {
547 | foreach ($packageToBeInstalled in $packagesToBeInstalled)
548 | {
549 | # this package can't be installed on standard
550 | if (IsNanoServer)
551 | {
552 | # if this is a nano, then systemSKU would be populated after isnanoserver call
553 | if (-not $packageToBeInstalled.Sku.Contains($script:systemSKU.ToString()))
554 | {
555 | $exception = New-Object System.ArgumentException "$($packageToBeInstalled.Name) cannot be installed on this edition of NanoServer"
556 | $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidData
557 | $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, "WrongNanoServerEdition", $errorCategory, $packageToBeInstalled.Name
558 |
559 | $PSCmdlet.ThrowTerminatingError($errorRecord)
560 | }
561 |
562 | # if this is nanoserver, then we should also have the version populated
563 | if (-not (NanoServerVersionMatched -dependencyVersionString $packageToBeInstalled.NanoServerVersion -version $script:systemVersion))
564 | {
565 |
566 | $exception = New-Object System.ArgumentException "The package '$($packageToBeInstalled.Name)' with version $($packageToBeInstalled.Version) requires $(ConvertNanoServerVersionToString $packageToBeInstalled.NanoServerVersion). But the current Nano Server has version $script:systemVersion which is out of this range. Please see https://github.com/OneGet/NanoServerPackage for instructions."
567 | $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidData
568 | $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, "WrongNanoServerVersion", $errorCategory, $packageToBeInstalled.Name
569 |
570 | $PSCmdlet.ThrowTerminatingError($errorRecord)
571 | }
572 | }
573 | }
574 | }
575 |
576 | $discard = $false
577 |
578 | try
579 | {
580 | # If no force, then just check whether the packages are already installed before proceeding
581 | if (-not $Force)
582 | {
583 | Write-Verbose "Getting available packages"
584 |
585 | # installing online
586 | if ([string]::IsNullOrWhiteSpace($ToVhd))
587 | {
588 | $availablePackages = (Get-WindowsPackage -Online).PackageName.ToLower()
589 | }
590 | else
591 | {
592 | if($PSCmdlet.ShouldProcess($mountDrive, "Get-WindowsPackage"))
593 | {
594 | Write-Progress -Activity "Getting available packages on $mountDrive" -PercentComplete 10
595 |
596 | $availablePackages = (Get-WindowsPackage -Path $mountDrive).PackageName.ToLower()
597 | }
598 | }
599 | }
600 |
601 | if($PSCmdlet.ShouldProcess($Name, "Install-NanoServerPackage"))
602 | {
603 | [bool]$success = $false
604 |
605 | if (-not [string]::IsNullOrWhiteSpace($ToVhd))
606 | {
607 | Write-Progress -Activity "Mounting $ToVhd to $mountDrive" -PercentComplete 20
608 | }
609 |
610 | #Installing the package
611 | $installedPackage = Install-PackageHelper -Name $Name `
612 | -Culture $Culture `
613 | -RequiredVersion $RequiredVersion `
614 | -MinimumVersion $MinimumVersion `
615 | -MaximumVersion $MaximumVersion `
616 | -imagePath $ToVhd `
617 | -mountDrive $mountDrive `
618 | -availablePackages $availablePackages `
619 | -successfullyInstalled ([ref]$success) `
620 | -Force:$Force `
621 | -PackagesToBeInstalled $packagesToBeInstalled
622 |
623 | if (-not $success)
624 | {
625 | $exception = New-Object System.ArgumentException "Cannot install package $packageName with culture $Culture and version $RequiredVersion"
626 | $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument
627 | $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, "FailedToInstallPackage", $errorCategory, $packageName
628 |
629 | Write-Error $errorRecord
630 | $discard = $true
631 | break
632 | }
633 |
634 | $installedPackage
635 | }
636 | }
637 | catch
638 | {
639 | $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument
640 | $errorRecord = New-Object System.Management.Automation.ErrorRecord $_.Exception, "FailedToInstallPackage", $errorCategory, $Name
641 | Write-Error $errorRecord
642 | $discard = $true
643 | }
644 | finally
645 | {
646 | # unmount the drive
647 | if ($null -ne $mountDrive)
648 | {
649 | Write-Progress -Activity "Unmounting mount drive $mountDrive" -PercentComplete 90
650 | Write-Verbose "Unmounting mount drive $mountDrive"
651 | Remove-MountDrive $mountDrive -discard $discard
652 | Write-Progress -Completed -Activity "Completed"
653 | }
654 | }
655 | }
656 |
657 | End
658 | {
659 | }
660 | }
661 |
662 | #endregion Stand-Alone
663 |
664 | #region Helpers
665 |
666 | function Find
667 | {
668 | [CmdletBinding()]
669 | param
670 | (
671 | [string[]]
672 | $Name,
673 |
674 | [System.Version]
675 | $MinimumVersion,
676 |
677 | [System.Version]
678 | $MaximumVersion,
679 |
680 | [System.Version]
681 | $RequiredVersion,
682 |
683 | [switch]
684 | $AllVersions,
685 |
686 | [string]
687 | $Culture,
688 |
689 | [switch]
690 | $Force
691 | )
692 |
693 | if(-not (CheckVersion $MinimumVersion $MaximumVersion $RequiredVersion $AllVersions))
694 | {
695 | return $null
696 | }
697 |
698 | $allSources = Get-Source
699 |
700 | $searchResults = @()
701 |
702 | if ($null -eq $Name -or $Name.Count -eq 0)
703 | {
704 | $Name = @('')
705 | }
706 |
707 | foreach($currSource in $allSources)
708 | {
709 | foreach ($singleName in $Name)
710 | {
711 | if ([string]::IsNullOrWhiteSpace($singleName) -or $singleName.Trim() -eq '*')
712 | {
713 | # if no name is supplied but min or max version is supplied, error out
714 | if ($null -ne $MinimumVersion -or $null -ne $MaximumVersion)
715 | {
716 | ThrowError -CallerPSCmdlet $PSCmdlet `
717 | -ExceptionName System.Exception `
718 | -ExceptionMessage "Name is required when either MinimumVersion or MaximumVersion parameter is used" `
719 | -ExceptionObject $Name `
720 | -ErrorId NameRequiredForMinOrMaxVersion `
721 | -ErrorCategory InvalidData
722 | }
723 | }
724 |
725 | $result = Find-Azure -Name $singleName `
726 | -MinimumVersion $MinimumVersion `
727 | -MaximumVersion $MaximumVersion `
728 | -RequiredVersion $RequiredVersion `
729 | -AllVersions:$AllVersions `
730 | -Repository $currSource `
731 | -Culture $Culture `
732 | -Force:$Force
733 |
734 | if($null -eq $result)
735 | {
736 | # Error must have been thrown already
737 | # Just continue
738 | continue
739 | }
740 |
741 | if ($result.GetType().IsArray -and $result.Count -eq 0)
742 | {
743 | $sourceName = $currSource.Name
744 | Write-Error "No matching packages could be found for $singleName in $sourceName"
745 | continue
746 | }
747 |
748 | $searchResults += $result
749 | }
750 | }
751 |
752 | return $searchResults
753 | }
754 |
755 | function Find-Azure
756 | {
757 | param
758 | (
759 | [string]
760 | $Name,
761 |
762 | [System.Version]
763 | $MinimumVersion,
764 |
765 | [System.Version]
766 | $MaximumVersion,
767 |
768 | [System.Version]
769 | $RequiredVersion,
770 |
771 | [switch]
772 | $AllVersions,
773 |
774 | [System.Object]
775 | $Repository,
776 |
777 | [string]
778 | $Culture,
779 |
780 | [switch]
781 | $Force
782 | )
783 |
784 | $searchFile = Get-SearchIndex -Force:$Force -fwdLink $Repository.SourceLocation
785 | $searchFileContent = Get-Content $searchFile
786 |
787 | if($null -eq $searchFileContent)
788 | {
789 | return $null
790 | }
791 |
792 | if(IsNanoServer)
793 | {
794 | $jsonDll = [Microsoft.PowerShell.CoreCLR.AssemblyExtensions]::LoadFrom($PSScriptRoot + "\Json.coreclr.dll")
795 | $jsonParser = $jsonDll.GetTypes() | Where-Object name -match jsonparser
796 | $searchContent = $jsonParser::FromJson($searchFileContent)
797 | $searchStuff = $searchContent.Get_Item("array0")
798 | $searchData = @()
799 | foreach($searchStuffEntry in $searchStuff)
800 | {
801 | $obj = New-Object PSObject
802 | $obj | Add-Member NoteProperty Name $searchStuffEntry.Name
803 | $obj | Add-Member NoteProperty Version $searchStuffEntry.Version
804 | $obj | Add-Member NoteProperty Description $searchStuffEntry.Description
805 | $obj | Add-Member NoteProperty SKU $searchStuffEntry.Sku
806 | $obj | Add-Member NoteProperty NanoServerVersion $searchStuffEntry.NanoServerVersion
807 |
808 | $languageObj = New-Object PSObject
809 | $languageDictionary = $searchStuffEntry.Language
810 | $languageDictionary.Keys | ForEach-Object {
811 | $languageObj | Add-Member NoteProperty $_ $languageDictionary.Item($_)
812 | }
813 |
814 | # process dependencies
815 | if ($searchStuffEntry.ContainsKey("Dependencies")) {
816 | $dependencies = @()
817 | foreach ($dep in $searchStuffEntry.Dependencies) {
818 | $depObject = New-Object PSObject
819 | $depObject | Add-Member NoteProperty Name $dep.Name
820 | $depObject | Add-Member NoteProperty Version $dep.Version
821 | $dependencies += $depObject
822 | }
823 |
824 | $obj | Add-Member NoteProperty Dependencies $dependencies
825 | }
826 |
827 | $obj | Add-Member NoteProperty Language $languageObj
828 | $searchData += $obj
829 | }
830 | }
831 | else
832 | {
833 | $searchData = $searchFileContent | ConvertFrom-Json
834 | }
835 |
836 | $searchResults = @()
837 | $searchDictionary = @{}
838 |
839 | # If name is null or whitespace, interpret as *
840 | if ([string]::IsNullOrWhiteSpace($Name))
841 | {
842 | $Name = "*"
843 | }
844 |
845 | # Handle the version not given scenario
846 | if((-not ($MinimumVersion -or $MaximumVersion -or $RequiredVersion -or $AllVersions)))
847 | {
848 | $MinimumVersion = [System.Version]'0.0.0.0'
849 | }
850 |
851 | foreach($entry in $searchData)
852 | {
853 | $toggle = $false
854 |
855 | # Check if the search string has * in it
856 | if ([System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($Name))
857 | {
858 | if($entry.name -like $Name)
859 | {
860 | $toggle = $true
861 | }
862 | else
863 | {
864 | continue
865 | }
866 | }
867 | else
868 | {
869 | if($entry.name -eq $Name)
870 | {
871 | $toggle = $true
872 | }
873 | else
874 | {
875 | continue
876 | }
877 | }
878 |
879 | $thisVersion = Convert-Version $entry.version
880 |
881 | if($MinimumVersion)
882 | {
883 | $convertedMinimumVersion = Convert-Version $MinimumVersion
884 |
885 | if(($thisVersion -ge $convertedMinimumVersion))
886 | {
887 | if($searchDictionary.ContainsKey($entry.name))
888 | {
889 | $objEntry = $searchDictionary[$entry.name]
890 | $objVersion = Convert-Version $objEntry.Version
891 |
892 | if($thisVersion -gt $objVersion)
893 | {
894 | $toggle = $true
895 | }
896 | else
897 | {
898 | $toggle = $false
899 | }
900 | }
901 | else
902 | {
903 | $toggle = $true
904 | }
905 | }
906 | else
907 | {
908 | $toggle = $false
909 | }
910 | }
911 |
912 | if($MaximumVersion)
913 | {
914 | $convertedMaximumVersion = Convert-Version $MaximumVersion
915 |
916 | if(($thisVersion -le $convertedMaximumVersion))
917 | {
918 | if($searchDictionary.ContainsKey($entry.name))
919 | {
920 | $objEntry = $searchDictionary[$entry.name]
921 | $objVersion = Convert-Version $objEntry.Version
922 |
923 | if($thisVersion -gt $objVersion)
924 | {
925 | $toggle = $true
926 | }
927 | else
928 | {
929 | $toggle = $false
930 | }
931 | }
932 | else
933 | {
934 | $toggle = $true
935 | }
936 | }
937 | else
938 | {
939 | $toggle = $false
940 | }
941 | }
942 |
943 | if($RequiredVersion)
944 | {
945 | $convertedRequiredVersion = Convert-Version $RequiredVersion
946 |
947 | if(($thisVersion -eq $convertedRequiredVersion))
948 | {
949 | $toggle = $true
950 | }
951 | else
952 | {
953 | $toggle = $false
954 | }
955 | }
956 |
957 | if($AllVersions)
958 | {
959 | if($toggle)
960 | {
961 | $searchResults += $entry
962 | }
963 | }
964 |
965 | if($toggle)
966 | {
967 | if($searchDictionary.ContainsKey($entry.name))
968 | {
969 | $searchDictionary.Remove($entry.Name)
970 | }
971 |
972 | $searchDictionary.Add($entry.name, $entry)
973 | }
974 | }
975 |
976 | if(-not $AllVersions)
977 | {
978 | $searchDictionary.Keys | ForEach-Object {
979 | $searchResults += $searchDictionary.Item($_)
980 | }
981 | }
982 |
983 | $searchLanguageResults = @()
984 |
985 | foreach($searchEntry in $searchResults)
986 | {
987 | $EntryName = $searchEntry.Name
988 | $EntryVersion = $searchEntry.Version
989 | $EntryDescription = $searchEntry.Description
990 | $langDict = $searchEntry.Language
991 | $props= Get-Member -InputObject $langDict -MemberType NoteProperty
992 | $theSource = $Repository.Name
993 | $sku = [string]::Join(";", @($searchEntry.Sku))
994 | $nanoServerVersion = $searchEntry.NanoServerVersion
995 |
996 | $dependencies = @()
997 | $dependenciesProperty = Get-Member -InputObject $searchEntry -MemberType NoteProperty -Name Dependencies
998 | if ($null -ne $dependenciesProperty) {
999 | $dependencies = $searchEntry.Dependencies
1000 | }
1001 |
1002 | if (-not [string]::IsNullOrWhiteSpace($Culture))
1003 | {
1004 | if(($props.Name -notcontains $Culture) -or `
1005 | ($Culture -eq "base"))
1006 | {
1007 | ThrowError -CallerPSCmdlet $PSCmdlet `
1008 | -ExceptionName System.Exception `
1009 | -ExceptionMessage "Culture: $Culture is not supported" `
1010 | -ExceptionObject $EntryName `
1011 | -ErrorId WildCardCharsAreNotSupported `
1012 | -ErrorCategory InvalidData
1013 | return
1014 | }
1015 |
1016 | $languageObj = New-Object PSObject
1017 | $languageObj | Add-Member NoteProperty "base" $langDict."base"
1018 | $languageObj | Add-Member NoteProperty $Culture $langDict.$Culture
1019 |
1020 | $ResultEntry = Microsoft.PowerShell.Utility\New-Object PSCustomObject -Property ([ordered]@{
1021 | Name = $EntryName
1022 | Version = $EntryVersion
1023 | Description = $EntryDescription
1024 | Source = $theSource
1025 | Locations = $languageObj
1026 | Culture = $Culture
1027 | Sku = $sku
1028 | Dependencies = $dependencies
1029 | NanoServerVersion = $NanoServerVersion
1030 | })
1031 | $ResultEntry.PSTypeNames.Insert(0, "Microsoft.PowerShell.Commands.NanoServerPackageItemInfo")
1032 | $searchLanguageResults += $ResultEntry
1033 | }
1034 | else
1035 | {
1036 | $langList = @()
1037 | $langListString = ""
1038 |
1039 | $props.Name | ForEach-Object {
1040 | $langList += $_
1041 | if($_ -ne "base"){
1042 | $langListString += $_
1043 | $langListString += ", "
1044 | }
1045 | }
1046 |
1047 | $langListString = $langListString.Substring(0, $langListString.Length - 2)
1048 |
1049 | $ResultEntry = Microsoft.PowerShell.Utility\New-Object PSCustomObject -Property ([ordered]@{
1050 | Name = $EntryName
1051 | Version = $EntryVersion
1052 | Description = $EntryDescription
1053 | Source = $theSource
1054 | Locations = $langDict
1055 | Culture = $langListString
1056 | Sku = $sku
1057 | Dependencies = $dependencies
1058 | NanoServerVersion = $NanoServerVersion
1059 | })
1060 | $ResultEntry.PSTypeNames.Insert(0, "Microsoft.PowerShell.Commands.NanoServerPackageItemInfo")
1061 | $searchLanguageResults += $ResultEntry
1062 | }
1063 | }
1064 |
1065 | return $searchLanguageResults
1066 | }
1067 |
1068 | ###
1069 | ### SUMMARY: Download the file given the URI to the given location
1070 | ###
1071 | function DownloadFile
1072 | {
1073 | [CmdletBinding()]
1074 | param($downloadURL, $destination, [switch]$noProgress)
1075 |
1076 | $startTime = Get-Date
1077 |
1078 | try
1079 | {
1080 | # Download the file
1081 | Write-Verbose "Downloading $downloadUrl to $destination"
1082 | $saveItemPath = $PSScriptRoot + "\SaveHTTPItemUsingBITS.psm1"
1083 | Import-Module "$saveItemPath"
1084 | Save-HTTPItemUsingBitsTransfer -Uri $downloadURL `
1085 | -Destination $destination `
1086 | -NoProgress:$noProgress
1087 | Write-Verbose "Finished downloading"
1088 |
1089 | $endTime = Get-Date
1090 | $difference = New-TimeSpan -Start $startTime -End $endTime
1091 | $downloadTime = "Downloaded in " + $difference.Hours + " hours, " + $difference.Minutes + " minutes, " + $difference.Seconds + " seconds."
1092 | Write-Verbose $downloadTime
1093 | }
1094 | catch
1095 | {
1096 | ThrowError -CallerPSCmdlet $PSCmdlet `
1097 | -ExceptionName $_.Exception.GetType().FullName `
1098 | -ExceptionMessage $_.Exception.Message `
1099 | -ExceptionObject $downloadURL `
1100 | -ErrorId FailedToDownload `
1101 | -ErrorCategory InvalidOperation
1102 | }
1103 | }
1104 |
1105 | function Install-PackageHelper
1106 | {
1107 | [cmdletbinding()]
1108 | param(
1109 | [string[]]$Name,
1110 | [string]$Culture,
1111 | [string]$source,
1112 | [string]$mountDrive,
1113 | [string]$imagePath,
1114 | [ref]$successfullyInstalled,
1115 | [version]$MinimumVersion,
1116 | [version]$MaximumVersion,
1117 | [version][Alias('Version')]$RequiredVersion,
1118 | [string[]]$availablePackages,
1119 | [switch]$Force,
1120 | [PSCustomObject[]]$PackagesToBeInstalled
1121 | )
1122 |
1123 | $installedWindowsPackages = @()
1124 |
1125 | $successfullyInstalled.Value = $false
1126 |
1127 | if ([string]::IsNullOrWhiteSpace($Culture))
1128 | {
1129 | # if the culture is null for the online case, we can find out easily
1130 |
1131 | if ([string]::IsNullOrWhiteSpace($mountDrive))
1132 | {
1133 | $Culture = (Get-Culture).Name
1134 | }
1135 | else
1136 | {
1137 | Write-Verbose "Determining the culture of $mountDrive"
1138 |
1139 | $fileKey = Get-FileKey -filePath $imagePath
1140 |
1141 | if (-not $script:imageCultureCache.ContainsKey($fileKey))
1142 | {
1143 | $Culture = Get-ImageCulture -mountDrive $mountDrive
1144 |
1145 | if ($null -eq $Culture)
1146 | {
1147 | Write-Verbose "Cannot determine culture of $mountDrive with /Get-Intl. Trying to find culture using a sample package"
1148 |
1149 | $packagesOnTheMachine = $availablePackages
1150 |
1151 | if ($null -eq $packagesOnTheMachine -or $packagesOnTheMachine.Count -eq 0)
1152 | {
1153 | $packagesOnTheMachine = (Get-WindowsPackage -Path $mountDrive).PackageName
1154 | }
1155 |
1156 | foreach ($package in $packagesOnTheMachine)
1157 | {
1158 | $Culture = $package.Split('~')[3]
1159 |
1160 | # we have found a culture from a package installed!
1161 | if (-not [string]::IsNullOrWhiteSpace($Culture))
1162 | {
1163 | break
1164 | }
1165 | }
1166 | }
1167 |
1168 | # if after all that, culture still null then we have to abort
1169 | if ($null -eq $Culture)
1170 | {
1171 | Write-Warning "Cannot determine culture of the vhd. Please supply it directly."
1172 | return
1173 | }
1174 |
1175 | $script:imageCultureCache[$fileKey] = $Culture
1176 | }
1177 | else
1178 | {
1179 | $Culture = $script:imageCultureCache[$fileKey]
1180 | }
1181 | }
1182 |
1183 | Write-Verbose "The culture to be installed is $Culture"
1184 | }
1185 |
1186 | foreach ($packageName in $Name)
1187 | {
1188 | $randomName = [System.IO.Path]::GetRandomFileName()
1189 | $destinationFolder = Join-Path $script:downloadedCabLocation $randomName
1190 |
1191 | $baseVersion = $null
1192 | $languageVersion = $null
1193 |
1194 | foreach ($availablePackage in $availablePackages)
1195 | {
1196 | # check whether base package is already installed
1197 | if (Test-PackageWithSearchQuery -fullyQualifiedName $availablePackage -name $packageName -requiredVersion $RequiredVersion -minimumVersion $MinimumVersion -maximumVersion $MaximumVersion -Culture "Base")
1198 | {
1199 | $baseVersion = Convert-Version ($availablePackage.Split('~')[4])
1200 | }
1201 | # check whether language pack is installed
1202 | elseif (Test-PackageWithSearchQuery -fullyQualifiedName $availablePackage -name $packageName -requiredVersion $RequiredVersion -minimumVersion $MinimumVersion -maximumVersion $MaximumVersion -Culture $Culture)
1203 | {
1204 | $languageVersion = Convert-Version ($availablePackage.Split('~')[4])
1205 | }
1206 | }
1207 |
1208 | # no force and both are installed, just returned
1209 | if (-not $Force)
1210 | {
1211 | if ($null -ne $baseVersion -and $null -ne $languageVersion)
1212 | {
1213 | Write-Verbose "Skipping installed package $packageName"
1214 | $successfullyInstalled.Value = $true
1215 |
1216 | # returned the package to be installed
1217 |
1218 | if ($null -ne $PackagesToBeInstalled)
1219 | {
1220 | $PackagesToBeInstalled | Where-Object {$_.Name -eq $packageName} | ForEach-Object {$_.Culture = $Culture; $_}
1221 | }
1222 |
1223 | continue
1224 | }
1225 | }
1226 |
1227 | # This means source is offline
1228 | if ((-not [string]::IsNullOrWhiteSpace($source)) -and (Test-Path $source))
1229 | {
1230 | Write-Verbose "Installing package from $source"
1231 | $savedCabFilesToInstall = @($source)
1232 | }
1233 | else
1234 | {
1235 | if (-not (Test-Path $destinationFolder))
1236 | {
1237 | $null = mkdir $destinationFolder
1238 | }
1239 |
1240 | Write-Verbose "Downloading cab files to $destinationFolder"
1241 | try {
1242 | $script:availablePackages = $availablePackages
1243 | $savedPackages = Save-NanoServerPackage -Name $packageName -Culture $Culture -RequiredVersion $RequiredVersion -MinimumVersion $MinimumVersion `
1244 | -MaximumVersion $MaximumVersion -Path $destinationFolder -Force
1245 | }
1246 | finally {
1247 | $script:availablePackages = @()
1248 | }
1249 | }
1250 |
1251 | $savedCabFilesToInstall = @()
1252 | $savedCabFilesToInstallTuple = @()
1253 |
1254 | foreach ($savedPackage in $savedPackages)
1255 | {
1256 | $basePackageFile = (Join-Path $destinationFolder (Get-FileName -name $savedPackage.Name -Culture "" -version $savedPackage.Version))
1257 |
1258 | $basePackagePath = ""
1259 |
1260 | if (Test-Path $basePackageFile) {
1261 | $savedCabFilesToInstall += $basePackageFile
1262 | $basePackagePath = $basePackageFile
1263 | }
1264 |
1265 | # proceed with installation,
1266 | $languagePackageFile = (Join-Path $destinationFolder (Get-FileName -name $savedPackage.Name -Culture $Culture -version $savedPackage.Version))
1267 |
1268 | $langPackagePath = ""
1269 |
1270 | if (Test-Path $languagePackageFile) {
1271 | $savedCabFilesToInstall += $languagePackageFile
1272 | $installedWindowsPackages += $savedPackage
1273 | $langPackagePath = $languagePackageFile
1274 | }
1275 |
1276 | $savedCabFilesToInstallTuple += ([System.Tuple]::Create($basePackagePath, $langPackagePath))
1277 | }
1278 |
1279 | $restartNeeded = $false
1280 |
1281 | try
1282 | {
1283 | # Installing offline scenario
1284 | if (-not [string]::IsNullOrWhiteSpace($mountDrive))
1285 | {
1286 | # in this scenario, the function that calls us already mount the drive
1287 | Write-Verbose "Installing to mountdrive $mountDrive"
1288 | $successfullyInstalled.Value = Install-CabOfflineFromPath -mountDrive $mountDrive -packagePaths $savedCabFilesToInstall
1289 | }
1290 | else
1291 | {
1292 | Write-Verbose "Installing cab files $savedCabFilesToInstallTuple"
1293 | $successfullyInstalled.Value = Install-Online $savedCabFilesToInstallTuple -restartNeeded ([ref]$restartNeeded)
1294 |
1295 | if ($restartNeeded)
1296 | {
1297 | Write-Warning "Restart is needed to complete installation"
1298 | }
1299 | }
1300 |
1301 | }
1302 | catch
1303 | {
1304 | $successfullyInstalled.Value = $false
1305 | ThrowError -CallerPSCmdlet $PSCmdlet `
1306 | -ExceptionName $_.Exception.GetType().FullName `
1307 | -ExceptionMessage $_.Exception.Message `
1308 | -ExceptionObject $Name `
1309 | -ErrorId FailedToInstall `
1310 | -ErrorCategory InvalidOperation
1311 | }
1312 | finally
1313 | {
1314 | # Remove the online source
1315 | if (([string]::IsNullOrWhiteSpace($source)) -or (-not (Test-Path $source)))
1316 | {
1317 | Remove-Item $destinationFolder -Recurse -Force
1318 | }
1319 | }
1320 | }
1321 |
1322 | $installedWindowsPackages
1323 | }
1324 |
1325 | ###
1326 | ### SUMMARY: Checks if the system is nano server or not
1327 | ### Look into the win32 operating system class
1328 | ### Returns True if running on Nano
1329 | ### False otherwise
1330 | ### 144: Server Standard
1331 | ### 143: Server Datacenter
1332 | ### 109: Test Images
1333 | ###
1334 | function IsNanoServer
1335 | {
1336 | if ($script:isNanoServerInitialized)
1337 | {
1338 | return $script:isNanoServer
1339 | }
1340 | else
1341 | {
1342 | $script:isNanoServerInitialized = $true
1343 | $operatingSystem = Get-CimInstance -ClassName win32_operatingsystem
1344 | $script:systemSKU = $operatingSystem.OperatingSystemSKU
1345 | $script:systemVersion = [System.Environment]::OSVersion.Version
1346 | $script:isNanoServer = ($systemSKU -eq 109) -or ($systemSKU -eq 144) -or ($systemSKU -eq 143)
1347 | return $script:isNanoServer
1348 | }
1349 | }
1350 |
1351 | ###
1352 | ### SUMMARY: Checks if the given destination is kosher or not
1353 | ###
1354 | function CheckDestination
1355 | {
1356 | param($Destination)
1357 |
1358 | # Check if entire path is folder structure
1359 | $dest_item = Get-Item $Destination `
1360 | -ErrorAction SilentlyContinue `
1361 | -WarningAction SilentlyContinue
1362 |
1363 | if($dest_item -is [System.IO.DirectoryInfo])
1364 | {
1365 | return $true
1366 | }
1367 | else
1368 | {
1369 | Write-Verbose "Creating directory structure: $Destination"
1370 | mkdir $Destination
1371 | return $true
1372 | }
1373 |
1374 | return $false
1375 | }
1376 |
1377 | function CheckVersion
1378 | {
1379 | param
1380 | (
1381 | [System.Version]$MinimumVersion,
1382 | [System.Version]$MaximumVersion,
1383 | [System.Version]$RequiredVersion,
1384 | [switch]$AllVersions
1385 | )
1386 |
1387 | if($AllVersions -and $RequiredVersion)
1388 | {
1389 | Write-Error "AllVersions and RequiredVersion cannot be used together"
1390 | return $false
1391 | }
1392 |
1393 | if($AllVersions -or $RequiredVersion)
1394 | {
1395 | if($MinimumVersion -or $MaximumVersion)
1396 | {
1397 | Write-Error "AllVersions and RequiredVersion switch cannot be used with MinimumVersion or MaximumVersion"
1398 | return $false
1399 | }
1400 | }
1401 |
1402 | if($MinimumVersion -and $MaximumVersion)
1403 | {
1404 | if($MaximumVersion -lt $MinimumVersion)
1405 | {
1406 | Write-Error "Minimum Version cannot be more than Maximum Version"
1407 | return $false
1408 | }
1409 | }
1410 |
1411 | return $true
1412 | }
1413 |
1414 | function Get-FileName
1415 | {
1416 | param(
1417 | [string]$Culture,
1418 | [string]$name,
1419 | [string]$version
1420 | )
1421 |
1422 | $fileName = $name + "_" + $Culture + "_" + $version.replace('.','-') + $script:WindowsPackageExtension
1423 | return $fileName
1424 | }
1425 |
1426 | ###
1427 | ### SUMMARY: Get the search index from Azure
1428 | ###
1429 | function Get-SearchIndex
1430 | {
1431 | param
1432 | (
1433 | [switch]
1434 | $Force,
1435 |
1436 | [string]
1437 | $fwdLink
1438 | )
1439 |
1440 | $fullUrl = Resolve-FwdLink $fwdLink
1441 | $fullUrl = $fullUrl.AbsoluteUri
1442 | $destination = $script:WindowsPackage + "\searchNanoPackageIndex.txt"
1443 |
1444 | if(Test-Path $destination)
1445 | {
1446 | Remove-Item $destination
1447 | DownloadFile -downloadURL $fullUrl `
1448 | -destination $destination `
1449 | -noProgress
1450 | }
1451 | else
1452 | {
1453 | DownloadFile -downloadURL $fullUrl `
1454 | -destination $destination `
1455 | -noProgress
1456 | }
1457 |
1458 | return $destination
1459 | }
1460 |
1461 | function Get-ImageCulture
1462 | {
1463 | param
1464 | (
1465 | [string]$mountDrive
1466 | )
1467 |
1468 | $languageSearch = dism /Image:$mountDrive /Get-Intl
1469 |
1470 | foreach ($languageString in $languageSearch)
1471 | {
1472 | if ($languageString -match "\s*Default\s*system\s*UI\s*language\s*:\s*([a-z][a-z]-[A-Z][A-Z])\s*")
1473 | {
1474 | return $matches[1]
1475 | }
1476 | }
1477 |
1478 | }
1479 |
1480 | ###
1481 | ### SUMMARY: Resolve the fwdlink to get the actual search URL
1482 | ###
1483 | function Resolve-FwdLink
1484 | {
1485 | param
1486 | (
1487 | [parameter(Mandatory=$false)]
1488 | [System.String]$Uri
1489 | )
1490 |
1491 | if(-not (IsNanoServer))
1492 | {
1493 | Add-Type -AssemblyName System.Net.Http
1494 | }
1495 | $httpClient = New-Object System.Net.Http.HttpClient
1496 | $response = $httpclient.GetAsync($Uri)
1497 | $link = $response.Result.RequestMessage.RequestUri
1498 |
1499 |
1500 | return $link
1501 | }
1502 |
1503 | function Resolve-PathHelper
1504 | {
1505 | param
1506 | (
1507 | [Parameter()]
1508 | [ValidateNotNullOrEmpty()]
1509 | [string[]]
1510 | $path,
1511 |
1512 | [Parameter()]
1513 | [switch]
1514 | $isLiteralPath,
1515 |
1516 | [Parameter()]
1517 | [ValidateNotNullOrEmpty()]
1518 | [System.Management.Automation.PSCmdlet]
1519 | $callerPSCmdlet
1520 | )
1521 |
1522 | $resolvedPaths =@()
1523 |
1524 | foreach($currentPath in $path)
1525 | {
1526 | try
1527 | {
1528 | if($isLiteralPath)
1529 | {
1530 | $currentResolvedPaths = Microsoft.PowerShell.Management\Resolve-Path -LiteralPath $currentPath -ErrorAction Stop
1531 | }
1532 | else
1533 | {
1534 | $currentResolvedPaths = Microsoft.PowerShell.Management\Resolve-Path -Path $currentPath -ErrorAction Stop
1535 | }
1536 | }
1537 | catch
1538 | {
1539 | # Caller checks and throws an error if required
1540 | $resolvedPaths += $currentPath
1541 | continue
1542 | }
1543 |
1544 | foreach($currentResolvedPath in $currentResolvedPaths)
1545 | {
1546 | $resolvedPaths += $currentResolvedPath.ProviderPath
1547 | }
1548 | }
1549 |
1550 | $resolvedPaths
1551 | }
1552 |
1553 | ### Function to get package dependencies that need to be install
1554 | ### This will return false if there is a dependency loop
1555 | function Get-DependenciesToInstall($availablePackages, $culture, [psobject]$package, [System.Collections.ArrayList]$dependenciesToBeInstalled)
1556 | {
1557 | # no dependencies to be installed
1558 | if ($null -eq $package.Dependencies -or $package.Dependencies.Count -eq 0) {
1559 | $dependenciesToBeInstalled.Add($package) | Out-NUll
1560 | return $true
1561 | }
1562 |
1563 | $permanentlyMarked = [System.Collections.ArrayList]::new()
1564 | $temporarilyMarked = [System.Collections.ArrayList]::new()
1565 |
1566 | if (-not (DepthFirstVisit -package $package `
1567 | -temporarilyMarked $temporarilyMarked `
1568 | -permanentlyMarked $permanentlyMarked `
1569 | -dependenciesToBeInstalled $dependenciesToBeInstalled `
1570 | -culture $culture `
1571 | -availablePackages $availablePackages)) {
1572 | return $false
1573 | }
1574 |
1575 | return $true
1576 | }
1577 |
1578 | function DepthFirstVisit(
1579 | [psobject]$package,
1580 | [System.Collections.ArrayList]$permanentlyMarked,
1581 | [System.Collections.ArrayList]$temporarilyMarked,
1582 | [System.Collections.ArrayList]$dependenciesToBeInstalled,
1583 | $culture,
1584 | $availablePackages) {
1585 |
1586 | # get the hash of the package which is name!#!version
1587 | $hash = $package.Name.ToLower() + "!#!" + (Convert-Version $package.Version)
1588 |
1589 | if ($temporarilyMarked.IndexOf($hash) -ge 0) {
1590 | # dependency loop!
1591 | return $false
1592 | }
1593 |
1594 | # no need to visit permanently marked node
1595 | if ($permanentlyMarked.IndexOf($hash) -ge 0) {
1596 | return $true
1597 | }
1598 |
1599 | $temporarilyMarked.Add($hash) | Out-Null
1600 |
1601 | foreach ($dependency in $package.Dependencies) {
1602 | $skip = $false
1603 |
1604 | # check which dependencies are already installed
1605 | foreach ($availablePackage in $availablePackages)
1606 | {
1607 | # check whether language pack is installed (don't need to check base because if language pack is installed then base must be there)
1608 | if (Test-PackageWithSearchQuery -fullyQualifiedName $availablePackage -name $dependency.Name -requiredVersion $dependency.Version -Culture $culture)
1609 | {
1610 | # if it is, skipped this dependency
1611 | $skip = $true
1612 | }
1613 | }
1614 |
1615 | if ($skip) {
1616 | continue
1617 | }
1618 |
1619 | $dependencyPackage = Find -Name $dependency.Name -RequiredVersion $dependency.Version -Culture $culture
1620 |
1621 | if (-not (DepthFirstVisit -package $dependencyPackage -permanentlyMarked $permanentlyMarked `
1622 | -temporarilyMarked $temporarilyMarked -culture $culture `
1623 | -availablePackages $availablePackages -dependenciesToBeInstalled $dependenciesToBeInstalled)) {
1624 | return $false
1625 | }
1626 | }
1627 |
1628 | # add to list to install later
1629 | $dependenciesToBeInstalled.Add($package) | Out-Null
1630 |
1631 | # mark the node permanently
1632 | $permanentlyMarked.Add($hash) | Out-Null
1633 |
1634 | # remove the temporary mark
1635 | $temporarilyMarked.Remove($hash) | Out-Null
1636 |
1637 | return $true
1638 | }
1639 |
1640 | <#
1641 | Parse and return a dependency version
1642 | The version string is either a simple version or an arithmetic range
1643 | e.g.
1644 | 1.0 --> 1.0 ≤ x
1645 | (,1.0] --> x ≤ 1.0
1646 | (,1.0) --> x lt 1.0
1647 | [1.0] --> x == 1.0
1648 | (1.0,) --> 1.0 lt x
1649 | (1.0, 2.0) --> 1.0 lt x lt 2.0
1650 | [1.0, 2.0] --> 1.0 ≤ x ≤ 2.0
1651 |
1652 | #>
1653 | function NanoServerVersionMatched([string]$dependencyVersionString, [version]$version)
1654 | {
1655 | if ([string]::IsNullOrWhiteSpace($dependencyVersionString) -or $version -eq $null)
1656 | {
1657 | return $true
1658 | }
1659 |
1660 | $dependencyVersionString = $dependencyVersionString.Trim()
1661 |
1662 | $first = $dependencyVersionString[0]
1663 | $last = $dependencyVersionString[-1]
1664 |
1665 | if ($first -ne '(' -and $first -ne '[' -and $last -ne ']' -and $last -ne ')')
1666 | {
1667 | # stand alone so it is min inclusive
1668 | $versionToBeCompared = Convert-Version $dependencyVersionString
1669 |
1670 | return ($versionToBeCompared -ge $version)
1671 | }
1672 |
1673 | # now dep version string must have length > 3
1674 | if ($dependencyVersionString.Length -lt 3)
1675 | {
1676 | return $true
1677 | }
1678 |
1679 | if ($first -ne '(' -and $first -ne '[')
1680 | {
1681 | # first character must be either ( or [
1682 | return $true
1683 | }
1684 |
1685 | if ($last -ne ']' -and $last -ne ')')
1686 | {
1687 | # last character must be either ] or )
1688 | return $true
1689 | }
1690 |
1691 | # inclusive if the first or last is [ or ], otherwise exclusive
1692 | $minInclusive = ($first -eq '[')
1693 | $maxInclusive = ($last -eq ']')
1694 |
1695 | $dependencyVersionString = $dependencyVersionString.Substring(1, $dependencyVersionString.Length - 2)
1696 |
1697 | $parts = $dependencyVersionString.Split(',')
1698 |
1699 | if ($parts.Length -gt 2)
1700 | {
1701 | return $true
1702 | }
1703 |
1704 | $minVersion = Convert-Version $parts[0]
1705 |
1706 | if ($parts.Length -eq 1)
1707 | {
1708 | $maxVersion = $minVersion
1709 | }
1710 | else
1711 | {
1712 | $maxVersion = Convert-Version $parts[1]
1713 | }
1714 |
1715 | if ($minVersion -eq $null -and $maxVersion -eq $null)
1716 | {
1717 | return $true
1718 | }
1719 |
1720 | # now we can compare
1721 | if ($minVersion -ne $null)
1722 | {
1723 | if ($minInclusive)
1724 | {
1725 | # min inclusive so version must be >= minversion
1726 | if ($version -lt $minVersion)
1727 | {
1728 | return $false
1729 | }
1730 | }
1731 | else
1732 | {
1733 | # not mininclusive so version must be > minversion
1734 | if ($version -le $minVersion)
1735 | {
1736 | return $false
1737 | }
1738 | }
1739 | }
1740 |
1741 | if ($maxVersion -ne $null)
1742 | {
1743 | if ($maxInclusive)
1744 | {
1745 | if ($version -gt $maxVersion)
1746 | {
1747 | return $false
1748 | }
1749 | }
1750 | else
1751 | {
1752 | if ($version -ge $maxVersion)
1753 | {
1754 | return $false
1755 | }
1756 | }
1757 | }
1758 |
1759 | return $true
1760 | }
1761 |
1762 | function ConvertNanoServerVersionToString([string]$NanoServerVersion)
1763 | {
1764 | $result = $NanoServerVersion
1765 |
1766 | if ([string]::IsNullOrWhiteSpace($NanoServerVersion))
1767 | {
1768 | return $result
1769 | }
1770 |
1771 | $NanoServerVersion = $NanoServerVersion.Trim()
1772 |
1773 | $first = $NanoServerVersion[0]
1774 | $last = $NanoServerVersion[-1]
1775 |
1776 | if ($first -ne '(' -and $first -ne '[' -and $last -ne ']' -and $last -ne ')')
1777 | {
1778 | return "minimum NanoServer version of $NanoServerVersion (inclusive)"
1779 | }
1780 |
1781 | # now dep version string must have length > 3
1782 | if ($NanoServerVersion.Length -lt 3)
1783 | {
1784 | return $NanoServerVersion
1785 | }
1786 |
1787 | if ($first -ne '(' -and $first -ne '[')
1788 | {
1789 | # first character must be either ( or [
1790 | return $NanoServerVersion
1791 | }
1792 |
1793 | if ($last -ne ']' -and $last -ne ')')
1794 | {
1795 | # last character must be either ] or )
1796 | return $NanoServerVersion
1797 | }
1798 |
1799 | # inclusive if the first or last is [ or ], otherwise exclusive
1800 | $minInclusive = ($first -eq '[')
1801 | $maxInclusive = ($last -eq ']')
1802 |
1803 | $NanoServerVersion = $NanoServerVersion.Substring(1, $NanoServerVersion.Length - 2)
1804 |
1805 | $parts = $NanoServerVersion.Split(',')
1806 |
1807 | if ($parts.Length -gt 2)
1808 | {
1809 | return $NanoServerVersion
1810 | }
1811 |
1812 | $minVersion = $parts[0]
1813 |
1814 | if ($parts.Length -eq 1)
1815 | {
1816 | $maxVersion = $minVersion
1817 | }
1818 | else
1819 | {
1820 | $maxVersion = $parts[1]
1821 | }
1822 |
1823 | if ($minVersion -eq $null -and $maxVersion -eq $null)
1824 | {
1825 | return $NanoServerVersion
1826 | }
1827 |
1828 | $result = ""
1829 |
1830 | # now we can compare
1831 | if (-not [string]::IsNullOrWhiteSpace($minVersion))
1832 | {
1833 | $result += "minimum NanoServer version of $minVersion"
1834 |
1835 | if ($minInclusive)
1836 | {
1837 | $result += " (inclusive)"
1838 | }
1839 | }
1840 |
1841 | if (-not [string]::IsNullOrWhiteSpace($maxVersion))
1842 | {
1843 | # there is already something in result, so add an and
1844 | if (-not [string]::IsNullOrWhiteSpace($result))
1845 | {
1846 | $result += " and "
1847 | }
1848 |
1849 | $result += "maximum NanoServer version of $maxVersion"
1850 | if ($maxInclusive)
1851 | {
1852 | $result += " (inclusive)"
1853 | }
1854 | }
1855 |
1856 | return $result
1857 | }
1858 |
1859 | #endregion Helpers
1860 |
1861 | #region Source
1862 |
1863 | ###
1864 | ### SUMMARY: Gets the source from where to get the images
1865 | ### Initializes the variables for find, download and install
1866 | ### RETURN:
1867 | ### Returns the type of
1868 | ###
1869 | function Get-Source
1870 | {
1871 | param($sources)
1872 |
1873 | Set-ModuleSourcesVariable
1874 |
1875 | $listOfSources = @()
1876 |
1877 | # if sources is supplied and we cannot find it, error out
1878 | if((-not [string]::IsNullOrWhiteSpace($sources)) -and (-not $script:windowsPackageSources.Contains($sources)))
1879 | {
1880 | ThrowError -CallerPSCmdlet $PSCmdlet `
1881 | -ExceptionName System.Exception `
1882 | -ExceptionMessage "Unable to find package source '$sources'. Use Get-PackageSource to see all available package sources." `
1883 | -ExceptionObject $sources `
1884 | -ErrorId WildCardCharsAreNotSupported `
1885 | -ErrorCategory InvalidData
1886 | }
1887 |
1888 | foreach($mySource in $script:WindowsPackageSources.Values)
1889 | {
1890 | if((-not $sources) -or
1891 | (($mySource.Name -eq $sources) -or
1892 | ($mySource.Location -eq $sources)))
1893 | {
1894 | $tempHolder = @{}
1895 |
1896 | $location = $mySource.SourceLocation
1897 | $tempHolder.Add("SourceLocation", $location)
1898 |
1899 | $packageSourceName = $mySource.Name
1900 | $tempHolder.Add("Name", $packageSourceName)
1901 |
1902 | $listOfSources += $tempHolder
1903 | }
1904 | }
1905 |
1906 | return $listOfSources
1907 | }
1908 |
1909 | function Set-ModuleSourcesVariable
1910 | {
1911 | if(Microsoft.PowerShell.Management\Test-Path $script:file_modules)
1912 | {
1913 | $script:windowsPackageSources = DeSerializePSObject -Path $script:file_modules
1914 | }
1915 |
1916 | if((-not (Microsoft.PowerShell.Management\Test-Path $script:file_modules)))
1917 | {
1918 | $script:windowsPackageSources = [ordered]@{}
1919 | $defaultModuleName = "NanoServerPackageSource"
1920 |
1921 | $defaultModuleSource = Microsoft.PowerShell.Utility\New-Object PSCustomObject -Property ([ordered]@{
1922 | Name = $script:defaultPackageName
1923 | SourceLocation = $script:defaultPackageLocation
1924 | Trusted=$false
1925 | Registered= $true
1926 | InstallationPolicy = "Untrusted"
1927 | })
1928 |
1929 | $script:windowsPackageSources.Add($defaultModuleName, $defaultModuleSource)
1930 | Save-ModuleSource
1931 | }
1932 | }
1933 |
1934 | function Get-PackageProviderName
1935 | {
1936 | return $script:providerName
1937 | }
1938 |
1939 | ###
1940 | ### SUMMARY: Deserializes the PSObject
1941 | ###
1942 | function DeSerializePSObject
1943 | {
1944 | [CmdletBinding(PositionalBinding=$false)]
1945 | Param
1946 | (
1947 | [Parameter(Mandatory=$true)]
1948 | $Path
1949 | )
1950 | $filecontent = Microsoft.PowerShell.Management\Get-Content -Path $Path
1951 | [System.Management.Automation.PSSerializer]::Deserialize($filecontent)
1952 | }
1953 |
1954 | function Save-ModuleSource
1955 | {
1956 | # check if exists
1957 | if(-not (Test-Path $script:WindowsPackage))
1958 | {
1959 | $null = mkdir $script:WindowsPackage
1960 | }
1961 |
1962 | # seralize module
1963 | Microsoft.PowerShell.Utility\Out-File -FilePath $script:file_modules `
1964 | -Force `
1965 | -InputObject ([System.Management.Automation.PSSerializer]::Serialize($script:windowsPackageSources))
1966 | }
1967 |
1968 | function Resolve-PackageSource
1969 | {
1970 | Set-ModuleSourcesVariable
1971 |
1972 | $SourceName = $request.PackageSources
1973 |
1974 | if(-not $SourceName)
1975 | {
1976 | $SourceName = "*"
1977 | }
1978 |
1979 | foreach($moduleSourceName in $SourceName)
1980 | {
1981 | if($request.IsCanceled)
1982 | {
1983 | return
1984 | }
1985 |
1986 | $wildcardPattern = New-Object System.Management.Automation.WildcardPattern $moduleSourceName,$script:wildcardOptions
1987 | $moduleSourceFound = $false
1988 |
1989 | $script:windowsPackageSources.GetEnumerator() |
1990 | Microsoft.PowerShell.Core\Where-Object {$wildcardPattern.IsMatch($_.Key)} |
1991 | Microsoft.PowerShell.Core\ForEach-Object {
1992 | $moduleSource = $script:windowsPackageSources[$_.Key]
1993 | $packageSource = New-PackageSourceFromModuleSource -ModuleSource $moduleSource
1994 | Write-Output -InputObject $packageSource
1995 | $moduleSourceFound = $true
1996 | }
1997 |
1998 | if(-not $moduleSourceFound)
1999 | {
2000 | $sourceName = Get-SourceName -Location $moduleSourceName
2001 | if($sourceName)
2002 | {
2003 | $moduleSource = $script:windowsPackageSources[$sourceName]
2004 | $packageSource = New-PackageSourceFromModuleSource -ModuleSource $moduleSource
2005 | Write-Output -InputObject $packageSource
2006 | }
2007 | }
2008 | }
2009 | }
2010 |
2011 | function Add-PackageSource
2012 | {
2013 | [CmdletBinding()]
2014 | param
2015 | (
2016 | [string]
2017 | $Name,
2018 |
2019 | [string]
2020 | $Location,
2021 |
2022 | [bool]
2023 | $Trusted
2024 | )
2025 |
2026 | Set-ModuleSourcesVariable
2027 |
2028 | $options = $request.Options
2029 | $Default = $false
2030 |
2031 | if($options)
2032 | {
2033 | foreach( $o in $options.Keys )
2034 | {
2035 | Write-Debug ("OPTION dictionary: {0} => {1}" -f ($o, $options[$o]) )
2036 | }
2037 |
2038 | if($options.ContainsKey('Default'))
2039 | {
2040 | $Default = $options['Default']
2041 | }
2042 | }
2043 |
2044 | if($Default)
2045 | {
2046 | $Name = $script:defaultPackageName
2047 | $Location = $script:defaultPackageLocation
2048 | }
2049 |
2050 | # Check if this package source already exists
2051 | foreach($psModuleSource in $script:windowsPackageSources.Values)
2052 | {
2053 | if(($Name -eq $psModuleSource.Name) -or
2054 | ($Location -eq $psModuleSource.SourceLocation))
2055 | {
2056 | throw "Package Source $Name with $Location already exists"
2057 | }
2058 | }
2059 |
2060 | # Add new module source
2061 | $moduleSource = Microsoft.PowerShell.Utility\New-Object PSCustomObject -Property ([ordered]@{
2062 | Name = $Name
2063 | SourceLocation = $Location
2064 | Trusted=$Trusted
2065 | Registered= $true
2066 | InstallationPolicy = if($Trusted) {'Trusted'} else {'Untrusted'}
2067 | })
2068 |
2069 | $script:windowsPackageSources.Add($Name, $moduleSource)
2070 | Save-ModuleSource
2071 | Write-Output -InputObject (New-PackageSourceFromModuleSource -ModuleSource $moduleSource)
2072 | }
2073 |
2074 | function Remove-PackageSource
2075 | {
2076 | param
2077 | (
2078 | [string]
2079 | $Name
2080 | )
2081 |
2082 | Set-ModuleSourcesVariable -Force
2083 |
2084 | if(-not $script:windowsPackageSources.Contains($Name))
2085 | {
2086 | Write-Error -Message "Package source $Name not found" `
2087 | -ErrorId "Package source $Name not found" `
2088 | -Category InvalidOperation `
2089 | -TargetObject $Name
2090 | continue
2091 | }
2092 |
2093 | $script:windowsPackageSources.Remove($Name)
2094 |
2095 | Save-ModuleSource
2096 | }
2097 |
2098 | function New-PackageSourceFromModuleSource
2099 | {
2100 | param
2101 | (
2102 | [Parameter(Mandatory=$true)]
2103 | $ModuleSource
2104 | )
2105 |
2106 | $packageSourceDetails = @{}
2107 |
2108 | # create a new package source
2109 | $src = New-PackageSource -Name $ModuleSource.Name `
2110 | -Location $ModuleSource.SourceLocation `
2111 | -Trusted $ModuleSource.Trusted `
2112 | -Registered $ModuleSource.Registered `
2113 | -Details $packageSourceDetails
2114 |
2115 | # return the package source object.
2116 | Write-Output -InputObject $src
2117 | }
2118 |
2119 | function Get-SourceName
2120 | {
2121 | [CmdletBinding()]
2122 | [OutputType("string")]
2123 | Param
2124 | (
2125 | [Parameter(Mandatory=$true)]
2126 | [ValidateNotNullOrEmpty()]
2127 | [string]
2128 | $Location
2129 | )
2130 |
2131 | Set-ModuleSourcesVariable
2132 |
2133 | foreach($psModuleSource in $script:windowsPackageSources.Values)
2134 | {
2135 | if(($psModuleSource.Name -eq $Location) -or
2136 | ($psModuleSource.SourceLocation -eq $Location))
2137 | {
2138 | return $psModuleSource.Name
2139 | }
2140 | }
2141 | }
2142 |
2143 | #endregion Source
2144 |
2145 | #region OneGet
2146 |
2147 | function Find-Package
2148 | {
2149 | [CmdletBinding()]
2150 | param
2151 | (
2152 | [string]
2153 | $Name,
2154 |
2155 | [string]
2156 | $requiredVersion,
2157 |
2158 | [string]
2159 | $minimumVersion,
2160 |
2161 | [string]
2162 | $maximumVersion
2163 | )
2164 |
2165 | $options = $request.Options
2166 | $languageChosen = $null
2167 | $wildcardPattern = $null
2168 | $force = $false
2169 | $allVersions = $false
2170 |
2171 | # path to the offline nano image
2172 | $imagePath = $null
2173 | $source = $null
2174 |
2175 | # check out what options the users give us
2176 | if($options)
2177 | {
2178 | foreach( $o in $options.Keys )
2179 | {
2180 | Write-Debug ("OPTION dictionary: {0} => {1}" -f ($o, $options[$o]) )
2181 | }
2182 |
2183 | if($options.ContainsKey('Force'))
2184 | {
2185 | $force = $options['Force']
2186 | }
2187 |
2188 | if ($options.ContainsKey("ImagePath"))
2189 | {
2190 | $imagePath = $options['ImagePath']
2191 | }
2192 |
2193 | if ($options.ContainsKey("Culture"))
2194 | {
2195 | $languageChosen = $options['Culture']
2196 | }
2197 |
2198 | if ($options.ContainsKey('Source'))
2199 | {
2200 | $source = $options['Source']
2201 | }
2202 |
2203 | if ($options.ContainsKey('AllVersions'))
2204 | {
2205 | $allVersions = $options['AllVersions']
2206 | }
2207 | }
2208 |
2209 | # Let find-windowspackage handle the query
2210 | $convertedRequiredVersion = Convert-Version $requiredVersion
2211 | $convertedMinVersion = Convert-Version $minimumVersion
2212 | $convertedMaxVersion = Convert-Version $maximumVersion
2213 |
2214 | if ([string]::IsNullOrWhiteSpace($Name))
2215 | {
2216 | $Name = @('*')
2217 | }
2218 |
2219 | $packages = Find -Name $Name `
2220 | -MinimumVersion $convertedMinVersion `
2221 | -MaximumVersion $convertedMaxVersion `
2222 | -RequiredVersion $convertedRequiredVersion `
2223 | -AllVersions:$AllVersions `
2224 | -Culture $languageChosen `
2225 | -Force:$Force
2226 |
2227 | if ($null -eq $packages)
2228 | {
2229 | return
2230 | }
2231 |
2232 | # check for packages that match the query
2233 | foreach ($package in $packages)
2234 | {
2235 | $swid = New-SoftwareIdentityFromWindowsPackageItemInfo $package
2236 | Write-Output $swid
2237 | }
2238 | }
2239 |
2240 | function Install-Package
2241 | {
2242 | [CmdletBinding()]
2243 | param
2244 | (
2245 | [string]
2246 | $fastPackageReference
2247 | )
2248 |
2249 | Write-Verbose $fastPackageReference
2250 |
2251 | # path to the offline nano image
2252 | $imagePath = $null
2253 |
2254 | $options = $request.Options
2255 |
2256 | $force = $false
2257 |
2258 | # check out what options the users give us
2259 | if($options)
2260 | {
2261 | foreach( $o in $options.Keys )
2262 | {
2263 | Write-Debug ("OPTION dictionary: {0} => {1}" -f ($o, $options[$o]) )
2264 | }
2265 |
2266 | if($options.ContainsKey('Force'))
2267 | {
2268 | $force = $options['Force']
2269 | }
2270 |
2271 | if ($options.ContainsKey("ToVhd"))
2272 | {
2273 | $imagePath = $options['ToVhd']
2274 | }
2275 |
2276 | if ($options.ContainsKey("Culture"))
2277 | {
2278 | $languageChosen = $options['Culture']
2279 | }
2280 | }
2281 |
2282 | # if image path is supplied and it points to non existing file, returns
2283 | if (-not [string]::IsNullOrWhiteSpace($imagePath) -and (-not ([System.IO.File]::Exists($ImagePath))))
2284 | {
2285 | ThrowError -CallerPSCmdlet $PSCmdlet `
2286 | -ExceptionName System.ArgumentException `
2287 | -ExceptionMessage "$ImagePath does not exist" `
2288 | -ExceptionObject $imagePath `
2289 | -ErrorId "InvalidImagePath" `
2290 | -ErrorCategory InvalidData
2291 |
2292 | return
2293 | }
2294 |
2295 | [string[]] $splitterArray = @("$separator")
2296 |
2297 | [string[]] $resultArray = $fastPackageReference.Split($splitterArray, [System.StringSplitOptions]::None)
2298 |
2299 | $name = $resultArray[0]
2300 | $version = $resultArray[1]
2301 | $Culture = $resultArray[3]
2302 | $Sku = $resultArray[4]
2303 | $NanoServerVersion = $resultArray[5]
2304 |
2305 | # if culture is a string, set it to null (this means user did not supply culture)
2306 | if ($Culture.Contains(','))
2307 | {
2308 | $Culture = ''
2309 | }
2310 |
2311 | $convertedVersion = Convert-Version $version
2312 |
2313 | [bool]$success = $false
2314 |
2315 | $mountDrive = $null
2316 |
2317 | $availablePackages = @()
2318 |
2319 | if (-not [string]::IsNullOrWhiteSpace($imagePath))
2320 | {
2321 | $mountDrive = New-MountDrive
2322 |
2323 | Write-Verbose "Mounting $imagePath to $mountDrive"
2324 |
2325 | $null = Mount-WindowsImage -ImagePath $imagePath -Index 1 -Path $mountDrive
2326 |
2327 | if (-not $force) {
2328 | $fileKey = Get-FileKey -filePath $imagePath
2329 |
2330 | $availablePackages = @(($script:imagePathCache[$fileKey]).Keys)
2331 | }
2332 |
2333 | # if this package does not apply to standard, we have to check whether the nano is standard or not
2334 | if (-not $Sku.Contains("144") -or (-not [string]::IsNullOrWhiteSpace($NanoServerVersion)))
2335 | {
2336 | $regKey = $null
2337 |
2338 | $mountedVhdEdition = "ERROR"
2339 | $vhdNanoServerVersion = $null
2340 |
2341 | try
2342 | {
2343 | reg load HKLM\NANOSERVERPACKAGEVHDSYS "$mountDrive\Windows\System32\config\SOFTWARE" | Out-Null
2344 | $regKey = dir 'HKLM:\NANOSERVERPACKAGEVHDSYS\Microsoft\Windows NT'
2345 | $mountedVHDEdition = $regKey.GetValue("EditionID")
2346 | $majorVersion = $regKey.GetValue("CurrentMajorVersionNumber")
2347 | $minorVersion = $regKey.GetValue("CurrentMinorVersionNumber")
2348 | $buildVersion = $regKey.GetValue("CurrentBuildNumber")
2349 | $vhdNanoServerVersion = [version]::new($majorVersion, $minorVersion, $buildVersion, 0)
2350 | }
2351 | catch
2352 | {
2353 | # ERROR
2354 | $mountedVHDEdition = "ERROR"
2355 | $vhdNanoServerVersion = $null
2356 | }
2357 | finally
2358 | {
2359 | try
2360 | {
2361 | if ($regKey -ne $null)
2362 | {
2363 | $regKey.Handle.Close()
2364 | [gc]::Collect()
2365 | reg unload HKLM\NANOSERVERPACKAGEVHDSYS | Out-Null
2366 | }
2367 | }
2368 | catch { }
2369 | }
2370 |
2371 | # if this is not applicable to server standard nano
2372 | if (-not $Sku.Contains("144") -and $mountedVHDEdition -eq "ServerStandardNano")
2373 | {
2374 | # cannot be installed
2375 | # unmount
2376 | if ($null -ne $mountDrive)
2377 | {
2378 | Write-Verbose "Unmounting mountdrive $mountDrive"
2379 | Remove-MountDrive $mountDrive -discard $true
2380 | }
2381 |
2382 | ThrowError -CallerPSCmdlet $PSCmdlet `
2383 | -ExceptionName System.ArgumentException `
2384 | -ExceptionMessage "$name cannot be installed on this edition of NanoServer" `
2385 | -ExceptionObject $fastPackageReference `
2386 | -ErrorId FailedToInstall `
2387 | -ErrorCategory InvalidData
2388 | }
2389 |
2390 | if (-not [string]::IsNullOrWhiteSpace($NanoServerVersion) -and -not (NanoServerVersionMatched -dependencyVersionString $NanoServerVersion -version $vhdNanoServerVersion))
2391 | {
2392 | ThrowError -CallerPSCmdlet $PSCmdlet `
2393 | -ExceptionName System.ArgumentException `
2394 | -ExceptionMessage "The package '$name $version' requires $(ConvertNanoServerVersionToString $NanoServerVersion). But the current Nano Server has version $vhdNanoServerVersion which is out of this range. Please see https://github.com/OneGet/NanoServerPackage for instructions." `
2395 | -ExceptionObject $fastPackageReference `
2396 | -ErrorId FailedToInstall `
2397 | -ErrorCategory InvalidData
2398 | }
2399 | }
2400 | }
2401 | else {
2402 | if (IsNanoServer)
2403 | {
2404 | # if this is a nano, then systemSKU would be populated after isnanoserver call
2405 | if (-not $Sku.Contains($script:systemSKU.ToString()))
2406 | {
2407 | ThrowError -CallerPSCmdlet $PSCmdlet `
2408 | -ExceptionName System.ArgumentException `
2409 | -ExceptionMessage "$name cannot be installed on this edition of NanoServer" `
2410 | -ExceptionObject $fastPackageReference `
2411 | -ErrorId FailedToInstall `
2412 | -ErrorCategory InvalidData
2413 | }
2414 |
2415 | # if this is nanoserver, then we should also have the version populated
2416 | if (-not (NanoServerVersionMatched -dependencyVersionString $NanoServerVersion -version $script:systemVersion))
2417 | {
2418 | ThrowError -CallerPSCmdlet $PSCmdlet `
2419 | -ExceptionName System.ArgumentException `
2420 | -ExceptionMessage "The package '$name' with version $version requires $(ConvertNanoServerVersionToString $NanoServerVersion). But current Nano Server has version $script:systemVersion which is out of this range. Please see https://github.com/OneGet/NanoServerPackage for instructions." `
2421 | -ExceptionObject $fastPackageReference `
2422 | -ErrorId FailedToInstall `
2423 | -ErrorCategory InvalidData
2424 | }
2425 | }
2426 |
2427 | if (-not $force) {
2428 | $availablePackages = @($script:onlinePackageCache.Keys)
2429 | }
2430 | }
2431 |
2432 | try
2433 | {
2434 | $installedPackages = Install-PackageHelper -Name $name `
2435 | -Culture $Culture `
2436 | -Version $convertedVersion `
2437 | -mountDrive $mountDrive `
2438 | -successfullyInstalled ([ref]$success) `
2439 | -availablePackages: $availablePackages
2440 |
2441 | foreach ($installedPackage in $installedPackages)
2442 | {
2443 | Write-Output (New-SoftwareIdentityFromWindowsPackageItemInfo ($installedPackage))
2444 | }
2445 | }
2446 | finally
2447 | {
2448 | # unmount
2449 | if ($null -ne $mountDrive)
2450 | {
2451 | Write-Verbose "Unmounting mountdrive $mountDrive"
2452 | Remove-MountDrive $mountDrive -discard (-not $success)
2453 | }
2454 | }
2455 | }
2456 |
2457 | function Download-Package
2458 | {
2459 | [CmdletBinding()]
2460 | param
2461 | (
2462 | [Parameter(Mandatory=$true)]
2463 | [ValidateNotNullOrEmpty()]
2464 | [string]
2465 | $FastPackageReference,
2466 |
2467 | [Parameter(Mandatory=$true)]
2468 | [ValidateNotNullOrEmpty()]
2469 | [string]
2470 | $Location
2471 | )
2472 |
2473 | [string[]] $splitterArray = @("$separator")
2474 |
2475 | [string[]] $resultArray = $fastPackageReference.Split($splitterArray, [System.StringSplitOptions]::None)
2476 |
2477 | $name = $resultArray[0]
2478 | $version = $resultArray[1]
2479 | #$source = $resultArray[2]
2480 | $Culture = $resultArray[3]
2481 | $convertedVersion = Convert-Version $version
2482 |
2483 | # if culture is a string, set it to null (this means user did not supply culture)
2484 | if ($Culture.Contains(','))
2485 | {
2486 | $Culture = ''
2487 | }
2488 |
2489 | # no culture given, use culture of the system
2490 | if ([string]::IsNullOrWhiteSpace($Culture))
2491 | {
2492 | $Culture = (Get-Culture).Name
2493 | }
2494 |
2495 | $force = $false
2496 | $options = $request.Options
2497 |
2498 | if ($options)
2499 | {
2500 | if ($options.ContainsKey('Force'))
2501 | {
2502 | $force = $options['Force']
2503 | }
2504 | }
2505 |
2506 | $savedWindowsPackageItems = Save-NanoServerPackage -Name $name `
2507 | -Culture $Culture `
2508 | -RequiredVersion $convertedVersion `
2509 | -Path $Location `
2510 | -Force:$force
2511 |
2512 | foreach ($savedWindowsPackageItem in $savedWindowsPackageItems)
2513 | {
2514 | Write-Output (New-SoftwareIdentityFromWindowsPackageItemInfo $savedWindowsPackageItem)
2515 | }
2516 | }
2517 |
2518 | function Get-InstalledPackage
2519 | {
2520 | [CmdletBinding()]
2521 | param
2522 | (
2523 | [Parameter()]
2524 | [string]
2525 | $Name,
2526 |
2527 | [Parameter()]
2528 | [Version]
2529 | $RequiredVersion,
2530 |
2531 | [Parameter()]
2532 | [Version]
2533 | $MinimumVersion,
2534 |
2535 | [Parameter()]
2536 | [Version]
2537 | $MaximumVersion
2538 | )
2539 |
2540 | $options = $request.Options
2541 | $wildcardPattern = $null
2542 | $languageChosen = $null
2543 |
2544 | # If name is null or whitespace, interpret as *
2545 | if ([string]::IsNullOrWhiteSpace($Name))
2546 | {
2547 | $Name = "*"
2548 | }
2549 |
2550 | if ([System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($Name))
2551 | {
2552 | $wildcardPattern = New-Object System.Management.Automation.WildcardPattern $Name,$script:wildcardOptions
2553 | }
2554 |
2555 | $force = $false
2556 |
2557 | # path to the offline nano image
2558 | $imagePath = $null
2559 |
2560 | # check out what options the users give us
2561 | if($options)
2562 | {
2563 | foreach( $o in $options.Keys )
2564 | {
2565 | Write-Debug ("OPTION dictionary: {0} => {1}" -f ($o, $options[$o]) )
2566 | }
2567 |
2568 | if($options.ContainsKey('Force'))
2569 | {
2570 | $force = $options['Force']
2571 | }
2572 |
2573 | if ($options.ContainsKey("FromVhd"))
2574 | {
2575 | $imagePath = $options['FromVhd']
2576 | }
2577 | elseif ($options.ContainsKey("ToVhd"))
2578 | {
2579 | # in case of install
2580 | $imagePath = $options['ToVhd']
2581 | }
2582 |
2583 | if ($options.ContainsKey("Culture"))
2584 | {
2585 | $languageChosen = $options['Culture']
2586 |
2587 | $cannotConvertCulture = $false
2588 |
2589 | # try to convert the culture
2590 | try
2591 | {
2592 | $convertedCulture = [cultureinfo]$languageChosen
2593 |
2594 | # apparently, converting culture 'blah' will not work but 'bla' will work ?!?
2595 | if ($null -eq $convertedCulture -or $null -eq $convertedCulture.DisplayName -or $convertedCulture.DisplayName.Trim() -match "Unknown Language")
2596 | {
2597 | $cannotConvertCulture = $true
2598 | }
2599 | }
2600 | catch
2601 | {
2602 | $cannotConvertCulture = $true
2603 | }
2604 |
2605 | # if we cannot convert culture, throw error
2606 | if ($cannotConvertCulture)
2607 | {
2608 | ThrowError -CallerPSCmdlet $PSCmdlet `
2609 | -ExceptionName System.ArgumentException `
2610 | -ExceptionMessage "$languageChosen is not a valid culture" `
2611 | -ExceptionObject $languageChosen `
2612 | -ErrorId InvalidCulture `
2613 | -ErrorCategory InvalidData
2614 | }
2615 | }
2616 | }
2617 |
2618 | if (-not [string]::IsNullOrWhiteSpace($imagePath))
2619 | {
2620 | $mountDrive = New-MountDrive
2621 |
2622 | Write-Verbose "Mounting $imagePath to $mountDrive"
2623 |
2624 | $id = Write-Progress -ParentId 1 -Activity "Getting packages information"
2625 | if (-not $id)
2626 | {
2627 | $id = 1
2628 | }
2629 |
2630 | Write-Progress -Activity "Mounting $imagePath to $mountDrive" -PercentComplete 0 -Id $id
2631 |
2632 | Mount-WindowsImage -ImagePath $imagePath -Index 1 -Path $mountDrive
2633 |
2634 | Write-Verbose "Done Mounting"
2635 |
2636 | # Now we can try to find the packages
2637 | try
2638 | {
2639 | # Get all the available packages on the mountdrive
2640 | $packages = Get-WindowsPackage -Path $mountDrive
2641 | Write-Verbose "Finished getting packages from $mountDrive with $($packages.Count) packages"
2642 | $count = 0
2643 |
2644 | Write-Progress -Activity "Getting packages information from $mountDrive" -PercentComplete 5 -Id $id
2645 |
2646 | $packagesToBeReturned = New-Object 'System.Collections.Generic.List[string]'
2647 |
2648 | $availablePackages = $packages.PackageName.ToLower()
2649 |
2650 | # check for packages that match the query
2651 | foreach ($fullyQualifiedName in $availablePackages)
2652 | {
2653 | if (Test-PackageWithSearchQuery -fullyQualifiedName $fullyQualifiedName -requiredVersion $RequiredVersion -Name $Name `
2654 | -minimumVersion $MinimumVersion -maximumVersion $MaximumVersion -Culture $languageChosen -wildCardPattern $wildcardPattern)
2655 | {
2656 | $packagesToBeReturned.Add($fullyQualifiedName)
2657 | }
2658 | }
2659 |
2660 | $fileKey = Get-FileKey -filePath $imagePath
2661 |
2662 | $packageDictionary = @{}
2663 |
2664 | # try to get the cache if it exists, otherwise create one
2665 | if (-not $script:imagePathCache.ContainsKey($fileKey))
2666 | {
2667 | $script:imagePathCache.Add($fileKey, $packageDictionary)
2668 | }
2669 |
2670 | $packageDictionary = $script:imagePathCache[$fileKey]
2671 |
2672 | foreach ($fullyQualifiedName in $availablePackages)
2673 | {
2674 | if (-not $packageDictionary.ContainsKey($fullyQualifiedName))
2675 | {
2676 | $packageDictionary[$fullyQualifiedName] = $null
2677 | }
2678 | }
2679 |
2680 | # Before we get more details, we will clump together base and language pack if they have same name and version
2681 | if ($packagesToBeReturned.Count -gt 0)
2682 | {
2683 | $packagesToBeReturned = Filter-Packages $packagesToBeReturned
2684 | }
2685 |
2686 | foreach ($package in $packagesToBeReturned)
2687 | {
2688 | # scale the percent from 1 to 80 to account for the initial and final step of mounting and dismounting
2689 | $percentComplete = (($count*80/$packages.Count) + 10) -as [int]
2690 | $count += 1
2691 |
2692 | Write-Progress -Activity `
2693 | "Getting package information for $package in $mountDrive" `
2694 | -PercentComplete $percentComplete `
2695 | -Id $id
2696 |
2697 | # store the information in cache if it's not there or if user uses force
2698 | if ((-not $packageDictionary.ContainsKey($package)) -or ($null -eq $packageDictionary[$package]) -or $force)
2699 | {
2700 | Write-Debug "Getting information for package $package and storing it in cache"
2701 | # store the information in cache
2702 | $packageDictionary[$package.ToLower()] = Get-WindowsPackage -PackageName $package -Path $mountDrive
2703 | }
2704 |
2705 | Write-Output (New-SoftwareIdentityPackage $packageDictionary[$package.ToLower()] -src $imagePath)
2706 | }
2707 |
2708 | # Get the list of packages that are in the cache but not in the latest list we have
2709 | $packageToBeRemoved = @()
2710 | foreach ($pkg in $packageDictionary.GetEnumerator())
2711 | {
2712 | if (-not $availablePackages.Contains($pkg.Name))
2713 | {
2714 | $packageToBeRemoved += $pkg.Name
2715 | }
2716 | }
2717 |
2718 | # Remove packages in this list from the cache
2719 | foreach ($pkg in $packageToBeRemoved)
2720 | {
2721 | if ($packageDictionary.ContainsKey($pkg))
2722 | {
2723 | $packageDictionary.Remove($pkg)
2724 | }
2725 | }
2726 | }
2727 | finally
2728 | {
2729 | Write-Progress -Activity "Unmounting image from $mountDrive" -PercentComplete 90 -Id $id
2730 | # Unmount and delete directory
2731 | Remove-MountDrive $mountDrive
2732 | Write-Progress -Completed -Id $id -Activity "Completed"
2733 | }
2734 | }
2735 | else
2736 | {
2737 | $count = 0;
2738 | $id = Write-Progress -ParentId 1 -Activity "Getting packages information"
2739 | if (-not $id)
2740 | {
2741 | $id = 1
2742 | }
2743 |
2744 | Write-Progress -Activity "Getting available packages on the system" -PercentComplete 0 -Id $id
2745 | # getting the packages on the current operating system
2746 | # getting basic information about all the packages online
2747 | $packages = Get-WindowsPackage -Online
2748 |
2749 | try
2750 | {
2751 | $packagesToBeReturned = New-Object 'System.Collections.Generic.List[string]'
2752 | $availablePackages = $packages.PackageName.ToLower()
2753 |
2754 | # Get the list of packages that match what the user input
2755 | foreach ($fullyQualifiedName in $availablePackages)
2756 | {
2757 | if (Test-PackageWithSearchQuery -fullyQualifiedName $fullyQualifiedName -requiredVersion $RequiredVersion -Name $Name `
2758 | -minimumVersion $MinimumVersion -maximumVersion $MaximumVersion -Culture $languageChosen -wildCardPattern $wildCardPattern)
2759 | {
2760 | # Store the whole name instead of just the name without language or version
2761 | $packagesToBeReturned.Add($fullyQualifiedName)
2762 | }
2763 |
2764 | if (-not ($script:onlinePackageCache.ContainsKey($fullyQualifiedName)))
2765 | {
2766 | $script:onlinePackageCache[$fullyQualifiedName] = $null
2767 | }
2768 | }
2769 |
2770 | # nothing matched!
2771 | if ($packagesToBeReturned.Count -gt 0)
2772 | {
2773 | # Before we get more details, we will clump together base and language pack if they have same name and version
2774 | $packagesToBeReturned = Filter-Packages $packagesToBeReturned
2775 | }
2776 |
2777 | # Only update the list of packages that the user gives
2778 | foreach ($package in $packagesToBeReturned)
2779 | {
2780 | $percentComplete = ($count*90/$packages.Count + 10) -as [int]
2781 | Write-Progress -Activity "Getting package information for $($package)" -PercentComplete $percentComplete -Id $id
2782 | $count += 1;
2783 |
2784 | # store the information in cache if it's not there or if user uses force
2785 | if ((-not $script:onlinePackageCache.ContainsKey($package)) -or ($null -eq $script:onlinePackageCache[$package]) -or $force)
2786 | {
2787 | Write-Debug "Getting information for package $package and storing it in cache"
2788 | # store the information in cache
2789 | $script:onlinePackageCache[$package.ToLower()] = Get-WindowsPackage -Online -PackageName $package
2790 | }
2791 |
2792 | if ($script:onlinePackageCache.ContainsKey($package))
2793 | {
2794 | # convert package to swid and return
2795 | Write-Output (New-SoftwareIdentityPackage $script:onlinePackageCache[$package] -src "Local Machine")
2796 | }
2797 | }
2798 |
2799 | # Get the list of packages that are in the cache but not in the latest list we have
2800 | $packageToBeRemoved = @()
2801 | foreach ($pkg in $script:onlinePackageCache.GetEnumerator())
2802 | {
2803 | if (-not $availablePackages.Contains($pkg.Name))
2804 | {
2805 | $packageToBeRemoved += $pkg.Name
2806 | }
2807 | }
2808 |
2809 | # Remove packages in this list from the cache
2810 | foreach ($pkg in $packageToBeRemoved)
2811 | {
2812 | if ($script:onlinePackageCache.ContainsKey($pkg))
2813 | {
2814 | $script:onlinePackageCache.Remove($pkg)
2815 | }
2816 | }
2817 | }
2818 | finally
2819 | {
2820 | Write-Progress -Completed -Id $id -Activity "Completed"
2821 | }
2822 | }
2823 | }
2824 |
2825 | function Uninstall-Package
2826 | {
2827 | [CmdletBinding()]
2828 | param
2829 | (
2830 | [Parameter(Mandatory=$true)]
2831 | [ValidateNotNullOrEmpty()]
2832 | [string]
2833 | $fastPackageReference
2834 | )
2835 |
2836 |
2837 | Write-Verbose $fastPackageReference
2838 |
2839 | # path to the offline nano image
2840 | $imagePath = $null
2841 |
2842 | $options = $request.Options
2843 |
2844 | $force = $false
2845 |
2846 | $languageChosen = $null
2847 |
2848 | # check out what options the users give us
2849 | if($options)
2850 | {
2851 | foreach( $o in $options.Keys )
2852 | {
2853 | Write-Debug ("OPTION dictionary: {0} => {1}" -f ($o, $options[$o]) )
2854 | }
2855 |
2856 | if($options.ContainsKey('Force'))
2857 | {
2858 | $force = $options['Force']
2859 | }
2860 |
2861 | if ($options.ContainsKey("FromVhd"))
2862 | {
2863 | $imagePath = $options['FromVhd']
2864 | }
2865 |
2866 | if ($options.ContainsKey("Culture"))
2867 | {
2868 | $languageChosen = $options['Culture']
2869 | }
2870 | }
2871 |
2872 | # if image path is supplied and it points to non existing file, returns
2873 | if (-not [string]::IsNullOrWhiteSpace($imagePath) -and (-not ([System.IO.File]::Exists($ImagePath))))
2874 | {
2875 | ThrowError -CallerPSCmdlet $PSCmdlet `
2876 | -ExceptionName System.ArgumentException `
2877 | -ExceptionMessage "$ImagePath does not exist" `
2878 | -ExceptionObject $imagePath `
2879 | -ErrorId "InvalidImagePath" `
2880 | -ErrorCategory InvalidData
2881 |
2882 | return
2883 | }
2884 |
2885 | [string[]] $splitterArray = @("$separator")
2886 |
2887 | [string[]] $resultArray = $fastPackageReference.Split($splitterArray, [System.StringSplitOptions]::None)
2888 |
2889 | $packageId = $resultArray[4]
2890 |
2891 | $basePackage = $null
2892 |
2893 | if ($null -eq $languageChosen) {
2894 | Write-Verbose "No language chosen, removing base too"
2895 |
2896 | $packageFragments = $packageId.Split("~")
2897 |
2898 | $packageFragments[3] = ""
2899 |
2900 | $basePackage = [string]::Join("~", $packageFragments)
2901 |
2902 | Write-Debug "New package id is $packageId and the base package is $basePackage"
2903 | }
2904 |
2905 | if (-not [string]::IsNullOrWhiteSpace($imagePath)) {
2906 | # removing from vhd
2907 | $mountDrive = New-MountDrive
2908 |
2909 | Write-Verbose "Mounting $imagePath to $mountDrive"
2910 |
2911 | $null = Mount-WindowsImage -ImagePath $imagePath -Index 1 -Path $mountDrive
2912 |
2913 | $success = $false
2914 |
2915 | try {
2916 | Write-Verbose "Removing $packageId from $mountDrive"
2917 |
2918 | # time to update the cache since we remove this package
2919 | $fileKey = Get-FileKey -filePath $imagePath
2920 |
2921 | if ($script:imagePathCache.ContainsKey($fileKey)) {
2922 | $packageDictionary = $script:imagePathCache[$fileKey]
2923 |
2924 | if ($null -ne $packageDictionary) {
2925 | if ($packageDictionary.ContainsKey($packageId)) {
2926 | Remove-WindowsPackage -PackageName $packageId -Path $mountDrive | Out-Null
2927 | $packageDictionary.Remove($packageId)
2928 | }
2929 | }
2930 | else {
2931 | # nothing in cache
2932 | Remove-WindowsPackage -PackageName $packageId -Path $mountDrive | Out-Null
2933 | }
2934 | }
2935 |
2936 |
2937 | if (-not ([string]::IsNullOrWhiteSpace($basePackage)))
2938 | {
2939 | Remove-WindowsPackage -PackageName $basePackage -Path $mountDrive | Out-Null
2940 | }
2941 |
2942 | if ($script:imagePathCache.ContainsKey($fileKey)) {
2943 | $packageDictionary = $script:imagePathCache[$fileKey]
2944 |
2945 | if ($null -ne $packageDictionary)
2946 | {
2947 | if ($packageDictionary.ContainsKey($packageId))
2948 | {
2949 | $packageDictionary.Remove($packageId)
2950 | }
2951 |
2952 | if ((-not [string]::IsNullOrWhiteSpace($basePackage)) -and $packageDictionary.ContainsKey($basePackage))
2953 | {
2954 | $packageDictionary.Remove($basePackage)
2955 | }
2956 | }
2957 | }
2958 |
2959 | $success = $true
2960 | }
2961 | catch {
2962 | $success = $false
2963 | }
2964 | finally {
2965 | # unmount
2966 | if ($null -ne $mountDrive)
2967 | {
2968 | Write-Verbose "Unmounting mountdrive $mountDrive"
2969 | Remove-MountDrive $mountDrive -discard (-not $success)
2970 | }
2971 | }
2972 | }
2973 | else {
2974 | Write-Verbose "Uninstalling $packageId online"
2975 |
2976 | $messages = $null
2977 |
2978 | if ($script:onlinePackageCache.ContainsKey($packageId)) {
2979 | # removing online
2980 | $messages = Remove-WindowsPackage -PackageName $packageId -Online -NoRestart -WarningAction Ignore
2981 | $script:onlinePackageCache.Remove($packageId)
2982 | }
2983 |
2984 | $restart = $messages -ne $null -and $messages.RestartNeeded
2985 |
2986 | if (-not [string]::IsNullOrWhiteSpace($basePackage))
2987 | {
2988 | if ($script:onlinePackageCache.ContainsKey($basePackage)) {
2989 | $messages = Remove-WindowsPackage -PackageName $basePackage -Online -NoRestart -WarningAction Ignore
2990 | $script:onlinePackageCache.Remove($basePackage)
2991 | $restart = $restart -or ($messages -ne $null -and $messages.RestartNeeded)
2992 | }
2993 | }
2994 |
2995 | if ($restart)
2996 | {
2997 | Write-Warning "Restart is needed to complete installation"
2998 | }
2999 | }
3000 | }
3001 |
3002 | #endregion OneGet
3003 |
3004 | #region OneGet Helpers
3005 |
3006 | # This is to display long name
3007 | function Get-Feature
3008 | {
3009 | Write-Output -InputObject (New-Feature -Name "DisplayLongName")
3010 | }
3011 |
3012 | function Get-DynamicOptions
3013 | {
3014 | param
3015 | (
3016 | [Microsoft.PackageManagement.MetaProvider.PowerShell.OptionCategory]
3017 | $category
3018 | )
3019 |
3020 | switch($category)
3021 | {
3022 | # This is for dynamic options used by install/uninstall and get-packages
3023 | Install
3024 | {
3025 | # Provides path to image
3026 | Write-Output -InputObject (New-DynamicOption -Category $category -Name "ToVhd" -ExpectedType File -IsRequired $false)
3027 | Write-Output -InputObject (New-DynamicOption -Category $category -Name "FromVhd" -ExpectedType File -IsRequired $false)
3028 | Write-Output -InputObject (New-DynamicOption -Category $Category -Name "DisplayCulture" -ExpectedType Switch -IsRequired $false)
3029 | Write-Output -InputObject (New-DynamicOption -Category $category -Name "Culture" -ExpectedType String -IsRequired $false)
3030 | }
3031 | Package
3032 | {
3033 | # Switch to display culture
3034 | Write-Output -InputObject (New-DynamicOption -Category $Category -Name "DisplayCulture" -ExpectedType Switch -IsRequired $false)
3035 | # Provides path to image
3036 | Write-Output -InputObject (New-DynamicOption -Category $category -Name "ImagePath" -ExpectedType String -IsRequired $false)
3037 | Write-Output -InputObject (New-DynamicOption -Category $category -Name "Culture" -ExpectedType String -IsRequired $false)
3038 | }
3039 | Source
3040 | {
3041 | Write-Output -InputObject (New-DynamicOption -Category $Category -Name "Default" -ExpectedType Switch -IsRequired $false)
3042 | }
3043 | }
3044 | }
3045 |
3046 | function Initialize-Provider
3047 | {
3048 | write-debug "In $script:providerName - Initialize-Provider"
3049 | }
3050 |
3051 | function Get-PackageProviderName
3052 | {
3053 | return $script:providerName
3054 | }
3055 |
3056 | function New-SoftwareIdentityFromWindowsPackageItemInfo
3057 | {
3058 | [Cmdletbinding()]
3059 | param(
3060 | [PSCustomObject]
3061 | $package
3062 | )
3063 |
3064 | $details = @{}
3065 | $Culture = $package.Culture
3066 |
3067 | $fastPackageReference = $package.Name +
3068 | $separator + $package.version +
3069 | $separator + $package.Source +
3070 | $separator + $Culture +
3071 | $separator + $package.Sku +
3072 | $separator + $package.NanoServerVersion
3073 |
3074 | $Name = [System.IO.Path]::GetFileNameWithoutExtension($package.Name)
3075 |
3076 | $deps = (new-Object -TypeName System.Collections.ArrayList)
3077 |
3078 | foreach( $dep in $package.Dependencies )
3079 | {
3080 | # Add each dependency and say it's from this provider.
3081 | $newDep = New-Dependency -ProviderName $script:providerName `
3082 | -PackageName $dep.Name `
3083 | -Version $dep.Version
3084 | $deps.Add( $newDep )
3085 | }
3086 |
3087 | $details["Sku"] = $package.Sku
3088 | $details["NanoServerVersion"] = $package.NanoServerVersion
3089 |
3090 | $params = @{FastPackageReference = $fastPackageReference;
3091 | Name = $Name;
3092 | Version = $package.version.ToString();
3093 | versionScheme = "MultiPartNumeric";
3094 | Source = $package.Source;
3095 | Summary = $package.Description;
3096 | Details = $details;
3097 | Culture = $Culture;
3098 | Dependencies = $deps;
3099 | }
3100 |
3101 | try
3102 | {
3103 | New-SoftwareIdentity @params
3104 | }
3105 | catch
3106 | {
3107 | # throw error because older version of packagemanagement does not have culture key
3108 | $params.Remove("Culture")
3109 | New-SoftwareIdentity @params
3110 | }
3111 | }
3112 |
3113 | # this function is used by get-installedpackage
3114 | function New-SoftwareIdentityPackage
3115 | {
3116 | [CmdletBinding()]
3117 | param(
3118 | [Parameter(Mandatory=$true)]
3119 | [Microsoft.Dism.Commands.AdvancedPackageObject]
3120 | $package,
3121 |
3122 | $src="",
3123 |
3124 | $InstallLocation=""
3125 | )
3126 |
3127 | $details = @{}
3128 |
3129 | $details.Add("Applicable", $package.Applicable)
3130 |
3131 | if ($null -ne $package.InstallTime)
3132 | {
3133 | $details.Add("InstallTime", $package.InstallTime)
3134 | }
3135 |
3136 | if ($null -ne $package.CompletelyOfflineCapable)
3137 | {
3138 | $details.Add("CompletelyOfflineCapable", $package.CompletelyOfflineCapable)
3139 | }
3140 |
3141 | if ($null -ne $package.PackageState)
3142 | {
3143 | $details.Add("PackageState", $package.PackageState)
3144 | }
3145 |
3146 | if ($null -ne $package.RestartRequired)
3147 | {
3148 | $details.Add("RestartRequired", $package.RestartRequired)
3149 | }
3150 |
3151 | if (-not [string]::IsNullOrWhiteSpace($package.ReleaseType))
3152 | {
3153 | $details.Add("ReleaseType", $package.ReleaseType)
3154 | }
3155 |
3156 | if ([string]::IsNullOrWhiteSpace($Package.ProductVersion)) {
3157 | $version = "0.0"
3158 | }
3159 | else {
3160 | $version = $Package.ProductVersion
3161 | }
3162 |
3163 | # format is name~publickeytoken~architecture~language~version
3164 |
3165 | $packageNameFractions = $Package.PackageName.Split('~')
3166 |
3167 | if (-not [string]::IsNullOrWhiteSpace($packageNameFractions[0]))
3168 | {
3169 | $name = $packageNameFractions[0]
3170 | }
3171 | else
3172 | {
3173 | $name = $package.PackageName
3174 | }
3175 |
3176 | # DISM team has a workaround where they add Feature in the name. We should remove that.
3177 | # THIS IS A TEMPORARY FIX
3178 | if ($name -like "*Feature-Package*")
3179 | {
3180 | $name = $name -replace "Feature-Package","Package"
3181 | }
3182 |
3183 | if (-not [string]::IsNullOrWhiteSpace($packageNameFractions[1]))
3184 | {
3185 | $details.Add("publickey", $packageNameFractions[1])
3186 | }
3187 |
3188 | if (-not [string]::IsNullOrWhiteSpace($packageNameFractions[2]))
3189 | {
3190 | $details.Add("architecture", $packageNameFractions[2])
3191 | }
3192 |
3193 | $Culture = $packageNameFractions[3]
3194 |
3195 | if (-not [string]::IsNullOrWhiteSpace($packageNameFractions[4]))
3196 | {
3197 | $version = $packageNameFractions[4]
3198 | }
3199 |
3200 | $fastPackageReference = $name + $separator + $version + $separator + $InstallLocation + $separator + $Culture + $separator + $package.PackageName
3201 |
3202 | $params = @{FastPackageReference = $fastPackageReference;
3203 | Name = $name;
3204 | Version = $version;
3205 | versionScheme = "MultiPartNumeric";
3206 | Source = $src;
3207 | Details = $details;
3208 | Culture = $Culture;
3209 | TagId = $Package.PackageName;
3210 | }
3211 |
3212 | try
3213 | {
3214 | New-SoftwareIdentity @params
3215 | }
3216 | catch
3217 | {
3218 | # throw error because older version of packagemanagement does not have culture key
3219 | $params.Remove("Culture")
3220 | $params.Remove("TagId")
3221 | New-SoftwareIdentity @params
3222 | }
3223 | }
3224 |
3225 | function Install-CabOfflineFromPath
3226 | {
3227 | [CmdletBinding()]
3228 | param
3229 | (
3230 | [string]$mountDrive,
3231 |
3232 | [string[]]$packagePaths
3233 | )
3234 |
3235 | $discard = $false
3236 |
3237 | $id = Write-Progress -ParentId 1 -Activity "Installing packages"
3238 |
3239 | if (-not $id)
3240 | {
3241 | $id = 1
3242 | }
3243 |
3244 | # Now we can try to install the package
3245 | try
3246 | {
3247 | $count = 0
3248 |
3249 | foreach ($packagePath in $packagePaths)
3250 | {
3251 | $percentComplete = ($count*100/$packagePaths.Count) -as [int]
3252 |
3253 | $count += 1
3254 |
3255 | Write-Progress -Activity `
3256 | "Installing package $packagePath" `
3257 | -PercentComplete $percentComplete `
3258 | -Id $id
3259 |
3260 | Write-Verbose "Adding $packagePath to $mountDrive"
3261 | Add-WindowsPackage -PackagePath $packagePath -Path $mountDrive -NoRestart -WarningAction Ignore | Out-Null
3262 | }
3263 | }
3264 | catch
3265 | {
3266 | $discard = $true
3267 | ThrowError -CallerPSCmdlet $PSCmdlet `
3268 | -ExceptionName $_.Exception.GetType().FullName `
3269 | -ExceptionMessage $_.Exception.Message `
3270 | -ExceptionObject $RequiredVersion `
3271 | -ErrorId FailedToInstall `
3272 | -ErrorCategory InvalidOperation
3273 | }
3274 | finally
3275 | {
3276 | Write-Progress -Completed -Id $id -Activity "Completed"
3277 | }
3278 |
3279 | # returns back whether we have successfully installed or not
3280 | return (-not $discard)
3281 | }
3282 |
3283 | function Install-Online
3284 | {
3285 | [CmdletBinding()]
3286 | param
3287 | (
3288 | $packagePaths,
3289 | [ref]$restartNeeded
3290 | )
3291 |
3292 | $rollBack = $false
3293 |
3294 | $count = 0;
3295 | Write-Verbose "Installing $($packagePaths.Count) packages online"
3296 | $id = Write-Progress -ParentId 1 -Activity "Installing packages online"
3297 | if (-not $id)
3298 | {
3299 | $id = 1
3300 | }
3301 |
3302 | try
3303 | {
3304 | # first package of each pair is base, second is language
3305 |
3306 | foreach ($packageTuple in $packagePaths)
3307 | {
3308 | $packagePath = $packageTuple.Item1
3309 |
3310 | $messages = $null
3311 |
3312 | $restart = $false
3313 |
3314 | $percentComplete = $count*100/$packagePaths.Count -as [int]
3315 |
3316 | for($i = 0; $i -lt 2; $i += 1)
3317 | {
3318 | # valid base path
3319 | if (-not [string]::IsNullOrWhiteSpace($packagePath))
3320 | {
3321 | Write-Progress -Activity "Installing package $($packagePath)" -PercentComplete $percentComplete -Id $id
3322 |
3323 | try
3324 | {
3325 | $messages = Add-WindowsPackage -PackagePath $packagePath -Online -NoRestart -WarningAction Ignore -ErrorAction Ignore
3326 |
3327 | if ($messages -ne $null -and $messages.RestartNeeded)
3328 | {
3329 | $restart = $true
3330 | }
3331 | }
3332 | catch { }
3333 | }
3334 |
3335 | # now install the language, even if the base fails, sometimes language will succeed
3336 | $packagePath = $packageTuple.Item2
3337 |
3338 | if (-not [string]::IsNullOrWhiteSpace($packagePath))
3339 | {
3340 | Write-Progress -Activity "Installing package $($packagePath)" -PercentComplete $percentComplete -Id $id
3341 |
3342 | $hasError = $false
3343 |
3344 | # first try
3345 | if ($i -eq 0)
3346 | {
3347 | # try catch for the first time we install
3348 | try
3349 | {
3350 | # don't try catch here because if this fails, that is it
3351 | $messages = Add-WindowsPackage -PackagePath $packagePath -Online -NoRestart -WarningAction Ignore
3352 |
3353 | # restart or not
3354 | if (-not $restart -and $messages -ne $null -and $messages.RestartNeeded)
3355 | {
3356 | $restart = $true
3357 | }
3358 |
3359 | if ($restart)
3360 | {
3361 | $restartNeeded.Value = $true
3362 | }
3363 | }
3364 | catch { $hasError = $true }
3365 |
3366 | # no error, break out
3367 | if (-not $hasError)
3368 | {
3369 | break
3370 | }
3371 | }
3372 | else
3373 | {
3374 | Write-Verbose "Trying to install $packagePath for a second time"
3375 |
3376 | # don't try catch here because if this fails, that is it
3377 | $messages = Add-WindowsPackage -PackagePath $packagePath -Online -NoRestart -WarningAction Ignore
3378 |
3379 | # restart or not
3380 | if (-not $restart -and $messages -ne $null -and $messages.RestartNeeded)
3381 | {
3382 | $restart = $true
3383 | }
3384 |
3385 | if ($restart)
3386 | {
3387 | $restartNeeded.Value = $true
3388 | }
3389 | }
3390 | }
3391 | }
3392 |
3393 | $count += 1
3394 | }
3395 | }
3396 | catch
3397 | {
3398 | $rollBack = $true
3399 | ThrowError -CallerPSCmdlet $PSCmdlet `
3400 | -ExceptionName $_.Exception.GetType().FullName `
3401 | -ExceptionMessage $_.Exception.Message `
3402 | -ExceptionObject $RequiredVersion `
3403 | -ErrorId FailedToInstall `
3404 | -ErrorCategory InvalidOperation
3405 | }
3406 | finally
3407 | {
3408 | Write-Progress -Completed -Id $id -Activity "Completed"
3409 | }
3410 |
3411 | # returns whether we installed
3412 | return (-not $rollBack)
3413 | }
3414 |
3415 | function New-MountDrive
3416 | {
3417 | # getting packages from an offline image
3418 | # Mount to directory
3419 | while ($true)
3420 | {
3421 | $randomName = [System.IO.Path]::GetRandomFileName()
3422 | $mountDrive = "$env:LOCALAPPDATA\NanoServerPackageProvider\MountDirectories\$randomName"
3423 |
3424 | if (Test-Path $mountDrive)
3425 | {
3426 | # We should create a directory that hasn't existed before
3427 | continue;
3428 | }
3429 | else
3430 | {
3431 | $null = mkdir $mountDrive
3432 | return $mountDrive
3433 | }
3434 | }
3435 | }
3436 |
3437 | function Remove-MountDrive([string]$mountDrive, [bool]$discard)
3438 | {
3439 | Write-Verbose "Dismounting $mountDrive"
3440 |
3441 | # Discard won't save anything we did to the image
3442 | if ($discard)
3443 | {
3444 | $null = Dismount-WindowsImage -Path $mountDrive -Discard
3445 | }
3446 | else
3447 | {
3448 | # save will saves packages that we add to the image
3449 | $null = Dismount-WindowsImage -Path $mountDrive -Save
3450 | }
3451 |
3452 | Write-Verbose "Deleting $mountDrive"
3453 | Remove-Item -Path $mountDrive -Recurse -Force
3454 | }
3455 |
3456 | # Given a fully qualified name of a package with the format name~publickeytoken~architecture~language~version
3457 | # checks whether this matches the search query
3458 | function Test-PackageWithSearchQuery
3459 | {
3460 | [CmdletBinding()]
3461 | param
3462 | (
3463 | [Parameter(ParameterSetName="FullyQualifiedName")]
3464 | [string]$fullyQualifiedName,
3465 |
3466 | [Parameter(ParameterSetName="WindowsPackage")]
3467 | [PSCustomObject]$WindowsPackage,
3468 |
3469 | [string]$requiredVersion,
3470 |
3471 | [string]$minimumVersion,
3472 |
3473 | [string]$maximumVersion,
3474 |
3475 | [string]$name,
3476 |
3477 | [string]$Culture,
3478 |
3479 | [System.Management.Automation.WildcardPattern]$wildCardPattern
3480 | )
3481 |
3482 | if ($null -eq $WindowsPackage)
3483 | {
3484 | # Split up the whole name since the name has language version and packagename in it
3485 | # format is name~publickeytoken~architecture~language~version
3486 | # now we want the package name to have en-us at the end if package is not base
3487 | $packageNameFractions = $fullyQualifiedName.Split('~')
3488 | $packageName = $packageNameFractions[0]
3489 | $packageLanguage = $packageNameFractions[3]
3490 | $version = $packageNameFractions[4]
3491 |
3492 | # DISM team has a workaround where they add Feature in the name. We should remove that.
3493 | # THIS IS A TEMPORARY FIX
3494 | if ($packageName -like "*Feature-Package")
3495 | {
3496 | $packageName = $packageName -replace "Feature-Package", "Package"
3497 | }
3498 | }
3499 | else
3500 | {
3501 | $packageName = $WindowsPackage.Name
3502 | $packageLanguage = $WindowsPackage.Culture
3503 | $version = $WindowsPackage.version.ToString()
3504 | }
3505 |
3506 | # there is a chance user supplies *
3507 | if (-not [string]::IsNullOrWhiteSpace($packageLanguage))
3508 | {
3509 | $packageNameWithLanguage = "$packageName" + "_" + "$packageLanguage"
3510 | }
3511 |
3512 | if ($null -ne $wildCardPattern)
3513 | {
3514 | # matching already ignore case
3515 | if (-not $wildCardPattern.IsMatch($packageName))
3516 | {
3517 | # we proceed if wildcard match _
3518 | if (-not [string]::IsNullOrWhiteSpace($packageLanguage))
3519 | {
3520 | if (-not $wildCardPattern.IsMatch($packageLanguage))
3521 | {
3522 | return $false
3523 | }
3524 | }
3525 | else
3526 | {
3527 | return $false
3528 | }
3529 | }
3530 | }
3531 | else
3532 | {
3533 | # no wildcard so check for name if we are given a name
3534 | # eq operation is case insensitive
3535 | if (-not [string]::IsNullOrWhiteSpace($name) -and $name -ne $packageName)
3536 | {
3537 | # there is a chance user supplies _
3538 | if (-not [string]::IsNullOrWhiteSpace($packageLanguage))
3539 | {
3540 | # we proceed if name match _
3541 | if ($name -ne $packageNameWithLanguage)
3542 | {
3543 | return $false
3544 | }
3545 | }
3546 | else
3547 | {
3548 | return $false
3549 | }
3550 | }
3551 | }
3552 |
3553 | # now we check language if the user providers it
3554 | if (-not [string]::IsNullOrWhiteSpace($Culture))
3555 | {
3556 | # if base, then packageLanguage needs to be null
3557 | if ($Culture -eq 'Base')
3558 | {
3559 | $Culture = ''
3560 | }
3561 |
3562 | if ($packageLanguage -ne $Culture)
3563 | {
3564 | return $false
3565 | }
3566 | }
3567 |
3568 | # normalize versions
3569 | $convertedVersion = Convert-Version $version
3570 |
3571 | # fails to normalize
3572 | if ($null -eq $convertedVersion)
3573 | {
3574 | return $false
3575 | }
3576 |
3577 | # now we check whether version is matched
3578 | if (-not [string]::IsNullOrWhiteSpace($RequiredVersion))
3579 | {
3580 | $convertedRequiredVersion = Convert-Version $RequiredVersion
3581 |
3582 | # fails if conversion fails or version does not match
3583 | if (($null -eq $convertedRequiredVersion) -or ($convertedRequiredVersion -ne $convertedVersion))
3584 | {
3585 | return $false
3586 | }
3587 | }
3588 |
3589 | # packagemanagement will make sure requiredversion is not used with either min or max so we don't have to worry about that
3590 | if (-not [string]::IsNullOrWhiteSpace($MinimumVersion))
3591 | {
3592 | $convertedMinimumVersion = Convert-Version $MinimumVersion
3593 |
3594 | # the converted version should be greater or equal to min version, not the other way round
3595 | if (($null -eq $convertedMinimumVersion) -or ($convertedMinimumVersion -gt $convertedVersion))
3596 | {
3597 | return $false
3598 | }
3599 | }
3600 |
3601 | if (-not [string]::IsNullOrWhiteSpace($MaximumVersion))
3602 | {
3603 | $convertedMaximumVersion = Convert-Version $MaximumVersion
3604 |
3605 | # converted version should be the same or less than max version
3606 | if (($null -eq $convertedMaximumVersion) -or ($convertedMaximumVersion -lt $convertedVersion))
3607 | {
3608 | return $false
3609 | }
3610 | }
3611 |
3612 | # reached here means the package satisfied the search query
3613 | return $true
3614 | }
3615 |
3616 | # Given the path of a file, returns a simple key
3617 | # This function assumes that the user test that filePath exists
3618 | function Get-FileKey([string]$filePath)
3619 | {
3620 | $info = Get-ChildItem $filePath
3621 | return ($filePath + $separator + $info.Length + $separator + $info.CreationTime.ToShortDateString())
3622 | }
3623 |
3624 | function Convert-Version([string]$version)
3625 | {
3626 | if ([string]::IsNullOrWhiteSpace($version))
3627 | {
3628 | return $null;
3629 | }
3630 |
3631 | # not supporting semver here. let's try to normalize the versions
3632 | if ($version.StartsWith("."))
3633 | {
3634 | # add leading zeros
3635 | $version = "0" + $version
3636 | }
3637 |
3638 | # let's see how many parts are we given with the version
3639 | $parts = $version.Split(".").Count
3640 |
3641 | # add .0 dependending number of parts since we need 4 parts
3642 | while ($parts -lt 4)
3643 | {
3644 | $version = $version + ".0"
3645 | $parts += 1
3646 | }
3647 |
3648 | [version]$convertedVersion = $null
3649 |
3650 | # try to convert
3651 | if ([version]::TryParse($version, [ref]$convertedVersion))
3652 | {
3653 | return $convertedVersion
3654 | }
3655 |
3656 | return $null;
3657 | }
3658 |
3659 | function Filter-Packages ([string[]]$packagesToBeReturned)
3660 | {
3661 | $helperDictionary = @{}
3662 | foreach ($package in $packagesToBeReturned)
3663 | {
3664 | # Split up the whole name since the name has language version and packagename in it
3665 | # format is name~publickeytoken~architecture~language~version
3666 | # now we want the package name to have en-us at the end if package is not base
3667 | $packageNameFractions = $package.Split('~')
3668 | $packageName = $packageNameFractions[0]
3669 | $packageLanguage = $packageNameFractions[3]
3670 | $version = $packageNameFractions[4]
3671 |
3672 | # use name and version as key
3673 | $key = $packageName + "~" + $version
3674 |
3675 | # haven't encountered this before
3676 | if (-not $helperDictionary.ContainsKey($key))
3677 | {
3678 | $helperDictionary[$key] = @()
3679 | }
3680 |
3681 | $helperDictionary[$key] += $package
3682 | }
3683 |
3684 | $result = @()
3685 |
3686 | foreach ($packageArray in $helperDictionary.Values)
3687 | {
3688 | if ($null -eq $packageArray)
3689 | {
3690 | continue
3691 | }
3692 |
3693 | # only 1 member, then return that
3694 | if ($packageArray.Count -eq 1)
3695 | {
3696 | $result += $packageArray[0]
3697 | continue
3698 | }
3699 |
3700 | # otherwise, only returns the 1 with language
3701 | foreach ($possiblePackage in $packageArray)
3702 | {
3703 | $packageNameFractions = $possiblePackage.Split('~')
3704 | $packageName = $packageNameFractions[0]
3705 | $packageLanguage = $packageNameFractions[3]
3706 | $version = $packageNameFractions[4]
3707 |
3708 | if ([string]::IsNullOrWhiteSpace($packageLanguage))
3709 | {
3710 | continue
3711 | }
3712 |
3713 | $result += $possiblePackage
3714 | }
3715 | }
3716 |
3717 | # group according to name
3718 | $groupedName = $result | Group-Object -Property {$_.Split('~')[0]}
3719 | foreach ($groupResult in $groupedName)
3720 | {
3721 | $groupResult.Group | Sort-Object -Property {Convert-Version $_.Split('~')[4]} -Descending
3722 | }
3723 | }
3724 |
3725 | # Utility to throw an errorrecord
3726 | function ThrowError
3727 | {
3728 | param
3729 | (
3730 | [parameter(Mandatory = $true)]
3731 | [ValidateNotNullOrEmpty()]
3732 | [System.Management.Automation.PSCmdlet]
3733 | $CallerPSCmdlet,
3734 |
3735 | [parameter(Mandatory = $true)]
3736 | [ValidateNotNullOrEmpty()]
3737 | [System.String]
3738 | $ExceptionName,
3739 |
3740 | [parameter(Mandatory = $true)]
3741 | [ValidateNotNullOrEmpty()]
3742 | [System.String]
3743 | $ExceptionMessage,
3744 |
3745 | [System.Object]
3746 | $ExceptionObject,
3747 |
3748 | [parameter(Mandatory = $true)]
3749 | [ValidateNotNullOrEmpty()]
3750 | [System.String]
3751 | $ErrorId,
3752 |
3753 | [parameter(Mandatory = $true)]
3754 | [ValidateNotNull()]
3755 | [System.Management.Automation.ErrorCategory]
3756 | $ErrorCategory
3757 | )
3758 |
3759 | $exception = New-Object $ExceptionName $ExceptionMessage;
3760 | $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $ErrorId, $ErrorCategory, $ExceptionObject
3761 | $CallerPSCmdlet.ThrowTerminatingError($errorRecord)
3762 | }
3763 |
3764 | #endregion OneGet Helpers
--------------------------------------------------------------------------------
/NanoServerPackage/SaveHTTPItemUsingBITS.psm1:
--------------------------------------------------------------------------------
1 | function Save-HTTPItemUsingBitsTransfer
2 | {
3 | [CmdletBinding()]
4 | param(
5 | [Parameter(Mandatory=$true)]
6 | $Uri,
7 | [Parameter(Mandatory=$true)]
8 | $Destination,
9 | [switch]
10 | $noProgress
11 | )
12 |
13 | begin
14 | {
15 | $fullUri = [Uri]$Uri
16 | if (($fullUri.Scheme -ne 'http') -and ($fullUri.Scheme -ne 'https'))
17 | {
18 | throw "Uri: $uri is not supported. Only http or https schema are supported."
19 | }
20 | }
21 |
22 | end
23 | {
24 | $jstate = $null
25 | [bool] $isTransferCompleted = $false
26 | try
27 | {
28 | $mycurrentPath = $script:MyInvocation.MyCommand.Path
29 | $myCurrentDirectory = Split-Path $mycurrentPath
30 | $bitsCommandPath = join-path $myCurrentDirectory "BitsOnNano.exe"
31 | $jobNameTemp = "SH{0}" -f (get-date).Ticks
32 | $output = & $bitsCommandPath -Start-Transfer -DisplayName $jobNameTemp -Source $Uri -Destination $Destination
33 | $le = $lastexitcode
34 |
35 | if (-not $noProgress) {
36 | $id = Write-Progress -ParentId 1 -Activity "Downloading from $Uri"
37 | }
38 |
39 | if($null -eq $id)
40 | {
41 | $id = 2
42 | }
43 |
44 | do
45 | {
46 | $jname,$jid,$jstate,$jbytesTransferred,$jbytesTotal,$null = $output -split ":"
47 |
48 | if ( (@("BG_JOB_STATE_ERROR", "BG_JOB_STATE_TRANSIENT_ERROR", "BG_JOB_STATE_CANCELLED") -contains $jstate) -or ($le))
49 | {
50 | & $bitsCommandPath -Stop-Transfer -ID $jid | Out-Null
51 |
52 | throw "Save-HTTPItem: Bits Transfer failed. Job State: $jstate ExitCode = $le"
53 | }
54 |
55 | if (@("BG_JOB_STATE_TRANSFERRING") -contains $jstate)
56 | {
57 | $percentComplete = ($jbytesTransferred / $jbytesTotal) * 100
58 | $status = "Downloaded {0}MB of total {1}MB" -f ($jbytesTransferred/1mb),($jbytesTotal/1mb)
59 | if (-not $noProgress) {
60 | $null = Write-Progress -Activity "Downloading from $Uri" -PercentComplete $percentComplete -Id $id
61 | }
62 | }
63 | elseif (@("BG_JOB_STATE_TRANSFERRED") -contains $jstate)
64 | {
65 | & $bitsCommandPath -Remove-Transfer -ID $jid | Out-Null
66 | $isTransferCompleted = $true
67 | break;
68 | }
69 | elseif (@("BG_JOB_STATE_QUEUED") -contains $jstate)
70 | {
71 | if (-not $noProgress) {
72 | $null = Write-Progress -Activity "QUEUED" -PercentComplete 0 -Id $id
73 | }
74 | }
75 | elseif (@("BG_JOB_STATE_CONNECTING") -contains $jstate)
76 | {
77 | if (-not $noProgress) {
78 | $null = Write-Progress -Activity "CONNECTING" -PercentComplete 0 -Id $id
79 | }
80 | }
81 | elseif (@("BG_JOB_STATE_ACKNOWLEDGED") -contains $jstate)
82 | {
83 | if (-not $noProgress) {
84 | $null = Write-Progress -Activity "ACKNOWLEDGED" -PercentComplete 0 -Id $id
85 | }
86 | }
87 |
88 | Start-Sleep -Seconds 1
89 | $output = & $bitsCommandPath -Get-TransferStatus -ID $jid
90 | $le = $lastExitCode
91 | }while($true);
92 | }
93 | finally
94 | {
95 | #"Calling finally: jstate:$jstate isTC:$isTransferCompleted"
96 | if (-not $noProgress) {
97 | $null = Write-Progress -Completed -Activity "Downloading from $Uri" -Id $id
98 | }
99 |
100 | if ((-not $jstate) -and (-not $isTransferCompleted))
101 | {
102 | "CleanUp:"
103 | & $bitsCommandPath -Stop-Transfer -ID $jid | Out-Null
104 | }
105 | }
106 | }
107 | }
--------------------------------------------------------------------------------
/NanoServerPackage/Test/Comprehensive.tests.ps1:
--------------------------------------------------------------------------------
1 |
2 | # you need to modify
3 | # 1. $vhdPath to match your Nano Vm
4 |
5 | $vhdPath = "C:\test\rtmRefreshStdEdition.vhd"
6 |
7 |
8 | $providerName = "NanoServerPackage"
9 | $commonPackages = @(
10 | "Microsoft-NanoServer-Defender-Package",
11 | "Microsoft-NanoServer-ShieldedVM-Package",
12 | "Microsoft-NanoServer-Compute-Package",
13 | "Microsoft-NanoServer-SecureStartup-Package",
14 | "Microsoft-NanoServer-Storage-Package",
15 | "Microsoft-NanoServer-OEM-Drivers-Package",
16 | "Microsoft-NanoServer-DSC-Package",
17 | "Microsoft-NanoServer-DNS-Package",
18 | "Microsoft-NanoServer-IIS-Package",
19 | "Microsoft-NanoServer-DCB-Package",
20 | "Microsoft-NanoServer-FailoverCluster-Package",
21 | "Microsoft-NanoServer-Host-Package",
22 | "Microsoft-NanoServer-Guest-Package",
23 | "Microsoft-NanoServer-Containers-Package",
24 | "Microsoft-NanoServer-SCVMM-Package",
25 | "Microsoft-NanoServer-SCVMM-Compute-Package",
26 | "Microsoft-NanoServer-SoftwareInventoryLogging-Package"
27 | )
28 | $packagesForServerDataCenter = @(
29 | "Microsoft-NanoServer-ShieldedVM-Package")
30 | $allPackages = $commonPackages + $packagesForServerDataCenter
31 | $cultures = ("cs-cz", "de-de", "en-us", "es-es", "fr-fr", "hu-hu", "it-it", "ja-jp", "ko-kr", "nl-nl", "pl-pl", "pt-br", "pt-pt", "ru-ru", "sv-se", "tr-tr", "zh-cn", "zh-tw")
32 |
33 |
34 | Describe "Find 18 packages" {
35 | It "Find all 18 packages" {
36 | foreach ($package in $allPackages) {
37 | foreach ($culture in $cultures) {
38 | $desiredPackage = Find-NanoServerPackage -Name $package -Culture $culture -Verbose
39 | $desiredPackage.Name | should match $package
40 | $desiredPackage.Culture | should match $culture
41 | }
42 | }
43 | }
44 | }
45 |
46 | Describe "Save 18 packages" {
47 | $savePackagePath = "$env:TMP\NanoServerPackageTest"
48 | md $savePackagePath
49 |
50 | It "Save all 18 packages" {
51 | foreach ($package in $allPackages) {
52 | Write-Host "Saving package $package"
53 |
54 | foreach ($culture in $cultures) {
55 | $desiredPackage = Save-NanoServerPackage -Name $package -Culture $culture -Path $savePackagePath -Verbose
56 |
57 | $desiredPackage.Name | should match $package
58 | $desiredPackage.Culture | should match $culture
59 |
60 | (Get-ChildItem $savePackagePath).Count | should be 2
61 |
62 | Remove-Item "$savePackagePath\*.cab"
63 | }
64 | }
65 | }
66 | }
67 |
68 | Describe "Install 18 packages" {
69 | It "Install all common packages" {
70 |
71 | # Install all of them
72 | foreach ($package in $commonPackages) {
73 | Write-Host "Installing package $package"
74 | Install-NanoServerPackage -Name $package
75 |
76 | $installedPackage = Get-Package -ProviderName $providerName -Name $package -Verbose
77 | $installedPackage.Name -match $package | should be $true
78 | }
79 | }
80 |
81 | It "Install server data center packages" {
82 | foreach ($package in $packagesForServerDataCenter) {
83 | Write-Host "Installing package $package"
84 | Install-NanoServerPackage -Name $package
85 |
86 | $installedPackage = Get-Package -ProviderName $providerName -Name $package -Verbose
87 | $installedPackage.Name -match $package | should be $true
88 | }
89 | }
90 |
91 | It "Install all common packages to offline image" {
92 |
93 | # Install all of them
94 | foreach ($package in $commonPackages) {
95 | Write-Host "Installing package $package to vhd $vhdPath"
96 | Install-NanoServerPackage -Name $package -ToVhd $vhdPath -Verbose
97 |
98 | $installedPackage = Get-Package -ProviderName $providerName -Name $package -FromVhd $vhdPath
99 | $installedPackage.Name -match $package | should be $true
100 | }
101 | }
102 |
103 | It "Install server data center packages to offline image" {
104 |
105 | # Install all of them
106 | foreach ($package in $packagesForServerDataCenter) {
107 | Write-Host "Installing package $package to vhd $vhdPath"
108 | Install-NanoServerPackage -Name $package -ToVhd $vhdPath -Verbose
109 |
110 | $installedPackage = Get-Package -ProviderName $providerName -Name $package -FromVhd $vhdPath
111 | $installedPackage.Name -match $package | should be $true
112 | }
113 | }
114 | }
--------------------------------------------------------------------------------
/NanoServerPackage/Test/NanoServerPackage.Find.Tests.ps1:
--------------------------------------------------------------------------------
1 | # only have 1 version for now :(
2 | $minVersion = "10.0.14300.1000"
3 | $maxVersion = "10.0.14393.1000"
4 | $requiredVersion = "10.0.14393.0"
5 | $totalPackages="17"
6 | $computePackage = "Microsoft-NanoServer-Compute-Package"
7 |
8 | Describe "Find-NanoServerPackage Stand-Alone" {
9 |
10 | BeforeAll {
11 | Import-packageprovider -force -name NanoServerPackage
12 | Import-module NanoServerPackage -force
13 |
14 | $cultures = ("cs-cz", "de-de", "en-us", "es-es", "fr-fr", "hu-hu", "it-it", "ja-jp", "ko-kr", "nl-nl", "pl-pl", "pt-br", "pt-pt", "ru-ru", "sv-se", "tr-tr", "zh-cn", "zh-tw")
15 | $names = ("containers", "nanoserver-compute", "defender", "dcb")
16 | }
17 |
18 | AfterAll {
19 | "Finished running the Find-NanoServerPackage Stand-Alone tests"
20 | }
21 |
22 | It "Find NanoServerPackage No Params" {
23 |
24 | $command = "Find-NanoServerPackage"
25 | $results = Invoke-Expression $command
26 | #Conformed that we have a total of 17 packages and each has 18 lang
27 | $results.count | should be $totalPackages
28 | }
29 |
30 | It "Find NanoServerPackage Name" {
31 |
32 | $name = $names | Get-Random
33 | $nameWithWildCards = "*$name*"
34 | $results = @()
35 | $results += (Find-NanoServerPackage -Name $nameWithWildCards)
36 | $results.count | should be 1
37 |
38 | foreach($result in $results)
39 | {
40 | $result.name | should match $name
41 | }
42 | }
43 |
44 | It "Find NanoServerPackage Bad Name" {
45 | $results = @()
46 | $results += (Find-NanoServerPackage -Name containers -ErrorAction SilentlyContinue)
47 | $results.count | should be 0
48 | }
49 |
50 | It "Find NanoServerPackage Minimum Version" {
51 | $command = Find-NanoServerPackage -MinimumVersion 10.0.10586.103 -name $computePackage
52 | $command.Name | should match $computePackage
53 | }
54 |
55 | It "Find NanoServerPackage Maximum Version" {
56 | Find-NanoServerPackage -MaximumVersion 10.0.10586.105 -name $computePackage -ErrorAction SilentlyContinue | should throw
57 | }
58 |
59 | It "Find NanoServerPackage Name, Minimum Version" {
60 |
61 | $results = @()
62 | $name = $names | Get-Random
63 | $nameWithWildCards = "*$name*"
64 | $results += (Find-NanoServerPackage -Name $nameWithWildCards -MinimumVersion $minVersion)
65 | $results.count | should be 1
66 |
67 | foreach($result in $results)
68 | {
69 | $result.name | should match $name
70 | #$result.Version | should be less than or equal to $minVersion
71 | }
72 | }
73 |
74 | It "Find NanoServerPackage Name, Maximum Version" {
75 |
76 | $name = $names | Get-Random
77 | $nameWithWildCards = "*$name*"
78 | $results = @()
79 | $results += (Find-NanoServerPackage -Name $nameWithWildCards -MaximumVersion $maxVersion)
80 | $results.count | should be 1
81 |
82 | foreach($result in $results)
83 | {
84 | $result.name | should match $name
85 | #$result.Version | should be less than or equal to $minVersion
86 | }
87 | }
88 |
89 | It "Find NanoServerPackage Name, Minimum-Maximum Version" {
90 |
91 | $name = $names | Get-Random
92 | $nameWithWildCards = "*$name*"
93 | $results = @()
94 | $results += (Find-NanoServerPackage -Name $nameWithWildCards -MinimumVersion $minVersion -MaximumVersion $maxVersion)
95 | $results.count | should be 1
96 | }
97 |
98 | It "Find NanoServerPackage All Versions No Name" {
99 |
100 | $results = @()
101 | $results += (Find-NanoServerPackage -AllVersions)
102 | $results.count | should be $totalPackages
103 | }
104 |
105 | It "Find NanoServerPackage All Versions with Name" {
106 |
107 | $results = @()
108 | $name = $names | Get-Random
109 | $nameWithWildCards = "*$name*"
110 | $results += (Find-NanoServerPackage -Name $nameWithWildCards -AllVersions)
111 | $results.count | should be 1
112 |
113 | foreach($result in $results)
114 | {
115 | $result.name | should match $name
116 | #$result.Version | should be less than or equal to $minVersion
117 | }
118 | }
119 |
120 | It "Find NanoServerPackage Required Version" {
121 | $results = @()
122 | $results += (Find-NanoServerPackage -RequiredVersion $requiredVersion)
123 | $results.count | should be $totalPackages
124 |
125 | foreach($result in $results)
126 | {
127 | $result.Version | should be $requiredVersion
128 | }
129 | }
130 |
131 | It "Find NanoServerPackage Name, Required Version" {
132 |
133 | $name = $names | Get-Random
134 | $nameWithWildCards = "*$name*"
135 | $results = @()
136 | $results += (Find-NanoServerPackage -Name $nameWithWildCards -RequiredVersion $requiredVersion)
137 | $results.count | should be 1
138 |
139 | foreach($result in $results)
140 | {
141 | $result.name | should match $name
142 | $result.Version | should be $requiredVersion
143 | }
144 | }
145 |
146 | It "Find NanoServerPackage Culture" {
147 |
148 | $culture = $cultures | Get-Random
149 | $command = "Find-NanoServerPackage -Culture $culture"
150 | $results = Invoke-Expression $command
151 | $results.count | should be $totalPackages
152 |
153 | foreach($result in $results)
154 | {
155 | $result.Culture | should be $culture
156 | }
157 | }
158 |
159 | It "Find NanoServerPackage Name, Culture" {
160 |
161 | $culture = $cultures | Get-Random
162 | $name = $names | Get-Random
163 | $namewithWildCards = "*$name*"
164 | $results = @()
165 | $results += (Find-NanoServerPackage -Name $namewithWildCards -Culture $culture)
166 | $results.count | should be 1
167 |
168 | foreach($result in $results)
169 | {
170 | $result.Culture | should be $culture
171 | $result.Name | should match $name
172 | }
173 | }
174 |
175 | It "Find NanoServerPackage Name, Minimum Version, Culture" {
176 |
177 | $culture = $cultures | Get-Random
178 | $name = $names | Get-Random
179 | $namewithWildCards = "*$name*"
180 | $results = @()
181 | $results += (Find-NanoServerPackage -Name $namewithWildCards -Culture $culture -MinimumVersion $minVersion)
182 | $results.count | should be 1
183 |
184 | foreach($result in $results)
185 | {
186 | $result.Culture | should be $culture
187 | $result.Name | should match $name
188 | #$result.Version | should be greater than or equal $minVersion
189 | }
190 | }
191 |
192 | It "Find NanoServerPackage Name, Maximum Version, Culture" {
193 |
194 | $culture = $cultures | Get-Random
195 | $name = $names | Get-Random
196 | $namewithWildCards = "*$name*"
197 | $results = @()
198 | $results += (Find-NanoServerPackage -Name $namewithWildCards -Culture $culture -MaximumVersion $maxVersion)
199 | $results.count | should be 1
200 |
201 | foreach($result in $results)
202 | {
203 | $result.Culture | should be $culture
204 | $result.Name | should match $name
205 | #$result.Version | should be less than or equal $maxVersion
206 | }
207 | }
208 |
209 | It "Find NanoServerPackage Name, Minimum Version, Maximum Version, Culture" {
210 |
211 | $culture = $cultures | Get-Random
212 | $name = $names | Get-Random
213 | $results = @()
214 | $namewithWildCards = "*$name*"
215 | $results += (Find-NanoServerPackage -Name $namewithWildCards -Culture $culture -MinimumVersion $minVersion -MaximumVersion $maxVersion)
216 | $results.count | should be 1
217 |
218 | foreach($result in $results)
219 | {
220 | $result.Culture | should be $culture
221 | $result.Name | should match $name
222 | #$result.Version | should be greater than or equal $minVersion
223 | #$result.Version | should be less than or equal $maxVersion
224 | }
225 | }
226 |
227 | It "Find NanoServerPackage Name, AllVersions, Culture" {
228 |
229 | $culture = $cultures | Get-Random
230 | $name = $names | Get-Random
231 | $namewithWildCards = "*$name*"
232 | $results = @()
233 | $results += (Find-NanoServerPackage -Name $namewithWildCards -Culture $culture -AllVersions)
234 | $results.count | should be 1
235 |
236 | foreach($result in $results)
237 | {
238 | $result.Culture | should be $culture
239 | $result.Name | should match $name
240 | }
241 | }
242 |
243 | It "Find NanoServerPackage Name, RequiredVersion, Culture" {
244 |
245 | $culture = $cultures | Get-Random
246 | $name = $names | Get-Random
247 | $namewithWildCards = "*$name*"
248 | $results = @()
249 | $results += (Find-NanoServerPackage -Name $namewithWildCards -Culture $culture -RequiredVersion $requiredVersion)
250 | $results.count | should be 1
251 |
252 | foreach($result in $results)
253 | {
254 | $result.Culture | should be $culture
255 | $result.name | should match $name
256 | $result.Version | should be $requiredVersion
257 | }
258 | }
259 |
260 | It "Find NanoServerPackage with Dependencies" {
261 | $scvmmCompute = Find-NanoServerPackage *scvmm-compute* -RequiredVersion $requiredVersion
262 | $scvmmCompute.Dependencies[0] | should match "nanoserverpackage:Microsoft-NanoServer-SCVMM-Package/$requiredVersion"
263 | $scvmmCompute.Dependencies[1] | should match "nanoserverpackage:Microsoft-NanoServer-Compute-Package/$requiredVersion"
264 | }
265 |
266 | }
267 |
268 | Describe "NanoServerPackage OneGet" {
269 |
270 | BeforeAll {
271 | Import-packageprovider -force -name NanoServerPackage
272 | Import-module NanoServerPackage -force
273 |
274 | $cultures = ("cs-cz", "de-de", "en-us", "es-es", "fr-fr", "hu-hu", "it-it", "ja-jp", "ko-kr", "nl-nl", "pl-pl", "pt-br", "pt-pt", "ru-ru", "sv-se", "tr-tr", "zh-cn", "zh-tw")
275 | $names = ("containers", "nanoserver-compute", "defender", "dcb")
276 | }
277 |
278 | AfterAll {
279 | "Finished running the Find-NanoServerPackage Stand-Alone tests"
280 | }
281 |
282 | It "Find NanoServerPackage No Params" {
283 |
284 | $command = "Find-Package -ProviderName NanoServerPackage"
285 | $results = Invoke-Expression $command
286 | $results.count | should be $totalPackages
287 | }
288 |
289 | It "Find NanoServerPackage Name" {
290 |
291 | $name = $names | Get-Random
292 | $nameWithWildCards = "*$name*"
293 | $results = @()
294 | $results += (Find-Package -ProviderName NanoServerPackage -Name $nameWithWildCards)
295 | $results.count | should be 1
296 |
297 | foreach($result in $results)
298 | {
299 | $result.name | should match $name
300 | }
301 | }
302 | <#
303 | It "Find NanoServerPackage Bad Name" {
304 | $command = "Find-Package -ProviderName NanoServerPackage -Name containers"
305 | {Invoke-Expression $command} | should throw
306 | }
307 | #>
308 | It "Find NanoServerPackage Minimum Version" {
309 | $command = Find-NanoServerPackage -MinimumVersion 10.0.10586.103 -name $computePackage
310 | $command.Name | should match $computePackage
311 | }
312 |
313 | It "Find NanoServerPackage Maximum Version" {
314 | Find-NanoServerPackage -MaximumVersion 10.0.10586.105 -name $computePackage -ErrorAction SilentlyContinue | should throw
315 | }
316 |
317 | It "Find NanoServerPackage Name, Minimum Version" {
318 |
319 | $results = @()
320 | $name = $names | Get-Random
321 | $nameWithWildCards = "*$name*"
322 | $results += (Find-Package -ProviderName NanoServerPackage -Name $nameWithWildCards -MinimumVersion $minVersion)
323 | $results.count | should be 1
324 |
325 | foreach($result in $results)
326 | {
327 | $result.name | should match $name
328 | #$result.Version | should be less than or equal to $minVersion
329 | }
330 | }
331 |
332 | It "Find NanoServerPackage Name, Maximum Version" {
333 |
334 | $name = $names | Get-Random
335 | $nameWithWildCards = "*$name*"
336 | $results = @()
337 | $results += (Find-Package -ProviderName NanoServerPackage -Name $nameWithWildCards -MaximumVersion $maxVersion)
338 | $results.count | should be 1
339 |
340 | foreach($result in $results)
341 | {
342 | $result.name | should match $name
343 | #$result.Version | should be less than or equal to $minVersion
344 | }
345 | }
346 |
347 | It "Find NanoServerPackage Name, Minimum-Maximum Version" {
348 |
349 | $name = $names | Get-Random
350 | $nameWithWildCards = "*$name*"
351 | $results = @()
352 | $results += (Find-Package -ProviderName NanoServerPackage -Name $nameWithWildCards -MinimumVersion $minVersion -MaximumVersion $maxVersion)
353 | $results.count | should be 1
354 | }
355 |
356 | It "Find NanoServerPackage All Versions No Name" {
357 |
358 | $results = @()
359 | $results += (Find-Package -ProviderName NanoServerPackage -AllVersions)
360 | $results.count | should be $totalPackages
361 | }
362 |
363 | It "Find NanoServerPackage All Versions with Name" {
364 |
365 | $results = @()
366 | $name = "container"
367 | $nameWithWildCards = "*$name*"
368 | $results += (Find-Package -ProviderName NanoServerPackage -Name $nameWithWildCards -AllVersions)
369 | $results.count | should be 1
370 |
371 | foreach($result in $results)
372 | {
373 | $result.name | should match $name
374 | #$result.Version | should be less than or equal to $minVersion
375 | }
376 | }
377 |
378 | It "Find NanoServerPackage Required Version" {
379 | $results = @()
380 | $results += (Find-Package -ProviderName NanoServerPackage -RequiredVersion $requiredVersion)
381 | $results.count | should be $totalPackages
382 |
383 | foreach($result in $results)
384 | {
385 | $result.Version | should be $requiredVersion
386 | }
387 | }
388 |
389 | It "Find NanoServerPackage Name, Required Version" {
390 |
391 | $name = $names | Get-Random
392 | $nameWithWildCards = "*$name*"
393 | $results = @()
394 | $results += (Find-Package -ProviderName NanoServerPackage -Name $nameWithWildCards -RequiredVersion $requiredVersion)
395 | $results.count | should be 1
396 |
397 | foreach($result in $results)
398 | {
399 | $result.name | should match $name
400 | $result.Version | should be $requiredVersion
401 | }
402 | }
403 |
404 | It "Find NanoServerPackage Culture" {
405 |
406 | $culture = $cultures | Get-Random
407 | $results = @()
408 | $results += (Find-Package -ProviderName NanoServerPackage -Culture $culture)
409 | $results.count | should be $totalPackages
410 |
411 | foreach($result in $results)
412 | {
413 | $result.Culture | should be $culture
414 | }
415 | }
416 |
417 | It "Find NanoServerPackage Name, Culture" {
418 |
419 | $culture = $cultures | Get-Random
420 | $name = $names | Get-Random
421 | $namewithWildCards = "*$name*"
422 | $results = @()
423 | $results += (Find-Package -ProviderName NanoServerPackage -Name $namewithWildCards -Culture $culture)
424 | $results.count | should be 1
425 |
426 | foreach($result in $results)
427 | {
428 | $result.Culture | should be $culture
429 | $result.Name | should match $name
430 | }
431 | }
432 |
433 | It "Find NanoServerPackage Name, Minimum Version, Culture" {
434 |
435 | $culture = $cultures | Get-Random
436 | $name = $names | Get-Random
437 | $namewithWildCards = "*$name*"
438 | $results = @()
439 | $results += (Find-Package -ProviderName NanoServerPackage -Name $namewithWildCards -Culture $culture -MinimumVersion $minVersion)
440 | $results.count | should be 1
441 |
442 | foreach($result in $results)
443 | {
444 | $result.Culture | should be $culture
445 | $result.Name | should match $name
446 | #$result.Version | should be greater than or equal $minVersion
447 | }
448 | }
449 |
450 | It "Find NanoServerPackage Name, Maximum Version, Culture" {
451 |
452 | $culture = $cultures | Get-Random
453 | $results = @()
454 | $name = $names | Get-Random
455 | $namewithWildCards = "*$name*"
456 | $results += (Find-Package -ProviderName NanoServerPackage -Name $namewithWildCards -Culture $culture -MaximumVersion $maxVersion)
457 | $results.count | should be 1
458 |
459 | foreach($result in $results)
460 | {
461 | $result.Culture | should be $culture
462 | $result.Name | should match $name
463 | #$result.Version | should be less than or equal $maxVersion
464 | }
465 | }
466 |
467 | It "Find NanoServerPackage Name, Minimum Version, Maximum Version, Culture" {
468 |
469 | $culture = $cultures | Get-Random
470 | $results = @()
471 | $name = $names | Get-Random
472 | $namewithWildCards = "*$name*"
473 | $results += (Find-Package -ProviderName NanoServerPackage -Name $namewithWildCards -Culture $culture -MinimumVersion $minVersion -MaximumVersion $maxVersion)
474 | $results.count | should be 1
475 |
476 | foreach($result in $results)
477 | {
478 | $result.Culture | should be $culture
479 | $result.Name | should match $name
480 | #$result.Version | should be greater than or equal $minVersion
481 | #$result.Version | should be less than or equal $maxVersion
482 | }
483 | }
484 |
485 | It "Find NanoServerPackage Name, AllVersions, Culture" {
486 |
487 | $culture = $cultures | Get-Random
488 | $name = "containers"
489 | $namewithWildCards = "*$name*"
490 | $results = @()
491 | $results += (Find-Package -ProviderName NanoServerPackage -Name $namewithWildCards -Culture $culture -AllVersions)
492 | $results.count | should be 1
493 |
494 | foreach($result in $results)
495 | {
496 | $result.Culture | should be $culture
497 | $result.Name | should match $name
498 | }
499 | }
500 |
501 | It "Find NanoServerPackage Name, RequiredVersion, Culture" {
502 |
503 | $culture = $cultures | Get-Random
504 | $name = $names | Get-Random
505 | $namewithWildCards = "*$name*"
506 | $results = @()
507 | $results += (Find-Package -ProviderName NanoServerPackage -Name $namewithWildCards -Culture $culture -RequiredVersion $requiredVersion)
508 | $results.count | should be 1
509 |
510 | foreach($result in $results)
511 | {
512 | $result.Culture | should be $culture
513 | $result.name | should match $name
514 | $result.Version | should be $requiredVersion
515 | }
516 | }
517 |
518 | It "Find NanoServerPackage with Dependencies" {$scvmmComputePackages = (Find-Package *scvmm-compute* -RequiredVersion $requiredVersion -ProviderName NanoServerPackage -IncludeDependencies)
519 | $scvmmComputePackages.Count | should be 3
520 |
521 | $scvmmComputePackages.Name -contains "microsoft-nanoserver-compute-package" | should be $true
522 | $scvmmComputePackages.Name -contains "microsoft-nanoserver-scvmm-package" | should be $true
523 | $scvmmComputePackages.Name -contains "microsoft-nanoserver-scvmm-compute-package" | should be $true
524 | }
525 | }
526 |
527 |
--------------------------------------------------------------------------------
/NanoServerPackage/Test/NanoServerPackage.Install.Tests.ps1:
--------------------------------------------------------------------------------
1 | #
2 | #
3 | # you need to modify
4 | # 1. $vhdPath to match your Nano Vm
5 | # 2. $requiredVersion needs to be updated if there are new packages published
6 | #
7 | #
8 |
9 | $vhdPath = "C:\test\rtmRefreshStdEdition.vhd"
10 | $culture = (Get-Culture).Name
11 | $dcbPackage = "Microsoft-NanoServer-DCB-Package"
12 | $computePackage = "Microsoft-NanoServer-Compute-Package"
13 | $containersPackage = "Microsoft-NanoServer-Containers-Package"
14 | $providerName = "NanoServerPackage"
15 | $requiredVersion = "10.0.14393.0"
16 |
17 | Describe "Install-NanoServerPackage Stand-Alone" {
18 | It "ERROR: Install with no name" {
19 | { Install-NanoServerPackage -Name '' } | should throw
20 | }
21 |
22 | It "ERROR: Install with wrong culture" {
23 | { Install-NanoServerPackage -Name $dcbPackage -Culture wrong } | should throw
24 | }
25 |
26 | It "ERROR: Install unknown packages" {
27 | { Install-NanoServerPackage -Name 'wrong package name' } | should throw
28 | }
29 |
30 | It "Install DCB package" {
31 | $package = Install-NanoServerPackage -Name $dcbPackage -Force
32 |
33 | $package.Name | should match $dcbPackage
34 | $package.Culture | should match $culture
35 |
36 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture
37 | $getPackage.Name | should match $package.Name
38 | $getPackage.Culture | should match $package.Culture
39 | }
40 |
41 | It "Install DCB package to vhd" {
42 | $package = Install-NanoServerPackage -Name $dcbPackage -ToVhd $vhdPath -Force
43 |
44 | $package.Name | should match $dcbPackage
45 | $package.Culture | should match $culture
46 |
47 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture -FromVhd $vhdPath
48 | $getPackage.Name | should match $package.Name
49 | $getPackage.Culture | should match $package.Culture
50 | }
51 |
52 | It "Install compute package with wrong culture" {
53 | $locale="it-it"
54 | $package = Install-NanoServerPackage -Name $dcbPackage -Culture $locale -Force
55 | $package.Name | should match $dcbPackage
56 | $package.Culture | should match $locale
57 | }
58 |
59 | It "Install compute package with correct culture" {
60 | $package = Install-NanoServerPackage -Name $computePackage -Culture $culture -Force
61 |
62 | $package.Name | should match $computePackage
63 | $package.Culture | should match $culture
64 |
65 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture
66 | $getPackage.Name | should match $package.Name
67 | $getPackage.Culture | should match $package.Culture
68 | }
69 |
70 | It "Install compute package with correct culture to vhd" {
71 | $package = Install-NanoServerPackage -Name $computePackage -Culture $culture -ToVhd $vhdPath -Force
72 |
73 | $package.Name | should match $computePackage
74 | $package.Culture | should match $culture
75 |
76 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture -FromVhd $vhdPath
77 | $getPackage.Name | should match $package.Name
78 | $getPackage.Culture | should match $package.Culture
79 | }
80 |
81 | It "Install compute package by piping from find" {
82 | $package = (Find-NanoServerPackage -Name *nanoserver-compute* | Install-NanoServerPackage -Force)
83 |
84 | $package.Name | should match $computePackage
85 | $package.Culture | should match $culture
86 |
87 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture
88 | $getPackage.Name | should match $package.Name
89 | $getPackage.Culture | should match $package.Culture
90 | }
91 |
92 | It "Install containers package with required version" {
93 | $package = Install-NanoServerPackage -Name $containersPackage -Culture $culture -RequiredVersion $requiredVersion -Force
94 |
95 | $package.Name | should match $containersPackage
96 | $package.Culture | should match $culture
97 | $package.Version.ToString() | should match $requiredVersion
98 |
99 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture -RequiredVersion $requiredVersion
100 | $getPackage.Name | should match $package.Name
101 | $getPackage.Culture | should match $package.Culture
102 | $getPackage.Version.ToString() | should match $requiredVersion
103 | }
104 |
105 | It "Install containers package with required version to vhd" {
106 | $package = Install-NanoServerPackage -Name $containersPackage -Culture $culture -RequiredVersion $requiredVersion -ToVhd $vhdPath -Force
107 |
108 | $package.Name | should match $containersPackage
109 | $package.Culture | should match $culture
110 | $package.Version | should match $requiredVersion
111 |
112 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture -RequiredVersion $requiredVersion -FromVhd $vhdPath
113 | $getPackage.Name | should match $package.Name
114 | $getPackage.Culture | should match $package.Culture
115 | }
116 |
117 | It "Install multiple packages to VHD" {
118 | $packages = Install-NanoServerPackage -Name $containersPackage,$computePackage -Culture $culture -Force
119 |
120 | $packages.Count | should be 2
121 | $packages.Name -contains $containersPackage | should be $true
122 | $packages.Name -contains $computePackage | should be $true
123 | }
124 |
125 | It "Install multiple packages to VHD" {
126 | $packages = Install-NanoServerPackage -Name $containersPackage,$computePackage -Culture $culture -Force -ToVhd $vhdPath
127 |
128 | $packages.Count | should be 2
129 | $packages.Name -contains $containersPackage | should be $true
130 | $packages.Name -contains $computePackage | should be $true
131 | }
132 |
133 | }
134 |
135 | Describe "Install-NanoServerPackage With OneGet" {
136 | It "ERROR: Install with wildcard name" {
137 | $Error.Clear()
138 | Install-Package -Name '*' -ProviderName $providerName -Force -ErrorAction SilentlyContinue
139 | $Error[0].FullyQualifiedErrorId | should match 'WildCardCharsAreNotSupported,Microsoft.PowerShell.PackageManagement.Cmdlets.InstallPackage'
140 | }
141 |
142 | It "ERROR: Install with wrong culture" {
143 | $Error.Clear()
144 | Install-Package -Name $dcbPackage -Culture wrong -ProviderName $providerName -Force -ErrorAction SilentlyContinue
145 | $Error[0].FullyQualifiedErrorId | should match 'NoMatchFoundForCriteria,Microsoft.PowerShell.PackageManagement.Cmdlets.InstallPackage'
146 | }
147 |
148 | It "ERROR: Install unknown packages" {
149 | $Error.Clear()
150 | Install-Package -ProviderName $providerName -Name 'wrong package name' -Force -ErrorAction SilentlyContinue
151 | $Error[0].FullyQualifiedErrorId | should match 'NoMatchFoundForCriteria,Microsoft.PowerShell.PackageManagement.Cmdlets.InstallPackage'
152 | }
153 |
154 | It "Install DCB package" {
155 | $package = Install-Package -ProviderName $providerName -Name $dcbPackage -Force
156 |
157 | $package.Name | should match $dcbPackage
158 | $package.Culture | should match $culture
159 |
160 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture
161 | $getPackage.Name | should match $package.Name
162 | $getPackage.Culture | should match $package.Culture
163 | }
164 |
165 | It "Install DCB package to vhd" {
166 | $package = Install-Package -ProviderName $providerName -Name $dcbPackage -ToVhd $vhdPath -Force
167 |
168 | $package.Name | should match $dcbPackage
169 | $package.Culture | should match $culture
170 |
171 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture -FromVhd $vhdPath
172 | $getPackage.Name | should match $package.Name
173 | $getPackage.Culture | should match $package.Culture
174 | }
175 |
176 | It "Install compute package with wrong culture" {
177 | $locale="it-it"
178 | $package = Install-Package -ProviderName $providerName -Name $dcbPackage -Culture $locale -Force -ErrorAction SilentlyContinue
179 | $package.Culture | should match $locale
180 | }
181 |
182 | It "Install compute package with correct culture" {
183 | $package = Install-Package -ProviderName $providerName -Name $computePackage -Culture $culture -Force
184 |
185 | $package.Name | should match $computePackage
186 | $package.Culture | should match $culture
187 |
188 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture
189 | $getPackage.Name | should match $package.Name
190 | $getPackage.Culture | should match $package.Culture
191 | }
192 |
193 | It "Install compute package with correct culture to vhd" {
194 | $package = Install-Package -ProviderName $providerName -Name $computePackage -Culture $culture -ToVhd $vhdPath -Force
195 |
196 | $package.Name | should match $computePackage
197 | $package.Culture | should match $culture
198 |
199 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture -FromVhd $vhdPath
200 | $getPackage.Name | should match $package.Name
201 | $getPackage.Culture | should match $package.Culture
202 | }
203 |
204 | It "Install compute package by piping from find" {
205 | $package = (Find-Package -ProviderName $providerName -Name $computePackage | Install-Package -Force)
206 |
207 | $package.Name | should match $computePackage
208 | $package.Culture | should match $culture
209 |
210 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture
211 | $getPackage.Name | should match $package.Name
212 | $getPackage.Culture | should match $package.Culture
213 | }
214 |
215 | It "Install containers package with required version" {
216 | $package = Install-Package -ProviderName $providerName -Name $containersPackage -Culture $culture -RequiredVersion $requiredVersion -Force
217 |
218 | $package.Name | should match $containersPackage
219 | $package.Culture | should match $culture
220 | $package.Version.ToString() | should match $requiredVersion
221 |
222 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture -RequiredVersion $requiredVersion
223 | $getPackage.Name | should match $package.Name
224 | $getPackage.Culture | should match $package.Culture
225 | $getPackage.Version.ToString() | should match $requiredVersion
226 | }
227 |
228 | It "Install containers package with required version to vhd" {
229 | $package = Install-Package $containersPackage -ProviderName $providerName -Culture $culture -RequiredVersion $requiredVersion -ToVhd $vhdPath -Force
230 |
231 | $package.Name | should match $containersPackage
232 | $package.Culture | should match $culture
233 | $package.Version | should match $requiredVersion
234 |
235 | $getPackage = Get-Package -ProviderName $providerName -Name $package.Name -Culture $package.Culture -RequiredVersion $requiredVersion -FromVhd $vhdPath
236 | $getPackage.Name | should match $package.Name
237 | $getPackage.Culture | should match $package.Culture
238 | }
239 |
240 | It "Install multiple packages to VHD" {
241 | $packages = Install-Package -ProviderName $providerName -Name $containersPackage,$computePackage -Culture $culture -Force
242 |
243 | $packages.Count | should be 2
244 | $packages.Name -contains $containersPackage | should be $true
245 | $packages.Name -contains $computePackage | should be $true
246 | }
247 |
248 | It "Install multiple packages to VHD" {
249 | $packages = Install-Package -ProviderName $providerName -Name $containersPackage,$computePackage -Culture $culture -Force -ToVhd $vhdPath
250 |
251 | $packages.Count | should be 2
252 | $packages.Name -contains $containersPackage | should be $true
253 | $packages.Name -contains $computePackage | should be $true
254 | }
255 | }
--------------------------------------------------------------------------------
/NanoServerPackage/Test/NanoServerPackage.Save.Tests.ps1:
--------------------------------------------------------------------------------
1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path
2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Save.Tests.ps1", ".psm1")
3 | #. "$here\..\$sut"
4 | $minVersion = "10.0.14300.1000"
5 | $maxVersion = "10.0.14393.1000"
6 | $requiredVersion = "10.0.14393.0"
7 |
8 | Describe "Save-WindowsPackage Stand-Alone" {
9 |
10 | BeforeAll {
11 |
12 | $pathToSaveWithWildCards = "$env:LOCALAPPDATA\N*Serv*age\Save\"
13 | $pathToSave = "$env:LOCALAPPDATA\NanoServerPackage\Save\"
14 | $badPath = "C:\DoesNotExist"
15 | $cultures = ("cs-cz", "de-de", "en-us", "es-es", "fr-fr", "hu-hu", "it-it", "ja-jp", "ko-kr", "nl-nl", "pl-pl", "pt-br", "pt-pt", "ru-ru", "sv-se", "tr-tr", "zh-cn", "zh-tw")
16 | $names = ("microsoft-NanoServEr-containers-package", "microsoft-NanoServEr-compute-package", "microsoft-NanoServEr-dcb-package")
17 |
18 | Import-packageprovider -force -name NanoServerPackage
19 | Import-module NanoServerPackage -force
20 |
21 | if(-not (Test-Path $pathToSave))
22 | {
23 | mkdir $pathToSave
24 | }
25 |
26 | # Remove all cab files under the save folder
27 | Remove-Item $pathToSave\*.cab
28 | }
29 |
30 | AfterAll {
31 | if(Test-Path $pathToSave)
32 | {
33 | rmdir $pathToSave -Force -Recurse
34 | }
35 |
36 | "Finished running the Find-NanoServerPackage Stand-Alone tests"
37 | }
38 |
39 | It "Save-NanoServerPackage Name, Path" {
40 | $name = $names | Get-Random
41 | $version = $requiredVersion
42 |
43 | $results = @()
44 | $results += (Save-NanoServerPackage -Name $name -Path $pathToSaveWithWildCards)
45 |
46 | $results.count | should be 1
47 | $results[0].name | should be $name
48 | $results[0].version | should be $version
49 | $results[0].culture | should be "en-us"
50 |
51 | $outputs = Get-ChildItem $pathToSave -Name *.cab
52 |
53 | foreach($output in $outputs)
54 | {
55 | $output | should match $name
56 | $output | should match $version
57 | }
58 |
59 | Remove-Item $pathToSave\*.cab
60 | }
61 |
62 | It "Save-NanoServerPackage Name, Path, Force" {
63 | $name = $names | Get-Random
64 |
65 | $SavePath = "$TestDrive\SaveFolder"
66 | (Test-Path $SavePath) | should be $false
67 |
68 | $results = @()
69 | $results += (Save-NanoServerPackage -Name $name -Path $SavePath -Force)
70 |
71 | $results.count | should be 1
72 | $results[0].name | should be $name
73 |
74 | $outputs = Get-ChildItem $SavePath -Name *.cab
75 |
76 | foreach($output in $outputs)
77 | {
78 | $output | should match $name
79 | }
80 |
81 | Remove-Item $SavePath -Recurse -Force
82 | }
83 |
84 | It "Save-NanoServerPackage Name, Path, Culture" {
85 | $name = $names | Get-Random
86 | $culture = $cultures | Get-Random
87 | $version = $requiredVersion
88 |
89 | $results = @()
90 | $results += (Save-NanoServerPackage -Name $name -Path $pathToSaveWithWildCards -Culture $culture)
91 |
92 | $results.count | should be 1
93 | $results[0].name | should be $name
94 | $results[0].version | should be $version
95 | $results[0].culture | should be $culture
96 |
97 | $outputs = Get-ChildItem $pathToSave -Name *.cab
98 |
99 | foreach($output in $outputs)
100 | {
101 | $output | should match $name
102 | $output | should match $version
103 | }
104 |
105 | Remove-Item $pathToSave\*.cab
106 | }
107 |
108 | It "Save-NanoServerPackage Name, Path, Culture, Minimum Version" {
109 | $name = $names | Get-Random
110 | $culture = $cultures | Get-Random
111 | $version = $requiredVersion
112 |
113 | $results = @()
114 | $results += (Save-NanoServerPackage -Name $name -Path $pathToSaveWithWildCards -Culture $culture -MinimumVersion $minVersion)
115 |
116 | $results.count | should be 1
117 | $results[0].name | should be $name
118 | $results[0].version | should be $version
119 | $results[0].culture | should be $culture
120 |
121 | $outputs = Get-ChildItem $pathToSave -Name *.cab
122 |
123 | foreach($output in $outputs)
124 | {
125 | $output | should match $name
126 | $output | should match $version
127 | }
128 |
129 | Remove-Item $pathToSave\*.cab
130 | }
131 |
132 | It "Save-NanoServerPackage Name, Path, Culture, Maximum Version" {
133 | $name = $names | Get-Random
134 | $culture = $cultures | Get-Random
135 | $version = $requiredVersion
136 |
137 | $results = @()
138 | $results += (Save-NanoServerPackage -Name $name -Path $pathToSaveWithWildCards -Culture $culture -MaximumVersion $maxVersion)
139 |
140 | $results.count | should be 1
141 | $results[0].name | should be $name
142 | $results[0].version | should be $version
143 | $results[0].culture | should be $culture
144 |
145 | $outputs = Get-ChildItem $pathToSave -Name *.cab
146 |
147 | foreach($output in $outputs)
148 | {
149 | $output | should match $name
150 | $output | should match $version
151 | }
152 |
153 | Remove-Item $pathToSave\*.cab
154 | }
155 |
156 | It "Save-NanoServerPackage Name, Path, Culture, Required Version" {
157 | $name = $names | Get-Random
158 | $culture = $cultures | Get-Random
159 | $version = $requiredVersion
160 |
161 | $results = @()
162 | $results += (Save-NanoServerPackage -Name $name -Path $pathToSaveWithWildCards -Culture $culture -RequiredVersion $requiredVersion)
163 |
164 | $results.count | should be 1
165 | $results[0].name | should be $name
166 | $results[0].version | should be $version
167 | $results[0].culture | should be $culture
168 |
169 | $outputs = Get-ChildItem $pathToSave -Name *.cab
170 |
171 | foreach($output in $outputs)
172 | {
173 | $output | should match $name
174 | $output | should match $version
175 | }
176 |
177 | Remove-Item $pathToSave\*.cab
178 | }
179 |
180 | It "Save-NanoServerPackage With Dependencies" {
181 | try {
182 | $name = "Microsoft-NanoServer-SCVMM-Compute-Package"
183 | $culture = $cultures | Get-Random
184 | $version = $requiredVersion
185 |
186 | $results = @()
187 | $results += (Save-NanoServerPackage -Name $name -Path $pathToSaveWithWildCards -Culture $culture -RequiredVersion $requiredVersion -Force)
188 |
189 | $results.count | should be 3
190 | $results.name -contains $name | should be $true
191 | $results.name -contains "Microsoft-NanoServer-Compute-Package" | should be $true
192 | $results.name -contains "Microsoft-NanoServer-SCVMM-Package" | should be $true
193 | $results[0].culture | should match $culture
194 | $results[1].culture | should match $culture
195 | $results[2].culture | should match $culture
196 | }
197 | finally {
198 | Remove-Item $pathToSave\*.cab
199 | }
200 | }
201 |
202 | }
203 |
204 | Describe "Save-NanoServerPackage One-Get" {
205 |
206 | BeforeAll {
207 |
208 | $pathToSave = "$env:LOCALAPPDATA\NanoServerPackageProvider\Save\"
209 | $badPath = "C:\DoesNotExist"
210 | $cultures = ("cs-cz", "de-de", "en-us", "es-es", "fr-fr", "hu-hu", "it-it", "ja-jp", "ko-kr", "nl-nl", "pl-pl", "pt-br", "pt-pt", "ru-ru", "sv-se", "tr-tr", "zh-cn", "zh-tw")
211 | $names = ("microsoft-NanoServEr-containers-package", "microsoft-NanoServEr-compute-package", "microsoft-NanoServEr-dcb-package")
212 |
213 | Import-packageprovider -force -name NanoServerPackage
214 | Import-module NanoServerPackage -force
215 |
216 | if(-not (Test-Path $pathToSave))
217 | {
218 | mkdir $pathToSave
219 | }
220 |
221 | # Remove all cab files under the save folder
222 | Remove-Item $pathToSave\*.cab
223 | }
224 |
225 | AfterAll {
226 | if(Test-Path $pathToSave)
227 | {
228 | rmdir $pathToSave -Force
229 | }
230 |
231 | "Finished running the Find-NanoServerPackage Stand-Alone tests"
232 | }
233 |
234 | It "Save-NanoServerPackage Name, Path" {
235 | $name = $names | Get-Random
236 | $version = $requiredVersion
237 |
238 | $results = @()
239 | $results += (Save-Package -ProviderName NanoServerPackage -Name $name -Path $pathToSave)
240 |
241 | $results.count | should be 1
242 | $results[0].name | should be $name
243 | $results[0].version | should be $version
244 | $results[0].culture | should be "en-us"
245 |
246 | $outputs = Get-ChildItem $pathToSave -Name *.cab
247 |
248 | foreach($output in $outputs)
249 | {
250 | $output | should match $name
251 | $output | should match $version
252 | }
253 |
254 | Remove-Item $pathToSave\*.cab
255 | }
256 |
257 | It "Save-NanoServerPackage Name, Path, Culture" {
258 | $name = $names | Get-Random
259 | $culture = $cultures | Get-Random
260 | $version = $requiredVersion
261 |
262 | $results = @()
263 | $results += (Save-Package -ProviderName NanoServerPackage $name -Path $pathToSave -Culture $culture)
264 |
265 | $results.count | should be 1
266 | $results[0].name | should be $name
267 | $results[0].version | should be $version
268 | $results[0].culture | should be $culture
269 |
270 | $outputs = Get-ChildItem $pathToSave -Name *.cab
271 |
272 | foreach($output in $outputs)
273 | {
274 | $output | should match $name
275 | $output | should match $version
276 | }
277 |
278 | Remove-Item $pathToSave\*.cab
279 | }
280 |
281 | It "Save-NanoServerPackage Name, Path, Culture, Minimum Version" {
282 | $name = $names | Get-Random
283 | $culture = $cultures | Get-Random
284 | $version = $requiredVersion
285 |
286 | $results = @()
287 | $results += (Save-Package -ProviderName NanoServerPackage -Name $name -Path $pathToSave -Culture $culture -MinimumVersion $minVersion)
288 |
289 | $results.count | should be 1
290 | $results[0].name | should be $name
291 | $results[0].version | should be $version
292 | $results[0].culture | should be $culture
293 |
294 | $outputs = Get-ChildItem $pathToSave -Name *.cab
295 |
296 | foreach($output in $outputs)
297 | {
298 | $output | should match $name
299 | $output | should match $version
300 | }
301 |
302 | Remove-Item $pathToSave\*.cab
303 | }
304 |
305 | It "Save-NanoServerPackage Name, Path, Culture, Maximum Version" {
306 | $name = $names | Get-Random
307 | $culture = $cultures | Get-Random
308 | $version = $requiredVersion
309 |
310 | $results = @()
311 | $results += (Save-Package -ProviderName NanoServerPackage -Name $name -Path $pathToSave -Culture $culture -MaximumVersion $maxVersion)
312 |
313 | $results.count | should be 1
314 | $results[0].name | should be $name
315 | $results[0].version | should be $version
316 | $results[0].culture | should be $culture
317 |
318 | $outputs = Get-ChildItem $pathToSave -Name *.cab
319 |
320 | foreach($output in $outputs)
321 | {
322 | $output | should match $name
323 | $output | should match $version
324 | }
325 |
326 | Remove-Item $pathToSave\*.cab
327 | }
328 |
329 | It "Save-NanoServerPackage Name, Path, Culture, Required Version" {
330 | $name = $names | Get-Random
331 | $culture = $cultures | Get-Random
332 | $version = $requiredVersion
333 |
334 | $results = @()
335 | $results += (Save-Package -ProviderName NanoServerPackage -Name $name -Path $pathToSave -Culture $culture -RequiredVersion $requiredVersion)
336 |
337 | $results.count | should be 1
338 | $results[0].name | should be $name
339 | $results[0].version | should be $version
340 | $results[0].culture | should be $culture
341 |
342 | $outputs = Get-ChildItem $pathToSave -Name *.cab
343 |
344 | foreach($output in $outputs)
345 | {
346 | $output | should match $name
347 | $output | should match $version
348 | }
349 |
350 | Remove-Item $pathToSave\*.cab
351 | }
352 |
353 | It "Save-NanoServerPackage With Dependencies" {
354 | try {
355 | $name = "Microsoft-NanoServer-SCVMM-Compute-Package"
356 | $culture = $cultures | Get-Random
357 | $version = $requiredVersion
358 |
359 | $results = @()
360 | $results += (Save-Package -ProviderName NanoServerPackage -Name $name -Path $pathToSave -Culture $culture -RequiredVersion $requiredVersion -Force)
361 |
362 | $results.count | should be 3
363 | $results.name -contains $name | should be $true
364 | $results.name -contains "Microsoft-NanoServer-Compute-Package" | should be $true
365 | $results.name -contains "Microsoft-NanoServer-SCVMM-Package" | should be $true
366 | $results[0].culture | should match $culture
367 | $results[1].culture | should match $culture
368 | $results[2].culture | should match $culture
369 | }
370 | finally {
371 | Remove-Item $pathToSave\*.cab
372 | }
373 | }
374 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # NanoServerPackage Provider
3 | A PackageManagement (aka OneGet) provider to find and install Optional Windows Packages (Windows feature and role) for Nano Server. For general information about these packages, please refer to the guide on Deploy Nano Server.
4 |
5 | ##### Note
6 | The public version 0.1.1.0 of NanoServerPackage Provider from the PowerShellGallery.com only supports Nano Server with Technical Preview 5 (TP5) version, i.e. 10.0.14300.1000, that is public in April 2016, and vice versa. It DOES NOT support Nano Server WS2016 GA version 10.0.14393.0. Please update your Nano Server to WS2016 GA release and install the latest version of the provider.
7 |
8 | ## Installing the provider
9 |
10 | You can find and install Nano Server packages from the online package repository by using the NanoServerPackage provider of the PackageManagement PowerShell module. To install this provider, use these cmdlets:
11 |
12 | ```powershell
13 | Install-PackageProvider NanoServerPackage
14 | Import-PackageProvider NanoServerPackage
15 | ```
16 |
17 | >`NOTE` If you experience errors when running Install-PackageProvider, check that you have [installed the latest cumulative update](https://technet.microsoft.com/en-us/windows-server-docs/get-started/update-nano-server) ([KB3206632](https://support.microsoft.com/kb/3206632) or later), or use the following steps to install the latest version of the provider:
18 | ```
19 | Save-Module -Path "$env:programfiles\WindowsPowerShell\Modules\" -Name NanoServerPackage -minimumVersion 1.0.1.0
20 | Import-PackageProvider NanoServerPackage
21 | ```
22 | To install the TP5 version of the provider 0.1.1.0 that works for TP5 OS version 10.0.14300.1000, use the following steps.
23 | ```
24 | # Your Nano Server OS version is 10.0.14300.1000
25 | Install-PackageProvider NanoServerPackage -requiredVersion 0.1.1.0
26 | Import-PackageProvider NanoServerPackage
27 | ```
28 |
29 | There are two sets of cmdlets provided in NanoServerPackage provider. The first set is specific to the provider:
30 | ```
31 | Find-NanoServerPackage
32 | Save-NanoServerPackage
33 | Install-NanoServerPackage
34 | ```
35 |
36 | The second set is generic PackageManagement cmdlets:
37 | ```
38 | Find-Package
39 | Save-Package
40 | Install-Package
41 | Get-Package
42 | ```
43 |
44 | The provider needs to be loaded in PowerShell before you can use any of the cmdlets. You can load the provider by running ```Import-PackageProvider NanoServerPackage```.
45 |
46 | ## Searching for Windows Packages
47 |
48 | Both ```Find-NanoServerPackage``` and ```Find-Package``` search and return a list of Windows Packages available in the online repository. You may want to provide ```-ProviderName NanoServerPackage``` to ```Find-Package``` so that OneGet will not use other providers. In addition, when using the generic OneGet cmdlet, you can also use a ```-DisplayCulture``` switch so the culture of the packages will be displayed.
49 |
50 | ##### Example 1
51 | Find the latest version of any Windows Packages that match a given name. Wildcard is also accepted
52 | ```
53 | Find-NanoServerPackage -Name Microsoft-NanoServer-Containers-Package
54 | Find-NanoServerPackage -Name *Containers*
55 | ```
56 | OR
57 | ```
58 | Find-Package -ProviderName NanoServerPackage -Name Microsoft-NanoServerPackage-Containers-Package -DisplayCulture
59 | Find-Package -ProviderName NanoServerPackage -Name *Containers* -DisplayCulture
60 | ```
61 |
62 | ##### Example 2
63 |
64 | Find the latest version of all available Windows Packages. The available cultures for each package are also displayed
65 | ```
66 | Find-NanoServerPackage
67 | ```
68 | OR
69 | ```
70 | Find-Package -ProviderName NanoServerPackage -DisplayCulture
71 | ```
72 |
73 | ##### Example 3
74 |
75 | Find the latest version of all available Windows Packages with culture en-us
76 | ```
77 | Find-NanoServerPackage -Culture en-us
78 | ```
79 | OR
80 | ```
81 | Find-Package -ProviderName NanoServerPackage -Culture en-us -DisplayCulture
82 | ```
83 |
84 |
85 | ##### Example 4
86 | Find Windows Packages that have a certain version using ```-RequiredVersion```
87 | ```
88 | Find-NanoServerPackage -RequiredVersion 10.0.14393.0
89 | ```
90 | OR
91 | ```
92 | Find-Package -ProviderName NanoServerPackage -RequiredVersion 10.0.14393.0 -DisplayCulture
93 | ```
94 |
95 | ##### Example 5
96 | Find Windows Packages within a certain version range using ```-MinimumVersion``` and ```-MaximumVersion```. The latest version of a package that satisfies the version range will be returned. For example, if the minimum version specified is 5.0, and the package have version 1.0, 5.0, 6.0 and 7.0, then the 7.0 version of the package will be returned.
97 | ```
98 | Find-NanoServerPackage -MinimumVersion 10.0
99 | ```
100 | OR
101 | ```
102 | Find-Package -ProviderName NanoServerPackage -MinimumVersion 10.0 -DisplayCulture
103 | ```
104 |
105 | ##### Example 6
106 | Find all available versions of a Windows Package using ```-AllVersions``` switch. This switch can also be used with ```-MinimumVersion``` and ```-MaximumVersion``` but not with ```-RequiredVersion```.
107 | ```
108 | Find-NanoServerPackage *Containers* -AllVersions
109 | Find-NanoServerPackage *Containers* -AllVersions -MinimumVersion 10.0
110 | ```
111 | OR
112 | ```
113 | Find-Package *Containers* -ProviderName NanoServerPackage -AllVersions -DisplayCulture
114 | Find-Package *Containers* -ProviderName NanoServerPackage -AllVersions -DisplayCulture -MinimumVersion 10.0
115 | ```
116 |
117 | ## Installing Windows Packages Online or Offline
118 |
119 | >`NOTE`
120 | > If you install an optional Nano Server package from media or online repository, it won't have recent security fixes included. To avoid a version mismatch between the optional packages and base operating system, you should install the [latest cumulative update](https://technet.microsoft.com/windows-server-docs/get-started/update-nano-server) immediately after installing any optional packages and **before** restarting the server.
121 |
122 | You can install a Windows Package (including its dependency packages, if any) using either ```Install-NanoServerPackage``` or ```Install-Package```. If you want to install the package to an offline NanoServer image, you can specify the path to the offline image with ```-ToVhd``` parameter. Otherwise, the cmdlets will install the package to the local machine.
123 |
124 | Both cmdlets accept pipeline result from the search cmdlets. The culture of the package has to match the culture of the machine you are installing it to for the package to work properly. The cmdlets have auto-detection logic that will determine the suitable culture.
125 |
126 | ##### Example 1
127 | Installing the latest version of the Containers package to the local machine
128 | ```
129 | Install-NanoServerPackage -Name Microsoft-NanoServer-Containers-Package
130 | ```
131 | OR
132 | ```
133 | Install-Package -ProviderName NanoServerPackage -Name Microsoft-NanoServer-Containers-Package -DisplayCulture
134 | ```
135 |
136 | ##### Example 2
137 | Install a package that depends on other packages. In this case, the dependency packages will be installed as well.
138 | ```
139 | Find-NanoServerPackage *scvmm-compute* | install-package
140 | ```
141 |
142 | ##### Example 3
143 | Install the latest version of the DCB package to an offline NanoServer image.
144 | ```
145 | Install-NanoServerPackage -Name Microsoft-NanoServer-DCB-Package -ToVhd C:\OfflineVhd.vhd
146 | ```
147 | OR
148 | ```
149 | Install-Package -Name Microsoft-NanoServer-DCB-Package -ToVhd C:\OfflineVhd.vhd -ProviderName NanoServerPackage -DisplayCulture
150 | ```
151 |
152 | ##### Example 4
153 | Install the Containers package by piping the result from the search cmdlets. Please do not specify ```-ProviderName``` on the ```Install-Package``` cmdlet if you use it this way.
154 | ```
155 | Find-NanoServerPackage *Containers* | Install-NanoServerPackage
156 | ```
157 | OR
158 | ```
159 | Find-Package -ProviderName NanoServerPackage *Containers* | Install-Package -DisplayCulture
160 | ```
161 |
162 | ## Dowloading Windows Packages
163 | You can download a Windows Package (including its dependency packages, if any) without installing it by using ```Save-NanoServerPackage``` or ```Save-Package``` cmdlets. Both cmdlets accept pipeline result from the search cmdlets. These cmdlets will download both the base package and the language package. If you do not specify the ```-Culture``` parameter, the culture of the local machine will be used.
164 |
165 | ##### Example 1
166 | Download and save the Containers package to a directory that matches the wildcard path using the culture of the local machine.
167 | ```
168 | Save-NanoServerPackage Microsoft-NanoServer-Containers-Package -Path C:\
169 | ```
170 | OR
171 | ```
172 | Save-Package -ProviderName NanoServerPackage Microsoft-NanoServer-Containers-Package -Path C:\ -DisplayCulture
173 | ```
174 |
175 | ##### Example 2
176 | Download and save version 10.0.14393.0 of the Containers package with de-de culture to the current directory.
177 | ```
178 | Save-NanoServerPackage Microsoft-NanoServer-Containers-Package -Path .\ -Culture de-de -RequiredVersion 10.0.14393.0
179 | ```
180 | OR
181 | ```
182 | Save-Package -ProviderName NanoServerPackage Microsoft-NanoServer-Containers-Package -Path .\ -Culture de-de -RequiredVersion 10.0.14393.0 -DisplayCulture
183 | ```
184 |
185 | ##### Example 3
186 | Download and save the it-it culture of the Shielded VM package to the current directory by piping the results from the search cmdlets. The dependency package will be downloaded as well.
187 | ```
188 | Find-NanoServerPackage -Name *shielded* -Culture it-it | Save-NanoServerPackage -Path .\
189 | ```
190 | OR
191 | ```
192 | Find-Package -ProviderName NanoServerPackage *shielded* -Culture it-it | Save-Package -Path .\ -DisplayCulture
193 | ```
194 |
195 | ## Inventory what Windows Packages are installed
196 | You can search for installed packages on your local NanoServer machine or an offline NanoServer image using ```Get-Package```.
197 |
198 | ##### Example 1
199 | Search for all Windows Packages installed on the local machine.
200 | ```
201 | Get-Package -ProviderName NanoServerPackage -DisplayCulture
202 | ```
203 |
204 | ##### Example 2
205 | Search for all Windows Packages installed on an offline NanoServer image.
206 | ```
207 | Get-Package -ProviderName NanoServerPackage -FromVhd C:\OfflineVhd.vhd -DisplayCulture
208 | ```
209 |
210 | ## Version
211 | 1.0.1.0
212 |
213 | ## Version History
214 | #### 1.0.1.0
215 | Public release for NanoServerPackage Provider that works for Nano Server WS2016 GA version
216 | #### 0.1.1.0
217 | Initial public release for Nano Package Provider that works for TP5 Nano Server
218 |
219 | ### Dependencies
220 | This module has no dependencies
221 |
222 | ## Known Issues
223 | 1. This provider does not support PowerShell Direct session.
224 | 2. Using the NanoServerPackage provider 1.0.1.0 to search for packages fails in Windows Containers. As a workaround, you may use the NanoServerPackage provider on another machine to download the packages, then copy and DISM install them in the container.
225 |
226 | ## Fixed issues in v1.0.1.0
227 | 1. In v0.1.1.0, you cannot install Microsoft-NanoServer-IIS-Package and Microsoft-NanoServer-SCVMM-Package online. There are two workarounds:
228 |
229 | i. Install another package that will require reboot such as Microsoft-NanoServer-Storage-Package first and without rebooting, install the required package.
230 |
231 | ii. Install these packages offline using -ToVhd
232 |
233 | 2. In v0.1.1.0, you might see an error as shown below while installing certain packages. This is mainly because this provider does not support discovering and installing dependencies. For these cases, refer to guide on Getting Started with Nano Server to identify the dependencies.
234 | ```
235 | install-package : Add-WindowsPackage failed. Error code = 0x800f0922
236 | + CategoryInfo : InvalidOperation: (System.String[]:String) [Install-Package], Exception
237 | + FullyQualifiedErrorId : FailedToInstall,Install-PackageHelper,Microsoft.PowerShell.PackageManagement.Cmdlets.InstallPackage
238 | ```
239 |
--------------------------------------------------------------------------------