Change value in registry on multiple servers using credentials - powershell

Looking to enable reg key on multiple remote machines.
Attempt 1:
$Servers = Get-Content "C:\PowerShell\TestServers.txt"
$Path = "HKLM:\SYSTEM\CurrentControlSet\Services\"
$Property = "*REG_WORD NAME*"
$Value = "1"
Foreach ($Server in $Servers)
{
Set-ItemProperty -Path $Path -Name $Property -Value $Value
}
Error: Set-ItemProperty : Requested registry access is not allowed.
NOTE: checked effective access, the account being used has FULLControl over the specific hive
Attempt 2:
Created a function, added the get-credential cmdlet
function Set-RemoteRegistryValue {
param (
$ComputerName,
$Path,
$Name,
$Value,
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty
)
$null = Invoke-Command -ComputerName $ComputerName -ScriptBlock {
Set-ItemProperty -Path $using:Path -Name $using:Name -Value $using:Value
} -Credential $Credential
}
I am now able to call the function and set the reg key value as desired, but only one machine at a time:
$remoteKeyParams = #{
ComputerName ='name'
Path = "HKLM:\SYSTEM\CurrentControlSet\Services\"
Name = "*keyname*"
Value = "1"
}
Set-RemoteRegistryValue #remoteKeyParams -Credential (Get-Credential)
I have tried putting multiple machines in as a string, and a text file:
[string]$ComputerName = "name","name","name"
ComputerName = c:\temp\testservers.txt
Am I doing something very wrong here?

Confirm you have one server per line and then this is how you should write it.
$Servers = Get-Content "C:\PowerShell\TestServers.txt"
$Path = "HKLM:\SYSTEM\CurrentControlSet\Services\"
$Property = "*REG_WORD NAME*"
$Value = "1"
Invoke-Command -ComputerName $servers -ScriptBlock {
Set-ItemProperty -Path $using:Path -Name $using:Name -Value $using:Value
} -Credential $Credential
When you pass all the server names to Invoke-Command it will run them all asynchronously (up to 32 by default on 5.1)

Related

Output from invoke-command not returning

I have the following scriptblock:
$scriptblock = {
$regpath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
$securitylayer = Get-ItemProperty -Path $regpath -Name SecurityLayer -ErrorAction SilentlyContinue
If (!($securitylayer) -or ($securitylayer.securitylayer -ne '0')) {
Write-Host -ForegroundColor Yellow "Regkey not present or value not 0. Creating/setting to 0"
#Commented out for testing purposes
#Set-ItemProperty -Path $regpath -Name SecurityLayer -Value 0
}
Else {Write-Host -ForegroundColor green "Regkey present and set to 0. Skipping."}
}
that I pass to a PSSession on a remote machine running Server 2003 SP2:
$computername = 'computer'
$pssession = New-PSSession -ComputerName $computername -Name $computername
Invoke-Command -Session $pssession -ScriptBlock {$scriptblock}
But i don't see any output.
I've also tried
write-output
but still don't see any output.
I saw another post that suggested doing the following, however nothing was returned:
$scriptblock = {
$regpath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
$securitylayer = Get-ItemProperty -Path $regpath -Name SecurityLayer -ErrorAction SilentlyContinue
If (!($securitylayer) -or ($securitylayer.securitylayer -ne '0')) {
new-object pscustomobject –property #{Result = "Regkey not present or value not 0. Creating/setting to 0"}
#Commented out for testing purposes
#Set-ItemProperty -Path $regpath -Name SecurityLayer -Value 0
}
Else {new-object pscustomobject –property #{Result = "Regkey present and set to 0. Skipping."}}
}
$computername = 'computer'
$pssession = New-PSSession -ComputerName $computername -Name $computername
$results = Invoke-Command -Session $pssession -ScriptBlock {$scriptblock}
$results.result
Code runs as expected when run on machine.
You are wrapping your scriptblock in a scriptblock, so it's not actually executing the script block. Just remove the {} from around the {$scriptblock}:
Invoke-Command -Session $pssession -ScriptBlock $scriptblock

How to pass param value to the Invoke-Command cmdlet?

I wrote a simple powershell script to modify hosts file on remote machines but something goes wrong.
Script:
param(
[string]$value
)
$username = 'username'
$password = 'password'
$hosts = "172.28.30.45","172.28.30.46"
$pass = ConvertTo-SecureString -AsPlainText $password -Force
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList $username,$pass
ForEach ($x in $hosts){
echo "Write in $x , value: $value"
Invoke-Command -ComputerName $x -ScriptBlock {Add-Content -Path "C:\Windows\system32\drivers\etc\hosts" -Value $value} -Credential $cred
echo "Finish writing."
}
echo "End of PS script."
When run, it writes a new empty line for each hosts file. This line echo "Write in $x , value: $value" displays $value value.
What I'm doing wrong?
You have to pass the parameter to the scriptblock by defining a param section within your scriptblock and pass the parameters using -ArgumentList :
Invoke-Command -ComputerName $x -ScriptBlock {
param
(
[string]$value
)
Add-Content -Path "C:\Windows\system32\drivers\etc\hosts" -Value $value
} -Credential $cred -ArgumentList $value
Or you leverage the using: variable prefix:
Invoke-Command -ComputerName $x -ScriptBlock {
Add-Content -Path "C:\Windows\system32\drivers\etc\hosts" -Value $using:value
} -Credential $cred

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

Remote Registry using Enter-PSSession

I am trying to read strings in a remote registry. When I run the script I am working on, it connects to the workstation in the list, but it only reads the local computer when running, not the remote. any Ideas?
#create open dialog box
Function Get-FileName($initialDirectory)
{
[void] [Reflection.Assembly]::LoadWithPartialName( 'System.Windows.Forms' );
$d = New-Object Windows.Forms.OpenFileDialog;
$d.ShowHelp = $True;
$d.filter = "Comma Separated Value (*.csv)| *.csv";
$d.ShowDialog( ) | Out-Null;
$d.filename;
}
# Set Variables with arguments
$strFile = Get-FileName;
$strComputer = Get-Content $strFile;
$date = Get-Date -Format "MM-dd-yyyy";
$outputFile = "C:\PowerShell\Reports";
$cred = Get-Credential
foreach($computer in $strComputer)
{
Enter-PSSession $computer -Credential $cred
Set-Location HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reliability
$systemInfo = Get-Item -Name LastComputerName
Write-Host $systemInfo
}
foreach($computer in $strComputer)
{
Enter-PSSession $computer -Credential $cred
..
..
}
The above code won't work. Enter-PSSession is not for using in a script. Anything written after that in a script won't run.
Instead, use Invoke-Command and pass rest of the script block as a parameter value. For example,
foreach ($computer in $strComputer) {
Invoke-Command -ComputerName $computer -Credential $cred -ScriptBlock {
Set-Location HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reliability
$systemInfo = Get-Item -Name LastComputerName
Write-Host $systemInfo
}
}
As the comments already explained, Enter-PSSession is for interactive use. To read remote registry entries, there are several ways.
Use plain reg.exe, it works well enough. Like so,
foreach($computer in $strComputers) {
reg query \\$computer\hklm\software\Microsoft\Windows\CurrentVersion\Reliability /v LastComputerName
}
Use PSSessions. Create a session and Invoke-Command to read registry. Like so,
function GetRegistryValues {
param($rpath, $ivalue)
Set-Location $rpath
$systemInfo = (Get-ItemProperty .).$ivalue
Write-Host $systemInfo
}
$session = New-PSSession -ComputerName $computer
Invoke-Command -Session $session -Scriptblock ${function:GetRegistryValues} `
-argumentlist "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reliability",`
"LastComputerName"
Remove-PSSession $session
Use .Net classes, Microsoft.Win32.RegistryKey. Like so,
$sk = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $server)
$k = $sk.opensubkey("SOFTWARE\Microsoft\Windows\CurrentVersion\Reliability", $false)
write-host $k.getvalue("LastComputerName")

