giving variables to another powershell console [duplicate] - powershell

This question already has answers here:
How to pass a variable to new console window in Powershell
(2 answers)
Closed last year.
I have a script that opens a powershell console as admin and do sth in eventlog.
I have two variables that i the new admin-PS console needs.
[string] $PiEventLog = "'Company Name Prv.Limt'"
[String] $PiEventLogSource = "'XY-Test'"
I am opening the new PS-Console like this
start powershell -Verb runas {
If(Get-EventLog -List | ?{$_.Log -like $PiEventLog}){
Write-Host "EventLog already exists." -ForegroundColor Yellow
}
else{
New-EventLog -LogName $PiEventLog -Source $PiEventLogSource -ErrorAction Stop
Write-Host "EventLog was successfully created." -ForegroundColor Green
}
Read-Host "Press any key to close the console..."
}
If i try to execute the script, i get the following error:
The argument for the parameter "LogName" cannot be checked. The
argument is NULL or empty. Specify an argument that is not NULL or
empty and re-execute the command.
anyone got an idea, how i can give those two variables to the new PS-console, without having to set two different variables in the new console?

I believe this should work, it's easier if you use a Here-String. Since you're using the -like operator, I would assume you're looking for a Log that "contains" the input given in $PiEventLog, in that case, you should use wildcard characters: -like "*$PiEventLog*".
param(
[string] $PiEventLog = 'Company Name Prv.Limt',
[String] $PiEventLogSource = 'XY-Test'
)
$command = #"
If(Get-EventLog -List | Where-Object Log -Like '*$PiEventLog*'){
Write-Host 'EventLog already exists.' -ForegroundColor Yellow
}
else{
New-EventLog -LogName $PiEventLog -Source $PiEventLogSource -ErrorAction Stop
Write-Host 'EventLog was successfully created.' -ForegroundColor Green
}
Read-Host "Press any key to close the console..."
"#
Start-Process powershell -Verb RunAs -ArgumentList '-c', $command
Then you call this script like:
PS /> ./script.ps1 -PiEventLog 'something' -PiEventLogSource 'something'

Related

Powershell Running as Administrator not working?

