Archive for the 'oneliner' Category

Find and fix broken inheritance

Broken permissions inheritance can be a source of multiple issues – with PowerShell you can get such issues located and fixed with an easy oneliner.

Getting security inheritance blocked is easy – locating and setting it back can be hard. One big customer of ours once had most of their mail transport paralyzed with a branch administrator clearing the inherit permissions checkbox he thought should not have been there. Nicolas is reporting similar issues with Exchange 2007 deployments.

Seeing whether an AD object has permissions inheritance blocked is as easy as checking the object’s DirectoryEntry.psbase.ObjectSecurity.AreAccessRulesProtected property.

So for example, to get a list of all users in the domain who has inheritance off you just need to run:

Get-QADUser -SizeLimit 0 | where {$_.DirectoryEntry.psbase.ObjectSecurity.AreAccessRulesProtected}

I am using -SizeLimit 0 so I retrieve all users and not just the default 1000.

Fixing inheritance is even easier with the new Set-QADObjectSecurity cmdlet introduced in AD cmdlets 1.1.

So if you want to fix inheritance for all AD users (caution: you might want to just get the list of the accounts first using the command above to make sure you do not “fix” legitimate exceptions) you just need to pipe the collection into Set-QADObjectSecurity -UnlockInheritance:

Get-QADUser -SizeLimit 0 | where {$_.DirectoryEntry.psbase.ObjectSecurity.AreAccessRulesProtected} | Set-QADObjectSecurity -UnlockInheritance


Tags: , , , , , , , , , ,


Changing AD permissions

I’ve recently blogged about retrieving AD security with PowerShell, as you can probably guess for every Get-* there is a Set-* and AD cmdlets 1.1 provide you an easy way to change the permissions set on any AD object.

Add-QADPermission and Remove-QADPermission are your biggest friends here.

Well, obviously and the power of the PowerShell pipeline. My favorite example is copying permissions from one object to another with that simple oneliner:

Get-QADPermission “Dmitry Sotnikov” | Add-QADPermission “Evil Tween”

This simple line is incredibly powerful. It takes all permissions directly set on the first objects and adds them onto the second one. Of course you could put where in the middle to do some filtering if you need.

Of course you can explicitly grant specific rights on specific objects. Suppose you want to give Administrator full control over an OU and everything in it. Easy:

Add-QADPermission OU=Demo,DC=mydomain,DC=local -Account Administrator -Rights GenericAll

You can use the -Deny parameter to deny access, -PropertySet to work with property sets πŸ™‚ and -ApplyTo to select whether you want to give rights only to this object or its children or any possible combination. So for example you could do:

Add-QADPermission dirObjectIdentity -Deny -Account trusteeIdentity -Rights WriteProperty -PropertySet (General-Information,Web-Information) -Property samAccountName -ApplyTo ThisObjectOnly

You can also pipe any AD object into these cmdlets (similar to reading the objects) for bulk operations:

Get-QADUser -City Orlando -SecurityMask Dacl | Add-QADPermission -Account Dmitry Sotnikov -Rights ReadProperty

And, as you can easily guess Remove-QADPermission can delete any ACE in much the same way. For example, let’s remove all the Deny ACEs from a particular object:

Get-QADPermission objectIdentity -Deny | Remove-QADPermission

You can find more information and examples in the user’s guide and by typing get-help for any of these cmdlets.

Download the cmdlets and give us your feedback at the AD PowerShell discussion forums.

Tags: , , , , , , , ,

Read Active Directory Permissions

One of the biggest advances of AD cmdlets 1.1 is support for AD security operations. In this post we will look at the Get-QADPermission cmdlet and how you can use it to read permissions set on AD objects.

To get a list of permissions set on an AD objects directly you just need to use:

Get-QADPermission Identity – where identity is Name, DN, Canonical name, Domain\Name, and so on. For example:

Get-QADPermission Dmitry Sotnikov

As usual you can pipeline a set of objects into the cmdlet to get results for all of them, e.g.:

Get-QADUser -SearchRoot domain.local/employees/chicago -SecurityMask DACL | Get-QADPermission

Here I am getting access control for all permissions directly set on users in the domain.local/employees/chicago OU. Note that I am also using the -SecurityMask parameter to tell the Get-QADUser cmdlet to retrieve the access list (DACL – Discretionary Account Control List). This is optionally but highly recommended because if you use this parameter Get-QADPermission does not have to retrieve the DACL again – less calls to the DC, better performance.

