MegaBites-AI/Windows-powershell
0372
1# Copyright (c) Microsoft Corporation.2# Licensed under the MIT License.3# Skip all tests on non-windows and non-PowerShellCore and non-elevated platforms.4 5$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()6$originalWarningPreference = $WarningPreference7$WarningPreference = "SilentlyContinue"8$skipTest = ! ($IsWindows -and $IsCoreCLR -and (Test-IsElevated)) -or (Test-IsWinWow64)9$PSDefaultParameterValues["it:skip"] = $skipTest10 11try12{13 Describe "Implicit remoting and CIM cmdlets with AllSigned and Restricted policy" -tags "Feature","RequireAdminOnWindows" {14 15 BeforeAll {16 17 if ($skipTest) { return }18 19 #20 # GET CERTIFICATE21 #22 23 $tempName = "TESTDRIVE:\signedscript_$(Get-Random).ps1"24 "123456" > $tempName25 $cert = $null26 foreach ($thisCertificate in (Get-ChildItem cert:\ -rec -codesigning))27 {28 $null = Set-AuthenticodeSignature $tempName -Certificate $thisCertificate29 if ((Get-AuthenticodeSignature $tempName).Status -eq "Valid")30 {31 $cert = $thisCertificate32 break33 }34 }35 36 # Skip the tests if we couldn't find a code sign certificate37 # This will happen in NanoServer and IoT38 if ($null -eq $cert)39 {40 $skipThisTest = $true41 return42 }43 $skipThisTest = $false44 45 # Ensure the cert is trusted46 if (-not (Test-Path "cert:\currentuser\TrustedPublisher\$($cert.Thumbprint)"))47 {48 $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "TrustedPublisher"49 $store.Open("ReadWrite")50 $store.Add($cert)51 $store.Close()52 }53 54 #55 # Create a remote session56 #57 58 $session = New-RemoteSession59 60 #61 # Set process scope execution policy to 'AllSigned'62 #63 64 $oldExecutionPolicy = Get-ExecutionPolicy -Scope Process65 Set-ExecutionPolicy AllSigned -Scope Process66 }67 68 AfterAll {69 if ($skipTest) { return }70 71 if ($null -ne $tempName) { Remove-Item -Path $tempName -Force -ErrorAction SilentlyContinue }72 if ($null -ne $oldExecutionPolicy) { Set-ExecutionPolicy $oldExecutionPolicy -Scope Process }73 if ($null -ne $session) { Remove-PSSession $session -ErrorAction SilentlyContinue }74 }75 76 #77 # TEST - Verifying that Import-PSSession signs the files78 #79 80 It "Verifies that Import-PSSession works in AllSigned if Certificate is used" -Skip:($skipTest -or $skipThisTest) {81 try {82 $importedModule = Import-PSSession $session Get-Variable -Prefix Remote -Certificate $cert -AllowClobber83 $importedModule | Should -Not -BeNullOrEmpty84 } finally {85 $importedModule | Remove-Module -Force -ErrorAction SilentlyContinue86 }87 }88 89 It "Verifies security error when Certificate parameter is not used" -Skip:($skipTest -or $skipThisTest) {90 { $importedModule = Import-PSSession $session Get-Variable -Prefix Remote -AllowClobber } | Should -Throw -ErrorId "InvalidOperation,Microsoft.PowerShell.Commands.ImportPSSessionCommand"91 }92 }93 94 Describe "Tests Import-PSSession cmdlet works with types unavailable on the client" -tags "Feature","RequireAdminOnWindows" {95 96 BeforeAll {97 98 if ($skipTest) { return }99 100 $typeDefinition = @"101 namespace MyTest102 {103 public enum MyEnum104 {105 Value1 = 1,106 Value2 = 2107 }108 }109"@110 #111 # Create a remote session112 #113 114 $session = New-RemoteSession115 116 Invoke-Command -Session $session -Script { Add-Type -TypeDefinition $args[0] } -Args $typeDefinition117 Invoke-Command -Session $session -Script { function foo { param([MyTest.MyEnum][Parameter(Mandatory = $true)]$x) $x } }118 }119 120 AfterAll {121 if ($skipTest) { return }122 if ($null -ne $session) { Remove-PSSession $session -ErrorAction SilentlyContinue }123 }124 125 It "Verifies client-side unavailable enum is correctly handled" {126 try {127 $module = Import-PSSession -Session $session -CommandName foo -AllowClobber128 129 # The enum is treated as an int130 (foo -x "Value2") | Should -Be 2131 # The enum is to-string-ed appropriately132 (foo -x "Value2").ToString() | Should -BeExactly "Value2"133 } finally {134 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }135 }136 }137 }138 139 Describe "Cmdlet help from remote session" -tags "Feature","RequireAdminOnWindows" {140 141 BeforeAll {142 143 if ($skipTest) { return }144 $session = New-RemoteSession145 }146 147 AfterAll {148 if ($skipTest) { return }149 if ($null -ne $session) { Remove-PSSession $session -ErrorAction SilentlyContinue }150 }151 152 It "Verifies that get-help name for remote proxied commands matches the get-command name" {153 try {154 $module = Import-PSSession $session -Name Select-Object -Prefix My -AllowClobber155 $gcmOutPut = (Get-Command Select-MyObject ).Name156 $getHelpOutPut = (Get-Help Select-MyObject).Name157 158 $gcmOutPut | Should -Be $getHelpOutPut159 } finally {160 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }161 }162 }163 }164 165 Describe "Import-PSSession Cmdlet error handling" -tags "Feature","RequireAdminOnWindows" {166 167 BeforeAll {168 169 if ($skipTest) { return }170 $session = New-RemoteSession171 }172 173 AfterAll {174 if ($skipTest) { return }175 if ($null -ne $session) { Remove-PSSession $session -ErrorAction SilentlyContinue }176 }177 178 It "Verifies that broken alias results in one error" {179 try {180 Invoke-Command $session { Set-Alias BrokenAlias NonExistantCommand }181 $module = Import-PSSession $session -CommandName:BrokenAlias -CommandType:All -ErrorAction SilentlyContinue -ErrorVariable expectedError -AllowClobber182 183 $expectedError | Should -Not -BeNullOrEmpty184 $expectedError[0].ToString().Contains("BrokenAlias") | Should -BeTrue185 } finally {186 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }187 Invoke-Command $session { Remove-Item alias:BrokenAlias }188 }189 }190 191 Context "Test content and format of proxied error message (Windows 7: #319080)" {192 193 BeforeAll {194 if ($skipTest) { return }195 $module = Import-PSSession -Session $session -Name Get-Variable -Prefix My -AllowClobber196 $oldErrorView = $ErrorView197 $ErrorView = "NormalView"198 }199 200 AfterAll {201 if ($skipTest) { return }202 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }203 $ErrorView = $oldErrorView204 }205 206 It "Test non-terminating error" {207 $results = Get-MyVariable blah,pid 2>&1208 209 ($results[1]).Value | Should -Not -Be $PID # Verifies that returned PID is not for this session210 211 $errorString = $results[0] | Out-String # Verifies error message for variable blah212 ($errorString -like "*VariableNotFound*") | Should -BeTrue213 }214 215 It "Test terminating error" {216 $results = Get-MyVariable pid -Scope blah 2>&1217 218 $results.Count | Should -Be 1 # Verifies that remote session pid is not returned219 220 $errorString = $results[0] | Out-String # Verifes error message for incorrect Scope parameter argument221 ($errorString -like "*Argument*") | Should -BeTrue222 }223 }224 225 Context "Ordering of a sequence of error and output messages (Windows 7: #405065)" {226 227 BeforeAll {228 if ($skipTest) { return }229 230 Invoke-Command $session { function foo1{1; Write-Error 2; 3; Write-Error 4; 5; Write-Error 6} }231 $module = Import-PSSession $session -CommandName foo1 -AllowClobber232 233 $icmErr = $($icmOut = Invoke-Command $session { foo1 }) 2>&1234 $proxiedErr = $($proxiedOut = foo1) 2>&1235 $proxiedOut2 = foo1 2> $null236 237 $icmOut = "$icmOut"238 $icmErr = "$icmErr"239 $proxiedOut = "$proxiedOut"240 $proxiedOut2 = "$proxiedOut2"241 $proxiedErr = "$proxiedErr"242 }243 244 AfterAll {245 if ($skipTest) { return }246 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }247 }248 249 It "Verifies proxied output = proxied output 2" {250 $proxiedOut2 | Should -Be $proxiedOut251 }252 253 It "Verifies proxied output = icm output (for mixed error and output results)" {254 $icmOut | Should -Be $proxiedOut255 }256 257 It "Verifies proxied error = icm error (for mixed error and output results)" {258 $icmErr | Should -Be $proxiedErr259 }260 261 It "Verifies proxied order = icm order (for mixed error and output results)" {262 $icmOrder = Invoke-Command $session { foo1 } 2>&1 | Out-String263 $proxiedOrder = foo1 2>&1 | Out-String264 265 $icmOrder | Should -Be $proxiedOrder266 }267 }268 269 Context "WarningVariable parameter works with implicit remoting (Windows 8: #44861)" {270 271 BeforeAll {272 if ($skipTest) { return }273 $module = Import-PSSession $session -CommandName Write-Warning -Prefix Remote -AllowClobber274 }275 276 AfterAll {277 if ($skipTest) { return }278 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }279 }280 281 It "Verifies WarningVariable" {282 $global:myWarningVariable = @()283 Write-RemoteWarning MyWarning -WarningVariable global:myWarningVariable284 ([string]($myWarningVariable[0])) | Should -Be 'MyWarning'285 }286 }287 }288 289 Describe "Tests Export-PSSession" -tags "Feature","RequireAdminOnWindows" {290 291 BeforeAll {292 293 if ($skipTest) { return }294 295 $sessionOption = New-PSSessionOption -ApplicationArguments @{myTest="MyValue"}296 $session = New-RemoteSession -SessionOption $sessionOption297 298 $file = [IO.Path]::Combine([IO.Path]::GetTempPath(), [Guid]::NewGuid().ToString())299 $results = Export-PSSession -Session $session -CommandName Get-Variable -AllowClobber -ModuleName $file300 $oldTimestamp = $($results | Select-Object -First 1).LastWriteTime301 }302 303 AfterAll {304 if ($skipTest) { return }305 if ($null -ne $file) { Remove-Item $file -Force -Recurse -ErrorAction SilentlyContinue }306 if ($null -ne $session) { Remove-PSSession $session -ErrorAction SilentlyContinue }307 }308 309 It "Verifies Export-PSSession creates a file/directory" {310 @(Get-Item $file).Count | Should -Be 1311 }312 313 It "Verifies Export-PSSession creates a psd1 file" {314 ($results | Where-Object { $_.Name -like "*$(Split-Path -Leaf $file).psd1" }) | Should -BeTrue315 }316 317 It "Verifies Export-PSSession creates a psm1 file" {318 ($results | Where-Object { $_.Name -like "*.psm1" }) | Should -BeTrue319 }320 321 It "Verifies Export-PSSession creates a ps1xml file" {322 ($results | Where-Object { $_.Name -like "*.ps1xml" }) | Should -BeTrue323 }324 325 It "Verifies that Export-PSSession fails when a module directory already exists" {326 $e = { Export-PSSession -Session $session -CommandName Get-Variable -AllowClobber -ModuleName $file -ErrorAction Stop } |327 Should -Throw -PassThru328 329 $e | Should -Not -BeNullOrEmpty330 # Error contains reference to the directory that already exists331 ([string]($e[0]) -like "*$file*") | Should -BeTrue332 }333 334 It "Verifies that overwriting an existing directory succeeds with -Force" {335 $newResults = Export-PSSession -Session $session -CommandName Get-Variable -AllowClobber -ModuleName $file -Force336 337 # Verifies that Export-PSSession returns 4 files338 @($newResults).Count | Should -Be 4339 340 # Verifies that Export-PSSession creates *new* files341 $newResults | ForEach-Object { $_.LastWriteTime | Should -BeGreaterThan $oldTimestamp }342 }343 344 Context "The module is usable when the original runspace is still around" {345 346 BeforeAll {347 if ($skipTest) { return }348 $module = Import-Module $file -PassThru349 }350 351 AfterAll {352 if ($skipTest) { return }353 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }354 }355 356 It "Verifies that proxy returns remote pid" {357 (Get-Variable -Name pid).Value | Should -Not -Be $PID358 }359 360 It "Verfies Remove-Module doesn't remove user's runspace" {361 Remove-Module $module -Force -ErrorAction SilentlyContinue362 (Get-PSSession -InstanceId $session.InstanceId) | Should -Not -BeNullOrEmpty363 }364 }365 }366 367 Describe "Proxy module is usable when the original runspace is no longer around" -tags "Feature","RequireAdminOnWindows" {368 BeforeAll {369 if ($skipTest) { return }370 371 $sessionOption = New-PSSessionOption -ApplicationArguments @{myTest="MyValue"}372 $session = New-RemoteSession -SessionOption $sessionOption373 374 $file = [IO.Path]::Combine([IO.Path]::GetTempPath(), [Guid]::NewGuid().ToString())375 $null = Export-PSSession -Session $session -CommandName Get-Variable -AllowClobber -ModuleName $file376 377 # Close the session to test the behavior of proxy module378 if ($null -ne $session) { Remove-PSSession $session -ErrorAction SilentlyContinue; $session = $null }379 }380 381 AfterAll {382 if ($skipTest) { return }383 if ($null -ne $file) { Remove-Item $file -Force -Recurse -ErrorAction SilentlyContinue }384 if ($null -ne $session) { Remove-PSSession $session -ErrorAction SilentlyContinue }385 }386 387 ## It requires 'New-PSSession' to work with implicit credential to allow proxied command to create new session.388 ## Implicit credential doesn't work in the Azure DevOps builder, so mark all tests here '-pending'.389 390 Context "Proxy module should create a new session" {391 BeforeAll {392 if ($skipTest) { return }393 $module = Import-Module $file -PassThru -Force394 $internalSession = & $module { $script:PSSession }395 }396 AfterAll {397 if ($skipTest) { return }398 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }399 }400 401 It "Verifies proxy should return remote pid" -Pending {402 (Get-Variable -Name PID).Value | Should -Not -Be $PID403 }404 405 It "Verifies ApplicationArguments got preserved correctly" -Pending {406 $(Invoke-Command $internalSession { $PSSenderInfo.ApplicationArguments.MyTest }) | Should -BeExactly "MyValue"407 }408 409 It "Verifies Remove-Module removed the runspace that was automatically created" -Pending {410 Remove-Module $module -Force411 (Get-PSSession -InstanceId $internalSession.InstanceId -ErrorAction SilentlyContinue) | Should -BeNullOrEmpty412 }413 414 It "Verifies Runspace is closed after removing module from Export-PSSession that got initialized with an internal r-space" -Pending {415 ($internalSession.Runspace.RunspaceStateInfo.ToString()) | Should -BeExactly "Closed"416 }417 }418 419 Context "Runspace created by the module with explicit session options" {420 BeforeAll {421 if ($skipTest) { return }422 $explicitSessionOption = New-PSSessionOption -Culture fr-FR -UICulture de-DE423 $module = Import-Module $file -PassThru -Force -ArgumentList $null, $explicitSessionOption424 $internalSession = & $module { $script:PSSession }425 }426 AfterAll {427 if ($skipTest) { return }428 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }429 }430 431 It "Verifies proxy should return remote pid" -Pending {432 (Get-Variable -Name PID).Value | Should -Not -Be $PID433 }434 435 # culture settings should be taken from the explicitly passed session options436 It "Verifies proxy returns modified culture" -Pending {437 (Get-Variable -Name PSCulture).Value | Should -BeExactly "fr-FR"438 }439 It "Verifies proxy returns modified culture" -Pending {440 (Get-Variable -Name PSUICulture).Value | Should -BeExactly "de-DE"441 }442 443 # removing the module should remove the implicitly/magically created runspace444 It "Verifies Remove-Module removes automatically created runspace" -Pending {445 Remove-Module $module -Force446 (Get-PSSession -InstanceId $internalSession.InstanceId -ErrorAction SilentlyContinue) | Should -BeNullOrEmpty447 }448 It "Verifies Runspace is closed after removing module from Export-PSSession that got initialized with an internal r-space" -Pending {449 ($internalSession.Runspace.RunspaceStateInfo.ToString()) | Should -BeExactly "Closed"450 }451 }452 453 Context "Passing a runspace into proxy module" {454 BeforeAll {455 if ($skipTest) { return }456 457 $newSession = New-RemoteSession458 $module = Import-Module $file -PassThru -Force -ArgumentList $newSession459 $internalSession = & $module { $script:PSSession }460 }461 AfterAll {462 if ($skipTest) { return }463 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }464 if ($null -ne $newSession) { Remove-PSSession $newSession -ErrorAction SilentlyContinue }465 }466 467 It "Verifies proxy returns remote pid" {468 (Get-Variable -Name PID).Value | Should -Not -Be $PID469 }470 471 It "Verifies switch parameters work" {472 (Get-Variable -Name PID -ValueOnly) | Should -Not -Be $PID473 }474 475 It "Verifies Adding a module affects runspace's state" {476 ($internalSession.Runspace.RunspaceStateInfo.ToString()) | Should -BeExactly "Opened"477 }478 479 It "Verifies Runspace stays opened after removing module from Export-PSSession that got initialized with an external runspace" {480 Remove-Module $module -Force481 ($internalSession.Runspace.RunspaceStateInfo.ToString()) | Should -BeExactly "Opened"482 }483 }484 }485 486 Describe "Import-PSSession with FormatAndTypes" -tags "Feature","RequireAdminOnWindows" {487 488 BeforeAll {489 if ($skipTest) { return }490 # remote into same powershell instance491 $samesession = New-RemoteSession -ConfigurationName $endpointName492 $session = New-RemoteSession493 function CreateTempPs1xmlFile494 {495 do {496 $tmpFile = [IO.Path]::Combine([IO.Path]::GetTempPath(), [IO.Path]::GetRandomFileName()) + ".ps1xml";497 } while ([IO.File]::Exists($tmpFile))498 $tmpFile499 }500 501 function CreateTypeFile {502 $tmpFile = CreateTempPs1xmlFile503 @"504 <Types>505 <Type>506 <Name>System.Management.Automation.Host.Coordinates</Name>507 <Members>508 <NoteProperty>509 <Name>MyTestLabel</Name>510 <Value>123</Value>511 </NoteProperty>512 </Members>513 </Type>514 <Type>515 <Name>MyTest.Root</Name>516 <Members>517 <MemberSet>518 <Name>PSStandardMembers</Name>519 <Members>520 <NoteProperty>521 <Name>SerializationDepth</Name>522 <Value>1</Value>523 </NoteProperty>524 </Members>525 </MemberSet>526 </Members>527 </Type>528 <Type>529 <Name>MyTest.Son</Name>530 <Members>531 <MemberSet>532 <Name>PSStandardMembers</Name>533 <Members>534 <NoteProperty>535 <Name>SerializationDepth</Name>536 <Value>1</Value>537 </NoteProperty>538 </Members>539 </MemberSet>540 </Members>541 </Type>542 <Type>543 <Name>MyTest.Grandson</Name>544 <Members>545 <MemberSet>546 <Name>PSStandardMembers</Name>547 <Members>548 <NoteProperty>549 <Name>SerializationDepth</Name>550 <Value>1</Value>551 </NoteProperty>552 </Members>553 </MemberSet>554 </Members>555 </Type>556 </Types>557"@ | Set-Content $tmpFile558 $tmpFile559 }560 561 function CreateFormatFile {562 $tmpFile = CreateTempPs1xmlFile563 @"564 <Configuration>565 <ViewDefinitions>566 <View>567 <Name>MySizeView</Name>568 <ViewSelectedBy>569 <TypeName>System.Management.Automation.Host.Size</TypeName>570 </ViewSelectedBy>571 <TableControl>572 <TableHeaders>573 <TableColumnHeader>574 <Label>MyTestWidth</Label>575 </TableColumnHeader>576 <TableColumnHeader>577 <Label>MyTestHeight</Label>578 </TableColumnHeader>579 </TableHeaders>580 <TableRowEntries>581 <TableRowEntry>582 <TableColumnItems>583 <TableColumnItem>584 <PropertyName>Width</PropertyName>585 </TableColumnItem>586 <TableColumnItem>587 <PropertyName>Height</PropertyName>588 </TableColumnItem>589 </TableColumnItems>590 </TableRowEntry>591 </TableRowEntries>592 </TableControl>593 </View>594 </ViewDefinitions>595 </Configuration>596"@ | Set-Content $tmpFile597 $tmpFile598 }599 600 $formatFile = CreateFormatFile601 $typeFile = CreateTypeFile602 }603 604 AfterAll {605 if ($skipTest) { return }606 if ($null -ne $session) { Remove-PSSession $session -ErrorAction SilentlyContinue }607 if ($null -ne $samesession) { Remove-PSSession $samesession -ErrorAction SilentlyContinue }608 if ($null -ne $formatFile) { Remove-Item $formatFile -Force -ErrorAction SilentlyContinue }609 if ($null -ne $typeFile) { Remove-Item $typeFile -Force -ErrorAction SilentlyContinue }610 }611 612 Context "Importing format file works" {613 BeforeAll {614 if ($skipTest) { return }615 616 $formattingScript = { New-Object System.Management.Automation.Host.Size | ForEach-Object { $_.Width = 123; $_.Height = 456; $_ } | Out-String }617 $originalLocalFormatting = & $formattingScript618 619 # Original local and remote formatting should be equal (sanity check)620 $originalRemoteFormatting = Invoke-Command $samesession $formattingScript621 $originalLocalFormatting | Should -Be $originalRemoteFormatting622 623 Invoke-Command $samesession { param($file) Update-FormatData $file } -ArgumentList $formatFile624 625 # Original remote and modified remote formatting should not be equal (sanity check)626 $modifiedRemoteFormatting = Invoke-Command $samesession $formattingScript627 $originalRemoteFormatting | Should -Not -Be $modifiedRemoteFormatting628 629 $module = Import-PSSession -Session $samesession -CommandName @() -FormatTypeName * -AllowClobber630 }631 632 AfterAll {633 if ($skipTest) { return }634 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }635 }636 637 It "modified remote and imported local should be equal" {638 $importedLocalFormatting = & $formattingScript639 $modifiedRemoteFormatting | Should -Be $importedLocalFormatting640 }641 642 It "original local and unimported local should be equal" {643 Remove-Module $module -Force644 $unimportedLocalFormatting = & $formattingScript645 $originalLocalFormatting | Should -Be $unimportedLocalFormatting646 }647 }648 649 It "Updating type table in a middle of a command has effect on serializer" {650 $results = Invoke-Command $session -ArgumentList $typeFile -ScriptBlock {651 param($file)652 653 New-Object System.Management.Automation.Host.Coordinates654 Update-TypeData $file655 New-Object System.Management.Automation.Host.Coordinates656 }657 658 # Should get 2 deserialized S.M.A.H.Coordinates objects659 $results.Count | Should -Be 2660 # First object shouldn't have the additional ETS note property661 $results[0].MyTestLabel | Should -BeNullOrEmpty662 # Second object should have the additional ETS note property663 $results[1].MyTestLabel | Should -Be 123664 }665 666 Context "Implicit remoting works even when types.ps1xml is missing on the client" {667 BeforeAll {668 if ($skipTest) { return }669 670 $typeDefinition = @"671 namespace MyTest672 {673 public class Root674 {675 public Root(string s) { text = s; }676 public Son Son = new Son();677 public string text;678 }679 680 public class Son681 {682 public Grandson Grandson = new Grandson();683 }684 685 public class Grandson686 {687 public string text = "Grandson";688 }689 }690"@691 Invoke-Command -Session $session -Script { Add-Type -TypeDefinition $args[0] } -ArgumentList $typeDefinition692 Invoke-Command -Session $session -Script { function foo { New-Object MyTest.Root "root" } }693 Invoke-Command -Session $session -Script { function bar { param([Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]$Son) $Son.Grandson.text } }694 695 $module = Import-PSSession $session foo,bar -AllowClobber696 }697 698 AfterAll {699 if ($skipTest) { return }700 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }701 }702 703 It "Serialization works for top-level properties" {704 $x = foo705 $x.text | Should -BeExactly "root"706 }707 708 It "Serialization settings works for deep properties" {709 $x = foo710 $x.Son.Grandson.text | Should -BeExactly "Grandson"711 }712 713 It "Serialization settings are preserved even if types.ps1xml is missing on the client" {714 $y = foo | bar715 $y | Should -BeExactly "Grandson"716 }717 }718 }719 720 Describe "Import-PSSession functional tests" -tags "Feature","RequireAdminOnWindows" {721 722 BeforeAll {723 if ($skipTest) { return }724 $session = New-RemoteSession725 726 # Define a remote function727 Invoke-Command -Session $session { function MyFunction { param($x) "x = '$x'; args = '$args'" } }728 729 # Define a remote proxy script cmdlet730 $remoteCommandType = $ExecutionContext.InvokeCommand.GetCommand('Get-Variable', [System.Management.Automation.CommandTypes]::Cmdlet)731 $remoteProxyBody = [System.Management.Automation.ProxyCommand]::Create($remoteCommandType)732 $remoteProxyDeclaration = "function Get-VariableProxy { $remoteProxyBody }"733 Invoke-Command -Session $session { param($x) Invoke-Expression $x } -Arg $remoteProxyDeclaration734 $remoteAliasDeclaration = "set-alias gvalias Get-Variable"735 Invoke-Command -Session $session { param($x) Invoke-Expression $x } -Arg $remoteAliasDeclaration736 Remove-Item alias:gvalias -Force -ErrorAction silentlycontinue737 738 # Import a remote function, script cmdlet, cmdlet, native application, alias739 $module = Import-PSSession -Session $session -Name MyFunction,Get-VariableProxy,Get-Variable,gvalias,cmd -AllowClobber -Type All740 }741 742 AfterAll {743 if ($skipTest) { return }744 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }745 if ($null -ne $session) { Remove-PSSession $session -ErrorAction SilentlyContinue }746 }747 748 It "Import-PSSession should return a PSModuleInfo object" {749 $module | Should -Not -BeNullOrEmpty750 }751 752 It "Import-PSSession should return a PSModuleInfo object" {753 ($module -as [System.Management.Automation.PSModuleInfo]) | Should -Not -BeNullOrEmpty754 }755 756 It "Helper functions should not be imported" {757 Get-Item function:*PSImplicitRemoting* -ErrorAction SilentlyContinue | Where-Object {$_.ModuleName -eq $module.Name} | Should -BeNullOrEmpty758 }759 760 It "Calls implicit remoting proxies 'MyFunction'" {761 (MyFunction 1 2 3) | Should -BeExactly "x = '1'; args = '2 3'"762 }763 764 It "proxy should return remote pid" {765 (Get-VariableProxy -Name:pid).Value | Should -Not -Be $PID766 }767 768 It "proxy should return remote pid" {769 (Get-Variable -Name:pid).Value | Should -Not -Be $PID770 }771 772 It "proxy should return remote pid" {773 $(& (Get-Command gvalias -Type alias) -Name:pid).Value | Should -Not -Be $PID774 }775 776 It "NoName-c8aeb5c8-2388-4d64-98c1-a9c6c218d404" {777 Invoke-Command -Session $session { $env:TestImplicitRemotingVariable = 123 }778 (cmd.exe /c "echo TestImplicitRemotingVariable=%TestImplicitRemotingVariable%") | Should -BeExactly "TestImplicitRemotingVariable=123"779 }780 781 Context "Test what happens after the runspace is closed" {782 BeforeAll {783 if ($skipTest) { return }784 785 Remove-PSSession $session786 787 # The loop below works around the fact that PSEventManager uses threadpool worker to queue event handler actions to process later.788 # Usage of threadpool means that it is impossible to predict when the event handler will run (this is Windows 8 Bugs: #882977).789 $i = 0790 while ( ($i -lt 20) -and ($null -ne (Get-Module | Where-Object { $_.Path -eq $module.Path })) )791 {792 $i++793 Start-Sleep -Milliseconds 50794 }795 }796 797 It "Temporary module should be automatically removed after runspace is closed" {798 (Get-Module | Where-Object { $_.Path -eq $module.Path }) | Should -BeNullOrEmpty799 }800 801 It "Temporary psm1 file should be automatically removed after runspace is closed" {802 (Get-Item $module.Path -ErrorAction SilentlyContinue) | Should -BeNullOrEmpty803 }804 805 It "Event should be unregistered when the runspace is closed" {806 # Check that the implicit remoting event has been removed.807 $implicitEventCount = 0808 foreach ($item in $ExecutionContext.Events.Subscribers)809 {810 if ($item.SourceIdentifier -match "Implicit remoting event") { $implicitEventCount++ }811 }812 $implicitEventCount | Should -Be 0813 }814 815 It "Private functions from the implicit remoting module shouldn't get imported into global scope" {816 @(Get-ChildItem function:*Implicit* -ErrorAction SilentlyContinue | Where-Object {$_.ModuleName -eq $module.Name}).Count | Should -Be 0817 }818 }819 }820 821 Describe "Implicit remoting parameter binding" -tags "Feature","RequireAdminOnWindows" {822 823 BeforeAll {824 if ($skipTest) { return }825 $session = New-RemoteSession826 }827 828 AfterAll {829 if ($skipTest) { return }830 if ($null -ne $session) { Remove-PSSession $session -ErrorAction SilentlyContinue }831 }832 833 It "Binding of ValueFromPipeline should work" {834 try {835 $module = Import-PSSession -Session $session -Name Get-Random -AllowClobber836 $x = 1..20 | Get-Random -Count 5837 $x.Count | Should -Be 5838 } finally {839 Remove-Module $module -Force840 }841 }842 843 Context "Pipeline-based parameter binding works even when client has no type constraints (Windows 7: #391157)" {844 BeforeAll {845 if ($skipTest) { return }846 847 Invoke-Command -Session $session -Scriptblock {848 function foo {849 [cmdletbinding(defaultparametersetname="string")]850 param(851 [string]852 [parameter(ParameterSetName="string", ValueFromPipeline = $true)]853 $string,854 855 [ipaddress]856 [parameter(ParameterSetName="ipaddress", ValueFromPipeline = $true)]857 $ipaddress858 )859 860 "Bound parameter: $($MyInvocation.BoundParameters.Keys | Sort-Object)"861 }862 }863 864 # Sanity checks.865 Invoke-Command $session {"s" | foo} | Should -BeExactly "Bound parameter: string"866 Invoke-Command $session {[ipaddress]::parse("127.0.0.1") | foo} | Should -BeExactly "Bound parameter: ipaddress"867 868 $module = Import-PSSession $session foo -AllowClobber869 }870 871 AfterAll {872 if ($skipTest) { return }873 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }874 }875 876 It "Pipeline binding works even if it relies on type constraints" {877 ("s" | foo) | Should -BeExactly "Bound parameter: string"878 }879 880 It "Pipeline binding works even if it relies on type constraints" {881 ([ipaddress]::parse("127.0.0.1") | foo) | Should -BeExactly "Bound parameter: ipaddress"882 }883 }884 885 Context "Pipeline-based parameter binding works even when client has no type constraints and parameterset is ambiguous (Windows 7: #430379)" {886 BeforeAll {887 if ($skipTest) { return }888 889 Invoke-Command -Session $session -Scriptblock {890 function foo {891 param(892 [string]893 [parameter(ParameterSetName="string", ValueFromPipeline = $true)]894 $string,895 896 [ipaddress]897 [parameter(ParameterSetName="ipaddress", ValueFromPipeline = $true)]898 $ipaddress899 )900 901 "Bound parameter: $($MyInvocation.BoundParameters.Keys)"902 }903 }904 905 # Sanity checks.906 Invoke-Command $session {"s" | foo} | Should -BeExactly "Bound parameter: string"907 Invoke-Command $session {[ipaddress]::parse("127.0.0.1") | foo} | Should -BeExactly "Bound parameter: ipaddress"908 909 $module = Import-PSSession $session foo -AllowClobber910 }911 912 AfterAll {913 if ($skipTest) { return }914 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }915 }916 917 It "Pipeline binding works even if it relies on type constraints and parameter set is ambiguous" {918 ("s" | foo) | Should -BeExactly "Bound parameter: string"919 }920 921 It "Pipeline binding works even if it relies on type constraints and parameter set is ambiguous" {922 ([ipaddress]::parse("127.0.0.1") | foo) | Should -BeExactly "Bound parameter: ipaddress"923 }924 }925 926 Context "pipeline-based parameter binding works even when one of parameters that can be bound by pipeline gets bound by name" {927 BeforeAll {928 if ($skipTest) { return }929 930 Invoke-Command -Session $session -Scriptblock {931 function foo {932 param(933 [DateTime]934 [parameter(ValueFromPipeline = $true)]935 $date,936 937 [ipaddress]938 [parameter(ValueFromPipeline = $true)]939 $ipaddress940 )941 942 "Bound parameter: $($MyInvocation.BoundParameters.Keys | Sort-Object)"943 }944 }945 946 # Sanity checks.947 Invoke-Command $session {Get-Date | foo} | Should -BeExactly "Bound parameter: date"948 Invoke-Command $session {[ipaddress]::parse("127.0.0.1") | foo} | Should -BeExactly "Bound parameter: ipaddress"949 Invoke-Command $session {[ipaddress]::parse("127.0.0.1") | foo -date (Get-Date)} | Should -BeExactly "Bound parameter: date ipaddress"950 Invoke-Command $session {Get-Date | foo -ipaddress ([ipaddress]::parse("127.0.0.1"))} | Should -BeExactly "Bound parameter: date ipaddress"951 952 $module = Import-PSSession $session foo -AllowClobber953 }954 955 AfterAll {956 if ($skipTest) { return }957 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }958 }959 960 It "Pipeline binding works even when also binding by name" {961 (Get-Date | foo) | Should -BeExactly "Bound parameter: date"962 }963 964 It "Pipeline binding works even when also binding by name" {965 ([ipaddress]::parse("127.0.0.1") | foo) | Should -BeExactly "Bound parameter: ipaddress"966 }967 968 It "Pipeline binding works even when also binding by name" {969 ([ipaddress]::parse("127.0.0.1") | foo -date $(Get-Date)) | Should -BeExactly "Bound parameter: date ipaddress"970 }971 972 It "Pipeline binding works even when also binding by name" {973 (Get-Date | foo -ipaddress ([ipaddress]::parse("127.0.0.1"))) | Should -BeExactly "Bound parameter: date ipaddress"974 }975 }976 977 Context "value from pipeline by property name - multiple parameters" {978 BeforeAll {979 if ($skipTest) { return }980 981 Invoke-Command -Session $session -Scriptblock {982 function foo {983 param(984 [System.TimeSpan]985 [parameter(ValueFromPipelineByPropertyName = $true)]986 $TotalProcessorTime,987 988 [System.Diagnostics.ProcessPriorityClass]989 [parameter(ValueFromPipelineByPropertyName = $true)]990 $PriorityClass991 )992 993 "Bound parameter: $($MyInvocation.BoundParameters.Keys | Sort-Object)"994 }995 }996 997 # Sanity checks.998 Invoke-Command $session {Get-Process -pid $PID | foo} | Should -BeExactly "Bound parameter: PriorityClass TotalProcessorTime"999 Invoke-Command $session {Get-Process -pid $PID | foo -Total 5} | Should -BeExactly "Bound parameter: PriorityClass TotalProcessorTime"1000 Invoke-Command $session {Get-Process -pid $PID | foo -Priority normal} | Should -BeExactly "Bound parameter: PriorityClass TotalProcessorTime"1001 1002 $module = Import-PSSession $session foo -AllowClobber1003 }1004 1005 AfterAll {1006 if ($skipTest) { return }1007 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }1008 }1009 1010 It "Pipeline binding works by property name" {1011 (Get-Process -Id $PID | foo) | Should -BeExactly "Bound parameter: PriorityClass TotalProcessorTime"1012 }1013 1014 It "Pipeline binding works by property name" {1015 (Get-Process -Id $PID | foo -Total 5) | Should -BeExactly "Bound parameter: PriorityClass TotalProcessorTime"1016 }1017 1018 It "Pipeline binding works by property name" {1019 (Get-Process -Id $PID | foo -Priority normal) | Should -BeExactly "Bound parameter: PriorityClass TotalProcessorTime"1020 }1021 }1022 1023 Context "2 parameters on the same position" {1024 BeforeAll {1025 if ($skipTest) { return }1026 1027 Invoke-Command -Session $session -Scriptblock {1028 function foo {1029 param(1030 [string]1031 [parameter(Position = 0, parametersetname = 'set1', mandatory = $true)]1032 $string,1033 1034 [ipaddress]1035 [parameter(Position = 0, parametersetname = 'set2', mandatory = $true)]1036 $ipaddress1037 )1038 1039 "Bound parameter: $($MyInvocation.BoundParameters.Keys | Sort-Object)"1040 }1041 }1042 1043 # Sanity checks.1044 Invoke-Command $session {foo ([ipaddress]::parse("127.0.0.1"))} | Should -BeExactly "Bound parameter: ipaddress"1045 Invoke-Command $session {foo "blah"} | Should -BeExactly "Bound parameter: string"1046 1047 $module = Import-PSSession $session foo -AllowClobber1048 }1049 1050 AfterAll {1051 if ($skipTest) { return }1052 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }1053 }1054 1055 It "Positional binding works" {1056 foo "blah" | Should -BeExactly "Bound parameter: string"1057 }1058 1059 It "Positional binding works" {1060 foo ([ipaddress]::parse("127.0.0.1")) | Should -BeExactly "Bound parameter: ipaddress"1061 }1062 }1063 1064 Context "positional binding and array argument value" {1065 BeforeAll {1066 if ($skipTest) { return }1067 1068 Invoke-Command -Session $session -Scriptblock {1069 function foo {1070 param(1071 [object]1072 [parameter(Position = 0, mandatory = $true)]1073 $p1,1074 1075 [object]1076 [parameter(Position = 1)]1077 $p21078 )1079 1080 "$p1 : $p2"1081 }1082 }1083 1084 # Sanity checks.1085 Invoke-Command $session {foo 1,2,3} | Should -BeExactly "1 2 3 : "1086 Invoke-Command $session {foo 1,2,3 4} | Should -BeExactly "1 2 3 : 4"1087 Invoke-Command $session {foo -p2 4 1,2,3} | Should -BeExactly "1 2 3 : 4"1088 Invoke-Command $session {foo 1 4} | Should -BeExactly "1 : 4"1089 Invoke-Command $session {foo -p2 4 1} | Should -BeExactly "1 : 4"1090 1091 $module = Import-PSSession $session foo -AllowClobber1092 }1093 1094 AfterAll {1095 if ($skipTest) { return }1096 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }1097 }1098 1099 It "Positional binding works when binding an array value" {1100 foo 1,2,3 | Should -BeExactly "1 2 3 : "1101 }1102 1103 It "Positional binding works when binding an array value" {1104 foo 1,2,3 4 | Should -BeExactly "1 2 3 : 4"1105 }1106 1107 It "Positional binding works when binding an array value" {1108 foo -p2 4 1,2,3 | Should -BeExactly "1 2 3 : 4"1109 }1110 1111 It "Positional binding works when binding an array value" {1112 foo 1 4 | Should -BeExactly "1 : 4"1113 }1114 1115 It "Positional binding works when binding an array value" {1116 foo -p2 4 1 | Should -BeExactly "1 : 4"1117 }1118 }1119 1120 Context "value from remaining arguments" {1121 BeforeAll {1122 if ($skipTest) { return }1123 1124 Invoke-Command -Session $session -Scriptblock {1125 function foo {1126 param(1127 [string]1128 [parameter(Position = 0)]1129 $firstArg,1130 1131 [string[]]1132 [parameter(ValueFromRemainingArguments = $true)]1133 $remainingArgs1134 )1135 1136 "$firstArg : $remainingArgs"1137 }1138 }1139 1140 # Sanity checks.1141 Invoke-Command $session {foo} | Should -BeExactly " : "1142 Invoke-Command $session {foo 1} | Should -BeExactly "1 : "1143 Invoke-Command $session {foo -first 1} | Should -BeExactly "1 : "1144 Invoke-Command $session {foo 1 2 3} | Should -BeExactly "1 : 2 3"1145 Invoke-Command $session {foo -first 1 2 3} | Should -BeExactly "1 : 2 3"1146 Invoke-Command $session {foo 2 3 -first 1 4 5} | Should -BeExactly "1 : 2 3 4 5"1147 Invoke-Command $session {foo -remainingArgs 2,3 1} | Should -BeExactly "1 : 2 3"1148 1149 $module = Import-PSSession $session foo -AllowClobber1150 }1151 1152 AfterAll {1153 if ($skipTest) { return }1154 if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue }1155 }1156 1157 It "Value from remaining arguments works" {1158 $( foo ) | Should -BeExactly " : "1159 }1160 1161 It "Value from remaining arguments works" {1162 $( foo 1 ) | Should -BeExactly "1 : "1163 }1164 1165 It "Value from remaining arguments works" {1166 $( foo -first 1 ) | Should -BeExactly "1 : "1167 }1168 1169 It "Value from remaining arguments works" {1170 $( foo 1 2 3 ) | Should -BeExactly "1 : 2 3"1171 }1172 1173 It "Value from remaining arguments works" {1174 $( foo -first 1 2 3 ) | Should -BeExactly "1 : 2 3"1175 }1176 1177 It "Value from remaining arguments works" {1178 $( foo 2 3 -first 1 4 5 ) | Should -BeExactly "1 : 2 3 4 5"1179 }1180 1181 It "Value from remaining arguments works" {1182 $( foo -remainingArgs 2,3 1 ) | Should -BeExactly "1 : 2 3"1183 }1184 }1185 1186 Context "non cmdlet-based binding" {1187 BeforeAll {1188 if ($skipTest) { return }1189 1190 Invoke-Command -Session $session -Scriptblock {1191 function foo {1192 param(1193 $firstArg,1194 $secondArg1195 )1196 1197 "$firstArg : $secondArg : $args"1198 }1199 }1200 