Tag Archives: Get-ADGroupMember

Remove and Add Users to an Active Directory Group

The more people that recognize that I’ve made an investment in Windows PowerShell, the more these posts write themselves. I was recently asked by a previous colleague to help them make some modifications in Active Directory (AD). They needed to strip out all the users from a single AD group, and then add a bunch of them back that were, conveniently, stored in a text file. While you can compare the current users in the group with the ones you want to add, this post will assume it’s suitable to momentarily remove all the users from a specific AD group.

When I completed this, out of the kindness of my heart, and because I want to promote and teach PowerShell whenever possible, I sent the previous colleague an email with the five steps I had completed. They might be useful for others as well, so here we are. I think I’m getting a lunch out of this, too. While it might not be Chipotle, I like food, so I’m sure it’ll be suitable.

1. Export Users in the AD Group: This command will return the current members of the VPN-Users group and store them in a Csv file. While this will allow us to compare our final results, its original purpose was as my safety net in case I made a mistake and wanted to make the previous members, members again.

PS> $GroupName = 'VPN-Users'
PS> Get-ADGroupMember -Identity $GroupName | Export-Csv -Path C:\VPN-Users01Pre.csv -NoTypeInformation

2. Remove Users from Group: This next command removes all the members of the group (which I had checked were only users). It did this by piping each member of the group to a ForEach-Object loop that ran Remove-ADGroupMember against the current user. Had there been other object types, you’d have to use a Where-Object command in between these two commands (command 1 | Where-Object… | command 2), filtering on the ObjectClass. The Remove-ADGroupMember cmdlet is using the -Confirm parameter with the $false value, so there’s no need for manual confirmation of each removal.

PS> Get-ADGroupMember -Identity $GroupName | Foreach-Object {Remove-ADGroupMember -Identity $GroupName -Members $_ -Confirm:$false}

3. Add Users to Group: After I allowed a few moments for replication, I went ahead and added all the users in the text file I had been supplied by using the Get-Content command and piping each entry to the ForEach-Object and Add-ADGroupMember cmdlets. It threw two errors when it wasn’t able to find a couple users in AD. While I didn’t, we could’ve written in precautions in this command to avoid this error, or programmatically fixed the file prior to this command.

PS> Get-Content -Path C:\UserNames.txt | Foreach-Object {Add-ADGroupMember -Identity $GroupName  -Members $_}

4. Export Users in the AD Group: Now that we have all the old group members removed, and the new ones added, we can create a new export of the group members for comparison. This isn’t necessary, but helps to see who was removed and added, if we desire that kind of information and comparison.

PS> Get-ADGroupMember -Identity $GroupName | Export-Csv -Path C:\VPN-Users02Post.csv -NoTypeInformation

5Compare Before and After Group Memberships: The next command will reach into the SamAccountName column inside both Csv files and compare them. Results that indicate <= mean they are in the file on the left (the file as the value to -ReferenceObject parameter), and results that indicate => mean they are only in the file on the right (-DifferenceObject parameter). Don’t forget, you can always use the -IncludeEqual parameter if you want to see which users where there before and after.

Compare-Object -ReferenceObject ((Import-Csv -Path C:\VPN-Users01Pre.csv).SamAccountName) -DifferenceObject ((Import-Csv -Path C:\VPN-Users02Post.csv).SamAccountName)

There ya go. The previous colleague said they had a problem finding some good examples of doing this online, so here’s to hoping that this will help someone when it they need it. Just maybe, I’ll be back with the right way to do this, where the users that are going to be added back, never get removed in the fist place.

Working with an Array of Arrays

There’s a group of servers that I use for a specific project. I can never remember their names or how those correspond to their roles (web front end vs. data back end). Although I’ve updated their Active Directory (AD) descriptions, and created two, specifically named AD groups for them, I wanted an even quicker way to remind myself whose who. With that in mind, I updated my profile with a custom variable that is an array, of arrays.

In this Quick Learn, we’ll work with an example that actually uses three groups of roles — our DCs, our web servers, and our SQL servers. As you’ll soon see, our servers are named after Santa Claus’ reindeer. These names have nothing to do with the role of these servers, and since their names are all closely related, it’s difficult to remember who does what.