The examples above deal only with the permissions set on the object directly, you can add inherited permissions by simply adding -Inherited. In a similar fashion, the -SchemaDefault parameter adds Account Control Entries (ACE) that came from the default security descriptor. So this will give you everything:

Get-QADPermission Dmitry Sotnikov -Inherited -SchemaDefault

Or the same but much faster:
Get-QADUser -Name Dmitry Sotnikov -SecurityMask DACL | Get-QADPermission -Inherited -SchemaDefault

You can look for the rights which specific trusties have:

Get-QADPermission Dmitry Sotnikov -Account (domain\bill, self) -UseTokenGroups

Note that I have added -UseTokenGroups to make sure I get Bill’s rights even if he got those via group membership.

Or for specific rights set on specific properties:

Get-QADPermission Dmitry Sotnikov -Rights WriteProperty -Property (samAccountName,name)

You can also check for extended rights. Let’s see if I can change my password:

Get-QADPermission Dmitry Sotnikov -account self,everyone -Allow -ExtendedRight User-Change-Password -Inherited –SchemaDefault

-Allow and -Deny parameters allow to check specifically for allowing and denying ACEs.

And there’s much much more: just check out:

get-help Get-QADPermission -detailed

Good job by the team trying to cover each and every case they could think of. If you can think of something they have not covered or implemented in a suboptimal way – please provide your feedback in the AD PowerShell forum – the team is there and listening.

Here’s the AD cmdlets download page which has the latest 1.1 beta drop.

Tags: , , , , , , , , ,

Managing Terminal Services attributes with PowerShell

Terminal Services properties is definitely a set of properties you would want to bulk-manage, and as we all know PowerShell is the best tool for any bulk operations.

We have recently (in the AD cmdlets 1.0.6 drop) improved the experience here (thanks to requests from Simon and George) and there are a few gotchas to keep in mind – so I thought I would summarize this all in one blog post.

Getting TS attributes

Retrieving terminal services properties is easy. You just execute Get-QADUser and the objects retrieved will have the corresponding properties – for your convenience, all starting with Ts.

PS C:\> get-qaduser "Dmitry Sotnikov" | format-list Ts*

TsProfilePath : \\server\tsprofiles\DSotnikov
TsHomeDirectory : \\server\tshome\DSotnikov
TsHomeDrive : P:
TsAllowLogon : True
TsRemoteControl : 0
TsMaxDisconnectionTime : 00:00:00
TsMaxConnectionTime : 00:00:00
TsMaxIdleTime : 00:00:00
TsReconnectionAction : 1
TsBrokenConnectionAction : 0
TsConnectClientDrives : True
TsConnectPrinterDrives : True
TsDefaultToMainPrinter : True
TsWorkDirectory : c:\
TsInitialProgram : C:\Program Files\Quest\Initialize.exe

Important: Terminal services properties are only available when AD cmdlets are run on Windows Server 2003 or 2008. Workstation operating systems (XP, Vista) do not support programmatic TS administration so the properties will not be retrieved.

[Update] See these instructions on enabling Terminal Services management on XP and Vista.

Changing TS attributes

TS properties are not (yet) available as Set-QADUser parameters and need to be changed as properties of retrived objects as shown in the example below:

$u = get-qaduser dsotnikov
$u.TsProfilePath = 'c:\profile'

[Update] With AD cmdlets 1.1.1 Set-QADUser exposes all the TS attributes as its parameters so changing TS attributes is now much easier:

get-qaduser -searchroot mydomain.local/uk/london | set-qaduser -TsHomeDrive 'P:'

Again, make sure you follow the system requirements.

Property reference

Here’s a quick reference to the properties (I borrowed some of the descriptions from the MSDN page):

Property Description
TsProfilePath Roaming or mandatory profile path to use when the user logs on to the terminal server. The profile path is in the following network path format:\\ServerName\profiles folder name\UserName

Note A Terminal Services profile path is used only for logging on to a terminal server.

TsHomeDirectory Home directory for the user. Each user on a terminal server has a unique home directory. This ensures that application information is stored separately for each user in a multi-user environment.To set a home directory on the local computer, specify a local path; for example, C:\Path. To set a home directory in a network environment, you must first set the TsHomeDrive property, and then set this property to a UNC path.
TsHomeDrive Home drive for the user. In a network environment, this property is a string containing a drive specification (a drive letter followed by a colon) to which the UNC path specified in the TsHomeDirectory property is mapped.To set a home directory in a network environment, you must first set this property and then set the TsHomeDirectory property.
TsAllowLogon Value that specifies whether the user is allowed to log on to the terminal server.
TsEnableRemoteControl Value that specifies whether to allow remote observation or remote control of the user’s Terminal Services session. For a description of these values, see the RemoteControl method of the Win32_TSRemoteControlSetting WMI class.

