I'm trying to set up a simple script.
Every time a user does log in, it should execute a simple PowerShell script by the GPO I set up, the script should then gather all data from Get-ComputerInfo and write it to a file on a shared folder.
I've tried many things, like Start-Job
$Task = Start-Job -ScriptBlock {
Get-ComputerInfo | Out-File "\\SERVER\ComputerInfo\$env:COMPUTERNAME -
$env:USERNAME.txt"
}
Wait-Job $Task.Name | Out-Null
It works great executing it on the desktop by the user, but as logon script it does not work. It creates the file, but it's empty.
My suspicion is that it does not wait until PowerShell gather all information from Get-ComputerInfo (it usually takes some seconds) and just writes an empty file.
Execution Policy, Permission for Folders, etc shouldn't be a problem, especially considering it creates the file and the script works on the desktop with a non-admin user.
Related
I am new to PowerShell and this is the first time I am attempting to use a job. I am running into an issue where I have a part of a script that looks for a file, creates it if it doesn't exist and then amends the file, and when I run the script (not as a job) it executes correctly, but when I put it in a job, it doesn't amend the file.
A much simplified example of what I have is this:
Start-job -Name HostCheck -ScriptBlock {
ForEach ($Host in (Get-Content -Path .\HostFile.txt) {
Add-Content .\somefile.txt "`nWrite something on a new line for $Host"
} | Out-Null
}
# Removes job once it is finished
Get-Job -Name HostCheck | Wait-Job | Remove-Job
Now I have tried adding | Receive-Job after the | Out-Null, but that didn't seem to change anything.
I've seen people write the entire script-block to a variable and just use the variable instead, so I am curious if that is a requirement (but I wouldn't think so).
Also, this might matter, I open the script with a .bat file that escalates the PowerShell console to admin as well as setting the execution policy of the process to Bypass. Now it seems that everything that runs in that console session or is kicked off by that console session (several scripts get ran, this is just part of one of them) seems to inherit those settings, but being new with jobs, I don't know if it would also inherit those settings, or how I would force it to (if not).
I discovered the problem:
-Your current working directory is lost when starting a job so my relative path .\somefile.txt would default to C:\Users\[Username]\Documents instead of the location where the .\somefile.txt resides.
I can get around this by using an absolute path, or I think there is a way to pass arguments to a job, but if anyone knows a better way to do this, please feel free to comment.
Here's a workaround, cd to the current dir of the caller.
start-job { cd $using:pwd; pwd } | Receive-Job -wait -auto
I have such script (here simplified):
while ($true)
{
Write-Host "Looping..."
Write-Host "Looping..." 6>> trash.txt
Start-Sleep -s 1
}
when I run it directly it works, but when I run it in the background:
Start-Job { .\sleeper.ps1 }
for a second it is seen as Running but shortly after as Failed and indeed file "trash.txt" is not created at all, so even one iteration is not executed.
What is wrong here?
I think the main issue is around the $PWD and -FilePath param, but I will list some info on Write-Host too:
Start-Job should be run with the -FilePath parameter. This is because {} is a ScriptBlock object, which is by default taken by the -ScriptBlock parameter, which you do not want. Example solution to that line: Start-Job -FilePath ./script.ps1
The $PWD or present working directory is, by default, the home directory / user profile of the current user when executed in a PowerShell job (Linux: $HOME // Windows: $Home/Documents). You can test this by executing a job simply with that variable (it may be $ENV:PWD on Windows?). Either way, it is likely not the same as the directory you are executing this in. trash.txt is being made and appended to, but it is being made in a different directory than your current directory. You will want your script to explicitly include an absolute path to the file being created, or give the script parameters that allow you to input the path at execution. Here is another StackOverflow article where a user had similar struggles, with two good solutions where one uses $args in the script and another uses the -InitializationScript parameter of Start-Job to set the $PWD: PowerShell Start-Job Working Directory
Often, Write-Host is low-priority as a selected output vs. using Write-Output / Write-Verbose / Write-Warning / Write-Error. More information about this can be found here: https://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/ - though, newer versions of PowerShell have added an information stream which I believe may make Write-Host output more accessible. More information on streams can be found with help about_Redirection
I am trying to write a script in PowerShell that gathers free space on the C: drive and then appends it to a .csv file on a network drive.
Our company does not have PowerShell enabled by default. Remote PowerShell commands are restricted. Because of this, I have to script things out, then kick them off using PsExec.
I have two scripts for this process. The first one, is the one I run from my machine, this script asks for my credentials and then uses a foreach loop to cycle through a .txt file with IP addresses telling each IP to launch the second script from a network drive. The second script gathers the free space, performs a math function on it, then appends the hostname and free space to a .csv file on the network.
However, the second script never runs. After the connection is established using PsExec, my console just sits there. It should kick off the script and then loop again to the next IP. I have tested the second script manually on some of the target machines with no issues.
Locally executed script:
$username = Read-Host "Username"
$outputPath = "\\network\directory\sub-directory\FileName.csv"
$IPpath = Read-Host "Enter the path for the list of IP addresses"
$IPs = Get-Content -Path $IPpath
Add-Content -Path $outputPath -Value """IP"",""Free Space"""
foreach ($IP in $IPs)
{
if (Test-Connection -Count 1 -ComputerName $IP -Quiet)
{
\\network\directory\subdirectory\psexec.exe -accepteula \\$IP -u domain\$username -h PowerShell -ExecutionPolicy Bypass -Command \\network\directory\sub-directory\diskSpaceCheck.ps1
}
else
{
Add-Content -Path $outputPath -Value """$IP"",""Offline"""
}
}
Remotely executed script (diskSpaceCheck.ps1):
$outputPath = "\\network\directory\sub-directory\FreeSpace.csv"
$hostname = hostname
$disk = Get-WmiObject Win32_LogicalDisk -Filter "DeviceID='C:'"
$freeSpace = [System.Math]::Round((($disk.FreeSpace) / 1GB))
Add-Content -Path $outputPath -Value "$($hostname), $($freeSpace)"
I don't think you can have an if statement inside a foreach also but I've tried it without the if test and got the same results. I can't figure out why the remote machine won't execute this script. I have another script that is copied to these machines and then executed in the same manner that works correctly.
Also, the target machines are locked down to limit their use for only specific purposes. These machines use a generic auto logon and generally have access to a few applications. Everything else is inaccessible, including Windows Explorer, Control Panel, etc. I tested this script on some of the machines local to my office, and it worked fine.
I was thinking maybe a networking latency issue? Can that impact PsExec or .ps1 scripts? I can remote onto the machine, log out, log in with my credentials and run the script while PsExec just sits at running the command I gave it.
Any advice would be appreciated.
This can occur if you are using PSExec to directly execute the Ps1 file. Instead use PSexec to execute PowerShell.exe and pass your ps1 file as a parameter.
Also check this question: Run PowerShell on remote PC
I figured it out. The script was running but it was just sitting there waiting for another command. I added a stop-process command to the script that runs remotely.
Stop-Process -Name powershell
Now it runs, and then exits with error code -1.
My setup:
One Powershell script with a menu. Upon selecting a a menu item, a specific Powershell script is run assigned to that menu item.
I'm attempting to make this second Powershell script run silently as in - nothing is shown in the main Powershell script.
Bonus:
The full setup I'm looking at is:
User runs Powershell script
User selects a menu item (this executes another Powershell script)
The information in the other Powershell script is not shown to the user
After the second Powershell script has been executed, the user is shown a line of text such as: "Script has been run successfully"
What I've tried:
Adding the following code to each secondary script
$t = '[DllImport("user32.dll")] public static extern bool ShowWindow(int handle, int state);'
add-type -name win -member $t -namespace native
[native.win]::ShowWindow(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle, 0)
Unfortunately this closes the main Powershell window which isn't what I desire.
You could run the secondary command as a background-job and wait for it to finish. That way no output is shown in the shell
Start-Job -FilePath YourSecondaryScript.ps1 | Wait-Job
if you need, you can still analyze the job result (the script output)
Fixed by appending
| out-null
at the end of each line of code in the secondary scripts.
I have a script(Let's call it myPSScript.ps1) which takes two parameters and performs predefined steps. Script is located in a Windows Server box which people login and execute the script. Supports two users to be logged in at the given time.
I want to find out who invoked the script.
(Get-WmiObject -Class Win32_Process | Where-Object {$_.ProcessName -eq 'explorer.exe'}).GetOwner() | Format-Table Domain, User
This works when the user is logged in currently and trying to run the script. But what if I have a batch file in scheduled tasks and run the same script?
In that case the same command returns a null exception, as there is no one logged into the machine.
Is there a way to find out who/which process invoked the powershell script. I vaguely remember Start-Transcript records which user the command is run from etc, so this should be possible?
Thanks!
Sanjeev
Interesting question. I wrote a script with three different ways to get the user like so:
([Environment]::UserDomainName + "\" + [Environment]::UserName) | out-file test.txt
"$env:userdomain\$env:username" | out-file -append test.txt
[Security.Principal.WindowsIdentity]::GetCurrent().Name | out-file -append test.txt
notepad test.txt
Saved it as test.ps1 and called it using runas as:
runas /user:domain\user "powershell e:\test.ps1"
And I got the domain\user all three times in the output. Used runas to just distinguish between the user I am logged in as (me!!) and the domain\user with which I was running it as. So it does give the user that is running the script.