how to delete all values in a registry key EXCEPT one value??

I need to do this registry code:

remove all values in the below key/path EXCEPT this one:

S-1-5-21-375116633-3803594802-1557413134-500

Remove-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\NetCache\PurgeAtNextLogoff</code>

I know there’s the -Exclude switch but this requires the path to have a wildcard??

Do I put a * at the end of the path to exclude *500??

e.g.
Remove-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\NetCache\PurgeAtNextLogoff* -Exclude *500

Thank you, Tom

Would this work??

Get-ChildItem -path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\NetCache\PurgeAtNextLogoff | where { $_.Name -NotMatch ‘-500’} | Remove-Item -Force

Thank you, Tom

I have a crazy idea: You could try it. :wink:

Really: Why don’t you try it?

If it doesn’t you still have the opportunity to come back and tell that it does not work.

That’s not a helpful response or suggestion.

I did try it with -WhatIf – no output appeared so I have no idea if it does what I need.

Not everyone here is a PowerShell expert.

In fact I do not consider myself a Powershell expert as well. :wink: But I’m used to test everything what comes to my mind to figure out how it works. So my suggestion would be to get a test system and try it without -WhatIf. :wink: If you tried that arleady as well you could have just say it. Most of us do not have the same conditions as you have in your environment so it’s a kind of effort to reproduce your case.

It’s easy to find scripts that MATCH and delete registry key values.

What I need is the proper REVERSE syntax – delete all registry key values that don’t match one specific key value (s). It’s not clear anywhere whether and how one should use wildcards to achieve the matching or what one should do.

Upon replying, please actually answer my question rather than telling me about -WhatIf etc., I know what it is. When no output appears, -WhatIf is not very helpful.

Thank you, Tom

Try this in a test environment:

$Path = 'REGISTRY::HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\NetCache\PurgeAtNextLogoff'
(Get-Item -Path $Path).GetValueNames() |
Where-Object {$_ -notmatch ‘-500’} |
ForEach-Object{
Remove-ItemProperty -Path $Path -Name $_
}

Well, if it were me, I would probably go about it a little different. First, I’m not a huge fan of the Remove-Itemproperty commandlet, so I tend to go about things a little more programmatically. To that end, I would start by opening the registry up as a connection like so (note: I’m freehanding this so if anything is misspelled or the wrong case you’ll need to fix that when you run it):

$targethost = “somemachinename”

$RemoteKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::'LocalMachine',$targethost

$regPath = “SOFTWARE\Microsoft\Windows\CurrentVersion\NetCache\PurgeAtNextLogoff\”

$regkey = $RemoteKey.OpenSubKey($regPath,$true)

$regkey.GetValueNames()

So with that you should now be reading all the keys in the location you want. The next logical step would be to drop those keys into an array, which you could do like so:

$array = $regkey.GetValueNames()

And then your next step would be to loop through what you have and pick out anything that doesn’t match your criteria. I’d do that like this:

Foreach($i in $array)

{
   If($i -ne “S-1-5-21-375116633-3803594802-1557413134-500”)
   Write-host $i
}

So now you can see what’s NOT matching the criteria, and you can work to delete it. From here I’d most likely change that write-host to

$regKey.DeleteValue()

And that should do it (minus any typos of course, again I’m free handing this). That should get you where you want to go, be sure to use a test machine… don’t play on production. And I would probably also create my own initial path and keys to monkey with first, then change it to what I really want at the end. But then, I’m pretty careful with the work I do. I hope this helps you.

Hello again everyone,

All the most recent posts from Olaf (thank you for your patience with me!!) and Hef62 are what I need. I can work with this, particularly the second one, since I have more than one value I need to test for. It’s all doable now with -WhatIf as you all recommended.

I’ll try to remember to post the updated script after I have it all sussed. :slight_smile:

Thank you, Tom

@Hef62, I tried out your code, but it always returns all the values, and even with a properly done -notmatch ‘-500$’ it still returns all the values. I’ll keep looking for and trying different things, I am thinking it will be easier to manually clean out all the values, then export and save the key, then automate importing the saved (cleaned-out) key. I’ll update this thread…

You may specify more detailed what you want to do. We might be able to help. But we have to know the exact requirement.

Well, let’s break down your issue a little. When you’re trying to write something, you have to break down the big plan into tiny steps, so I provided the following tiny skill steps in my previous post for you

  1. Connecting to a remote registry
  2. Walking that remote registry to a specific key
  3. Reading all the elements in that key
  4. Tucking those elements into an array
  5. Reading the array
  6. And how to delete a target
  7. One specific method for identifying a string
Now you're trying to identify a string based on a portion of it via the -notmatch comparison operator. We could have as easily used the -notlike, or we could change our logic and use -match and -like instead of their -not variants. So let's get into a little code shall we?

To begin, we make a connection to the registry. In my case, I don’t have the key you’re looking at, so I’m going to change it to another area.

clear

#set your target
[string]$targethost = “midnight”

#Marshalled OBject targeting HKEY_Local_Machine
$RemoteKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::'LocalMachine',$targethost)

