Archive for the 'one-liner' Category

Find users in too many groups

Large Kerberos tokens (caused by too many groups listed in them) can be an issue in some environments (I’ve just had a similar trouble myself in an ADFS deployment). Luckily PowerShell is here to help. This quick script will list all users who are members of more than 75 groups:

$limit = 75
Get-QADUser -SizeLimit 0 -DontUseDefaultIncludedProperties |
  ForEach-Object {
    $groups = Get-QADGroup -ContainsIndirectMember $_.DN -SizeLimit $limit `
      -DontUseDefaultIncludedProperties -WarningAction SilentlyContinue
    if ($groups.Count -ge $limit) { $_ }
  }

Here’s a quick overview of what the script is doing:

  1. I assign the limit (75) to a variable. This is just for my convenience of reuse. E.g. I could turn this line into param($limit = 75) – and save this as a parameterized script or turn it into a function.
  2. I user Get-QADUser to retrieve all (-SizeLimit 0) user accounts from my current domain and I make sure to not retrieve any attributes along – so I save memory and improve performance (-DontUseDefaultIncludedProperties)
  3. For each user in my domain, I retrieve the first 75 (-SizeLimit $limit) groups to which the user belongs directly or through nesting (-ContainsIndirectMember $_.DN). There’s obviously no need to retrieve all groups – we just need to know if the user reached the limit. Again, we do not need any attributes (-DontUseDefaultIncludedProperties). I also tell PowerShell to not warn me if there are more groups than the size limit I specified (-WarningAction SilentlyContinue).
  4. Finally, if indeed we reached the limit, I output that user object.

You can obviously then just see the list on the screen or output it to CSV or HTML report.

Tags: , , , , , , ,

Get a list of users’ email addresses

Here’s a one-liner to turn members of a group into a list of email addresses, separated by semicolon. I am using it every now and then when someone from our partners (which obviously do not have access to our address book) ask me for a list of folks to include in some discussions, or grant access to some resources, and so on.

Here’s the oneliner (for PowerShell v2):

(Get-QADGroupMember MyGroupName -Type user -Indirect |
    Select -expand Email) -join ';'

PowerShell v1 version has a slightly different syntax for join:

[string]::join(';',
  (Get-QADGroupMember MyGroupName -Type user -Indirect |
    Select -expand Email))

And here’s a quick explanation of what it does:

  • I use Get-QADGroupMember to retrieve all members of the group. Note that -Indirect parameter gives me all members of nested groups, and -Type user makes sure that nested groups themselves get excluded.
  • Then I am taking the collection of user objects and turn that into a collection of just one property of the objects (Email) using  Select -expand.
  • Finally I am using join to turn that collection into a string and using semicolon as separator.

Hope this is useful.

List all empty OUs

Here’s a one-liner you can use to quickly find empty organizational units in your Active Directory:

Get-QADObject -Type organizationalUnit -DontUseDefaultIncludedProperties |
  where {
    -not ( Get-QADObject -SearchRoot $_.DN -DontUseDefaultIncludedProperties `
    -SearchScope OneLevel -SizeLimit 1 -WarningAction SilentlyContinue )
  }

A quick explanation of what I am doing here:

  1. I am retrieving all organizationalUnit objects from my domain (and use the -DontUseDefaultIncludedProperties switch to save a few milliseconds ;) )
  2. Then for each of the OUs I am retrieving all AD objects that are in that OU by doing a Get-QADObject and limiting the search scope to the DN of the current OU.
  3. Note that (like we did when looking for large groups) I am using the -SizeLimit parameter to see if I can get 1 item in the call (all I need is to learn whether there is anything in the OU – I don’t need the whole list) – which obviously makes the whole script magnitudes of time faster. I use -SearchScope
  4. Based on Kirk’s recommendation I am using -SearchScope OneLevel to exclude the OU itself.
  5. I am using -not operator so I get only the OUs for which this Get-QADObject evaluates to $null (nothing found) and thus -not $null evaluates to $true.

P.S. This is the code I was using initially, which I then corrected based on Kirk’s comments below:

$emptyOUs = Get-QADObject -Type organizationalUnit -DontUseDefaultIncludedProperties | where {(Get-QADObject -SearchRoot $_.DN -SizeLimit 2 -DontUseDefaultIncludedProperties).Count -lt 2}

Tags: , , , , , ,

What was that group again?

A simple one-liner can help. Just earlier today I could not remember the name of a particular distribution list but knew a couple of its members. The quick one-liner to help me out was:

Get-QADGroup -ContainsMember ('User A', 'User B')

And while we are on it, here’s an even better one-liner if you want to find a DL which has all direct reports of, say, your company’s VP:

Get-QADGroup -ContainsMember (Get-QADUser -Manager 'User C')

And obviously if such a DL does not exist, you should probably just go ahead and create it ;)

New-QADGroup -Name 'Top Managers' -Member (Get-QADUser -Manager 'User C')