Name Value
Disable 0
EnableInputNotify 1
EnableInputNoNotify 2
EnableNoInputNotify 3
EnableNoInputNoNotify 4
TsMaxDisconnectionTime Maximum amount of time, in minutes, that a disconnected Terminal Services session remains active on the terminal server. After the specified number of minutes have elapsed, the session is terminated.
TsMaxConnectionTime Maximum duration, in minutes, of the Terminal Services session. After the specified number of minutes have elapsed, the session can be disconnected or terminated.
TsMaxIdleTime Maximum amount of time, in minutes, that the Terminal Services session can remain idle. After the specified number of minutes have elapsed, the session can be disconnected or terminated.
TsReconnectionAction Value that specifies whether to allow reconnection to a disconnected Terminal Services session from any client computer. The value is 1 if reconnection is allowed from the original client computer only, and 0 if reconnection from any client computer is allowed.

Note This property currently is not used by Windows Server Terminal Services.

TsBrokenConnectionAction Value that specifies the action to take when a Terminal Services session limit is reached. The value is 1 if the client session should be terminated, and 0 if the client session should be disconnected.
TsConnectClientDrivesAtLogon Value that specifies whether to reconnect to mapped client drives at logon. The value is 1 if reconnection is enabled, and 0 if reconnection is disabled.

Note This property currently is not used by Windows Server Terminal Services.

TsConnectClientPrintersAtLogon Value that specifies whether to reconnect to mapped client printers at logon. The value is 1 if reconnection is enabled, and 0 if reconnection is disabled.
TsDefaultToMainPrinter Value that specifies whether to print automatically to the client’s default printer. The value is 1 if printing to the client’s default printer is enabled, and 0 if it is disabled.
TsWorkDirectory Working directory path for the user.To set an initial application to start when the user logs on to the terminal server, you must first set the TsInitialProgram property, and then set this property.
TsInitialProgram Path and file name of the application that the user wants to start automatically when the user logs on to the terminal server.To set an initial application to start when the user logs on, you must first set this property and then set the TsWorkDirectory property. If you set only the TsInitialProgram property, the application starts in the user’s session in the default user directory.

Tags: , , , , , ,

Mail-Enabling Objects in Exchange 2003

How do you create thousands of mail-enabled user accounts and contacts on Exchange 2000 or 2003?

With Exchange 2007 the answer is obvious – use PowerShell!

With Exchange 2000 and 2003 the answer is… – use PowerShell. These systems don’t have native Exchange Management Shell, but WMI and AD cmdlets still give much easier solutions than VBScript and other alternative.

See this forum thread in which someone is providing PowerShell one-liners he used to create and mail-enable 18,000 users and 79,000 contacts.

Talk about productivity gains… πŸ˜‰

Tags: , , , , , , ,

AD User Provisioning from CSV Got Easier

Creating new AD user accounts from a csv-file data has become even easier. In versions prior to 1.0.6 you could import a csv file, and then use ForEach-Object and manual column mapping to assign the values to the attributes (for example, see this post on populating test environments):

Import-CSV c:\users.csv | ForEach-Object { New-QADUser -Name $_.Name -SamAccountName $_.Name -Department $_.Department -ParentContainer mydoman.local/demo }

This works but is somewhat redundant. If the CSV file already has a column named Name, why wouldn’t PowerShell assign that column to the Name property automatically?

This is exactly what we have added in AD cmdlets 1.0.6. Now you no longer have to use ForEach-Object (pipe import- directly into new-!) and don’t need to specify the parameters which are already in the CSV file!

This means that if I have a csv file like this:

"Aaron Nelson",anelson,Engineer
"Justin Starin",jstarin,Janitor

I can create these two new user accounts with the simple command below:

Import-Csv users.csv | New-QADUser -ParentContainer mydomain.local/test Import

Notice how much simpler it has become compared to the one used in the beginning of the post!

But wait, it becomes even better! You can mix and match the approaches by using some of the parameters from the CSV and adding others in the command-line. Let’s use the same CSV but set the City property to the accounts we create:

Import-Csv users.csv | New-QADUser -ParentContainer mydomain.local/test -City Melbourne Import

