Archive for the 'cmdlets' 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: , , , , , , ,

Add filepath to ConvertTo-HTML

Converting PowerShell data into an HTML report and save it to disk with no need for extra pipeline has long been my dream. Unfortunately, there’s no native Export-HTML cmdlet (unlike, say, Export-CSV), and ConvertTo-HTML does not have -Path parameter and only displays the html code on the screen (very useful ;) ) unless you pipe it to Out-File.

So being inspired by Kirk adding parameters to Import-CSV and using PowerShell 2.0 code-snippets, I created my Export-HTML function, which behaves exactly like ConvertTo-HTML but adds optional -Path parameter to specify the output file.

Download it, copy/paste the function into PowerShell (or dot-source it, or include it in your PowerShell profile) and you will be able to do something like:

Get-Process | Export-Html -Path C:\pr.htm

or

Get-Process |
    Export-Html C:\pr.htm -Title My Processes

or

Get-Process |
    Export-Html C:\pr.htm -Property Name, Handles -Title My Processes

You can download the code here, or copy/paste it from the text below.

You may also consider renaming the function name from Export-HTML to ConvertTo-HTML (or use set-alias to make them the same thing), because the -Path parameter is optional, and old behavior of outputting HTML code to the console/pipeline is supported as well as all native parameters.

Here’s how I created this proxy function:

  1. Downloaded and installed PowerShell 2.0 code snippets.
  2. Used the function (proxy) snippet to generate the proxy for ConvertTo-
    HTML
    .
  3. Added Path to the parameters section:
  4.     [Parameter(Position=0)]
        [Alias('PSPath', 'FilePath')]
        [ValidateNotNullOrEmpty()]
        [System.String]
        ${Path},
    
  5. Added a variable to store modified PowerShell code to be executed:
  6. $scriptCmdPipeline = ''
  7. Added parameter handling in which I (if Path is present) append the Out-File code to the pipeline:
  8.         if ($Path) {
                $PSBoundParameters.Remove('Path') | Out-Null
                $scriptCmdPipeline += " | Out-File -FilePath $Path"
            }
    
  9. Got the original command-line for ConvertTo-HTML
  10.         $scriptCmd = {& $wrappedCmd @PSBoundParameters}
    
  11. And added this new pipeline to it:
  12.         $scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
                    [string]$scriptCmd + $scriptCmdPipeline
                )
    

The rest was handled by the code snippet.

Here’s the resultant code:

#Requires -Version 2.0

<#
    Export-Html behaves exactly like native ConvertTo-HTML
    However it has one optional parameter -Path
    Which lets you specify the output file: e.g.
    Get-Process | Export-Html C:\temp\processes.html
#>

function Export-Html {
[CmdletBinding(DefaultParameterSetName='Page')]
param(
    [Parameter(ValueFromPipeline=$true)]
    [System.Management.Automation.PSObject]
    ${InputObject},

# Adding Path parameter 
# (made it Position 0, and incremented Position for others)
    [Parameter(Position=0)]
    [Alias('PSPath', 'FilePath')]
    [ValidateNotNullOrEmpty()]
    [System.String]
    ${Path},

    [Parameter(Position=1)]
    [ValidateNotNullOrEmpty()]
    [System.Object[]]
    ${Property},

    [Parameter(ParameterSetName='Page', Position=4)]
    [ValidateNotNullOrEmpty()]
    [System.String[]]
    ${Body},

    [Parameter(ParameterSetName='Page', Position=2)]
    [ValidateNotNullOrEmpty()]
    [System.String[]]
    ${Head},

    [Parameter(ParameterSetName='Page', Position=3)]
    [ValidateNotNullOrEmpty()]
    [System.String]
    ${Title},

    [ValidateSet('Table','List')]
    [ValidateNotNullOrEmpty()]
    [System.String]
    ${As},

    [Parameter(ParameterSetName='Page')]
    [Alias('cu','uri')]
    [ValidateNotNullOrEmpty()]
    [System.Uri]
    ${CssUri},

    [Parameter(ParameterSetName='Fragment')]
    [ValidateNotNullOrEmpty()]
    [Switch]
    ${Fragment},

    [ValidateNotNullOrEmpty()]
    [System.String[]]
    ${PostContent},

    [ValidateNotNullOrEmpty()]
    [System.String[]]
    ${PreContent})

begin
{
    try {
        $outBuffer = $null
        if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
        {
            $PSBoundParameters['OutBuffer'] = 1
        }
        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('ConvertTo-Html',
            [System.Management.Automation.CommandTypes]::Cmdlet)

        # define string variable to become the target command line
        #region Initialize helper variable to create command
        $scriptCmdPipeline = ''
        #endregion

        # add new parameter handling
        #region Process and remove the Path parameter if it is present
        if ($Path) {
            $PSBoundParameters.Remove('Path') | Out-Null
            $scriptCmdPipeline += " | Out-File -FilePath $Path"
        }
        #endregion

        $scriptCmd = {& $wrappedCmd @PSBoundParameters}

        # redefine command invocation
        #region Append our pipeline command to the wrapped command script block
        $scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
                [string]$scriptCmd + $scriptCmdPipeline
            )
        #endregion

        $steppablePipeline =
          $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
        $steppablePipeline.Begin($PSCmdlet)
    } catch {
        throw
    }
}