I love PowerShell. :)

Tags: , , , , ,

Detect AD schema version from PowerShell

Just wanted to share a quick one-liner to check Active Directory domain and forest functional level with AD cmdlets:

[PS] C:\ >Get-QADRootDSE | Format-List *

DomainControllerFunctionality : Windows2003
DomainFunctionality : Windows2003
ForestFunctionality : Windows2003
IsGlobalCatalogReady : True
... and so on ...

Quick, easy and comes quite handy. Besides the AD schema info there’s other useful information – so definitely worth adding to your arsenal.

[UPDATE] Shay and MoW both contacted me saying that I did not include the one-liner which would give you the exact version number. Here you go:

(Get-QADObject (Get-QADRootDSE).SchemaNamingContext -IncludedProperties objectVersion).objectVersion

AD Recovery from PowerShell

Want to roll back any Active Directory change with a PowerShell one-liner? We’ve just published an online reference to the cmdlets shipped with Quest Recovery manager for Active Directory.

These cmdlets use backups so they are not limited to tombstone reanimation (as regular cmdlets).

For example, if you restore a user you get all the attributes including group membership and so on.

So to restore a deleted object you simply call Restore-RMDeletedActiveDirectoryObject and have the tool handle everything.

However, what makes it way more cool is that you have full power to restore any attributes of any users. So you are not limited to just undeleting stuff. Let’s say you had some kind of script/tool go wild and corrupt an attribute or two across all user accounts. Good luck restoring just these 2 attributes manually or with any kind of UI tool.

With these cmdlets it is as easy as:

# Select the backup you want - e.g. the latest
$b = (Get-RMBackup –Domain dom1.local | Sort-Object –Property Date)[-1]

# For every user in AD restore extensionattribute1

Get-QADUser | foreach {
Restore-RMActiveDirectoryObject –Backup $b.Path –Object $_.DN –Attribute extensionattribute1
}

Is it cool or what? ;)

Here’s the full list of cmdlets linked to the help info for each of them:

Note that unlike AD cmdlets these are actually a part of commercial product so there is cost involved. You can get a trial license from the product page. If you are a Microsoft MVP you can also get a free NFR license by applying here.

(Get-RMDeletedActiveDirectoryObject dc1.dom1.lab.local) | Where-Object { $_.Properties["objectclass"] –contains "user" }

C:\PS>foreach ($u in $users) { Restore-RMDeletedActiveDirectoryObject –Name $u.Properties["name"] –DirectoryHost dc1.dom1.lab.local }

Tags: , , , , , , ,

Find large objects in AD

How do you find the user accounts which take up the most space in Active Directory database?

I have just had this very question from a customer who has some BLOB attributes added to user objects and suspect that some of these got much bigger than the others. As result, the overall AD database is now way bigger than the customer would like to have (affecting performance, backups, replication, and so on.)

The problem they had is finding these objects.

My first reaction was: just do a Get-QADUser and sort the objects by size – how much easier can it get? Well, the problem is that there is just no SizeOf function in PowerShell – the system would not tell you how big a given object is.

The workaround I found was very simple. If we cannot get the in-memory size of an object – we can still export it to a file and measure the file size. :)

So here is my script:

# Use a different value of SizeLimit 
# if you want a subset of accounts to test the script
Get-QADUser -SizeLimit 0 -IncludeAllProperties | ForEach {
    $_ |  Export-Clixml "$($_.samAccountName).xml"
}
dir | sort Length -Descending

In a nutshell, all it does is goes through all AD user accounts, and exports each into xml file.

Then I just sort them by size.

The cool part about using ForEach-Object and not keeping all objects in an array is that this is actually very efficient from memory consumption perspective – each object gets cleared from memory after it is saved to xml.

Throughout running the script powershell.exe process was consuming only about 30-40MB of RAM.

One thing to note is that in most domains this script will take a long time to execute (hours). You can make it faster if you can limit the scope of Get-QADUser either by some attributes (SearchRoot, Enabled/Disabled, City, and so on) or properties (I was retrieving all, but if you actually know which properties contribute the most to the size you can include just these properties.) Again, see this post for more consideration on optimizing the script.

Tags: , , , , , ,

AD cmdlets for object undelete

Another great new feature in Quest’s free AD cmdlets 1.2 is ability to locate deleted (tombstoned) Active Directory objects and restore them back.

Locating is very straight-forward: you just add the -Tombstone switch to the Get-* cmdlet of your choice and now your query searches deleted rather than live objects.

Restoring is even easier – all you need is pipeline the deleted objects into Restore-QADDeletedObject.

And the best thing of all is that this works great with Windows 2003 Active Directory – so you can start taking advantage of the feature right away!

For example:

# List all tombstoned user accounts
Get-QADUser -Tombstone

# Restore accounts deleted from a specific OU
Get-QADUser -Tombstone -LastKnownParent 'OU=People,DC=company,dc=local' | Restore-QADDeletedObject