#set the path you're looking for
#[string]$regPath = “SOFTWARE\Microsoft\Windows\CurrentVersion\NetCache\PurgeAtNextLogoff\”
[string]$regPath = “SOFTWARE\Microsoft\Windows\CurrentVersion\FileAssociations\UpgradeChoice”

#Open your remote key with write permissions
$regkey = $RemoteKey.OpenSubKey($regPath,$true)

#Get the name of all elements within the key
$regkey.GetValueNames()

#Declare an array 
$array = @()

#Dump the elements into the array 
$array = $regkey.GetValueNames()
Write-Host `n

Now that we have a connection, and I’m dumping out some values, let’s see what I get

SkipOOBEUpgradeChoice
AppUpgradeVersion
Photos
Webbrowser
Music
Video

I’m going to pick a partial word - in this case I’m going to say “Version”. So if any value has Version in the name, I want to do something with it. I could jump straight to the answer here, but what I’m trying to do is teach you to use what you have available. A lot of times we look in forums, tutorials, etc. which can be useful in the right circumstances, but at this level you could simply write some code testing several options, and keep playing so you learn what they all do. A young programmer often doesn’t recognize the options right in front of them. So, for teaching purposes, I created a foreach loop and looked at several choices:

 

#Identity everything that is not the value you want 
foreach($i in $array)
{
If($i -contains "version")
{
write-host "This is -contains $i"
Write-Host `n
}