This first example, below, demonstrates how we create a new variable, or modify an already existing variable. When we echo the contents of our variable, we get all the computer names, regardless of what array they are in, within the base array. The term ‘base array’ is probably not something you’ll hear or read about outside this post. It’s being used here to help distinguish the array that holds all the other arrays — the container array.

PS C:\> Set-Variable -Name Computers -Value @(('dasher','vixen','cupid'),('comet','dancer','donner'),('blitzen','rudolph','prancer'))
PS C:\> $Computers
dasher
vixen
cupid
comet
dancer
donner
blitzen
rudolph
prancer

We can use an index to return one of the arrays within the base array. In the examples below, you can see how each can be accessed. This is probably a good time to review indexes: The first item in an array is index zero, the second item is index one, the third item is index two, and so on.

PS C:\> $Computers[0]
dasher
vixen
cupid
PS C:\> $Computers[1]
comet
dancer
donner
PS C:\> $Computers[2]
blitzen
rudolph
prancer

In the following example, we can use two indexes to access a specific server. The first index represents which array (within the base array) I want to return, like it did above, and the second index indicates which server I want to return.

PS C:\> $Computers[0][2]
cupid
PS C:\> $Computers[1][0]
comet
PS C:\> $Computers[2][1]
rudolph

The difficult part is going to be able to remember which index is for the DCs, the web servers, or the SQL servers. In that case, I’ll create three more variables to use in place of those index integers.

PS C:\> Set-Variable -Name DCs -Value 0
PS C:\> Set-Variable -Name Web -Value 1
PS C:\> Set-Variable -Name SQL -Value 2
PS C:\> $DCs,$Web,$SQL
0
1
2

With the combination of my $Computers variable and the three, role-specific variables, I am able to easily return the set of servers I want.

PS C:\> $Computers[$DCs]
dasher
vixen
cupid
PS C:\> $Computers[$Web]
comet
dancer
donner
PS C:\> $Computers[$SQL]
blitzen
rudolph
prancer

Now that we have this all figured out, I can use them in different commands. Here’s a couple examples:

PS C:\> $Computers[$SQL] | ForEach-Object {Test-Connection $_ -Count 1}

Source        Destination     IPV4Address      IPV6Address                              Bytes    Time(ms)
------        -----------     -----------      -----------                              -----    --------
TOMMYMS PC... blitzen         10.10.10.80                                               32       1
TOMMYMS PC... rudolph         10.10.10.81                                               32       1
TOMMYMS PC... prancer         10.10.10.82                                               32       1

PS C:\> Get-Service -ComputerName ($Computers[$DCs]) -Name *bit* | Select-Object MachineName,Name,Status | Format-Table -AutoSize

MachineName Name  Status
----------- ----  ------
dasher      BITS Stopped
vixen       BITS Running
cupid       BITS Stopped

The unfortunate thing about hard coding computer names in our profile, is that we’ll run into problems when new servers are added and old ones are decommissioned. Therefore, we’re going to use AD groups — something I mentioned earlier — to populate our array variable. We’ll pull our DCs from the Domain Controllers, and our web servers and SQL servers from two fictions AD groups: WebServers and SQLBoxes. Here’s the command we’ll add to our profile to ensure we always have the correct server names. While this can all be on a single line command I’ve added line breaks to give it an easier-to-read appearance.

Set-Variable -Name Computers -Value @(
    ((Get-ADDomainController -Filter *).Name),
    ((Get-ADGroupMember -Identity WebServers).Name),
    ((Get-ADGroupMember -Identity SQLBoxes).Name)
)

And, that’s it. If for some reason you’re using PowerShell 2.0 or lower (and I really hope you’re not), you’ll need to include Import-Module -Name ActiveDirectory in your profile. As well — and it should go without saying — you’ll need to be working from a computer that actually has the ActiveDirectory module installed. Although I’m using PowerShell 4.0, I still include Import-Module -Name ActiveDirectory in my profile, so I don’t have to wait for it to auto load, when I run my first AD cmdlet.