Redundantly Minimalist, redux

If I told you that before I cross the street, I stop, look, and listen, perhaps you’d think I was sensible. If I told you I just listen, you know I’d probably have a run-in with a silent electric car. Yet if I mentioned that I look left, right, down, and up (in case there’s something falling in the street), maybe you’d think I was being paranoid about being hit by a meteor or space junk?

With messaging services, how much redundancy is sensible and productive? Can you have too much redundancy? With Exchange Server 2007, Five Things About Exchange Server 2007 You Probably Don’t Know (part 2), we looked at going from one Exchange server to four servers for minimal redundancy. Exchange Server 2010 gives us some extra freedoms beyond its predecessor.

During a recent First Look at Exchange Server 2010 session, among the many questions were “How are consolidated redundant servers accomplished?” and “I understand resiliency, but how do we make the HT and CAS redundant?” Let’s look at a few aspects of each of these questions.

Like Exchange Server 2007, the 2010 version has Edge Transport, Unified Messaging, and of course the triumvirate Hub Transport, Client Access, and Mailbox roles. Single or multiple instances of these roles could be deployed in Exchange Server 2010 environments, and with the exception of the Edge Transport role, the other roles can be consolidated and combined onto a single Windows Server 2008 (R1 SP2 or R2) based server. But certain rules have changed in Exchange 2010 compared with the past.

Exchange Server 2010 no longer requires the clustered mailbox roles (active and passive) be hosted by dedicated servers, unable to host other roles as the 2007 version had. This opens up many possibilities. While the Hub Transport (HT) and Client Access (CA or CAS) server roles could be consolidated using Exchange 2007, when using Exchange 2010, the Mailbox (MB) role could also be consolidated with CA and HT roles, and perhaps Unified Messaging (UM) as well, as long as there is sufficient capacity on the server. Yes, even with clustering for mailbox resiliency.

One of the ramifications of this new flexibility is that Exchange Server 2010 allows deployments of two servers instead of four as a minimal redundant configuration. There are several new features which make this possible.

1. Beside Exchange ActiveSync (EAS), Outlook Web App/Access (OWA), POP3, IMAP4, Web Services, and RPC/HTTP proxy access, even local Messaging Application Programming Interface (MAPI) access via the Remote Procedure Call (RPC) goes through the Client Access (CA) role in Exchange Server 2010. Contrast this with Exchange 2007 allowing local MAPI/RPC clients to connect directly with Mailbox (MB) role servers.

1. Mailbox Databases in Exchange 2010 are no longer configured under the auspices of each server which hosts the MB role. Instead, the databases are configured within a Database Availability Group (DAG). The servers with the MB role then are configured to participate in the DAG.

1. Mailbox resiliency is supported by allowing the servers added to a DAG to replicate the databases in the DAG. The mailboxes in those databases are therefore stored redundantly. For two or more servers with the MB role within the same Active Directory (AD) site, this is similar to Exchange 2007’s Cluster Continuous Replication (CCR). Between AD sites, a delay can be added, reminiscent of Exchange 2007’s Standby Continuous Replication (SCR).

1. Transport resiliency is supported by utilizing Shadow Redundancy which allows multiple transport servers to retain a given message until it has been delivered to one of the MB servers in the DAG. Then the transport servers move the message from the regular queues to a shadow queue. Once the message has been replicated to all DAG replicas, the transport servers remove their copies of the message to their transport dumpster.

When these features are combined with a failover cluster to support the storage redundancy, and a network load balancing cluster to support the network protocol redundancy, some of Exchange Server 2010’s advantages can be realized. With redundant MB, HT, CA, and UM roles in just two or more servers, architects and administrators deploying and maintaining Exchange Server 2010 have much more control over scalability and availability at potentially much lower cost.

PowerShell cmdlets (W2K8R2) for Fine-Grained Password Policy

object394

Many people still hold on to archaic notions that UNIX systems are always managed via a command line and Windows systems are always managed with shiny graphical user interfaces. Neither has been true for a decade or two now, but I nevertheless find it intriguing when someone asks a question such as:

“The lab exercise I just did on Fine-Grained Password Policy helped me to understand what is involved in creating these policies, but isn’t there a better way than using ADSIedit.msc?”