If($i -like "version")
{
write-host "This is -like $i"
Write-Host `n
}

If($i -like "*version*")
{
write-host "This is -like with wildcards $i"
Write-Host `n
}
If($i -match "version")
{
write-host "This is -match $i"
Write-Host `n
} 

If($i -notmatch "version")
{
Write-Host "This is -notmatch $i"
Write-Host `n
}
If($i -notlike "version")
{
Write-Host "This is -notlike $i"
Write-Host `n
} 
}

So using this loop, what were the results?

This is -notmatch SkipOOBEUpgradeChoice

This is -notlike SkipOOBEUpgradeChoice

This is -like with wildcards AppUpgradeVersion

This is -match AppUpgradeVersion

This is -notlike AppUpgradeVersion

This is -notmatch Photos

This is -notlike Photos

This is -notmatch Webbrowser

This is -notlike Webbrowser

This is -notmatch Music

This is -notlike Music

This is -notmatch Video

This is -notlike Video

Hmm, we see

-notmatch

-like

-match

-notlike

but what we don’t see is -contains. So right away you know that option won’t work as it’s written. But let’s look a little closer at the output. The -notlike (without wildcards) operator kicked out one with version in the name, and the only -like I see is one with the wildcards. So now you know you have to use the ones with wildcards if you use the -like operator. So let’s take this to the next level… you wanted to target everything that didn’t have a specific partial value for deletion… programmatically we could handle that several ways, but let’s do it by creating a new array of what we want to delete. And while we’re at it, let’s see what we’re adding to the array, and what we’re skipping.

#so now we go about this programmatically 
$targetdeletionarray = @()
foreach ($x in $array)
{
if($x -match "version")
{
write-host "*****************************************************"
Write-Host "***We're skipping $x because it matches***"
Write-Host "*****************************************************"
}
Else 
{
write-host "Adding $x to deletion targets"
$targetdeletionarray += $x
}
}

Write-Host `n
Write-Host "We would delete these "

foreach ($y in $targetdeletionarray)
{
Write-Host $y
}

And our output is:

Adding SkipOOBEUpgradeChoice to deletion targets
*****************************************************
***We're skipping AppUpgradeVersion because it matches***
*****************************************************
Adding Photos to deletion targets
Adding Webbrowser to deletion targets
Adding Music to deletion targets
Adding Video to deletion targets


We would delete these 
SkipOOBEUpgradeChoice
Photos
Webbrowser
Music
Video

The top section is us telling ourselves what we’re adding to the deletion array, and what we’re skipping. The bottom is what’s actually in the deletion array. So, we created a new array of what we want to delete, we omitted from that array anything with the partial value we didn’t want to delete. Now we just have to write a new loop to delete those targets.

Some final thoughts:

  1. Always make the big things into smaller bites
  2. In my first freehand I didn't declare variables, in this one I did a little more. PS is a little forgiving in this, other languages aren't. You should learn to declare variables, so if you're really going to learn - do that.
  3. Posting my code into this forum tends to remove formatting - but on my screen it's formatted well. I say this to you because if you're going to learn, you need to make the code easier to read.
  4. This is all one section of code, but for complex programs I always create functions and call them. If you're wanting to expand, learn this.
  5. Learn to put comments in your code... good comments that explain the logic. It will help you later, and it helps anyone else reading it.
  6. You should release things when done. So for example $RemoteKey.Close(). You'll want to do a little research on this. You'll probably also want to remove variables when your program closes. The larger the script, the more distributed com, wmi, .net calls, etc. the more resources you have open. Generally PS is pretty good about cleaning up behind itself, however it doesn't always get it all, and this is a good programmers habit to be in.
  7. The last thought it just this... I'm not a teacher, and the folks I generally interact with are Systems Administrators or security folks. What I mean by that is I'm not precisely in my element here, so while I'm trying to help you, it may not be perfect. I'm also new to this forum for what that's worth.
Take care, and good luck.

 

Doing some quick hunting, this would probably also be a good article for you to read https://www.pluralsight.com/blog/software-development/powershell-operators-like-match

@Hef62 THANK YOU!! for this excellent tutorial!! :slight_smile: :slight_smile: :slight_smile:

I am a systems admin where I work, but not as fluent as most people with PowerShell.

I’ll work on this tonight etc., it will help me get my mind off some other things. :slight_smile:

As well as post the final code when I get it all sussed.

Thank you, Tom

Usually -exclude qualifies the path. I have a little script called get-itemproperty2.ps1:

param([parameter(ValueFromPipeline)]$key)

process { 
  $key.getvaluenames() |
  foreach {
    $value = $_
    [pscustomobject] @{
      Path = $key -replace 'HKEY_CURRENT_USER',
        'HKCU:' -replace 'HKEY_LOCAL_MACHINE','HKLM:'
      Name = $Value
      Value = $Key.GetValue($Value)
      Type = $Key.GetValueKind($Value)
    }
  }
}

And with that I would just do:

get-item HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\NetCache\PurgeAtNextLogoff | 
  .\get-itemproperty2 | where name -notlike *500 | remove-itemproperty -whatif

[quote quote=186230]

  • Connecting to a remote registry
  • [/quote] Sorry if that sounds picky but where do you know its about a remote system? The OP never said that. ;-)

    I tend to recommend - at least for beginners - to keep it as simple as possible and as close to the pure Powershell syntax as possible. I have the feeling the OP is a little overwelmed by the amount of information from your posts. :wink:

    Hello everyone,

    I just finished assembling a new vacuum cleaner, hopefully this bodes well for tackling all this later tonight, not my first idea of something to do but I need to figure it out before Monday. :frowning:

    All kinds of good ideas and information here. :slight_smile:

    Thank you, :slight_smile: p

    The remote machine was a fairly easy logical jump / educated guess.

    Clue 1, he’s looking to remove S-1-5-21* which is a SID.

    Clue 2, the beginning of that SID tells me he’s dealing with a domain credential.

    Clue 3, the -500 on the end tells me he’s trying to exclude removing the administrator cached properties.

    Clue 4, all the numbers in between are the unique to the domain itself, which was just another confirmation it was from a domain.

    Clue 5, if you were dealing with a single box there are easier ways to do this, anywhere from the reg command to having .reg file you load up that overwrites what’s there.

    This leads me to extrapolate he’s probably trying to do this for either more than one machine, a remote machine, or setting something through one of a myriad of ways. Now, that’s not fool proof logic, but it’s was fairly easy to come to from the initial post. Even if it wasn’t correct though, it wouldn’t hurt him to learn the skills I laid out (if he didn’t already have them, and if he did he could pass over those parts quick enough), the thought processes behind it, etc. Even more so where he said he was an SA, those skills will come in handy.

    As for pure PowerShell syntax your beliefs and mine don’t match at all. For almost anything I do I’m mixing in (from within PowerShell, or c# or whatever other language I happen to be writing in) WMI, ADSI, .net, WPF, SQL…and so on. Maybe it’s because of the work I do, but to get my work done that’s how it works.

    Now sure, I’m not dragging him into the deep end on a discussion about managed vs. unmanaged code and Marshaling Windows APIs, that would be just plain mean, and way to deep for someone at this level. But I did show him how to get what he wanted, with lessons along the way on how to make the connection, walk the tree, read the variables, match a string, loop it through, and how to test things out. I provided explanations, and pointers to things he may want to look into. And the way I showed him would transfer easily to another language, and it has flexibility. And again, those skills will be handy to him as an SA, will help someone else doing research, and it gave him an answer on how to do what he was after.

    Hello everyone,

    I didn’t get to this last night and just now finished testing the code.

    It works as it’s supposed to work!! THANK YOU!! @Hef62 for going above and beyond the call of duty. :slight_smile: :slight_smile: :slight_smile:

    A few comments:

    1. I do indeed intend and require this script to run on several servers, probably at reboot. Hence the requirement for excluding -500 in case it's ever needed for any reason, even though the local administrator account is rarely used. I have another key upon which similar code must run, but there's more key values to exclude, hence more testing ahead.
    2. I had read about like and match (but not the specific article @Hef62 cites, I don't think) but I got confused by all the commentary I found regarding wildcard.
    3. Finally, I wish *I* could be as articulate as @Hef62's most gracious responses in posts #186230 and #186272. I'm intelligent and I understand it all, but I am a tool user, an assembler of parts to create something, I'm nowhere near the tool maker (aka trained programmer) that many people on this forum are.
    4. Those two posts are exemplary examples of how to respond to someone inquiring for knowledge. :) :)
    5. I will research the 'releasing' mentioned above. This is a pretty small script but I do understand the reasoning for it, having worked with Access 20## programming in the past.