Get remote registry value

I have the below script that I want it to go out to multiple servers and get the value of a registry. Unfortunately it is currently just posting back the local registry value of the machine that I am running the script on.
How do I get the script to run against remote registry?
SCRIPT:
clear
#$ErrorActionPreference = "silentlycontinue"
$Logfile = "C:\temp\NEWnetbackup_version.log"
Function LogWrite
{
param([string]$logstring)
Add-Content $Logfile -Value $logstring
}
$computer = Get-Content -Path c:\temp\netbackup_servers1.txt
foreach ($computer1 in $computer){
$Service = Get-WmiObject Win32_Service -Filter "Name = 'NetBackup Client Service'" -ComputerName $computer1
if (test-connection $computer1 -quiet)
{
$NetbackupVersion1 = $(Get-ItemProperty hklm:\SOFTWARE\Veritas\NetBackup\CurrentVersion).PackageVersion
if($Service.state -eq 'Running')
{
LogWrite "$computer1 STARTED $NetbackupVersion1"
}
else
{
LogWrite "$computer1 STOPPED $NetbackupVersion1"
}
}
else
{
LogWrite "$computer1 is down" -foregroundcolor RED
}
}
You can try using .net:
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $computer1)
$RegKey= $Reg.OpenSubKey("SOFTWARE\\Veritas\\NetBackup\\CurrentVersion")
$NetbackupVersion1 = $RegKey.GetValue("PackageVersion")
Try the Remote Registry Module, the registry provider cannot operate remotely:
Import-Module PSRemoteRegistry
Get-RegValue -ComputerName $Computer1 -Key SOFTWARE\Veritas\NetBackup\CurrentVersion -Value PackageVersion
If you have Powershell remoting and CredSSP setup then you can update your code to the following:
$Session = New-PSSession -ComputerName $Computer1 -Authentication CredSSP
$NetbackupVersion1 = Invoke-Command -Session $Session -ScriptBlock { $(Get-ItemProperty hklm:\SOFTWARE\Veritas\NetBackup\CurrentVersion).PackageVersion}
Remove-PSSession $Session
another option ... needs remoting ...
(invoke-command -ComputerName mymachine -ScriptBlock {Get-ItemProperty HKLM:\SOFTWARE\VanDyke\VShell\License -Name Version }).version
For remote registry you have to use .NET with powershell 2.0
$w32reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$computer1)
$keypath = 'SOFTWARE\Veritas\NetBackup\CurrentVersion'
$netbackup = $w32reg.OpenSubKey($keypath)
$NetbackupVersion1 = $netbackup.GetValue('PackageVersion')
If you need user's SID and browse remote HKEY_USERS folder, you can follow this script :
<# Replace following domain.name with yours and userAccountName with remote username #>
$userLogin = New-Object System.Security.Principal.NTAccount(“domain.name“,”userAccountName“)
$userSID = $userLogin.Translate([System.Security.Principal.SecurityIdentifier])
<# We will open HKEY_USERS and with accurate user’s SID from remoteComputer #>
$remoteRegistry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(‘Users’,”remoteComputer“)
<# We will then retrieve LocalName value from Control Panel / International subkeys #>
$key = $userSID.value+”\Control Panel\International”
$openKey = $remoteRegistry.OpenSubKey($key)
<# We can now retrieve any values #>
$localName = $openKey.GetValue(‘LocaleName’)
Source : http://techsultan.com/how-to-browse-remote-registry-in-powershell/