The Active Directory Services Interface (ADSI)-based editor (ADSIedit.msc) is a GUI which is useful for managing aspects of Active Directory which don’t have nicely laid out display specifiers for the other AD management tools. The Windows Server 2008 version of ADSIedit.msc does allow editing Password Settings Objects (PSOs) used with Fine-Grained Password Policies (FGPPs), however each setting of the policy is configured on a separate property sheet. Even with Active Directory Users and Computers’ Attribute Editor in Windows Server 2008, we can see the various attributes of a FGPP in a list, but it’s not very pretty nor as manageable as some people would like.

Luckily we have Windows PowerShell. With Windows PowerShell version 2.0 on Windows Server 2008 R2, we have a number of cmdlets used for managing FGPP and the Default Domain Password Policy (DDPP) as well. Yes, from a command line if you wish, or in a script if you prefer, yet you could build a GUI using these cmdlets if you wanted to.

As these cmdlets are part of the ActiveDirectory module of PowerShell 2.0, you’ll want to ensure that this module has been imported. Next, it is useful to obtain a list of related cmdlets. Consider the following two commands:

import-module ActiveDirectory
get-command -CommandType cmdlet -module ActiveDirectory *pol* | FT Name,Definition -auto

Piping the Get-Command output to “Group Noun” prior to using Format-Table (FT) will show that there are four cmdlets used for working directly with the ADFineGrainedPasswordPolicy, two cmdlets for ADDefaultDomainPasswordPolicy, three which deal with assigning password policy to certain users or groups (ADFineGrainedPasswordPolicySubject), and the most valuable cmdlet of them all, Get-ADUserResultantPasswordPolicy for confirming which policy is affecting a particular user.

Without getting into all of the great details of fine-grained password policy, let’s look at a very brief example to get you started. Here’s the overall process. First you’ll want to define a new password policy, next assign it to apply to certain groups of users or individual users, and finally, confirm the correct configuration.

New-ADFineGrainedPasswordPolicy “Help Desk PP” 30

In its most basic form, the New-ADFineGrainedPasswordPolicy cmdlet just needs a new policy name and a precedence value. Of course, many more parameters could be configured at the time of creation of the policy by adding them to the command line. By default, the FGPP object will be created with the original default values which the default domain password policy starts with, not the current values from that policy nor another FGPP. Yet if you want to get a little more advanced, you can copy settings from one FGPP to another, from the DDPP to a new FGPP, or programmatically hand craft a FGPP using the .NET interface.

Add-ADFineGrainedPasswordPolicySubject “Help Desk PP” “Ferris Bueller”

If the global group, user, and inetOrgPerson objects to which the policy should apply were not specified when the FGPP (PSO) was created, the Add-ADFineGrainedPasswordPolicySubject cmdlet can be used to add them later. Someone can be removed from a policy later with the Remove-ADFineGrainedPasswordPolicySubject cmdlet.

Note that for all of these cmdlets, when you’re using them in PowerShell interactively, you don’t have to type the full name. For example, typing New-ADF<tab> expands to the new policy cmdlet name, typing Add-ADF<tab> expands to the add subject cmdlet name. Similarly, typing Get-ADUserR<tab> expands to the Get-ADUserResultantPasswordPolicy cmdlet.

Get-ADUserResultantPasswordPolicy “Ferris Bueller”

Using Windows PowerShell 2.0 cmdlets for managing password policies in Windows Server 2008 R2 is fairly straightforward. If you’re using fine-grained password policies in an Active Directory Domain Services environment, this approach to managing these policies is worth a look.

Assigning Fine-Grained Password Policies

In an article a few months ago, we looked at Windows Server 2008’s Fine-Grained Password Policy for facilitating differentiated password policies within a domain. We then revisited the topic and focused on the meanings of the settings involved, except for one. Fine-Grained Password Policy (FGPP) is specified in password settings objects (PSOs). Technically, the object class in the Active Directory for a PSO is msDS-PasswordSettings. Perhaps the most important attribute of this class is the msDS-PSOAppliesTo, which specifies who this password settings object applies to.