process
{
    try {
        $steppablePipeline.Process($_)
    } catch {
        throw
    }
}

end
{
    try {
        $steppablePipeline.End()
    } catch {
        throw
    }
}
<#

.ForwardHelpTargetName ConvertTo-Html
.ForwardHelpCategory Cmdlet

#>}

Hope you find this useful and it gets you the feature you wanted without waiting for PowerShell v3. ;)

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: , , , , , ,

Adding Direct Reports action

Here’s my quick addition to Kamran’s “How to export data from Active Directory using the free PowerGUI tool“:

If you might need to find users managed by someone more than once, there are a couple of ways you can make this simpler for your self:

A. Download and install free Org Chart powerpack, or

B. Follow these instructions to add a Direct Reports button to PowerGUI admin console:

1. Start PowerGUI admin console and browse to Active Directory / Users.

2. In the right-hand pane, right-click the category to which you want to add the action and pick New / Script Action from the shortcut menu.

new-script-action

3. In the New Action dialog box, type in the action name (e.g. Direct Reports), and script:

$input | ForEach-Object {
    Get-QADUser -Manager $_
}

(The script is quite trivial: for each user account currently selected in the PowerGUI grid, we get the list of AD users which have this object specified as Manager).

direct-reports

3. On that same dialog box, click the Display Configuration button, and then in the Display results group, select the Display the results in a nested view option.

This tells PowerGUI that your action outputs objects to the grid (which it does – we need to see the reports selected user has!)

nested-view

4. That’s it! Click OK on both dialog boxes, select the user account whose reports you need to see, and click your newly added Direct Reports action.

Big thanks to Kamran for posting his original tip and video.

Tags: , , , , , ,

Find large AD groups

Here’s a quick script which I wrote yesterday for one of our professional services engineers. Basically they wanted to give customer a report for large groups, i.e. groups with more than certain number of user accounts including the ones in nested groups.

This turned out to be very straight-forward with QAD cmdlets.

The script is below:

function Get-LargeADGroup {
param($limit = 75)

 Get-QADGroup | Foreach-Object {
   $members = $_ |
           Get-QADGroupMember -Indirect -Type 'user' `
              -DontUseDefaultIncludedProperties `
              -SizeLimit ($limit+1) -WarningAction SilentlyContinue
   if ( ($members -ne $null) -and
        ($members.gettype().Name -eq 'Object[]') -and
        ($members.Count -ge $limit)) {
     $_
   }
 }
}

#Usage
Get-LargeADGroup -limit 75 | Select Name, DN | Export-Csv c:\largegroups.csv

One trick worth pointing out is the use of -SizeLimit to not retrieve all members of the group – after all I just need to know if there is more than certain number of them.

Hope this helps! :)

PowerShell cmdlets in SharePoint 2010

[UPDATE] See this blog post for up to date SharePoint 2010 PowerShell Reference.

SharePoint has for a long time been a notable absentee at the PowerShell party – but it looks like the wait is now almost over. Technical Preview of the product shared by Microsoft just a few days ago has at least a few cmdlets shipped with it.

If you download “Customizing the Ribbon in Windows® SharePoint® Services “14”” documentation publicly available here (found this via Frank Migacz) you can find these instructions in the PDF file:

To deploy a Feature by using Windows PowerShell, you install it and then enable it by using the following commands.
Install-SPFeature FeatureId
Enable-SPFeature FeatureId -Url http://server/site/subsite

Judging by these examples, nouns in cmdlets are pre-fixed with “SP” to avoid possible naming conflicts.

I’ll keep you posted on any new developments and public information on PowerShell in SharePoint 2010.

For now, it looks like this is a huge step forward by the SharePoint team making the new version much more manageable. Awesome news and great work!

Tags: , ,

Restore Exchange data from PowerShell

Working with mail backups can be a pain. Especially when you need to do some kind of bulk operations like scan backups for particular mailboxes and find all emails with certain keywords and then export then to a PST or add them to a public folder.

Quest has recently PowerShell-enabled its Exchange/Notes/PST backup/recovery product – and we got a lot of great functionality available for your scripts/command line.

Here are a few examples:

  • Here’s how you can attach a standard Exchange backup and retrieve its messages as .msg files:

Attach-RMEExchangeDB -EdbPath "C:\Data\Edb\EdbSearchTest\EdbSearchTest.edb" | Get-RMEMessage | Export-RMEMessage -ExportPath "C:\Data\Exported\Msg" -Type Msg

  • Or look for particular keywords in Lotus Notes data:

Attach-RMELotusDB "C:\Data\Nsf" | Get-RMEMessage -SearchIn Subject, Body -Text "test" | Export-RMEMessage -ExportPath "C:\Data\Exported\Eml" -Type Eml

  • Or restore a folder to PST:

Attach-RMEExchangeDB "C:\Data\Edb\EdbSearchTest\EdbSearchTest.edb" | Get-RMEFolder "John Doe\Inbox" | Restore-RMEFolder -TargetPath "C:\Data\Restored\Pst"
Obviously you can restore to live mailboxes and public folders, work with attachments, and so on, and so forth.

Here’s full list of the cmdlets – each of the pages has examples and details on parameters, etc.:

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.

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: , , , , , ,

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