PowerShell: Invoke-Parallel throwing error with "start-process" - shell

I'm using the Invoke-Parallel CmdLet that can be found at the link here. When I have some dummy code in my script block, it works correctly, but when I add a start-process... command it fails with an error saying
Get-RunspaceData : This command cannot be run due to the error: The system
cannot find the file specified.
At c:\crm\Interfaces\Batches\Invoke-Parallel.ps1:592 char:13
My script block looks like so. Long story short, I'm feeding file names into a block, and I am telling a 3rd party application to use that file as an input for a process. When the start-process line is removed, it works, with it, it fails.
$childFiles| Invoke-Parallel -ImportVariables {
$importformat = '-f ' + '"CU Imp P19 FI"'
$importfile = '-d ' + $_ + " " + $user + " " + $pass
Write-Host $importformat + " " + $_
start-process .\mmim.exe -ArgumentList $user, $pass, '-l:1',
$importformat, $importfile -Wait -NoNewWindow
return "blah"
}
Does anyone have any idea of what might be going on? My PowerShell version is the following
Major:5
Minor:0
Build: 10586
Revision: 117
Any help is greatly appreciated.

Each PowerShell runspace have its own current location. If Invoke-Parallel does not change current location of spawned runspaces to match current location of main runspace, then relative path .\mmim.exe may be resolved to entire different executable or not resolved at all and produce given error.

Related

Why does my powershell script not wrap my path correctly?