The msDS-PSOAppliesTo is a multi-valued attribute which specifies the distinguished names (DNs) of the users, inetOrgPersons, and global groups (general or shadow) to which this PSO may apply. When a user changes their password, their password is reset, or their password is set for the first time upon account creation, the list of PSOs in the domain is examined. For each PSO, the group memberships of the user are considered along with their individual identity. According to the msDS-PSOAppliesTo values in each PSO, there could be several matching candidate PSOs which might apply to this user. If this is the case, the list is sorted by type and precedence and the PSO with the lowest numbered precedence value is chosen, with preference given to direct user/inetOrgPerson application above group membership. That one PSO will be enforced for this user. If there are no matching PSOs for this user based on their identity and group memberships, the domain-based password policy will be in force.

If you’re managing PSOs using ADSIedit.msc, you can add users or groups to the list of msDS-PSOAppliesTo by Windows account or distinguished name. Under the hood, if you’re using LDIF, LDAP, or other direct methods for managing the PSOs, the distinguished name format must be used.

Note that if you apply a PSO to a non-security (i.e. distribution) group, or a group of universal or domain local scope, it will not have any effect. Only global security groups can be used. If there is not an existing global security group with the proper members who must have the same password policy, a group could be created just for this purpose. In fact, if the membership of such a group shadows the set of user accounts contained in a particular organizational unit (OU), it is customary to manually make the membership agree even when user accounts (or inetOrgPerson) are added and removed from the OU. When we maintain a global security group with membership which matches the set of users contained in a particular OU, we could call that group a shadow group.

Fine-Grained Password Policy Settings

aduc-msDS-Password-Settings

 

In an article a few months ago, we looked at Windows Server 2008’s Fine-Grained Password Policy for facilitating differentiated password policies within a domain. As promised, let’s revisit the topic and focus on the meanings of the settings involved.

Fine-Grained Password Policy (FGPP) is specified in password settings objects (PSOs). Technically, the object class in the Active Directory for a PSO is msDS-PasswordSettings. Here is an example from the earlier article.

dn: CN=Sales Password Policy,CN=Password Settings Container,CN=System,DC=777,DC=wernerconsulting,DC=com

objectClass: msDS-PasswordSettings

cn: Sales Password Policy

msDS-MaximumPasswordAge: -18144000000000

msDS-MinimumPasswordAge: -6048000000000

msDS-MinimumPasswordLength: 16

msDS-PasswordHistoryLength: 24

msDS-PasswordComplexityEnabled: TRUE

msDS-PasswordReversibleEncryptionEnabled: FALSE

msDS-LockoutObservationWindow: -6000000000

msDS-LockoutDuration: -18000000000

msDS-LockoutThreshold: 3

msDS-PasswordSettingsPrecedence: 10

This is an LDIF style description of one Password Settings Object (msDS-PasswordSettings). Note that the first line is the distinguished name (DN) of the object which is within the Password Settings Container (in the System container) of the domain. The objectClass is msDS-PasswordSettings as mentioned earlier. The common name (CN) of the object is the first relative distinguished name of the DN, which in this case is CN=Sales Password Policy. In the cn attribute value, the CN= tag is not used, although this tag does appear as part of the DN. That’s just regular characteristics of any object in the directory (Active Directory in this case).

The remainder of the attributes of the PSO shown here all begin with the msDS- prefix, signifying that these attributes are Microsoft Directory Services specific and not industry standard attribute types. Some of these values take on Boolean values (true/false), others are small positive integers as counts or lengths, while several of these settings represent time intervals.

msDS-MaximumPasswordAge: -18144000000000

First let’s look at the maximum password age. The attribute type is msDS-MaximumPasswordAge. The value of this attribute is a time interval, represented as a negative integer indicating the number of one hundred nanosecond (100 ns) time units to allow a user under the influence of this policy to retain the same password. The example value is -18144000000000 in 100 ns units, which is 1814400 seconds. Of course, dividing that by 86,400 seconds (the number of seconds in one 24-hour “day” (nychthemeron)), reveals that this is 21 days. Therefore, according to this part of the policy, users affected by this policy would need to change their password within 21 days.

msDS-MinimumPasswordAge: -6048000000000

