Escapaing double-quotes problem in mixed batch/powershell script running - windows

Here's what I'm trying to do:
#ECHO OFF
CALL powershell -ExecutionPolicy RemoteSigned -Command "$sh = new-object -com 'Shell.Application'; $sh.ShellExecute('powershell', '-NoExit -Command "$path = """HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}""";echo $path"', '', 'runas')"
PAUSE
Basically, I want to have a batch file that I can double-click, which will run a powershell script that calls another powershell script but asks for admin privileges and runs that command as admin.
I'm having problems though, with the double-quotes I think... I've tried many things but can't seem to fix it, here's the powershell error message:
Bad numeric constant: 4D.
At line:1 char:57
+ $path = HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D <<<< 36E972-E325-11C
E-BFC1-08002BE10318};echo $path
+ CategoryInfo : ParserError: (4D:String) [], ParentContainsError
RecordException
+ FullyQualifiedErrorId : BadNumericConstant
PS C:\Windows\system32>

I would use the built-in command Start-Process rather than creating a shell object e.g.:
CALL powershell -ExecutionPolicy RemoteSigned -NoProfile -Command "& {Start-Process PowerShell -Verb runas -Arg '-NoExit -Command & {$path=''foo'';$path}'}"
For anything of significance the quoting is going to be annoying. Can you put the final script in a file and execute the script file using the -File parameter on PowerShell.exe?

I solved it, here's the long batch one-liner for my real problem, so people can see a real example:
CALL powershell -ExecutionPolicy RemoteSigned -Command "$sh = new-object -com 'Shell.Application'; $sh.ShellExecute('powershell', '-NoExit -Command ""$path = ''HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}''; Get-Childitem $path -ErrorAction SilentlyContinue | Where { (Get-ItemProperty $_.PSPath DriverDesc) -Match ''VMnet'' } | Foreach { New-ItemProperty -ErrorAction SilentlyContinue $_.PSPath -Name ''*NdisDeviceType'' -Value ''1'' -PropertyType DWord }; netsh interface set interface name=''VMware Network Adapter VMnet1'' admin=DISABLED; netsh interface set interface name=''VMware Network Adapter VMnet1'' admin=ENABLED; netsh interface set interface name=''VMware Network Adapter VMnet8'' admin=DISABLED; netsh interface set interface name=''VMware Network Adapter VMnet8'' admin=ENABLED""', '', 'runas')"
P.S: In case anyone's wondering what it's for... I run this every time I install/update VMware Workstation to hide the virtual network adapters from appearing on the Network and Sharing Center in Windows Vista/7.

Related

Registry value not change

so i just start learn about powershell script
my objective is to uncheck this one
system properties
so i create powershell script to run the file.reg
this is my test1.ps1
$username = "desktop-2ussd\viola"
$password = "qwerty"
$AdminCred = New-Object System.Management.Automation.PSCredential -ArgumentList #($username,
(ConvertTo-SecureString -String $password -AsPlainText -Force))
$regFile = ".\file.reg"
$regArg1 = "import $regFile"
Start-Process reg.exe -ArgumentList $regArg1 -Credential $AdminCred
and this is my file.reg
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server]
"fDenyTSConnections"=dword:00000000
"updateRDStatus"=dword:00000001
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp]
"UserAuthentication"=dword:00000000
after that i run the script like this
powershell -ExecutionPolicy Bypass -File .\test1.ps1
there is no error output but the checkbox is still checked
please help me
Currently you wont recognize if something goes wrong as you do not get a return code. In case of start-process you would need to specify the parameters:
-wait
-passthru
to get the return code.
But you can directly write to the registry from PowerShell instead of using reg.exe. - e.g.:
set-itemproperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -name fDenyTSConnections -value 0
The above mentioned registry change gets effective immediately without restarting the related service.
Based on your comment you missed to specify the computer where the command should run. Also make use of $using to access variables of the caller machine from the remote machine e.g.:
$code = {
$newValue = $using:value
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "UserAuthentication" -Value $newValue
}
$value = 0
invoke-command -computername [TargetComputerName] -credential $cred -scriptblock $code
In your example you did pass the value to the paramter -value as $args[0] - this only works if you specify the paramter -argumentlist of the invoke-command cmdlet. But I would advise to use $using as outlined in my example.