I've been tinkering with a Powershell script to automate some Streamlink stuff, and it's going okay, but I'm having an issue with the way streamlink is passing a command along, specifically with regards to wrapping the file path.
Here is the code (forgive the terrible formatting, I am not a pro, I'm only a beginner.)
Set-Location (Split-Path $MyInvocation.MyCommand.Path)
$scriptdir = Split-Path $MyInvocation.MyCommand.Path
$streamurl = Read-Host -Prompt 'Stream URL'
$channel = youtube-dl --cookies $scriptdir\Resources\cookies.txt --write-description --skip-download -o "%(uploader)s" --get-filename $streamurl
if (-not (Test-Path -LiteralPath $scriptdir\Recordings\$channel\)) {
try {
New-Item -Path $scriptdir\Recordings\$channel\ -ItemType Directory -ErrorAction Stop | Out-Null #-Force
}
catch {
Write-Error -Message "Unable to create directory '$channel'. Error was: $_" -ErrorAction Stop
}
"Successfully created directory '$channel'."
}
streamlink-auth $scriptdir\Resources\cookies.txt --retry-streams 10 -o "$scriptdir\Recordings\$channel\stream.mp4" $streamurl best
$scriptdir = Where the script is running
$streamurl = Self explanatory
$channel = the channel name of the stream
The issue I'm having is that when this command is passed along to streamlink, it seems to be wrapping wrong, as when streamlink tries to run the command, I get the following error:
streamlink args: --retry-streams 10 -o W:\recordings\Recordings\【LIVE】新宿 大ガード交差点 Tokyo Shinjuku Live Ch\stream.mp4 https://www.youtube.com/watch?v=RQA5RcIZlAM best
usage: streamlink [OPTIONS] <URL> [STREAM]
streamlink: error: unrecognized arguments: Shinjuku Live Ch\stream.mp4 https://www.youtube.com/watch?v=RQA5RcIZlAM best
Source:【LIVE】新宿 大ガード交差点 Tokyo Shinjuku Live Ch
I also tested this with a fully english channel (with the same script) and got the following:
streamlink args: --retry-streams 10 -o W:\recordings\Recordings\Watson Amelia Ch. hololive-EN\stream.mp4 https://www.youtube.com/watch?v=EeC6OHBRFTc best
usage: streamlink [OPTIONS] [STREAM]
streamlink: error: unrecognized arguments: hololive-EN\stream.mp4 https://www.youtube.com/watch?v=EeC6OHBRFTc best
Source: Watson Amelia Ch. hololive-EN
It seems that while the directory can be created fine, passing the variables into the streamlink command fails somewhere along the line. This can be worked around by simply moving into the channel directory with:
cd $scriptdir\Recordings\$channel\
and running the command without the extra variables in the path, but that's not the way I'd like to do it. I also want to add that youtube-dl has no issues with writing files to the directories created with this script as my postprocessing script writes files there without trouble, and powershell can still remove files there too.
I have asked the streamlink folks, and it's definitely not on their end, so obviously it's a powershell thing.
How do I solve this problem, because I am genuinely stumped now.
I'm dumb.
Turns out when you pass something into a command, you have to pass it with Double quote, then single quote.
So instead of:
streamlink-auth $scriptdir\Resources\cookies.txt --retry-streams 10 -o "$scriptdir\Recordings\$channel\stream.mp4" $streamurl best
It needs to be:
streamlink-auth $scriptdir\Resources\cookies.txt --retry-streams 10 -o "'$scriptdir\Recordings\$channel\stream.mp4'" $streamurl best
Sorry for being the dum.

Automating Windows Server Updates with PSwindowsUpdate module. Issue

I am trying to automate windows server update instllation for multiple servers. I have installed the module on all servers and also added the hostnames in winrm trust hosts.
All server hostnames are stored in txt file and are looped trought for each loop with different commands from teh PSwindowswupdate module.
$Hostname = Get-Content -Path "C:\TEMP\powershell_patching_script\module\hostnamesallwsus.txt"
Import-Module PSWindowsUpdate
foreach ($i in $Hostname) {
write-host $i
Get-WUHistory -ComputerName $i -last 3
}
Issue is that randomly the loop is failing for some hostnames, with error :
BGxxxxxxx01 #this is the hostname
Get-WUHistory : BGxxxxxxx01: Unknown failure.
At C:\TEMP\powershell_patching_script\Module\History.ps1:10 char:1
+ Get-WUHistory -ComputerName $i -last 3
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (:) [Get-WUHistory], Exception
+ FullyQualifiedErrorId : Unknown,PSWindowsUpdate.GetWUHistory
If I run the command with the hostname instead of variable it is failing again with the same error.
If I run the same but with $ in front of the hostname (even if such varaiable is not defined) the command works!ly appriciated
Get-WUHistory -ComputerName $BGxxxxxxx01 -last 3
Localy executed the commands are also working.
This issue seams to occure on random bases for multiple hostnames form my list.
I am unable find anything helpful regarding this error.
Any help will be highly appriciated!
Thanks in advance!
I found that Invoke-command works.
Just need to put the command in the script block of Invoke-command.

New-WebBinding: Cannot retrieve the dynamic parameters for the cmdlet

We're using Windows 2012 Server R2.
We're trying to automate the creation of LetsEncrypt certificates. We're using LetsEncrypt-Win-Simple (https://github.com/Lone-Coder/letsencrypt-win-simple).
Once the cert is created (via LetsEncrypt.exe) we have a .bat script that gets called (using the --script and --scriptparameters flags). This runs powershell.exe and tries to create the necessary IIS binding. The line in the .bat file is:
powershell.exe -file c:\temp\SSLIISBinding.ps1 %1 %2 %3 %4
The %1-4 are args passed in by LetsEncrypt. In the powershell script, the command we're trying to run is:
$iis_host_name = $args[0]
$iis_site_name = $args[1]
$certificate_hash = $args[2]
$certificate_store = $args[3]
"IIS Host Name: " + $iis_host_name
"IIS Site Name: " + $iis_site_name
"Certificate Hash: " + $certificate_hash
"Certificate Store: " + $certificate_store
$guid = [guid]::NewGuid().ToString("B")
netsh http add sslcert hostnameport="${iis_host_name}:443" certhash=$certificate_hash certstorename=$certificate_store appid="$guid"
New-WebBinding -name $iis_site_name -Protocol https -HostHeader $iis_host_name -Port 443 -SslFlags 1
The args are passed into the .bat fine, as we output them and they are showing correctly.
If we run the .bat file on its own, it works perfectly. If it gets called by LetsEncrypt.exe it fails, reporting the following issue:
New-WebBinding : Cannot retrieve the dynamic parameters for the cmdlet.
Retrieving the COM class factory for component with CLSID
{688EEEE5-6A7E-422F-B2E1-6AF00DC944A6} failed due to the following error:
80040154 Class not registered (Exception from HRESULT: 0x80040154
(REGDB_E_CLASSNOTREG)).
At C:\temp\SSLIISBinding.ps1:13 char:1
+ New-WebBinding -name $iis_site_name -Protocol https -HostHeader
$iis_host_name ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~
+ CategoryInfo : InvalidArgument: (:) [New-WebBinding], Parameter
BindingException
+ FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.IIs.Powe
rShell.Provider.NewWebBindingCommand
I've googled, some mentioning something about 32bit vs 64bit powershell, but I've tried using all the different powershell.exe available.
Anyone hit this issue, or know to resolve.
If we call .bat directly from command line it works fine, just as part of being called via LetsEncrypt.exe. A permission problem? Wrong powershell.exe?
That part of your question:
I've googled, some mentioning something about 32bit vs 64bit powershell
is already half of an answer. Some commands do not run properly if bitness of PowerShell process does not match bitness of operation system. So, you need to run powershell.exe, which located in this %windir%\System32\WindowsPowerShell\v1.0\ directory. But there is a little problem described in this documentation topic:
In most cases, whenever a 32-bit application attempts to access %windir%\System32, the access is redirected to %windir%\SysWOW64.
Thus, if 32-bit program on 64-bit OS invoke %windir%\System32\WindowsPowerShell\v1.0\powershell.exe, it will actually invoke 32-bit version of PowerShell from here %windir%\SysWOW64\WindowsPowerShell\v1.0\ instead of 64-bit one. To actually invoke 64-bit PowerShell from 32-bit application you need to use this trick:
32-bit applications can access the native system directory by substituting %windir%\Sysnative for %windir%\System32. WOW64 recognizes Sysnative as a special alias used to indicate that the file system should not redirect the access.
I've got the same error when running the following cmdlet:
PS> Remove-WebAppPool -Name 'Test'
Remove-WebAppPool : Cannot retrieve the dynamic parameters for the cmdlet. Retrieving the COM class factory for
component with CLSID {688EEEE5-6A7E-422F-B2E1-6AF00DC944A6} failed due to the following error: 80040154 Class not
registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
At line:1 char:1
+ Remove-WebAppPool -Name 'Test'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Remove-WebAppPool], ParameterBindingException
+ FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.IIs.PowerShell.Provider.RemoveAppPoolCommand
The reason was because I ran it using Windows PowerShell (x86) on my Windows 10 x64 machine.
When I tried the same but using Windows PowerShell, which is 64 bit version, it worked just fine.
I think your $guid is the issue. The GUID needs to be the GUID of the program to bind the cert to. For your example port 443 is only bound to a random GUID, and not your program's GUID. IIS and other apps have a static GUID that you will want to use. If the GUID for a powershell script then Get-host is the powershell host executing code so that's the GUID you need. It changes for every powershell session and the netsh binding needs to as well.
$appid = "appid={"+(get-host).InstanceId.guid+"}"
$certhash = ls Cert:\LocalMachine\my | where {$.EnhancedKeyUsageList -Match 'Server' -and $.subject -match (hostname)}|sort-object $_.NotAfter|select -expand Thumbprint -last 1
$cmdline='netsh http add sslcert ipport=0.0.0.0:443 certhash=' + $certhash + ' "' + $appid + '"'
netsh http delete sslcert ipport=0.0.0.0:443
Invoke-Expression $cmdline
A google search for "Cannot retrieve the dynamic parameters for the cmdlet" brought me here but my issue was using powershell from the command line, and the answer was to escape the double quotes on the command...
I've got a problem with the same error. This happens when i'm trying to Add-WebBinding to my IIS site remotely, using Invoke-Command from different agent machines at time.
It's worked for me, maybe it helps someone too:
$Mutex = New-Object -TypeName System.Threading.Mutex($false, "Global\Mutex")
if ($Mutex.WaitOne(300000)) {
#For example
#$Command = {
#New-WebBinding -name $iis_site_name -Protocol https -HostHeader
#$iis_host_name -Port 443 -SslFlags 1
#}
#Invoke-Command -Command $Command
} else {
Write-Warning "Timed out acquiring mutex!"
}
$Mutex.Dispose()

Scheduled Powershell Script wont run

I have a ps1 script like below, it filters files and outputs a formatted HTML file.
$a = "<style>"
$a = $a + "BODY{background-color:peachpuff;}"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$a = $a + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:thistle}"
$a = $a + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:PaleGoldenrod}"
$a = $a + "</style>"
$b = Get-Date -Format u
Get-ChildItem -Recurse K:\AppData\*.* -Filter *.CATPart | Where{$_.LastWriteTime -gt (Get-Date).AddDays(-6)} | sort LastWriteTime -descending | select name,LastWriteTime,Directory | convertto-html -head $a -body "<H2>CATIA PAST 7 DAYS -- $b </H2>" | out-file C:\aaa\catia_result.htm
I can run this script manually with no problem at all. but when I schedule it to run, it only gives me the formatted htm file without any filtered data in there. This is arguments I used in task scheduler:
powershell.exe -ExecutionPolicy Bypass -Command "C:\aaa\RLTP_HTML_FINAL.ps1"
I tried change executionpolicy to Unrestricted, it still wont work. The task history shows the task completed, but there is no data in the HTML file.
I also tried to use a batch file call up powershell to run the script, it is the same result that it only works with manual operation but task scheduler.
Most likely, when you schedule the script to execute, it may not have a mapping to the K:\ drive. Make sure that:
The K drive is mapped in the script, using the Get-PSDrive cmdlet
Your credentials that the scheduled task is set to use have access to the K:\ drive
Alternatively, you could simply specify a UNC path, instead of referencing the K:\ drive.
ps. Good job using -ExecutionPolicy Bypass. That helps to avoid any issues with the execution policy! :) It doesn't matter what the execution policy is set to, as long as you use that parameter.
If you want to capture any errors in your script, make this your last line:
Add-Content -Path $PSScriptRoot\error.log -Value $error;
You might see something about the K:\ drive missing.