Next we have the minimum password age (msDS-MinimumPasswordAge) for which our example shows a value of -6048000000000, which is 604,800 seconds or seven days.  This parameter controls a mandatory waiting period intended to prevent users from changing their passwords too frequently. In this example, users following this policy cannot change their password within seven days of having previously changed it.

msDS-MinimumPasswordLength: 16

Password strength is, to  a degree, related to length. Of course, if a hacker knows that someone’s favorite letter is ‘j’ and the user uses this letter often or exclusively in passwords, guessing ‘jjjjjjjjjjjjjjjj’ (16*’j’) as a password by weighted brute force isn’t hard, especially if the attacker knows that the minimum length of the password must be sixteen characters. Indeed, the part of a PSO which controls minimum password length is the msDS-MinimumPasswordLength attribute, shown in this example with a value of 16. If you’re not using smartcards or multi-factor authentication, use of pass-phrases is recommended over simple passwords, and using the Unicode character set wisely is also recommended as length alone without variability and complexity isn’t incredibly secure. Using at least 15 or 16 characters exceeds the classic LAN Manager 7+7 = 14 character chunking rule.

msDS-PasswordHistoryLength: 24

Many people don’t like having to change their passwords, and if we require long passwords, they might want to reuse a recent password. For example, let’s imagine that someone only had to change their password every three months. They could have four different passwords, one per season/quarter, and rotate back around to the first. Actually, some people have been known to try having just two passwords and flip-flopping between them. The msDS-PasswordHistoryLength governs the number of past passwords kept per user account which forces people to use different passwords each time they change their password. In other words, with a password history length setting of 24, twenty-four passwords would be remembered per user such that we would have to have at least 25 different passwords or just give up on reusing passwords ever.

msDS-PasswordComplexityEnabled: TRUE

Windows Server 2008’s password complexity rules are designed to promote the use of passwords which are harder to guess, harder to dictionary attack, and take longer to brute force attack. The built in rules are that you need to include characters from three out of the following four groups: capital (upper case) Latin letters [A-Z], regular (lower case) Latin letters [a-z], decimal digits [0-9], and other non-alphanumeric characters. In addition, the password must also be at least eight characters in length and not include part of the username. The msDS-PasswordComplexityEnabled attribute is set to TRUE in our example.

msDS-PasswordReversibleEncryptionEnabled: FALSE

According to part of the Windows Server 2008 Security Guide on Domain Policy Settings, “You must enable [the ‘store passwords using reversible encryption’] policy setting when using the Challenge-Handshake Authentication Protocol (CHAP) through remote access or Network Policy Server service. It is also required when using Digest Authentication in Internet Information Services (IIS).”

The PSO attribute for this setting is msDS-PasswordReversibleEncryptionEnabled, which we have set to a value of FALSE in this example. Except when required by specific applications and services, this setting should be disabled (enabled = false) as we’ve done here. If enabling password storage with reversible encryption is a requirement in part of your organization, being able to focus it on particular user accounts with fine-grained password policy is a great advantage over the old domain-based password policy.

msDS-LockoutObservationWindow: -6000000000

If I type my password wrong several times in a row, or if someone is trying to guess my password with repeated logon attempts, Windows can decide to lock out my user account. There are three main settings which are used to control this behavior in policy. The first of these, msDS-LockoutObservationWindow, is used to specify the duration of time during which failed logon attempts will be counted. This time window is set to -6000000000 units (100 ns ticks) in this example, which is 600 seconds, or 10 minutes. This means that if I mistype my password a certain number of times during a ten minute interval from the first failure, my account could be locked out.

msDS-LockoutDuration: -18000000000

Another part of the account lockout functionality is how long to lock out an account which has passed the bad logon attempt threshold during that observation window. The msDS-LockoutDuration attribute controls this lockout time, for which our example used a value of -18000000000 ticks, which because it’s negative is a duration (time interval) rather than absolute time from the Windows epoch, and again in 100 ns ticks. Therefore, this value is 30 minutes. This interval starts counting from the time of account lockout.

msDS-LockoutThreshold: 3

The msDS-LockoutThreshold attribute is the number of bad logon attempts within the observation window at which to lock out the account. Here, the value of three (3) in the context of the ten minute observation window and 30 minute lockout duration means that if I mistype my password when logging on three times within 10 minutes, my account will be locked for 30 minutes. People who have account management abilities would be able to unlock the account. Using a policy with a lockout threshold of zero (0) effectively disables the account lockout feature.

