Jump to content

[Powershell] Check a registry key on a remote machine


Recommended Posts

Posted (edited)

Background: Moving staff slowly from Roaming profiles to Mandatory ones. This requires removing the copies of their roaming profiles from machines they frequently use, else when they next login it will try and load the roaming profile and fail.

 

$AllStaffPCs += (Get-ADComputer -SearchBase 'OU=Teacher Computers,' -Filter 'ObjectClass -eq "Computer"' | Select -ExpandProperty DNSHostName )
$AllStaffPCs += ((Get-ADComputer -SearchBase 'OU=Admin Computers,' -Filter 'ObjectClass -eq "Computer"' | Select -ExpandProperty DNSHostName ) | Sort-Object)

 

This pulls a list of machines from AD.

 

ForEach ($PC in $AllStaffPCs) {
if ((Test-Connection -Quiet $PC -Count 1) -eq $true) {
	if ((Test-Path "\\$PC\C$\Users\$User*") -eq $true) {
		$ValidPCs += ($PC + "`r`n")
	}
}
}

 

This uses that list to check if the PC is connected to the network and if it is, look for the corresponding C:\Users\ folder.. However, this doesn't differentiate between mandatory and roaming profiles, so is only useful for when we first originally move them, but because it only returns switched-on machines it's likely that they'll logon to some other PC that's turned off at the time and it won't work. Noseying around in the registry has led me to find that roaming profiles contain a value in their respective S-1-5-21-* key labelled "CentralProfile", which is their .V2 profile location on our network. What I'm looking to create then, would be something like this..

 

ForEach ($PC in $AllStaffPCs) {
if ((Test-Connection -Quiet $PC -Count 1) -eq $true) {
	[color="#FF0000"]IF EXIST Key: ($PC\HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\S-1-5-21-*) Value: (CentralProfile) {
		IF Value: (CentralProfile)[/color] -imatch $User {
			$ValidPCs += ($PC + "`r`n")
		}
	}
}
}

 

I know Test-Path can be used for registry, but I can't seem to find a way to make it work remotely. I've found a fair few sites that give an example function to run, but I'm having a bit of difficulty following them as they seem to just be "HERE. HAVE SOME CODE!" rather than explaining it so I can modify it to suit my needs.. Anyone have any clue where I should go with this?

Edited by Garacesh
Posted

According to this link on technet you can't use Test-Path for remote registries but can use another tool. So you'd be looking at something like that as a basis, and then trying to add in Get-ChildItem or similar since you'd have to specify the ID for each reg key.

 

The other options I can think of would be PSRemoting or Invoke-Command (though I think that's seen as bad practice to use it) and using pretty much what you have within the Test-Connection IF statement but within a remote session (or Invoke-Command script block), perhaps modifying the $ValidPCs variable to update a csv file on a shared drive to do the logging you're looking for there.

Posted (edited)

I would agree with @halbaradkenafin in that you will likely find it easier to get your script to write to a text file (be it CSV or TXT) then import it back into your main script. I've had success with getting variables into a remote sesion, but not writing a variable on a remote machine and bringing it out again. Look into the use of New-PSSession and Enter-PSSession cmdlets. I had a play on my domain and have as yet had no luck getting the kind of data you want purely through remote sessions and variables.

 

Invoke command on it's own is frowned upon, but something like this should be alright:

 

$Session = New-PSSession -ComputerName $RemoteComputer
Invoke-Command -Session $Session -ScriptBlock {
   Enter Code Here
}

 

This page may help for testing of registry items, amongst other things.

 

In the meantime, if you do figure it out, please post here, if only to satisfy my curiosity!

Edited by Sephiroth
Posted

Hm, call it paranoia (or just being plain old fussy) I'll keep Invoke-Command as a last resort.. My machine currently runs Powershell 4.. The vast majority of our staff machines only have Powershell 2 (as per W7 standard install) so if nothing else, I don't want to risk bad scripting not working on the remote machines.

 

An issue with this is I need the script to scan every S-1-5-21-* key for the CentralProfile value.. It's possible I could do a foreach loop to work that if wildcards don't work, but that's for after I get into remote registries..

Posted
However, this doesn't differentiate between mandatory and roaming profiles

The Win32_UserProfile WMI class has a property called 'Status' that can tell you whether a profile is mandatory or roaming.

 

Get-WmiObject -Class Win32_UserProfile | Where-Object { $_.Status -eq '[color="#FF0000"]2[/color]' }

<#
0 = Undefined
1 = Temporary
2 = Roaming
4 = Mandatory
8 = Corrupted
#>

 

Using -Filter instead of Where-Object is quicker when I tested it so you could try this instead...

 

Get-WmiObject -Class Win32_UserProfile -Filter "Status='2'"

 

I don't want to risk bad scripting not working on the remote machines.

Use -WhatIf.

Posted (edited)

Odd.. Get-WmiObject does have remote capabilities (-ComputerName) but when using that it doesn't seem to want you to use a filter O.o so Get-WmiObject -ComputerName $PC -Class Win32_UserProfile | Where-Object { $_.LocalPath -imatch $User -and $_.Status -imatch "2" } works, but using -Filter "RoamingPath='$Path-To-Profile-On-Server'"* doesn't.

 

[color="#FF0000"][b]Get-WmiObject : Invalid query "select * from Win32_UserProfile where RoamingPath='$Path-To-Profile-On-Server'"
At line:1 char:2
+ (Get-WmiObject -ComputerName "$PC" -Class Win32_UserProfile -F ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   + CategoryInfo          : InvalidArgument: ( [Get-WmiObject], ManagementException
   + FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand[/b][/color]

 

* Using "Status='2'" returns all roaming profiles on the machine and since staff aren't being moved one-by-one, I only need to identify the specific staff member that's being moved at the time. It's not perfect but we can't just move everyone over at once.

Edited by Garacesh
Posted
Odd.. Get-WmiObject does have remote capabilities (-ComputerName) but when using that it doesn't seem to want you to use a filter

It should. The syntax of your filter doesn't look quite right though. Try this...

 

$UserProfiles = Get-WmiObject -Class Win32_UserProfile -Filter "RoamingPath Like '%\\DC-01\\ProfileShare\\USER%' and Status='2'" | Select-Object -ExpandProperty RoamingPath
ForEach ($Profile in $UserProfiles) {
   
   # Do Stuff
}

Posted (edited)
It should. The syntax of your filter doesn't look quite right though. Try this...

 

$UserProfiles = Get-WmiObject -Class Win32_UserProfile -Filter "RoamingPath Like '%\\DC-01\\ProfileShare\\USER%' and Status='2'" | Select-Object -ExpandProperty RoamingPath
ForEach ($Profile in $UserProfiles) {
   
   # Do Stuff
}

 

Ah, of course, A) Double backslashes. Derp. Forgot about that.

I'd tried to design my filter around yours, so I was using -Filter "RoamingPath='\\Path\To\Profile\Share\$User.V2'" which wasn't working.. But:

 

Get-WmiObject -ComputerName $PC -Class Win32_UserProfile -Filter "RoamingPath Like '\\\\Path\\To\\Profile\\Share\\$User.V2' and Status='2'" does work. Yay.

 

Running them side-by-side and there seems to be no difference in speed, but it's still nicer to do things more efficiently rather than have it examine things multiple times. Thanks a lot! :)

 

Edit: Doing it this way gets me a few 'The RPC server is unavailable' errors. Only on a few machines, though.

Edit 2: Nope, both ways get me the errors, I just mustn't have noticed. Probably remote admin is turned off or something.

Edited by Garacesh

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now



×
×
  • Create New...