# Restore accounts deleted today
Get-QADUser -Tombstone –LastChangedOn (get-date) | Restore-QADDeletedObject

# Restore a specific deleted user
Get-QADUser -Tombstone –Name 'John Smith*' | Restore-QADDeletedObject

One gotcha to keep in mind is that when objects are tombstoned computer and user objects are stored in AD exactly the same way. This makes Get-QADUser actually return both user and computer objects. Shay found this workaround to make sure that only user objects are returned:

# Return all tombstoned user accounts but no computer objects
Get-QADUser -Tombstone -SizeLimit 0 -ldap '(&(!samAccountName=*$))'

Other Get-* cmdlets which now have these -Tombstone and -LastKnownParent parameters are:

For more information on what a tombstoned object is and how tombstone-based undelete is different from full recovery see Gil’s article here.

PowerShell script to select email recipients

Here’s a real-life problem I had earlier this week. I had to set up a meeting which would have all key people from our St. Petersburg office. Now… How would Outlook know who “key people” are? PowerShell helped me figure that out with a quick one-liner!

We have a couple of local DLs which I could use (let’s call them “SPb Project Managers” and “SPb Program Managers”) but I was worried that these might not have dev architects who I also wanted to have. Turns out, we at Quest have another DL (let’s call it “Architects” – which has the folks I need but… spawns all Quest offices worldwide).

So looks like I had to invite “SPb Project Managers” and “SPb Program Managers” DLs, and everyone who is in the “Architects” one but only from St. Petersburg and outside the two other DLs. Turns out that new group membership AD cmdlets make this a piece of cake:

Get-QADUser -City 'Saint Petersburg' `
    -MemberOf 'Architects' `
    -NotIndirectMemberOf 'SPb Project Managers', 'SPb Program Managers'

Now I just need to turn this user list into a single string of names separated by semicolons (so I can copy/paste it into Outlook). This means I need to take only Name properties from the values and then join them with a separator.

In PowerShell v1 this can be done with [string]::join(), in v2 using the new -join operator.

So here’s the final PowerShell v1 code:

[string]::join( "; ", (Get-QADUser -City 'Saint Petersburg' `
    -MemberOf 'Architects' `
    -NotIndirectMemberOf 'SPb Project Managers', 'SPb Program Managers' |
    ForEach-Object { $_.Name }))

And here’s the one for v2:

(Get-QADUser -City 'Saint Petersburg' `
    -MemberOf 'Architects' `
    -NotIndirectMemberOf 'SPb Project Managers', 'SPb Program Managers' |
    ForEach-Object { $_.Name }) -join "; "

Now, if only I could type PowerShell right inside the Outlook To field… One day it will hopefully get that pervasive. ;)

Tags: , , , , , , ,

So who did we hire last month?

CreatedOn, CreatedAfter, and CreatedBefore parameters added in 1.2 to all Get-QAD* cmdlets are another new feature I would like to spotlight in my blog.

These can accept DateTime values or just strings which PowerShell can convert to such (automated type conversion in PowerShell is pretty cool.)

# Let's see all the new user accounts created since the 1st of the month
Get-QADUser -CreatedAfter "April 1, 2009"

# Can narrow it down to specific OU to exclude service accounts
Get-QADUser -CreatedAfter "April 1, 2009" -SearchRoot mydomain.local/employees

# Same thing for groups, computers, OUs, or any AD objects
Get-QADComputer -CreatedAfter "April 1, 2009"
Get-QADGroup -CreatedAfter "April 1, 2009"

# Did we hire anyone today?
Get-QADUser -CreatedOn "April 17, 2009"

# Let's if we have anyone who is with the company for more than 10 years ;) 
Get-QADUser -CreatedBefore (Get-Date).AddYears(-10) -SearchRoot d.local/emp -Enabled 

# Let's count how many employees we were hiring monthly
for ($d = Get-Date "January 1, 2008"; $d -le (Get-Date); $d = $d.AddMonths(1)) {
  "$($d.ToShortDateString()) to $($d.AddMonths(1).ToShortDateString()):"
  (Get-QADUser -CreatedAfter $d -CreatedBefore $d.AddMonths(1)).Count
}

And you can use other parameters to get the data by department, city, title, and so on!

Now go to your HR and ask them if they can provide data like that so quickly. ;)

Tags: , , , , , ,

Next Page »


View Dmitry Sotnikov's profile on LinkedIn

Follow Dmitry Sotnikov at Twitter

My Recent Tweets

Archives

See you at:

Legal

The posts on this blog are provided “as is” with no warranties and confer no rights. The opinions expressed on this site are mine and mine alone, and do not necessarily represent those of my employer Quest Software or anyone else for that matter. All trademarks acknowledged.

© 2007 Dmitry Sotnikov

Pages

 

November 2009
M T W T F S S
« Oct    
 1
2345678
9101112131415
16171819202122
23242526272829
30