How do I deduce path to Azure SDK csrun.exe conveniently?

I have some problems with Azure Compute Emulator not restarting properly. To resolve this I want to add csrun /devfabric:stop call to a pre-build step in Visual Studio solution.
The problem is csrun.exe is located in C:\Program Files\Windows Azure SDK\v1.4\bin on my machine and that path is not on the %PATH% directories list. I don't want to hardcode that path in my solution.
Is there some way to deduce the path like using some environment variable or something similar?
You can read the Azure SDK path from the registry by version. The last part of the path is the version ... Your code can either be set to a version or you can iterate over the v keys finding the latest. I would recommend having a constant for the version you support and as you take a new SDK as a pre-req.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\ServiceHosting\v1.4
There's an "InstallPath" key under those paths.
I had this same problem and I produced a PowerShell script that sets an environment variable with the path to the SDK bin folder. It will automatically search the registry and find the latest installed version. It also has a fallback to the alternate registry location, depending whether your script runs in 32bit or 64bit mode. Hope it helps!
Disclaimer: I removed some stuff from the script before posting it here and I didn't test it afterwards but I think it's not difficult to debug/adjust it to your needs.
#the script attempts to perform the following:
#1. look for the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\ServiceHosting" registry key
#2. if the above key is present then read the child keys and retrieve the largest version number
#3. from the largest version number key retrieve the "InstallPath" string value to determine the path of the latest Azure SDK installation
#4. add an environment variable called "AzureSDKBin" (if not already added) with the path to the "bin" folder of the latest Azure SDK installation
#define the name of the config variable
$azureSDKPathVariable = 'AzureSDKBin'
$azureRegistryKey = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\ServiceHosting'
$azureAlternateRegistryKey = 'HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\ServiceHosting' #this is in case the PowerShell runs in 32bit mode on a 64bit machine
$azureMatchedKey = ''
#check if the environment variable was already defined
if ([environment]::GetEnvironmentVariable($azureSDKPathVariable,"User").Length -eq 0) {
'Variable ' + $azureSDKPathVariable + ' is not defined, proceeding...'
#try reading the registry key
$keyExists = Get-Item -Path Registry::$azureRegistryKey -ErrorAction SilentlyContinue
$azureMatchedKey = $azureRegistryKey #make a note that we found this registry key
#stop if the key does not exist
if ($keyExists.Length -eq 0) {
'Could not find registry key in primary location: ' + $azureRegistryKey + ', attempting search in alternate location: ' + $azureAlternateRegistryKey
#search the alternate location
$keyExists = Get-Item -Path Registry::$azureAlternateRegistryKey -ErrorAction SilentlyContinue
$azureMatchedKey = $azureAlternateRegistryKey #make a note that we found this registry key
if ($keyExists.Length -eq 0) {
'Could not find registry key for determining Azure SDK installation: ' + $azureAlternateRegistryKey
'Script failed...'
exit 1
}
}
'Found Azure SDK registry key: ' + $azureMatchedKey
#logic for determining the install path of the latest Azure installation
#1. get all child keys of the matched key
#2. filter only keys that start with "v" (e.g. "v2.2", "v2.3")
#3. sort the results by the "PSChildName" property from which we removed the starting "v" (i.e. only the version number), descending so we get the latest on the first position
#4. only keep the first object
#5. read the value named "InstallPath" under this object
$installPath = (Get-ChildItem -Path Registry::$azureMatchedKey | Where-Object { $_.PSChildName.StartsWith("v") } | sort #{expression={ $_.PSChildName.TrimStart("v") }} -descending | Select-Object -first 1| Get-ItemProperty -name InstallPath).InstallPath
'Detected this Azure SDK installation path: "' + $installPath + '"'
#set the variable with the "bin" folder
[Environment]::SetEnvironmentVariable($azureSDKPathVariable, $installPath + 'bin\', "User")
'Assigned the value "' + [environment]::GetEnvironmentVariable($azureSDKPathVariable,"User") + '" to environment variable "' + $azureSDKPathVariable + '"'
}
else {
'Environment variable "' + $azureSDKPathVariable + '" is already defined and has a value of "' + [environment]::GetEnvironmentVariable($azureSDKPathVariable,"User") + '"'
}

Resources