Copying to network share does not work with powershell Copy-Item

I try to copy with powershell in Windows 10 a install folder to a network share (samba on linux)
powershell.exe -Version 5 -Command "Copy-Item -Force -Path 'install' -Recurse -Destination
'\\\\intranet.server.com\install'" -Container
It always fails, even if files can be written in the explorere and I have access, with a lot of these messages:
Copy-Item : The target file "\\intranet.server.com\install\a\b\c" is a directory, not a file.
Deleteing any existing folder works well with Remove-Item '\\intranet.server.com\install\*' -Force -Recurse
I dont know if it has to do with credentials, how can I debug this?
The problem lies in the SMB2 Redirector Cache of Windows:
We need to disable the annoying cache temporarily by doing:
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Start-Process PowerShell -Verb RunAs "-NoProfile -ExecutionPolicy Bypass -Command `"cd '$pwd'; & '$PSCommandPath';`"";
exit;
}
$c = Get-SmbClientConfiguration
$dc=$c.DirectoryCacheLifetime
$fc=$c.FileInfoCacheLifetime
$fnc=$c.FileNotFoundCacheLifetime
Set-SmbClientConfiguration -Force -DirectoryCacheLifetime 0 -FileInfoCacheLifetime 0 -FileNotFoundCacheLifetime 0
// Normal kopieren hier !!!!
Set-SmbClientConfiguration -Force -DirectoryCacheLifetime $dc -FileInfoCacheLifetime $fc -FileNotFoundCacheLifetime $fnc
This is a pain in the ass, but solves the issues.

Installing windows updates remotely, using PowerShell. (some PowerCLI)

I am having a problem with a script for remote windows updates.
My goal: update all the windows servers, without using WSUS or SCCM, from PowerShell without having to log into all of them and start the update process.
For my script I am using powercli and PSWindowsUpdate modules.
For test purposes I am using a single host in the $hostname variable.
So here is what I have.
$Cred = Get-Credential
Import-Module PSWindowsUpdate
Invoke-Command -ComputerName $HostName {Import-Module PSWindowsUpdate} -Verbose -Credential $Cred
Invoke-Command -ComputerName $HostName {Add-WUServiceManager -ServiceID 7971f918-a847-4430-9279-4a52d1efe18d -Confirm:$false} -Verbose -Credential $Cred
Invoke-Command -ComputerName $HostName -ScriptBlock {Get-WUInstall -MicrosoftUpdate -IgnoreUserInput -AcceptAll -AutoReboot -Confirm:$FALSE -Verbose | Out-File C:\Setup\PSWindowsUpdate.log } -credential $cred
Get-Content \\$HostName\c$\Setup\PSWindowsUpdate.log
After running the script everything works from the shut down, snapshot, power on but I can't install any updates.
I am getting the error below :
WARNING: To perform some operations you must run an elevated Windows PowerShell console.
WARNING: Can't access to machine "hostName". Try use: winrm qc
As I was searching I see that I can not make any elevation in PowerShell itself and I am reading some posts about running a CMD bat to start PowerShell in elevated rights.
It is not a problem for me to copy to every windows server the update script and have a batch to run it in elevated rights but is this the only way?
I will do the batch solution to see what I can achieve but is there any other solution in the problem I am facing?
Has anyone tried that out?
Thank you very much for your time!
I am also open for any comment on my code or fixes!
If Admin right, elevated prompts are your issue, the following may help you.
PS Code to check if running as an Administrator. If not, It will relaunch as an Administrator.
If (-Not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$Arguments = "& '" + $MyInvocation.MyCommand.Definition + "'"
Start-Process Powershell -Verb RunAs -ArgumentList $Arguments
Break
}
You will also likely need to modify ConsentPromptBehaviorAdmin at HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ in the Registry
-
Here is a simple BAT file that will will get around the PS Execution policy and then launch the PS Script (Assuming they are named the same).
REG ADD "HKLM\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" /T REG_SZ /V ExecutionPolicy /D Unrestricted /F
Start PowerShell.exe -Command "& '%~dpn0.ps1'"
Add the end of your PS Script you can use Set-ExecutionPolicy -ExecutionPolicy Restricted -Scope LocalMachine -Force to set the execution policy back to restricted or what you have ti set to.
I ran into this issue when trying to import modules on remote machines using Invoke-Command. There's a security function that doesn't allow you to do it. You might try running the following to see if you're more successful at importing a new module.
$myPsSession = New-PSSession -ComputerName $HostName
Import-Module -Name PSWindowsUpdate -PSSession $myPsSession
I went with the task scheduler solution as it seems easier.
I created a script named WUpdates.ps1
$Timestamp=((Get-Date).ToString('dd_MM_yyyy_HH_mm'))
Import-Module PSWindowsUpdate -Verbose
Add-WUServiceManager -ServiceID 7971f918-a847-4430-9279-4a52d1efe18d -Confirm:$false -Verbose
Get-WUInstall -MicrosoftUpdate -IgnoreUserInput -AcceptAll -AutoReboot -Confirm:$FALSE -Verbose | Format-Table -AutoSize | Out-File C:\Setup\WUpdates\PSWindowsUpdate_$Timestamp.log
and a script to create a schedule task named WinUpdateScheduler.ps1
Unregister-ScheduledTask -TaskName "WindowsUpdates" -Confirm:$False
$Action = New-ScheduledTaskAction -Execute C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Argument '-f "C:\Setup\WUpdates\WUpdates.ps1"'
$Trigger = New-ScheduledTaskTrigger -Once -At (get-date).AddSeconds(30)
Register-ScheduledTask -Action $Action -Trigger $Trigger -RunLevel Highest -User system -TaskName "WindowsUpdates" -Description "Running Windows updates."
then in the main script I call the schedule script and have my updates installed.
Invoke-Command -ComputerName $HostName { c:\setup\WUpdates\WinUpdateScheduler.ps1 } -Credential $Cred
If anyone want the full script I can provide it.
As of the Nixphoe and Backin answers, I will check them and I will come back later to comment on them.
Thank you very much for your time.
Invoke-WUInstall -ComputerName Test-1 -Script { ipmo PSWindowsUpdate; Get-WUInstall -AcceptAll | Out-File C:\PSWindowsUpdate.log }
-Confirm:$false –Verbose
https://4sysops.com/archives/install-windows-updates-remotely-with-the-powershell/

Remote Installation of Python using Powershell fails using Invoke-Command

I am trying to use this script to install Python on the remote computer. If I run this file directly on the server. This is the Python_Pip_Proxy_PyWinAuto.ps1 file. It works.
Set-ExecutionPolicy -ExecutionPolicy Unrestricted
Write-Host("Hi")
$installer="C:\temp\python-3.6.2.exe"
& $installer /quiet PrependPath=1 InstallAllUsers=1 TargetDir="C:\Python36"
However if I run the Invoke-Command using the following script to run this remotely on the same server, It print's the Hi message so I know that the file is running but Python doesn't get installed.
# Getting the list of servers from a .txt file to an array #
$SRVListFile = "C:\Scripts\ServerList.txt"
$SRVList = Get-Content $SRVListFile -ErrorAction SilentlyContinue
# Copying the .exe file from a shared location to each server in the array #
# Invoking the .ps1 file which runs natively on each server #
Foreach($computer in $SRVList) {
Get-Service remoteregistry -ComputerName $computer | start-service
Copy-item -Path "E:\Software\python-3.6.2.exe" -Destination \\$computer\c$\temp -Recurse
Copy-item -Path "C:\My Files\Work\Episode 003 - MongoDB Back Up\Python_GUI.py" -Destination \\$computer\c$\temp -Recurse
Invoke-Command -ComputerName $computer -FilePath "C:\My Files\Work\Episode 003 - MongoDB Back Up\Python_Pip_Proxy_PyWinAuto.ps1"
}
What is going wrong. What should I change the code to?
Try using the -scriptblock {Your command here} parameter to execute the command inside the scriptblock parenthesis on the remote computer.
Perhaps you can do it like
$Scriptblock = {
PowerShell -file "C:\My Files\Work\Episode 003 - MongoDB Back Up\Python_Pip_Proxy_PyWinAuto.ps1"
"This is Working" | out-file "C:\Hi.txt"
}
Invoke-Command -ComputerName $computer -Scriptblock $Scriptblock
You might want to remove the Write-Host "Hi" part because that gives the script an interactive nature. If you want to check for execution on remote computer, you can use out-file cmdlet to create a file on the remote computer as an indication.

Suppressing The Command Window Opening When Using Start-Process

I'm trying to find a way to get PowerShell not to spawn a command window when running an executable using Start-Process.
If I call the executable directly within the script (e.g. .\program.exe) then the program runs (with its arguments) and the output is returned to the PowerShell window.
If I use Start-Process the program spawns a command window where the program runs and returns it's output.
If I try and use the -NoNewWindow switch of Start-Process the script then errors out saying it can't find the exe file.
I would prefer to use Start-Process to have access to the -Wait switch, as the programs and configurations the script makes can take some time individually to finish, and I don't want later commands starting up.
This code runs the executable in a separate command window:
Start-Process DeploymentServer.UI.CommandLine.exe -ArgumentList "download --autoDownloadOn --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime" -Wait
This code runs the exe within the PowerShell console:
.\DeploymentServer.UI.CommandLine.exe download --autoDownloadOn --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime
If I add the -NoNewWindow to the Start-Process code
Start-Process DeploymentServer.UI.CommandLine.exe -ArgumentList "download --autoDownloadOn --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime" -Wait -NoNewWindow
I get the following error:
Start-Process : This command cannot be executed due to the error: The system
cannot find the file specifie
At C:\Temp\SOLUS3Installv1.3.ps1:398 char:22
+ Start-Process <<<< DeploymentServer.UI.CommandLine.exe -ArgumentList "download --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime" -Wait -NoNewWindow
+ CategoryInfo : InvalidOperation: (:) [Start-Process], InvalidOperationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand
You should prefix the executable name with the current directory when you use the -NoNewWindow switch:
Start-Process .\DeploymentServer.UI.CommandLine.exe -ArgumentList "download --autoDownloadOn --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime" -Wait -NoNewWindow
Background information:
The first thing Start-Process tries to do is to resolve the value of the -FilePath parameter by PowerShell rules. If it succeeds, it replaces the value value passed with the full path to the command. If not, it leaves the value untouched.
In the Windows API there are two ways to start a new process: CreateProcess and ShellExecute. ShellExecute is the default, but if you use a cmdlet parameter that requires CreateProcess (for example, -NoNewWindow), then CreateProcess will be used. The difference between them, which matters for this question, is that when looking for a command to execute, CreateProcess uses the current process' working directory, while ShellExecute uses the specified working directory (which Start-Process by default passes based on the current filesystem-provider location, unless explicitly specified via -WorkingDirectory).
PS Test:\> 1..3 |
>> ForEach-Object {
>> New-Item -Path $_ -ItemType Directory | Out-Null
>> Add-Type -TypeDefinition #"
>> static class Test {
>> static void Main(){
>> System.Console.WriteLine($_);
>> System.Console.ReadKey(true);
>> }
>> }
>> "# -OutputAssembly $_\Test.exe
>> }
PS Test:\> [IO.Directory]::SetCurrentDirectory((Convert-Path 2))
PS Test:\> Set-Location 1
PS Test:\1> Start-Process -FilePath Test -WorkingDirectory ..\3 -Wait # Use ShellExecute. Print 3 in new windows.
PS Test:\1> Start-Process -FilePath .\Test -WorkingDirectory ..\3 -Wait # Use ShellExecute. Print 1 in new windows.
PS Test:\1> Start-Process -FilePath Test -WorkingDirectory ..\3 -Wait -NoNewWindow # Use CreateProcess.
2
PS Test:\1> Start-Process -FilePath .\Test -WorkingDirectory ..\3 -Wait -NoNewWindow # Use CreateProcess.
1
PowerShell does not update the current process' working directory when you change the current location for the FileSystem provider, so the directories can differ.
When you type:
Start-Process DeploymentServer.UI.CommandLine.exe -Wait -NoNewWindow
Start-Process cannot resolve DeploymentServer.UI.CommandLine.exe by PowerShell rules, since it does not look in the current FileSystem location by default. And it uses CreateProcess, since you specify -NoNewWindow switch. So, it ends up looking for DeploymentServer.UI.CommandLine.exe in the current process' working directory, which does not contains this file and thus causes an error.

Resources