msDS-PasswordSettingsPrecedence: 10

The precedence for the PSO is stored in the msDS-PasswordSettingsPrecedence attribute, shown here with a value of ten. This value is only important when there are two or more policies which would apply to a particular user. The lowest numbered policy would be used. Just remember at the next team sporting event you go to, shouting “We’re number one!” is a lot better than yelling “We’re number ten!”

Although it was not shown in the above example, perhaps the most important attribute is the msDS-PSOAppliesTo, which specifies who this password settings object applies to. Details on this will be addressed in another article.

 

 

Methodologically Sending Messages

posh-send-email

 

Long, long ago, in a blog post not too far away, I wrote about how to use CDO.Message to send email messages via Windows PowerShell. In an Exchange Server 2007 class, the topic came up again and I ended up briefly demonstrating the basics of composing the to, from, subject, and body of a message.

Certainly, putting this sort of functionality into a script or function that can be quickly and easily used has immense power… and danger. But this is true of scripting in general. The whole idea of writing scripts and functions is that they make it incredibly easy to do something which you might need to do often, or even things you do rarely but don’t want the hassle of remember all of the little incantations which make up a useful operation.

Whether you need to send messages for systems management reporting, or as a part of an application or workflow you’re automating, having a quick functional technique for sending email is indispensable. Therefore, I thought it was worth revisiting using CDO.Message’s Send method to send email. Whether you’re using PowerShell version 1.0, PowerShell 2.0, and using XP, Vista, Windows 7, or servers running Windows Server 2003, 2008, 2008 R2, with or without Exchange locally, if you have CDO.Message installed, for example with Outlook or Exchange, this tiny script should work.

param(

$to = “coolpeople@gk.com

$from = “administrator@acme.com

$subject = “This is a test”

)

$o = new-object -com CDO.Message

$o.To = $to

$o.From = $from

$o.Subject = $subject

$o.TextBody = $input

$o.Send()

While this script could have easily been implemented as a function, the important thing is not its implementation, but the ease of use. Let’s look at some quick examples. Note that the body of the email is formulated from the input to the script, while the to, from, and subject values are passed as parameters. Our first example uses the default values in the script’s parameter (param) block for the to, from, and subject fields of the message.

“Hello, this is a scripted message” | ./send

This sends the message to the default recipient coolpeople@gk.com, from administrator@acme.com, with the subject of “This is a test.” Of course, the body of the message is simply the string “Hello, this is a scripted message” which was piped into the script. Next, here is an example of assigning a value for the To parameter instead of the default.

“Hello, this is a scripted message” | ./send -to “Administrator@acme.com

Using the -To parameter name, we specify a value of the recipient, “administrator@acme.com“. Note however that because this is the first parameter declared in the parameter block of the script’s definition, we could actually have left out the “-to” parameter name and just passed the “administrator@acme.com” as a position parameter. Any of the parameters could be specified in the declared order: to, from, subject, or using their parameter names.

But how would we send something more interesting for the body of the message? Easily. We can use any command, cmdlet, PowerShell expression or pipeline to generate the body and just pipe it into the send script. For example:

type send.ps1 | ./send -subject “This is a little script to send messages.”

The above example uses the “type” alias for the Get-Content cmdlet to retrieve the contents of the file, actually this same script file being described herein, and pipes that to the send script, invoked with a specific subject.

With a bit of thought and some creativity, you can compose elaborate messages. For extended features such as attachments and multiple recipients, some minor changes could be made to this script to better utilize additional features of the CDO.Message class. An example worth investigating is using the ConvertTo-HTML cmdlet to convert tables to HTML content to be used as the body of a message. The input could be anything, from Get-Process, Get-Service, Get-ChildItem (dir, ls, gci), Get-WMIObject, and thousands of other possibilities.

What kinds of messages would you send with such a script? Hopefully this brief script helps you along the road to smoother systems administration.

Cloning Parallel OU Hierarchy

Earlier we looked at how to create an organizational unit and delegate management control over it. A related but different question also arose recently in class. A somewhat succinct summary is “How would you copy an OU hierarchy without its contents?”