This takes the Name, SamAccountName, and Title from CSV, and adds the City from our command:

Get-QADUser -SearchRoot mydomain.local/test | Format-Table Name,samAccountName,Title,City

Name          samaccountname Title    City
----          -------------- -----    ----
Aaron Nelson  anelson        Engineer Melbourne
Justin Starin jstarin        Janitor  Melbourne

The whole purpose of PowerShell and AD cmdlets is to make AD management easier and more intuitive, and it is good to see another step in that direction.

By the way, this feature was influenced by the requests we were getting from the community: for example, from Jonathan Walz and his comments here. Thanks Jonathan and everyone on the AD PowerShell discussion forum!

Tags: , , , , , , ,

Copy AD accounts with PowerShell

Being able to copy AD accounts with one line of PowerShell code is probably my favorite feature of AD cmdlets RTM version.

Suppose you have an account the properties of which you would like to use to create another account? You probably want the new account to have a different name, password, etc. but it needs to have the same location, department and other attributes. The solution is extremely easy and straight-forward. You just need to do Get-QADUser for the sample account, and pipe into New-QADUser while specifying the new location and unique properties.

For example:

Get-QADUser 'James Johns' -export | New-QADUser -ParentContainer mydomain.local/test -Name 'Janny Grant' -SamAccountName jgrant -DisplayName 'Janny Grant' -FirstName Janny -LastName Grant -UserPassword 'J@nnysPwd' -import

One gotcha is that it will not copy the group membership, so you will have to use another oneliner for that:

(Get-QADUser 'James Johns').MemberOf | Add-QADGroupMember -Member ps64\jgrant

How cool is that? πŸ˜‰

Tags: , , , , , ,

Out-vCard: Exporting Outlook Address Book

Our local identity management guru Jackson Shaw tasked me with giving him an easy way to export contact information from corporate address book so you can then send it to someone for their reference. The standard format for Outlook to import contact information is vCard, but the problem is that Outlook can export to vCard only personal contacts, but not GAL entries. Needless to say, PowerShell is the answer. πŸ˜‰

This is the command-line which solves the task:

Get-QADUser "Dmitry Sotnikov" | Out-vCard

This will locate a user in your AD whose name is "Dmitry Sotnikov" (which probably means you work for Quest) and create a file "Dmitry Sotnikov.vcf" at the c:\ drive root.

If you want to export all members of a DL – this will work too:

Get-QADGroupMember DL.ProjectA | Out-vCard

This will create a vCard for each DL member.

And because it only reads data from your Active Directory you don’t need any administrative privileges. This will work for any domain user.

To make this work you need to:

  1. Install PowerShell and AD cmdlets.
  2. Copy/paste the following function into PowerShell command-line shell before running the commands or add it to your profile (My Documents/WindowsPowerShell/profile.ps1):

