Security.ps1
|
# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. function Invoke-Elevated { <# .SYNOPSIS Invoke script in elevated Powershell session .DESCRIPTION Executes script in an elevated Powershell session. If elevation is needed, user is prompted via UAC, new elevated process is created, input and output objects are transferred between processes. Beware that not all objects are deserialized well by internally used Import-CliXml. If output received is unreadable try using | Out-String at the end of the script. .PARAMETER Scriptblock Script block that needs to be invoked in elevated session. .PARAMETER State State object that would be passes as argument to the executed script. On elevation state is serialized via Export-CliXml. .EXAMPLE PS> $name = "test" PS> $cred = Get-Credential PS> $state = construct name cred PS> Invoke-Elevated { param( $state ) $state; Test-Elevated } $state This sample shows how to call script in elevated session and pass a complex argument into it. Test-Elevated would return true here. .NOTES For text output it is possible to redirect it to the main program in async way. But that would not work for Powershell objects. #> param ( [Parameter(Mandatory = $true)] [ScriptBlock] $Scriptblock, [object] $State ) # Do direct invoke if we are already elevated if( Test-Elevated ) { return & $scriptblock $state } # Prepare input and output files $stateFile = [IO.Path]::GetTempFileName() $outputFile = [IO.Path]::GetTempFileName() $state | Export-CliXml -Depth 1 $stateFile # Prepare encoded command to be called $commandString = @" Set-Location '$($pwd.Path)' `$state = Import-CliXml '$stateFile' `$output = & { $($scriptblock.ToString()) } `$state *>&1 `$output | Export-CliXml -Depth 1 '$outputFile' "@ $commandBytes = [Text.Encoding]::Unicode.GetBytes($commandString) $commandEncoded = [Convert]::ToBase64String($commandBytes) $commandLine = "-EncodedCommand $commandEncoded" # Start elevated PowerShell process try { $process = Start-Process ` -FilePath (Get-Command powershell).Definition ` -ArgumentList $commandLine ` -WindowStyle Hidden ` -Verb RunAs ` -Passthru } catch { # This is to make cancelled UAC a terminating error # -ea Stop doesn't work here for some reason throw } $process.WaitForExit() # Return output to the user and cleaning up Import-CliXml $outputFile Remove-Item $outputFile Remove-Item $stateFile } function Test-Interactive { <# .SYNOPSIS Determine if the current Powershell session is interactive .DESCRIPTION Interactive shell should have human being observing it =) You can ask something him/her via Read-Host command. If there is no human being, no reason to ask, right? .EXAMPLE Test-Interactive Would return true for a regular Powershell session. Would return false for an automation job. Would return false for a remote session. Does not detect -NonInteractive Powershell calling argument. #> [Environment]::UserInteractive } function Test-Elevated { <# .SYNOPSIS Test if current Powershell session is elevated .DESCRIPTION Several commands need to be executed in an elevated session to have administrator rights. This function allows safely and robustly detect if current session is elevated. .EXAMPLE Test-Elevated Would return true for an elevated Powershell session with administrator rights. Would return false for a regular Powershell session. Would return true for a remote Powershell session that is started under user that is a local administrator (by default in Powershell 3.0/Windows there is no way of running not elevated remote session if the user in in the administrator group). #> $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = [Security.Principal.WindowsPrincipal] $identity $role = [Security.Principal.WindowsBuiltInRole] "Administrator" $principal.IsInRole($role) } function Set-DelayLock { <# .SYNOPSIS Lock machine after the specified timeout #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseShouldProcessForStateChangingFunctions', '', Justification='Intended to be this way')] param ( [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Minutes")] [int] $Minutes, [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "TimeSpan")] [timespan] $Timeout ) if( $Minutes ) { $Timeout = [timespan]::FromMinutes($Minutes) } "Setting timer for $timeout" "Computer would lock at $((Get-Date) + $timeout)" Start-Job -ArgumentList ($timeout.TotalSeconds) -ScriptBlock { Start-Sleep -Seconds $args[0] rundll32.exe user32.dll,LockWorkStation } | Out-Null } |