In other words, what if you have an OU called Miami with a number of subordinate OUs, which happen to be full of users, computers, groups, and other such objects. These sub-OUs could represent departments, projects, and other such divisions within the Miami operations of the organization. How would you create a copy of this OU structure, the departments, for a new branch office in Vancouver? Let’s take a look at how we could do this in Windows PowerShell. The technique here doesn’t assume PowerShell 2.0 nor the use of Active Directory cmdlets – just plain old ds* commands.*

To understand a possible solution, first let’s see how we could enumerate the OUs in the Miami OU.

dsquery ou | select-string “Miami”

While “dsquery ou” could be given a DN such as “ou=Miami,dc=woodgrovebank,dc=com” as the search base for the query, simply using select-string, although less precise, doesn’t require knowing or typing the DN. Furthermore, dsquery’s “-o rdn” option could be used to simply display just the relative distinguished name of the matching OUs, and the -scope option could be used with one of the keywords: subtree, base, or onelevel to yield the whole hierarchy beneath the Miami base OU (subtree), just Miami (base), or just the immediate sub-OUs (onelevel). Again, here none of those options were used – we’re just selecting any OUs whose DN includes the string Miami.

Let’s assume that the sub-OUs are BranchManagers, CustomerService, Investments, and Marketing, then this would result in the list:

ou=Miami,dc=woodgrovebank,dc=com

ou=BranchManagers,ou=Miami,dc=woodgrovebank,dc=com

ou=CustomerService,ou=Miami,dc=woodgrovebank,dc=com

ou=Investments,ou=Miami,dc=woodgrovebank,dc=com

ou=Marketing,ou=Miami,dc=woodgrovebank,dc=com

To create a Vancouver OU with BranchManagers, CustomerService, Investments, and Marketing sub-OUs in it, merely do the following:

dsquery ou | select-string “Miami” | foreach-object { dsadd ou “$($_ -replace ‘Miami’,’Vancouver’)” }

Note that we used apostrophes in the -replace operator rather than quotations marks because the strings ‘Miami’ and ‘Vancouver’ are already within a pair of quotation marks around the whole expression “$($_ -replace ‘Miami’,’Vancouver’)”. As an alternative, escaping the inner quotation marks with a grave accent could have been used instead of the apostrophes. Also, the ForEach-Object cmdlet could have been invoked using its alias % or the alias foreach.

In summary, creating an OU with the same sub-OUs as another can be done rather easily, without even knowing or typing the full name of the original OU. In this case, we described confirming the list of original OUs first before diving into the attempt to create the new ones. Remember that just using the up-arrow key recalls the previous command, so the sanity check isn’t really much extra typing.

There are many operations which can be done in PowerShell rather simply, without getting into complicated APIs, cmdlets, or variables.

*Note, if you are managing from a Windows Server 2008 machine which is not a domain controller, you can install the AD DS remote management tools for domain controllers including the ds* commands (e.g. dsquery) by running:

servermanagercmd -install RSAT-ADDC

Using dsadd and dsacls for Template OUs

Having recently noted (see “Learning PowerShell in Degrees – Creating Template OUs”) that there are varying degrees to which we may choose to apply classic command line management, classic shell style management, and programmatic-style management when using Windows PowerShell, now let’s see what makes the example script for Creating Template OUs tick.

This is a simple function which was mostly written ad hoc during a class demonstration. Rather than use .NET/COM object classes with the Active Directory Services Interface (ADSI), or using PowerShell version 2.0 Active Directory provider and cmdlets, this particular incarnation. First let’s look at the whole function, which predominantly just uses dsadd and dsacls commands with some string manipulation. Then we’ll walk through each line.

function New-DelegatedOU( $ou ){

$dom = ([ADSI]””).distinguishedName

$ouDN = “ou=$ou,$dom”

dsadd ou $ouDN

dsadd user “cn=$($ou) Template,$($ouDN)” -dept “$ou”

dsadd group “cn=$($ou) Users,$($ouDN)” -members “cn=$($ou) Template,$($ouDN)”

$groupDN = “cn=$($ou) Admins,$($ouDN)”

dsadd group “$groupDN”

dsacls $ouDN /g “$($groupDN):SDRCWDWOWPRPCALO” | out-null

dsacls $ouDN /g “$($groupDN):CCDC;user;” | out-null

dsacls $ouDN /g “$($groupDN):CCDC;group;” | out-null

dsacls $ouDN /g “$($groupDN):SDRCWDWO;;user” | out-null

}

