Invoke-command path null? - powershell

I am working on this command to be able to view and edit a registry key remotely to a computer on a joined domain when I need to test something. In this case, I am looking at Excel's "vbawarninsg" key. This works just fine.
cls
$computername = Read-Host "Enter computer name..."
Invoke-Command -ComputerName $computername {Get-ItemProperty -Path 'REGISTRY::HKEY_USERS\xxxxxxx\Software\Policies\Microsoft\office\16.0\excel\security' } |
Select-Object PSComputerName, vbawarnings, PSParentPath | fl
$name = "vbawarnings"
The next part is to set a new value for the "vbawarnings" key using New-ItemProperty. When I assigned a variable for the -Path name it gives me an error "Cannot bind argument to parameter 'Path' because it is null."
This is the script that gives me an error
cls
$computername = Read-Host "Enter computer name..."
$registryPath = 'REGISTRY::HKEY_USERS\xxxxxxx\Software\Policies\Microsoft\office\16.0\excel\security'
Invoke-Command -ComputerName $computername {Get-ItemProperty -Path $registryPath } |
Select-Object PSComputerName, vbawarnings, PSParentPath | fl
$name = "vbawarnings"
$value = Read-Host "To modify...Enter a value"
New-ItemProperty -Path $registryPath -Name $name -Value $value `
-PropertyType DWORD -Force -Verbose | Out-Null
Any help is greatly appreciated!

In order to use a variable remotely (such as the case with Invoke-Command), you need to use the $using: variable scope:
Invoke-Command -ComputerName $cn {
Get-ItemProperty -Path $using:regPath
}
or pass it as a parameter:
Invoke-Command -ComputerName $cn {
param($path)
Get-ItemProperty -Path $path
} -ArgumentList '-path', $regPath
See this article

When you do Invoke-Command, that scriptblock gets sent to the remote server.
Invoke-Command -ComputerName $computername {Get-ItemProperty -Path $registryPath }
On the remote server, $registryPath is null even though you have it locally in your script.
So just hardcode the registry path instead:
Invoke-Command -ComputerName $computername {Get-ItemProperty -Path 'REGISTRY::HKEY_USERS\xxxxxxx\Software\Policies\Microsoft\office\16.0\excel\security' }

Related

Create registry keys to remote systems but with issue

To me if I am able to start a service on a remote machine when referencing them from a text file I should be able to manipulate registry keys. But I am not sure why I'm not able to do so. What am I doing wrong here? The StartWinRM works on remote systems and it enables it without any issues. When I ran this it creates the keys local to my machine. Why is this?
$Computers = Get-Content -Path 'D:\HomeFolder\MachineNames.txt'
foreach ($computer in $Computers) {
If (test-connection -ComputerName $computer -Count 1 -Quiet) {
Try {
Set-Service -ComputerName $computer -Name WinRM -StartupType Automatic -ErrorAction SilentlyContinue
StartWinRM -Computer $computer
Invoke-Command -ComputerName $computer -ScriptBlock {
New-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\" -Name Name1 -PropertyType String -Value 0 -Force
New-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\" -Name Name2 -PropertyType String -Value "Description" -Force
New-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\" -Name Name3 -PropertyType String -Value 1 -Force
} -Credential $cred
$status = "Success"
}
Catch {
$status = "Failed"
}
}
}
I get this error:
Connecting to remote server ComputerName failed with the following error message : The WinRM client cannot process the request. If the
authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine
must be added to the TrustedHosts configuration setting. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be
authenticated.
If anyone interested, I fixed my problem:
$computers = Get-Content "D:\HomeFolder\MachineNames.txt"
foreach ($computer in $computers){
if (Test-Path -Path "\\$computer\d$\<some_older_existing_folder>"){
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $computer )
$regKey= $reg.OpenSubKey("SOFTWARE\\WOW6432Node",$true)
$regKey.SetValue("Key1","0",[Microsoft.Win32.RegistryValueKind]::String)
$regKey.SetValue("Key2","1",[Microsoft.Win32.RegistryValueKind]::String)
$regKey.SetValue("Key3","1",[Microsoft.Win32.RegistryValueKind]::String)
}
Write-Host "Keys already exist" -ForegroundColor Yellow
}

Updating the current logged on user, HKEY_USERS hive registry, with system account

This script makes changes to all users' profiles.
Here is the script:
# Get each user profile SID and Path to the profile
$UserProfiles = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*" |
Where {$_.PSChildName -match "S-1-5-21-(\d+-?){4}$" } |
Select-Object #{Name="SID"; Expression={$_.PSChildName}}, #{Name="UserHive";Expression={"$($_.ProfileImagePath)\NTuser.dat"}}
# Loop through each profile on the machine
foreach ($UserProfile in $UserProfiles) {
# Load User ntuser.dat if it's not already loaded
if (($ProfileWasLoaded = Test-Path Registry::HKEY_USERS\$($UserProfile.SID)) -eq $false) {
Start-Process -FilePath "CMD.EXE" -ArgumentList "/C REG.EXE LOAD HKU\$($UserProfile.SID) $($UserProfile.UserHive)" -Wait -WindowStyle Hidden
}
}
# Manipulate the registry
$key = "Registry::HKEY_USERS\$($UserProfile.SID)\Software\SomeArchaicSoftware\Configuration"
New-Item -Path $key -Force | Out-Null
New-ItemProperty -Path $key -Name "LoginURL" -Value "https://www.myCompany.local" -PropertyType String -Force | Out-Null
New-ItemProperty -Path $key -Name "DisplayWelcome" -Value 0x00000001 -PropertyType DWORD -Force | Out-Null
$key = "$key\UserInfo"
New-Item -Path $key -Force | Out-Null
New-ItemProperty -Path $key -Name "LoginName" -Value "$($ENV:USERDOMAIN)\$($ENV:USERNAME)" -PropertyType STRING -Force | Out-Null
# Unload NTuser.dat
if ($ProfileWasLoaded -eq $false) {
[GC]::Collect()
Start-Sleep 1
Start-Process -FilePath "CMD.EXE" -ArgumentList "/C REG.EXE UNLOAD HKU\$($UserProfile.SID)" -Wait -WindowStyle Hidden| Out-Null
}
I only need changes to the current logged on user HKEY_USERS hive.
Can anyone help me change the script so it's only the current logged in user who gets the changes?
You can determine the SID of a currently logged-in user via WMI. Check for the owner of a running explorer.exe process, then resolve the account name to its SID:
$user = (Get-WmiObject Win32_Process -Filter "Name='explorer.exe'").GetOwner()
$fltr = "Name='{0}' AND Domain='{1}'" -f $user.User, $user.Domain
$sid = (Get-WmiObject Win32_UserAccount -Filter $fltr).SID
Still, I think a logon script would be a better place for changes to a user's registry settings.

Powershell : build dynamic invoke-command for SSL and NOSSL connections

We have servers that are setup with type type of WinRM connection Secure and normal.
I need to establish a remote powershell connection with or without the -usessl switch.
I wanted to avoid having two identical script blocks with the only difference being having or not having the -usessl switch.
I tried using argument splatting but can't deal with the -usessl switch, also tried using invoke-expression but that broke the ability to retrieve data; Hash value from the remote job.
Any suggestions ?
if (test-wsman -ErrorAction SilentlyContinue -cn "$($Cmpname.name).sample.com" –UseSSL ) {
Write-host $Cmpname.name,WSMan Connected SSL
$strWSMAN = "SSL"
$strRemoteHash = invoke-command -cn "$($Cmpname.name).sample.com" -usessl -scriptblock {
Write-host "calculating Hash values"
$strLocalhash = Get-ChildItem -Path "c:\Windows\ccmcache" -Filter "Windows10.0-KB4041691-x64.cab" -Recurse -ErrorAction SilentlyContinue -Force | Get-FileHash -Algorithm sha1
New-Object pscustomobject -Property #{
RemotePath = $strLocalhash.path
RemoteHash = $strLocalhash.hash
}
}
} else {
$strWSMAN = "NoSSL"
$strRemoteHash = invoke-command -cn "$($Cmpname.name).sample.com" -scriptblock {
Write-host "calculating Hash values"
$strLocalhash = Get-ChildItem -Path "c:\Windows\ccmcache" -Filter "Windows10.0-KB4041691-x64.cab" -Recurse -ErrorAction SilentlyContinue -Force | Get-FileHash -Algorithm sha1
New-Object pscustomobject -Property #{
RemotePath = $strLocalhash.path
RemoteHash = $strLocalhash.hash
}
}
}
As mjolinor said, you need to provide a boolean value for a switch parameter when splatting:
$ParameterValues = #{
ComputerName = "$($Cmpname.name).sample.com"
UseSSL = [bool](Test-WSMan -ErrorAction SilentlyContinue -ComputerName "$($Cmpname.name).sample.com" –UseSSL)
ScriptBlock = {
Write-host "calculating Hash values"
$strLocalhash = Get-ChildItem -Path "c:\Windows\ccmcache" -Filter "Windows10.0-KB4041691-x64.cab" -Recurse -ErrorAction SilentlyContinue -Force | Get-FileHash -Algorithm sha1
New-Object pscustomobject -Property #{
RemotePath = $strLocalhash.path
RemoteHash = $strLocalhash.hash
}
}
}
Invoke-Command #ParameterValues

Pulling value of the function in to a Variable

The function Get-loggedOnUser shows 3 items, and the code can be found here LoggedInUser
Computer Name
Logged on user
Sid of the user
I would like to be able to take the results of the LoggedOnUser function (only need the logged in user) and pipe the results to the variable below called Username.
Import-Module ActiveDirectory
function Get-LoggedOnUser
$credential = Get-Credential
$computers = "MachineName"
foreach ($machine in $computers)
{
Get-LoggedOnUser -ComputerName $machine
$username = $_.LoggedOn
$sid = ((get-aduser $username).SID).Value
Invoke-Command -ComputerName $machine -Credential $credential -ScriptBlock { New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS; New-ItemProperty -Path "HKU:\$($args[0])\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -PropertyType String -Name AutoConfigURL -Value "http://proxy.domain.com/proxies/proxy.pac" -Force } -argumentlist $sid
}
$loggedOn=Get-LoggedOnUser -computername $machine
$username = $loggedOn.LoggedOn
$sid = $loggedOn.Sid #as this is already returned from above funtion
$sid = ((get-aduser $username).sid).value #if you want to still get it from AD

Run Reg.exe in PS script will not work on remote machine ERROR: Invalid key name

If I run the command from with in the script
reg add "HKU\$sid\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v AutoConfigURL /t REG_SZ /d "http://mydomaon.com/proxies/proxy.pac" /f
it works perfectly, because it is local.
When I try to change the same registry key on a remote machine and changing the line to this
reg add "\\$machinename\HKU\$sid\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v AutoConfigURL /t REG_SZ /d "http://mydomaon.com/proxies/proxy.pac" /f
It fails with
ERROR: reg : ERROR: Invalid key name. + CategoryInfo : NotSpecified: (ERROR: Invalid key name.:String) [], RemoteException
ERROR: + FullyQualifiedErrorId : NativeCommandError
ForEach ($machine in $machines)
{
$User = New-Object System.Security.Principal.NTAccount($env:UserName)
$sid = $User.Translate([System.Security.Principal.SecurityIdentifier]).value
New-PSDrive HKU Registry HKEY_USERS
# Get-Item "HKU:\${sid}"
Set-Location HKU:\
cd HKU:\
reg add '$SID\Software\Microsoft\Windows\CurrentVersion\Internet Settings' /v AutoConfigURL /t REG_SZ /d 'http://proxy.domain.com/proxies/proxy.pac' /f
}
Here is an approach that should work (also better then using reg.exe):
Local Computer:
New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS
New-ItemProperty -Path "HKU:\$sid\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -Name AutoConfigURL -PropertyType String -value "http://proy.domain.com/proxies/proxy.pac" -Force
Remote Computer:
$credential = Get-Credential
Foreach($machine in $computers){
Invoke-Command -ComputerName $machine -Credential $credential -ScriptBlock {$temp = $args[0];
New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS; New-ItemProperty -Path "HKU:\$temp\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -PropertyType String -Name AutoConfigURL -Value "http://proy.domain.com/proxies/proxy.pac" -Force} -argumentlist $sid
}
Also here are the Type mappings for New-Itemproperty´s -PropertyType:
REG_SZ = String
REG_DWORD = DWord
REG_QWORD = QWord
REG_MULTI_SZ = MultiString
REG_BINARY = Binary
So after a few problems here is an updated version which should do everything including determining the local users sid by username (added linebreaks for readability, remove them when you copy)
$username = "yourUsername"
$credential = Get-Credential
Foreach($machine in $computers){
Invoke-Command -ComputerName $machine -Credential $credential -ScriptBlock {$temp = $args[0];
New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS;
$sid = (Get-CimInstance -Class Win32_UserAccount | where {$_.Name -eq $temp} | select SID).SID;
New-ItemProperty -Path "HKU:\$sid\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -PropertyType String -Name AutoConfigURL -Value "http://proy.domain.com/proxies/proxy.pac" -Force
} -argumentlist $username
}
After furhter discussion with Kent and clearly specifying the environment (Windows XP domain-joined machines, user logged on) the script has to run in this was what we came up with:
(this works only if the user is logged in on XP)
$sid = ((get-aduser $username).SID).Value
$credential = Get-Credential
Foreach($machine in $computers){
Invoke-Command -ComputerName $machine -Credential $credential -ScriptBlock {New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS; New-ItemProperty -Path "HKU:\$($args[0])\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -PropertyType String -Name AutoConfigURL -Value "http://proxy.domain.com/proxies/proxy.pac" -Force} -argumentlist $sid
}
This is how it would work if the user was not currently log in on XP:
$defaultprofilepath = "C:\documents and settings"
$username = "username"
$profilepath = "$defaultprofilepath\$username"
$credential = Get-Credential
Foreach($machine in $computers){
Invoke-Command -ComputerName $machine -Credential $credential -ScriptBlock {reg load "HKU\TempDir" "$($args[0])\NTUSER.DAT"; reg add "HKU\TempDir\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v AutoConfigURL /t REG_SZ /d 'http://proxy.domain.com/proxies/proxy.pac' /f; reg unload "HKU\TempDir"} -argumentlist $profilepath
}
P.S. If someone has a clue if there is a powershell cmdlet to load registry hives please comment, i could not find one on the fly
Using WMI you could do it like this:
$computers = ...
$user = New-Object Security.Principal.NTAccount($env:USERNAME)
$sid = $user.Translate([Security.Principal.SecurityIdentifier]).Value
$subkey = "$sid\Software\Microsoft\Windows\CurrentVersion\Internet Settings"
$url = 'http://proy.domain.com/proxies/proxy.pac'
$HKU = [convert]::ToUInt32('0x80000003', 16)
$credential = Get-Credential
foreach ($machine in $computers) {
$reg = Get-WmiObject -List -Namespace 'root/default' -Computer $machine `
-Credential $credential | ? { $_.Name -eq 'StdRegProv' }
$reg.SetStringValue($HKU, $subkey, 'AutoConfigURL', $url) | Out-Null
}
Note, however, that for remote WMI access you need administrative privileges on the remote host. Without admin privileges you could use Invoke-Command for local WMI access on the remote host:
foreach ($machine in $computers) {
Invoke-Command -Computer $machine -Credential $credential -ScriptBlock {
$reg = Get-WmiObject -List -Namespace 'root/default' |
? { $_.Name -eq 'StdRegProv' }
$reg.SetStringValue($args[0], $args[1], 'AutoConfigURL', $args[2]) | Out-Null
} -ArgumentList $HKU, $subkey, $url
}
Note also that only domain accounts have the same SID accross hosts. For local accounts you need to determine the SID of the account on the remote host:
foreach ($machine in $computers) {
Invoke-Command -Computer $machine -Credential $credential -ScriptBlock {
$user = New-Object Security.Principal.NTAccount($args[0])
$sid = $user.Translate([Security.Principal.SecurityIdentifier]).Value
$subkey = "$sid\Software\Microsoft\Windows\CurrentVersion\Internet Settings"
$reg = Get-WmiObject -List -Namespace 'root/default' |
? { $_.Name -eq 'StdRegProv' }
$reg.SetStringValue($args[1], $subkey, 'AutoConfigURL', $args[2]) | Out-Null
} -ArgumentList $env:USERNAME, $HKU, $url
}
If the account in question is a domain account, you may want to provide the domain name when creating the NTAccount object, because otherwise you'd be getting the wrong account/SID when there's a local account with the same name as the domain account.
$user = New-Object Security.Principal.NTAccount($env:USERDOMAIN, $env:USERNAME)
$sid = $user.Translate([Security.Principal.SecurityIdentifier]).Value