I need some way to be able to run the below script as Administrator.
Script to get the Security Event log:
$DateAfter = (Get-Date).AddDays(-1)
$DateBefore = (Get-Date)
$EventLogTest = Get-EventLog -LogName Security -InstanceId 4625 -Before $DateBefore -After $DateAfter -Newest 5
$WinEventTest = Get-WinEvent -FilterHashtable #{ LogName = 'Security'; Id = 4625; StartTime = $DateAfter; EndTime = $DateBefore } -MaxEvents 5
Write-Host "$EventLogTest result is: "
$EventLogTest
Write-Host "$WinEventTest result is: "
$WinEventTest
I have compiled the below snippets, but somehow, the result is not displayed or nothing?
Combined Script:
$Role = "Domain Admins"
$CurrentLoginPrincipal = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent())
$IsDomainAdminGroupMember = $CurrentLoginPrincipal.IsInRole($Role)
$IsLocalComputerAdminMember = $CurrentLoginPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
If( -not ($IsDomainAdminGroupMember -and $IsLocalComputerAdminMember) ) {
Write-Warning "You are not running this as $($Role) and Local Administrator of $($ENV:COMPUTERNAME).$($ENV:USERDNSDOMAIN). The script will be re-executed as Local Administrator"
Try {
Start-Process PowerShell -Verb RunAs "-NoProfile -ExecutionPolicy Bypass -Command `"cd '$pwd'; & '$PSCommandPath';`"" -Verbose
}
Catch {
Write-Warning -Message "[PROCESS] Something wrong happened"
Write-Warning -Message $Error[0].Exception.Message
$out.Details = $_.Exception.Message
Write-Host " ERROR: $($out.Details)" -ForegroundColor Red
}
}
Else {
#a user running the script has the Domain Admins and Local PC Admin rights
Write-Host " $($CurrentLoginPrincipal.Identity.Name.ToString()) is currently member of $($Role) and Local Administrator of $($ENV:COMPUTERNAME).$($ENV:USERDNSDOMAIN) " -ForegroundColor Green
}
$DateAfter = (Get-Date).AddDays(-1)
$DateBefore = (Get-Date)
$EventLogTest = Get-EventLog -LogName Security -InstanceId 4625 -Before $DateBefore -After $DateAfter -Newest 5
$WinEventTest = Get-WinEvent -FilterHashtable #{ LogName = 'Security'; Id = 4625; StartTime = $DateAfter; EndTime = $DateBefore } -MaxEvents 5
Write-Host "$EventLogTest result is: "
$EventLogTest
Write-Host "$WinEventTest result is: "
$WinEventTest
However, it is still not executing as Administrator to get the result displayed. How can I fix this?
First thing I noticed is that your if condition is wrong. It uses -and where that should be or (because either a Domain admin OR a local Administrator can run this)
Next, the arguments for Start-Process are incorrect. Personally, I like using the -ArgumentList as array.
Finally, in the catch block you use an undefined variable $out with an equally undefined property $out.Details. In the code below I have changed that to simply re-throw the exception.
Starting from where the if..else is:
if( -not ($IsDomainAdminGroupMember -or $IsLocalComputerAdminMember) ) {
Write-Warning "You are not running this as $($Role) or Local Administrator of $($ENV:COMPUTERNAME).$($ENV:USERDNSDOMAIN). The script will be re-executed as Local Administrator"
# give the user some time to see this message
Start-Sleep 4
# Build base arguments for powershell.exe as string array
$argList = '-NoLogo', '-NoProfile', '-NoExit', '-ExecutionPolicy Bypass', '-File', ('"{0}"' -f $PSCommandPath)
# Add script arguments if any
$argList += $MyInvocation.BoundParameters.GetEnumerator() | ForEach-Object {"-$($_.Key)", "$($_.Value)"}
try {
Start-Process PowerShell.exe -Verb Runas -WorkingDirectory $pwd -ArgumentList $argList -Verbose -ErrorAction Stop
# exit the current script.
exit # Use return if you want to keep this instance open aswell
}
catch {
throw
}
}
else {
#a user running the script has the Domain Admins and Local PC Admin rights
Write-Host " $($CurrentLoginPrincipal.Identity.Name.ToString()) is currently member of $($Role) and Local Administrator of $($ENV:COMPUTERNAME).$($ENV:USERDNSDOMAIN) " -ForegroundColor Green
}

Restart script as admin and preserve switch and string parameters

I'm writing a PowerShell script that uses parameters / arguments and needs to be run as administrator but I'm trying to make it as user-friendly as possible so I'm trying to write it so that if it wasn't run as administrator then it can auto-elevate itself and preserve the original parameters (only switches and strings).
I was not able to find a solution online, hence this post.
I managed to accomplish this by "stringifying" $PsBoundParameters then using that with Start-Process PowerShell -Verb Runas -ArgumentList.
Note: $PsBoundParameters uses the parameters of the current scope ("root" vs inside a function, for example) so if you need to reference the command-line parameters (as I do) then you'll need to either use this variable outside of a function or first pass the variable to the function (as I've done here).
I've created a demonstration of this:
Param(
[switch]$ExampleSwitch,
[string]$ExampleString
)
Function Restart ($AllParameters, $Admin) {
$AllParameters_String = "";
ForEach ($Parameter in $AllParameters.GetEnumerator()){
$Parameter_Key = $Parameter.Key;
$Parameter_Value = $Parameter.Value;
$Parameter_Value_Type = $Parameter_Value.GetType().Name;
If ($Parameter_Value_Type -Eq "SwitchParameter"){
$AllParameters_String += " -$Parameter_Key";
} Else {
$AllParameters_String += " -$Parameter_Key $Parameter_Value";
}
}
$Arguments = "-File `"" + $PSCommandPath + "`" -NoExit" + $AllParameters_String;
If ($Admin -Eq $True){
Start-Process PowerShell -Verb Runas -ArgumentList $Arguments;
} Else {
Start-Process PowerShell -ArgumentList $Arguments;
}
}
$RanAsAdministrator = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator);
Write-Host "ExampleSwitch value:" $ExampleSwitch;
Write-Host "ExampleString value:" $ExampleString;
Write-Host "";
If ($RanAsAdministrator -Eq $True){
Write-Host "Running as administrator: Yes.";
} Else {
Write-Host "Running as administrator: No.";
}
$Elevate = Read-Host "Restart as current user or admin? (u/a)";
Write-Host "";
If ($Elevate -Like "u"){
Restart $PsBoundParameters;
} ElseIf ($Elevate -Like "a") {
Restart $PsBoundParameters -Admin $True;
}
Start-Sleep -Seconds 9999;
Param(
[Switch]$MySwitch,
[String]$MyString,
[Switch]$NoExit
)
If (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$PSHost = If ($PSVersionTable.PSVersion.Major -le 5) {'PowerShell'} Else {'PwSh'}
Start-Process -Verb RunAs $PSHost (#(' -NoExit')[!$NoExit] + " -File `"$PSCommandPath`" " + ($MyInvocation.Line -split '\.ps1[\s\''\"]\s*', 2)[-1])
Break
}
Write-Host "MySwitch:" $MySwitch;
Write-Host "MyString:" $MyString;
Explanation:
($MyInvocation.Line -split '\.ps1[\s\''\"]\s*', 2)[-1]) will resolve the current parameters
$MyHost determines the current PowerShell Host (PowerShell for Windows or PowerShell Core) and use the same host for the elevated window.
The -NoExit switch will prevent the elevated window to automatically close
Example:
.\RunAsAdministrator.ps1 -MyString "Test 123" -MySwitch -NoExit

Running commands as logged on user (remotely)

Thought I would share this quick function I made for myself, feel free to adapt it and improve it according to your needs.
Sometimes you want to run commands as the logged on user of a remote computer.
As you know, some commands show output for the user who runs it and if you run the same command with Invoke-Command, it won't return the user's information, but yours). Get-Printer is an example amongst many others.
There is no easy, quick way of running commands as the logged on user natively without any third-party apps like PsExec or others so I made this quick function that uses VBS, PS1 and Scheduled Task to make it happen.
It runs completly silently for the user (thanks to the VBS) and the output is shown in your console. Please note it assumes the remote computer has a C:\TEMP.
Created in a Windows 10, powershell v 5.1.17763.503 environement.
I don't pretend it's final and perfect, it's the simplest way I found to do what is needed and I just wanted to share it with you guys as it can be very useful!
Check the comments for explanation of the code and feel free to use it as you wish. Please share your version as I'm curious to see people improve it. A good idea would be to make it support multiple computers, but as I said it's a quick function I did I don't have too much time to put into refining it.
That being said, I had no problems using it multiple times as is :)
*Output returned is in form of a string, if you want to have a proper object, add '| ConvertFrom-String' and play with it :)
PLEASE NOTE: The surefire way of grabbing the username of who is currently logged on is via QWINSTA (since Win32_ComputerSystem - Username is only reliable if a user is logged on LOCALLY, it won't be right if a user is using RDP/RemoteDesktop). So this is what I used to grab the username, however, please note that in our french environement the name of the username property in QWINSTA is "UTILISATEUR",so you have to change that to your needs (english or other language) for it to work. If I remember correctly, it's "USERNAME" in english.
On this line:
$LoggedOnUser = (qwinsta /SERVER:$ComputerName) -replace '\s{2,22}', ',' | ConvertFrom-Csv | Where-Object {$_ -like "*Acti*"} | Select-Object -ExpandProperty UTILISATEUR
See code in the answer below.
function RunAsUser {
Param ($ComputerName,$Scriptblock)
#Check that computer is reachable
Write-host "Checking that $ComputerName is online..."
if (!(Test-Connection $ComputerName -Count 1 -Quiet)) {
Write-Host "$ComputerName is offline" -ForegroundColor Red
break
}
#Check that PsRemoting works (test Invoke-Command and if it doesn't work, do 'Enable-PsRemoting' via WMI method).
#*You might have the adjust this one to suit your environement.
#Where I work, WMI is always working, so when PsRemoting isn't, I enable it via WMI first.
Write-host "Checking that PsRemoting is enabled on $ComputerName"
if (!(invoke-command $ComputerName { "test" } -ErrorAction SilentlyContinue)) {
Invoke-WmiMethod -ComputerName $ComputerName -Path win32_process -Name create -ArgumentList "powershell.exe -command Enable-PSRemoting -SkipNetworkProfileCheck -Force" | Out-Null
do {
Start-Sleep -Milliseconds 200
} until (invoke-command $ComputerName { "test" } -ErrorAction SilentlyContinue)
}
#Check that a user is logged on the computer
Write-host "Checking that a user is logged on to $ComputerName..."
$LoggedOnUser = (qwinsta /SERVER:$ComputerName) -replace '\s{2,22}', ',' | ConvertFrom-Csv | Where-Object {$_ -like "*Acti*"} | Select-Object -ExpandProperty UTILISATEUR
if (!($LoggedOnUser) ) {
Write-Host "No user is logged on to $ComputerName" -ForegroundColor Red
break
}
#Creates a VBS file that will run the scriptblock completly silently (prevents the user from seeing a flashing powershell window)
#"
Dim wshell, PowerShellResult
set wshell = CreateObject("WScript.Shell")
Const WindowStyle = 0
Const WaitOnReturn = True
For Each strArg In WScript.Arguments
arg = arg & " " & strArg
Next 'strArg
PowerShellResult = wshell.run ("PowerShell " & arg & "; exit $LASTEXITCODE", WindowStyle, WaitOnReturn)
WScript.Quit(PowerShellResult)
"# | out-file "\\$ComputerName\C$\TEMP\RAU.vbs" -Encoding ascii -force
#Creates a script file from the specified '-Scriptblock' parameter which will be ran as the logged on user by the scheduled task created below.
#Adds 'Start-Transcript and Stop-Transcript' for logging the output.
$Scriptblock = "Start-Transcript C:\TEMP\RAU.log -force" + $Scriptblock + "Stop-Transcript"
$Scriptblock | out-file "\\$ComputerName\C$\TEMP\RAU.ps1" -Encoding utf8 -force
#On the remote computer, create a scheduled task that runs the .ps1 script silently in the user's context (with the help of the vbs)
Write-host "Running task on $ComputerName..."
Invoke-Command -ComputerName $ComputerName -ArgumentList $LoggedOnUser -ScriptBlock {
param($loggedOnUser)
$SchTaskParameters = #{
TaskName = "RAU"
Description = "-"
Action = (New-ScheduledTaskAction -Execute "wscript.exe" -Argument "C:\temp\RAU.vbs C:\temp\RAU.ps1")
Settings = (New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -DontStopOnIdleEnd)
RunLevel = "Highest"
User = $LoggedOnUser
Force = $true
}
#Register and Start the task
Register-ScheduledTask #SchTaskParameters | Out-Null
Start-ScheduledTask -TaskName "RAU"
#Wait until the task finishes before continuing
do {
Write-host "Waiting for task to finish..."
$ScheduledTaskState = Get-ScheduledTask -TaskName "RAU" | Select-Object -ExpandProperty state
start-sleep 1
} until ( $ScheduledTaskState -eq "Ready" )
#Delete the task
Unregister-ScheduledTask -TaskName "RAU" -Confirm:$false
}
Write-host "Task completed on $ComputerName"
#Grab the output of the script from the transcript and remove the header (first 19) and footer (last 5)
$RawOutput = Get-Content "\\$ComputerName\C$\temp\RAU.log" | Select-Object -Skip 19
$FinalOutput = $RawOutput[0..($RawOutput.length-5)]
#Shows output
return $FinalOutput
#Delete the output file and script files
Remove-Item "\\$ComputerName\C$\temp\RAU.log" -force
Remove-Item "\\$ComputerName\C$\temp\RAU.vbs" -force
Remove-Item "\\$ComputerName\C$\temp\RAU.ps1" -force
}
#____________________________________________________
#Example command
#Note: Sometimes Start-Transcript doesn't show the output for a certain command, so if you run into empty output, add: ' | out-host' or '| out-default' at the end of the command not showing output.
$Results = RunAsUser -ComputerName COMP123 -Scriptblock {
get-printer | Select-Object name,drivername,portname | Out-host
}
$Results
#If needed, you can turn the output (which is a string for the moment) to a proper powershell object with ' | ConvertFrom-String'