Now we’ll take it one line at a time.

function New-DelegatedOU( $ou ){

As the function declaration line indicates, the purpose of the function is to allow simple creation of an organizational unit (OU) for which management will be delegated to a group. Actually, that’s not all evident from the name New-DelegatedOU, however now that you know the primary purpose of the script, hopefully the name makes sense. Notice that this function takes one parameter, $ou, which is intended to be an Organizational Unit (OU) name to create. It is in the second line of the body of the function, $ouDN = “ou=$ou,$dom”, that it is implied that the value of $ou has no ou= tag and is not a distinguished name (DN) including the parent OU or domain. Certainly, if this function were to be generalized, it would be advantageous to have the ability to handle a full DN to select a specific domain or to create subordinate OUs rather than just those at the top level. Instead, this example keeps it simple, albeit inflexible.

$dom = ([ADSI]””).distinguishedName

Continuing into the body of the function, we first save away the distinguished name (DN) of the current Active Directory domain name into the variable $dom. As the [ADSI] type accelerator with an empty string returns an object with many attributes of the domain object, we simply use the parentheses to refer to the object and use the dot operator to extract the distinguishedName property. If the parentheses weren’t used, the dot operator would attempt to extract the attribute from the string “”, which would result in a null value, which in turn would be passed to the [ADSI] accelerator.

$ouDN = “ou=$ou,$dom”

Once we have the DN of the domain in the $dom variable, it’s simple string manipulation to formulate the DN of an organizational unit which is one level deep inside the domain. In this case, the $ou variable simply holds the tagless name of the OU and therefore we concatenate the tag “ou=” with the variable $ou, a literal comma, followed by the domain’s DN in $dom simply using the string expression “ou=$ou,$dom” and save the catenated value in the variable $ouDN. The use of regular quotation marks (often called double quotes) rather than apostrophes (often called single quotes) is intentional as we do want the values of the variables $ou and $dn within the quotation to be evaluated rather than taken literally as the name dollar sign, oh, you, for example.

If this script were modified to allow one or more hierarchical DNs for OUs to be specified, the logic here would necessarily become a bit more complicated. Or not. In the case a sequence of relative distinguished names (RDNs) up to but not including the domain level, we would merely have to catenate that sequence with the domain’s DN. In the case of a  fully qualified DN, there would be nothing to do here other than make sure we’re referring to the proper variable. For iteration through DNs, merely add a ForEach-Object loop or some other such control structure at this point, with the resulting $ouDN variable usable inside the loop.

dsadd ou $ouDN

Just as the name New-DelegatedOU implies, a part of or goal here is to create an organizational unit object in the directory. There are of course numerous ways to accomplish this. If you’ve done any VBscript or other similar scripting, you may be thinking of using ADSI, which could be used from PowerShell too. If you really wanted to, you could use LDIFE, CSVDE, or many other such tools. Creating an OU could also be accomplished with PowerShell version 2.0, in even easier ways, with the Active Directory Module on Windows Server 2008 R2, we could use the New-ADOrganizationalUnit cmdlet.

Instead, we use a far simpler mechanism: dsadd. The ds* commands (e.g. dsquery, dsget, dsmod, dsmove, dsrm, and dsadd) are supported in Windows Server 2008 R2, Windows Server 2008, and even Windows Server 2003 (RTM + R2). We’ll use another ds* command, dsacls, later. For now let’s look at the use of dsadd here – dsadd ou $ouDN – that’s pretty simple. It creates the OU with the given distinguished name. Why use the other beautiful yet complicated methods and cmdlets when this friendly little command does the trick so elegantly?

dsadd user “cn=$($ou) Template,$($ouDN)” -dept “$ou”

But we want to do more with this function than merely create an OU. The next line adds a user account to the OU which you could use as a template for other user accounts created here. While crafting useful template user accounts is a bit of a fine art, we’ll only make a little skeleton user. Here we use “dsadd user.” In this example, we do two things which are slightly more complicated than when we created the OU. First, we formulate the DN in a string expression on the same line as we invoke dsadd. The string “cn=$($ou) Template,$($ouDN)” is used only one more time here, therefore we don’t really need to save the result in a variable. In a real template user scenario, we would likely create group memberships, manager relationship, street address, and other such attributes of the user account. Here we simply add a department value. That’s the second minor complexity in this dsadd user example beyond the dsadd ou case. We simply give the -dept parameter name and “$ou” for the department value.

dsadd group “cn=$($ou) Users,$($ouDN)” -members “cn=$($ou) Template,$($ouDN)”

Here we make a security group for all of the ordinary mere mortal users in the OU and make the template user the first member of the group. When this template user is later copied to create other users in the OU, many attributes of the template user, including this group membership, will be copied to the resultant new user accounts. It can be quite useful for security and messaging reasons to have a group with all of the users in the OU as members.

$groupDN = “cn=$($ou) Admins,$($ouDN)”

Next the name of another group for the administrators of the OU is calculated.

dsadd group “$groupDN”

Back to the easy style of when the OU was created, this OU admins group is created with the preformed DN, without any other parameters or members. It is up to the invoker of this function, or someone else, to add members to this group after the New-DelegatedOU is created. But simply calling a group “Marketing Admins” does nothing magical with respect to delegation – that magic comes next.

dsacls $ouDN /g “$($groupDN):SDRCWDWOWPRPCALO” | out-null

One of the most powerful and potentially confusing of the ds* commands is dsacls. Like cacls and xcacls, this is not something describing anything witches do. Just as cacls (change access control lists) and xcacls (extended cacls) do for the NTFS file system, dscacls allow us to view, assert, grant, deny, and otherwise manipulate access control lists (ACLs) on objects in the directory service (i.e. Active Directory).

Our use of dsacls here has two parts, the first of which is simple. Indeed, the first parameter to dsacls is the $ouDN variable which refers to our newly created OU. Now the delegation begins. The second part starts with the /g switch which means that the following argument is a parameter specifying permissions to grant (/g = Grant) to a specific security principal. In this case, the security principal is none other than the OU admins group which was recently created. The permissions granted in this particular case follow the colon after the group name, as identified in the string “SDRCWDWOWPRPCALO” which may be easier to pronounce than supercalifragilisticexpialidocious if you take it two characters at a time. Here’s a quick break-down of the codes used here:

This is not the only delegation we want to perform, so the function continues with some additional lines which use dsacls to grant various more specific permissions.

dsacls $ouDN /g “$($groupDN):CCDC;user;” | out-null

In this example, have have the permissions specification after the group DN of “CCDC;user;” which indicates the ability to create and delete child objects of type “user” within this OU.

dsacls $ouDN /g “$($groupDN):CCDC;group;” | out-null

Similarly, the ability to create and delete security groups and distribution groups within the OU is delegated to the OU admins.

dsacls $ouDN /g “$($groupDN):SDRCWDWO;;user” | out-null

Finally, we assign another slightly different kind of rights, granted to the OU admins. Here, note that the word user appears not between the two semicolons, but after the second one in the “SDRCWDWO;;user” specification. Although this entry is granted at the OU level, it could be inherited from the OU down to all of the contained user account objects. The dsacls command supports other generic, specific, and per-property permissions which we shall not enumerate here. Consider looking at Microsoft’s Support article “How to Use Dsacls.exe in Windows Server 2003 and Windows 2000” which mostly applies to Windows Server 2008 as well. Other TechNet articles and references on the web may provide further details.

}

That’s the end of this quick example function New-DelegatedOU. As the needs of each organization, department, and project may come with specific requirements for delegation, the techniques of this elementary example could be tailored to meet your needs.

We’ll end with two questions. If you are automating or scripting management in Windows PowerShell, or any environment for that matter, would you prefer to use regular command line tools, more object-based cmdlets, authoring of importable/exportable files, script-writing-scripts, programming interfaces such as ADSI, or some other technique? Secondly, do you prefer using pipelining heavily, or lots of nested expressions (think (lots of) parentheses), loads of variables, or another style?