Running PowerShell from RunOnce - powershell

I am trying to run a set of powershell commands during a template customization. These commands are injected into the RunOnce in the registry.
%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -command &{Add-Type -Assembly System.Web; [Web.Security.Membership]::GeneratePassword(21,7) | Select-Object #{N="Group";E={"Test Group"}}, #{N="Username";E={"TestUser"}}, #{N="Password";E={$_}} | Export-Csv -Path C:\stuff\user.csv -NoTypeInformation}
I know that these commands work, as I can run them in a powershell console. I know that "%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -command &{PSCODE}" is correct because other runonce commands will run correctly. I cannot figure out what part of my syntax is failing.

This could be a long trial and error session, to save you precious time, I suggest you use a script file instead of that long command:
%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -File c:\script.ps1

Just want to add, that in order to run a sequence of commands you have to run it without quotes. At least only in this way it worked for me:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noexit Import-Module Hyper-V; Get-VMSwitch \"*Virtual*\" | Remove-VMSwitch -Force; New-VMSwitch -NetAdapterName \"Ethernet\" -Name \"VMNet\"

Related

Powershell Script won't run correctly in Task Scheduler

I recently created some PowerShell code that runs perfectly fine on its own or via a command prompt (or a .bat file). Hell, it runs fine as a .cmd file too. When I create a task scheduler task for it to run automatically, the result claims that the job runs 'successfully' but there isn't any output in the folder destination.
The whole goal of the script is to look at a particular subfolder within outlook and find files with a particular name. Then it outputs the latest related attachments to a specific destination.
PowerShell Script
$olFolderInbox = 6
$limit = (Get-Date).AddDays(-1)
$filepath = "C:\Scripts\attachment Project\Spreadsheets"
$ol = New-Object -com outlook.application;
$ns = $ol.GetNamespace("MAPI");
$acct = $ns.GetDefaultFolder($olFolderInbox)
$targetfolder = $acct.Folders | where-object { $_.name -eq "Training" }
$targetfolder.Items | foreach {
if ($_.ReceivedTime -ge $limit) {
$_.attachments |
foreach {
Write-Host "attached file: ",$_.filename
If ($_.filename -match 'SessionCompletions' -or $_.filename -match 'Certifications'){
$_.saveasfile((Join-Path $filepath $_.FileName))
$ol.quit()
}
}
}
}
I've tried all kinds of things to get the Task within Task Scheduler to work and nothing so far seems to help. To start I tried using a basic bat file to run the task. Mind you, this basic format has worked with many other PowerShell related tasks that I set up to run via a bat file.
Basic Bat Script
powershell -file extract-spreadsheets.ps1
Now sometimes I've needed to include an -executionpolicy argument as well but it sadly didn't work in this case.
A little more complex Script
Powershell.exe -executionpolicy RemoteSigned -File "C:\Scripts\attachment Project\extract spreadsheets.ps1"
Note: I made sure the get-executionpolicy on the computer was / is set to RemoteSigned. I've also tried Unrestricted as well.
I've also tried even more complex / different approaches such as . . .
Even more Complex of a Script
#ECHO OFF
SET ThisScriptsDirectory=C:\Scripts\David attachment Project\
SET PowerShellScriptPath=%ThisScriptsDirectory%extract-spreadsheets.ps1
PowerShell -NoProfile -ExecutionPolicy RemoteSigned -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy RemoteSigned -File ""%PowerShellScriptPath%""' -Verb RunAs}";
And I've even tried getting a .cmd file going and trying to use that within Task Scheduler:
##:: This prolog allows a PowerShell script to be embedded in a .CMD file.
##:: Any non-PowerShell content must be preceeded by "##"
##setlocal
##set POWERSHELL_BAT_ARGS=%*
##if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%
##PowerShell -Command Invoke-Expression $('$args=#(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join(';',$((Get-Content '%~f0') -notmatch '^^##'))) & goto :EOF
$olFolderInbox = 6
$limit = (Get-Date).AddDays(-1)
$filepath = "C:\Scripts\attachment Project\Spreadsheets"
$ol = New-Object -com outlook.application;
$ns = $ol.GetNamespace("MAPI");
$acct = $ns.GetDefaultFolder($olFolderInbox)
$targetfolder = $acct.Folders | where-object { $_.name -eq "Training" }
$targetfolder.Items | foreach {
if ($_.ReceivedTime -ge $limit) {
$_.attachments |
foreach {
Write-Host "attached file: ",$_.filename
If ($_.filename -match 'SessionCompletions' -or $_.filename -match 'Certifications'){
$_.saveasfile((Join-Path $filepath $_.FileName))
$ol.quit()
}
}
}
}
In terms of Task Scheduler, I've tried a multitude of things. To start . . .
For Program / script, I tried various relative and explicit destinations / paths for bat, cmd and PowerShell.
Some examples:
Powershell.exe (again I've also done the full path as well)
C:\Scripts\attachment Project\Training_Reports.bat
Training_Reports.bat
C:\Scripts\attachment Project\extract-spreadsheets.cmd
Note: I've added the above Programs / Actions with and without quotes.
Again, the more frustrating thing is the basic format of Training_Reports.bat with the start in (optional) field of:
C:\Scripts\attachment Project or C:\Scripts\attachment Project\
Has worked with every other script / task I have set up in this format!
I've tried numerous arguments for Execution Policy such as RemoteSigned, Bypass and Unrestricted. I've Added other arguments like: -NoProfile, -noninteractive, -File, -command, -NoLogo etc etc.
Examples of what the Arguments Field has looked like within Task Scheduler:
-ExecutionPolicy Bypass -File "C:\Scripts\attachment Project\extract-spreadsheets.ps1"
-NoLogo -NonInteractive -ExecutionPolicy RemoteSigned -File "C:\Scripts\attachment Project\extract-spreadsheets.ps1"
-NoProfile -ExecutionPolicy Unrestricted -Command "C:\Scripts\attachment Project\extract-spreadsheets.ps1"
I tried using various accounts to 'run the task'.
my domain account (which my account is a global domain admin and has
full permissions)
local Admin account
another Domain Admin Account
I've tried even SYSTEM
None of the accounts above have changed the outcome. Other settings I've looked at that people claim could mess with Tasks.
Turned off 'Start the task only if the computer is on AC power'
Ran the task whether user is logged on or not
Also ran it as when user is logged on
Set it for 'Highest privileges'
I tried running the task in 'configure for' drop down menu for
Windows 10 and Windows 7 / Server 2008.
Other things I have tried:
The initial PowerShell Script had mapped drive destinations. I changed the code to use C drive locations.
Explicitly giving that user account full control over the
directories involved.
Changing ownership of the directories involved to the same user.
Changing the security permissions on the ps1, cmd, bat, outlook.exe files to always Run as Administrator.
Tried running the script with Outlook running and not running.
Note: running it manually works regardless if Outlook is running.
Why is this task within Task Scheduler not outputting correctly?

PowerShell function not running as expected

I have a curious case that I cannot fathom the reason for...
Please know I am a novice to PowerShell.
I am working on a PowerShell menu system to help automate building out new computers in my environment. I have a PS1 file that holds the script for an app install. When I use the script to reference this I am able to run it and have no issue. However, when I try inserting this into a function and referencing it does not.
This works:
4 # Microsoft Office 32-bit
{
Write-Host "`nMicrosoft Office 32-bit..." -ForegroundColor Yellow
# {installMS32Bit}
Invoke-Expression "cmd /c start powershell -NoExit -File '\\**SERVERPATH**\menuItems\ms_office\32-bit\install.ps1'"
Start-Sleep -seconds 2
}
This does not:
function installMS32Bit(){
Invoke-Expression "cmd /c start powershell -NoExit -File '\\**SERVERPATH**\menuItems\ms_office\32-bit\install.ps1'"
}
}
4 # Microsoft Office 32-bit
{
Write-Host "`nMicrosoft Office 32-bit..." -ForegroundColor Yellow
{installMS32Bit}
Start-Sleep -seconds 2}
install.ps1 file:
# Copy MS Office uninstall and setup to local then run and install 32-bit Office
Copy-Item -Path '\\**SERVERPATH**\menuItems\ms_office\setup.exe' -Destination 'C:\temp\' -Force
Copy-Item -Path '\\**SERVERPATH**\menuItems\ms_office\uninstall.xml' -Destination 'C:\temp\' -Force
Copy-Item -Path '\\**SERVERPATH**\menuItems\ms_office\32-bit\Setup.exe' -Destination 'C:\temp' -Force
Invoke-Expression ("cmd /c 'C:\temp\setup.exe' /configure 'C:\temp\uninstall.xml'")
Start-Process -FilePath 'C:\temp\Setup.exe'
Secondary question and a little explanation for Invoke-Expression...
I like to see progress and like to have secondary windows open to monitor the new process being run. I was unable to find a solution with a persistent window that worked for me to do this without Invoke-Expression.
If there is a better way to do this in PowerShell I am all ears!
{installMS32Bit}
As Mathias points out in a comment on the question, this statement doesn't call your function, it wraps it in a script block ({ ... })[1], which is a piece of reusable code (like a function pointer, loosely speaking), for later execution via &, the call (execute) operator.
To call your function, just use its name (by itself here, given that there are no arguments to pass): installMS32Bit
Invoke-Expression should generally be avoided; definitely don't use it to invoke an external program, as in your attempts.
Additionally, there's generally no need to call an external program via cmd.exe (cmd /c ...), just invoke it directly.
For instance, replace the last Invoke-Epression call from your question with:
# If the EXE path weren't quoted, you wouldn't need the &
& 'C:\temp\setup.exe' /configure 'C:\temp\uninstall.xml'
I like to see progress and like to have secondary windows open to monitor the new process being run. I was unable to find a solution with a persistent window that worked for me to do this without Invoke-Expression.
(On Windows), Start-Process by default executes a console application in a new window (unless you specify -NoNewWindow), asynchronously (unless you specify -Wait).
You cannot pass a .ps1 script directly to Start-Process (it will be treated like a document to open rather than an executable to call), but you can pass it to PowerShell's CLI via the -File parameter:
Start-Process powershell.exe '-File install.ps1'
The above is short for:
Start-Process -FilePath powershell.exe -ArgumentList '-File install.ps1'
That is, PowerShell will execute the following in a new window:
powershell.exe -File install.ps1
[1] Since you're not assigning the script block being created to a variable, it is implicitly output (printed to the display, in the absence of a redirection); a script block stringifies by its literal contents, excluding the enclosing { and }, so string installMS32Bit will print to the display.

how do I make it easy for my parents to run this Powershell command?

I am not a programmer and my parents' Windows 10 PC tends to loose its start menu and cortana processes, resulting in start menu not showing up at all when the start icon is clicked.
I made a quick search and found + tested this Powershell command and it worked:
Get-AppxPackage | % { Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppxManifest.xml" -verbose }
I wish to turn this command into a shortcut/batchfile that executes the command and restarts the PC whenever the desktop icon is double clicked, in order to avoid explaining to my parents what to do to fix the problem. Can any one help me out please?
Thank you in Advance.
you can encode the command and put the whole thing into a single batch file (no .ps1 necessary)
details here
https://blogs.msdn.microsoft.com/timid/2014/03/26/powershell-encodedcommand-and-round-trips/
or you can use this function
https://github.com/gangstanthony/PowerShell/blob/master/Encode-Text.ps1
first, either use Get-Content or Get-Clipboard (copy your whole script to the clipboard) to encode your desired script
PS> Encode-Text (Get-Clipboard | out-string)
RwBlAHQALQBBAHAAcAB4AFAAYQBjAGsAYQBnAGUAIAB8ACAAJQAgAHsAIABBAGQAZAAtAEEAcABwAHgAUABhAGMAawBhAGcAZQAgAC0ARABpAHMAYQBiAGwAZQBEAGUAdgBlAGwAbwBwAG0AZQBuAHQATQBvAGQAZQAgAC0AUgBlAGcAaQBzAHQAZQByACAAIgAkACgAJABfAC4ASQBuAHMAdABhAGwAbABMAG8AYwBhAHQAaQBvAG4AKQBcAEEAcABwAHgATQBhAG4AaQBmAGUAcwB0AC4AeABtAGwAIgAgAC0AdgBlAHIAYgBvAHMAZQAgAH0ADQAKAA==
then you can use that in your batch file like so
powershell -encodedcommand RwBlAHQALQBBAHAAcAB4AFAAYQBjAGsAYQBnAGUAIAB8ACAAJQAgAHsAIABBAGQAZAAtAEEAcABwAHgAUABhAGMAawBhAGcAZQAgAC0ARABpAHMAYQBiAGwAZQBEAGUAdgBlAGwAbwBwAG0AZQBuAHQATQBvAGQAZQAgAC0AUgBlAGcAaQBzAHQAZQByACAAIgAkACgAJABfAC4ASQBuAHMAdABhAGwAbABMAG8AYwBhAHQAaQBvAG4AKQBcAEEAcABwAHgATQBhAG4AaQBmAGUAcwB0AC4AeABtAGwAIgAgAC0AdgBlAHIAYgBvAHMAZQAgAH0ADQAKAA==
You could execute the PowerShell script via a batch file.
Batch file:
set powerscriptPath=C:\Example.ps1
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%powerscriptPath%""' -Verb RunAs}"
This will bypass the execution policies on the computer allowing the script to run in Administrator mode too. NOTE: You will need to edit the powerscriptPath to point to your PowerShell script location, I just used C:\Example.ps1 as an example.
You will want to add Restart-Computer -Force to the end of your PowerShell script to restart the computer
Get-AppxPackage | % { Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppxManifest.xml" -verbose }
Restart-Computer -Force
Make a bat file which executes powershell with that file. Then add a shortcut to the bat file
I am really unsure why you would run a batch file just to call a powershell script! Talk about hokey approaches to a non-problem.
To call a powershell script is really no different than calling a batch script:
It's simply path to PowerShell, and the script path as a parameter:
"%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe" "C:\users\austinfrench\desktop\example.ps1"
You can also use the exact same format as the target for a desktop shortcut.

Capturing different streams in file

I'm trying to capture the Verbose, Error and other streams of a PowerShell script in a file. This to monitor the output of my script.
The following code works fine:
$LogFile = 'S:\ScriptLog.log'
$ScriptFile = 'S:\TestieScript.ps1'
powershell -Command $ScriptFile *>&1 > $LogFile
However, the moment I try to put a space in one of the file paths, it's no longer working. I tried a lot of things, like double quotes, single quotes, .. but no luck.
To illustrate, the following code doesn't work:
$LogFile = 'S:\ScriptLog.log'
$ScriptFile = 'S:\Testie Script.ps1'
powershell -Command $ScriptFile *>&1 > $LogFile
One person in this thread has the same issue.
Thank you for your help.
You're trying to run a file whose name contains a space as a command without proper quoting, so you're most likely getting an error like this in your log:
The term 'S:\Testie' is not recognized as the name of a cmdlet, function, script file, or operable program.
Either add proper quoting (and the call operator &, because your path is now a string):
powershell -Command "& '$ScriptFile'" *>&1 > $LogFile
or (better) use the -File parameter, as #CB. already suggested:
powershell -File $ScriptFile *>&1 > $LogFile
which has the additional advantage that the call will return the actual exit code of the script.
Edit: If you want to run the command as a scheduled task you'll need to use something like this:
powershell -Command "& 'S:\Testie Script.ps1' *>&1 > 'S:\ScriptLog.log'; exit $LASTEXITCODE"
because the redirection operators only work inside a PowerShell process.
try using -file parameter:
powershell -file $ScriptFile *>&1 > $LogFile

Simple PowerShell script refuses to write file when running from command line but works fine in ISE

So I have a pretty simple PowerShell script used to update a version number during a build.
$InputFile = 'D:\BuildAgent\work\c9716e2651305a2e\web\WEB-INF\classes\version.properties'
$OutputFile = $InputFile
Write-Host $('Updating "' + $OutputFile + '" to reflect version "2.10.0.51"')
(Get-Content $InputFile) |
Foreach-Object {
$_ -replace 'version.release\s*:.*','version.release: 2.10.0.51'
} |
Out-File $OutputFile
It works completely fine from within ISE, but when I run it from the command line with
powershell.exe -Command - < powershell2447437064467034590.ps1
or
powershell.exe -NoProfile -NonInteractive -ExecutionPolicy ByPass
-Command - < powershell2447437064467034590.ps1
All this does though is blank out the file. How can this work fine from ISE but not from the command line?
"Windows Powershell does not load commands from the current location by default. If you trust this command, instead type ".\test.ps1"
-Windows Powershell
powershell.exe .\powershell2447437064467034590.ps1
I tested with Powershell command prompt and classic command prompt
For more information
Run from an elevated script or command, Powershell 3.0
Update-Help
Get-help about_Command_Precedence
I figured it out. I used tabs in the file to pretty print and format the text. Apparently that breaks it. If I copy and past it into ISE, it silently converts all the tabs into spaces which is why it worked there. Pretty surprising that the tokenizer doesn't treat all whitespace the same.