Write-Information does not show in a file transcribed by Start-Transcript

I'm using PowerShell 5.1 and I am trying to determine why Write-Information messages do not show in the transcript log created by Start-Transcript unless I set $InformationPreference to SilentlyContinue. I want to both display the messages in the console and have them written to the log file.
I looked here:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-5.1#informationpreference
Then I decided to create this script to test what gets written and when. See the preference section right underneath Testing explicit behavior with transcripts -------------
Clear-Host
$ErrorActionPreference = "Stop"
try {
Write-Host "Starting transcript"
Start-Transcript -Force -Path "$PSScriptRoot\default.txt"
<#
In PowerShell 5.1 the default behavior is as follows:
$DebugPreference = SilentlyContinue
$InformationPreference = SilentlyContinue
$ProgressPreference = Continue
$VerbosePreference = SilentlyContinue
See the following for more information:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-5.1
#>
# I am not testing Write-Output as I am not worried about programmatic/pipeline stuff, just contextual messages for end-users or logging
Write-Host "`nTesting default behavior with transcripts --------------------------------`n"
# Setting these just in case I launch this script in a session where a previous script might have modified the preference variables
$DebugPreference = "SilentlyContinue"
$InformationPreference = "SilentlyContinue"
$ProgressPreference = "Continue"
$VerbosePreference = "SilentlyContinue"
Write-Host "Calling Write-Host"
Write-Debug "Calling Write-Debug"
Write-Error "Calling Write-Error" -ErrorAction "Continue"
Write-Information "Calling Write-Information"
Write-Progress "Calling Write-Progress"
Write-Verbose "Calling Write-Verbose"
Stop-Transcript
Start-Transcript -Force -Path "$PSScriptRoot\everything_continue.txt"
Write-Host "`nTesting explicit behavior with transcripts --------------------------------`n"
# Turn everything on
$DebugPreference = "Continue"
$InformationPreference = "Continue" # Setting this to SilentlyContinue makes it show up in the log but not the console. Setting this to 'Continue' makes it show up in the console but not the log.
$ProgressPreference = "Continue"
$VerbosePreference = "Continue"
Write-Host "Calling Write-Host"
Write-Debug "Calling Write-Debug"
Write-Error "Calling Write-Error" -ErrorAction "Continue"
Write-Information "Calling Write-Information"
Write-Progress "Calling Write-Progress"
Write-Verbose "Calling Write-Verbose"
Stop-Transcript
Write-Host "`nResults -------------------------------------------------------------------`n"
# See what actually gets captured and written by the transcriber
$messageTypes = #("Write-Debug", "Write-Error", "Write-Host", "Write-Information", "Write-Verbose")
Write-Host "Default" -ForegroundColor Cyan
$lines = Get-Content "$PSScriptRoot\default.txt"
foreach ($message in $messageTypes) {
if ($lines -like "*Calling $message*") {
Write-Host " $message PRESENT" -ForegroundColor Green
}
else {
Write-Host " $message MISSING" -ForegroundColor Red
}
}
Write-Host "Everything Continue" -ForegroundColor Cyan
$lines = Get-Content "$PSScriptRoot\everything_continue.txt"
foreach ($message in $messageTypes) {
if ($lines -like "*Calling $message*") {
Write-Host " $message PRESENT" -ForegroundColor Green
}
else {
Write-Host " $message MISSING" -ForegroundColor Red
}
}
}
catch {
Write-Host "----------------------------------------------------------------------------------------------------"
Write-Host $_.Exception
Write-Host $_.ScriptStackTrace
Write-Host "----------------------------------------------------------------------------------------------------"
try { Stop-Transcript } catch { }
throw $_
}
What you're seeing is a bug in Windows PowerShell (as of v5.1.17134.590) that has been fixed in PowerShell Core (as of at least v6.1.0 - though other transcript-related problems persist; see this GitHub issue).
I encourage you to report it in the Windows PowerShell UserVoice forum (note that the PowerShell GitHub-repo issues forum is only for errors also present in PowerShell Core).
Here's how to verify if the bug is present in your PowerShell version:
Create a script with the code below and run it:
'--- Direct output'
$null = Start-Transcript ($tempFile = [io.path]::GetTempFileName())
# Note that 'SilentlyContinue' is also the default value.
$InformationPreference = 'SilentlyContinue'
# Produces no output.
Write-Information '1-information'
# Prints '2-Information' to the console.
Write-Information '2-information' -InformationAction Continue
$null = Stop-Transcript
'--- Write-Information output transcribed:'
Select-String '-information' $tempFile | Select-Object -ExpandProperty Line
Remove-Item $tempFile
With the bug present (Windows PowerShell), you'll see:
--- Direct output
2-information
--- Write-Information output transcribed:
INFO: 1-information
That is, the opposite of the intended behavior occurred: the transcript logged the call it should'nt have (because it produced no output), and it didn't log the one it should have.
Additionally, the logged output is prefixed with INFO: , which is an inconsistency that has also been fixed in PowerShell Core.
There is no full workaround, except that you can use Write-Host calls in cases where do you want the output logged in the transcript - but such calls will be logged unconditionally, irrespective of the value of preference variable $InformationPreference (while Write-Host formally provides an -InformationAction common parameter, it is ignored).
With the bug fixed (PowerShell Core), you'll see:
--- Direct output
2-information
--- Write-Information output transcribed:
2-information
The transcript is now consistent with the direct output.

Exiting from a powershell menu

I've created a powershell menu script but occasionally I don't want to use the menu function I have created I want to come out and run a custom command, or even better add a menu feature which allows me to write a custom command.
Can anyone help me achieve this please?
write-host "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
write-host "Exchange Online Management"
write-host
write-host "Press 1: To assign Full Access To A Mailbox"
write-host "Press 2: Get Mailbox Size"
write-host "Press 3: Custom Exchange Command"
write-host "Press Q: To Leave Exchange Management"
write-host
write-host
write-host "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
$SM2 = Read-Host "Please Make A Selection"
Switch ($SM2) {
'1'{
DO something
}
'2' {
$Mailboxuser = read-host "Who's Mailbox are you trying to query?"
Get-MailboxStatistics $Mailboxuser | ft DisplayName, TotalItemSize, ItemCount
}
'3' {
return
}
'Q' {
Remove-PSSession $Session
return
}
}
}
until ($SM -eq 'Q')
The PowerShell console window is closing after it has finished executing your script.
You can get around this by running another powershell process with your script:
'Q' {
Remove-PSSession $Session
& powershell
}
The other option is to create a shortcut to your script and add the -NoExit switch, which stops the window from closing when the script is finished.
PowerShell -NoExit "C:\folder\script.ps1"