function Out-vCard {
$input | ForEach-Object {

    $filename = "c:\" + $_.Name + ".vcf"
    Remove-Item $filename -ErrorAction SilentlyContinue
    add-content -path $filename "BEGIN:VCARD"
    add-content -path $filename "VERSION:2.1"
    add-content -path $filename ("N:" + $_.LastName + ";" + $_.FirstName)
    add-content -path $filename ("FN:" + $_.Name)
    add-content -path $filename ("ORG:" + $_.Company)
    add-content -path $filename ("TITLE:" + $_.Title)
    add-content -path $filename ("TEL;WORK;VOICE:" + $_.PhoneNumber)
    add-content -path $filename ("TEL;HOME;VOICE:" + $_.HomePhone)
    add-content -path $filename ("TEL;CELL;VOICE:" + $_.MobilePhone)
    add-content -path $filename ("TEL;WORK;FAX:" + $_.Fax)
    add-content -path $filename ("ADR;WORK;PREF:" + ";;" + $_.StreetAddress + ";" + $_.PostalCode + " " + $_.City + ";" + $ + ";;" + $_.Country)
    add-content -path $filename ("URL;WORK:" + $_.WebPage)
    add-content -path $filename ("EMAIL;PREF;INTERNET:" + $_.Email)
    add-content -path $filename "END:VCARD"


Note that the script is something I put together in 15 minutes to help Jackson, so it still needs a few improvements when I have time:

  1. Need to add an optional parameter for the output folder.
  2. Need to actually look at vCard spec to make sure all attributes translate right.
  3. Need to look whether I need to check whether attributes are present. Does vCard format permit empty values or should their keys be in that case omitted?

Anyways, this seems to solve the task for now, I hope I have a few hours later to make it perfect. Feel free to do so yourself if you are interested.

Tags: , , , , , ,

Date/Time User Properties

One of the enhancements of the 1.0.5 release is more attributes being exposed in their native formats. For example, AD cmdlets automatically convert the properties which should be date/time to the DateTime time so you don’t have to worry about the conversions and can just work with them.

Let’s see which Date/Time attributes my account has:

Get-QADUser "Dmitry Sotnikov" -IncludeAllProperties | Get-Member -MemberType NoteProperty | where {$_.Definition -like "*DateTime*" } | Format-List Name, Definition

Name : accountExpires
Definition : System.DateTime accountExpires=12/31/9999 11:59:59 PM

Name : badPasswordTime
Definition : System.DateTime badPasswordTime=10/29/2007 1:22:06 PM

Name : createTimeStamp
Definition : System.DateTime createTimeStamp=6/16/2004 3:59:22 PM

Name : lastLogoff
Definition : System.DateTime lastLogoff=1/1/1601 12:00:00 AM

Name : lastLogon
Definition : System.DateTime lastLogon=10/30/2007 12:04:22 PM

Name : lockoutTime
Definition : System.DateTime lockoutTime=1/1/1601 12:00:00 AM

Name : modifyTimeStamp
Definition : System.DateTime modifyTimeStamp=10/29/2007 12:20:24 AM

Name : pwdLastSet
Definition : System.DateTime pwdLastSet=8/27/2007 4:09:54 PM

Name : whenChanged
Definition : System.DateTime whenChanged=10/29/2007 12:20:24 AM

Name : whenCreated
Definition : System.DateTime whenCreated=6/16/2004 3:59:22 PM

Let’s see what I was doing in the command above:

  1. I retrieved my user object using Get-QADUser and supplying the name.
  2. Used –IncludeAllProperties to make the cmdlet retrieve all AD attributes and not just the default set (which would not have: createTimeStamp and modifyTimeStamp).
  3. Used Get-Member and Where to only leave Property members of the DateTime type.
  4. Formatted the output as a list with the names and definitions (type and value).

Note that you can operate DateTime values for filtering too.

For example, to see a list of accounts which never logged on this year you would do:

$threshold = (Get-Date).AddYears(-1)
Get-QADUser -IncludedProperties lastLogonTimestamp | where { $_.lastLogonTimestamp -le $threshold }

Pretty straight-forward, right? πŸ˜‰


  • To have lastLogonTimestamp replicated between DCs the domain should be in Windows 2003 mode.
  • If your domain is still in Windows 2000 mode, you have to query for lastLogon from each DC (for every user), as lastLogon is a non-replicated attribute.
  • lastLogonTimestamp is updated each 14 days by default (in reality it is more often):

Tags: , , , , , ,

Using expressions in pipeline parameters

Often times you need the second cmdlet in a pipeline to not only accept the collection from the first one but also re-use some of the properties of the members. For example:

  • Renaming objects so the new name is the old one plus some prefix, or
  • Move objects to an OU based on some of objects’ attributes, or
  • Set password for a user to something containing his/her username.

I guess you got the idea. The problem with that is that in PowerShell if you just try to add this as an expression this will not work.

With AD cmdlets 1.0.5 the problem finally got a solution. Although PowerShell still does not allow to use expressions in the parameters you can use scriptblocks and AD cmdlets will handle the rest.

Just remember to use {} brackets instead of ():

[Update] This scriptblock approach works for Move/Rename but not for Set cmdlets:

# Add prefix to a set of user accounts:

Get-QADUser -SearchRoot ps64.local/test | Rename-QADObject -NewName {"Test_" + $_.Name}

# Restructure AD:

Get-QADUser -SearchRoot ps64.local/employees | Move-QADObject -to {"ps64.local/employees/" + $_.City}

For other cases you still need to use ForEach:

# Set password to username (note that your AD password restrictions might prevent you from setting passwords containing usernames)

Get-QADUser Test* | ForEach { Set-QADObject $_ -UserPassword {$_.sAMAccountName} }

#Set description including user properties

Get-QADUser | ForEach { Set-QADObject $_ -Description {$_.Title + " from " + $_.City + " " + $_.Department}}

Good luck with your bulk changes!

Tags: , , , , , ,


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 - WSO2 or anyone else for that matter. All trademarks acknowledged.

Β© 2007-2014 Dmitry Sotnikov

May 2023

%d bloggers like this: