The Inflatable Dinghy

The Inflatable Dinghy

Preface: Don’t do this on prod gear. This is a bad idea!

I’ve long been a fan of automated deployment.

During the fourth semester technical project at Fanshawe, I had a wonderful domain tree with OUs and global groups, group policies and delegated permissions. It was truly a nice domain. It just…. Felt a little lonely.

Enter PowerShell. Using a fairly simple script, I was able to cozy up the domains with the right users in the right places.

This table shows the imaginary organization we were presented with. By simply pasting this into a CSV file, we can easily bring it into the script:

$csvStructure = Import-Csv -Delimiter ',' -Path .\OrganizationalStructure.csv

This assumes the following about this domain:

  • There is an OU in the root of all your domains called ‘Accounts’
  • This script will be done as the Administrator in the domain that holds the InfrastructureMaster role.
  • We’re crazy enough to let a script written by a stranger on the Internet run on one of the most important servers in the organization

Okay. Let’s try it out..!


[System.Collections.ArrayList]$Departments = (($csvStructure | gm -MemberType NoteProperty).Name)

foreach ($Unit in $csvStructure) {

  Write-Verbose $Unit.BusinessName -Verbose

  $UnitDN = "ou=Accounts"
  Write-Output $UnitDN
  $UnitDNFullyQualifiedPath = $Unit.domain.split('.')
  $UnitDNFullyQualifiedPath | foreach { $UnitDN += ",dc=$_" }

Starting off, let’s establish the base DN of the business unit. In the case of HealthUnit this is "ou=HealthUnit,ou=Accounts,DC=sub,DC=domain,DC=ca"

Let’s create a container there, as well as a security group for all objects in that Unit:


  New-ADOrganizationalUnit `
    -name $Unit.BusinessName `
    -Path $UnitDN `
    -server $Unit.DomainControllerFQDN

  $UnitDN = "ou=$($Unit.businessName),$UnitDN"

  New-ADGroup -Name "$($Unit.BusinessName)" 
    -Path $businessUnitDN `
    -GroupCategory Security `
    -GroupScope DomainLocal ` 
    -Server $Unit.DomainControllerFQDN

Next, we iterate through each department and create sub containers for only occupied departments. We then create a User and Computer container within that Unit’s Department.

  Foreach ($Department in $Departments ) {

    if($Unit.$Department -eq 0) { Continue; }

    # Create business unit OU
    $DepartmentDN = "ou=$Department,$UnitDN"
    Write-Verbose "`t$DepartmentDN "
    New-ADOrganizationalUnit -name $Department ` 
      -Path $businessUnitDN `
      -Server $Unit.DomainControllerFQDN

    # Create a User container in that Unit's Department:
    Write-Verbose "`t`tou=UserAccounts,$DepartmentDN"
    New-ADOrganizationalUnit `
      -name "UserAccounts" `
      -path $DepartmentDN `
      -server $Unit.DomainControllerFQDN
    
    # Create a Workstation container in that Unit's Department:
    Write-Verbose "`t`tou=Workstations,$DepartmentDN"
    New-ADOrganizationalUnit `
      -name "Workstations" `
      -path $DepartmentDN `
      -server $Unit.DomainControllerFQDN 

Finally, let’s create an AD group for that Unit’s Department, and add it to the Unit’s security group:

    New-ADGroup `
      -name "$($Unit.BusinessName)-$Department" `
      -path "$DepartmentDN" `
      -GroupCategory Security -GroupScope DomainLocal `
      -server $Unit.DomainControllerFQDN
    
    Add-ADGroupMember `
      -Identity "cn=$($Unit.BusinessName),$UnitDN" `
      -Members "cn=$($Unit.BusinessName)-$Department,$DepartmentDN" `
      -Server $Unit.DomainControllerFQDN
  }
}

Nice! Now we have a fully laid out and standardized AD domain!

…We still have a problem though. We’ve built all the rooms and hallways, but we’re still horribly lonely in here!

For this part, I found a list of the top 10K first names and last names on the internet. These aren’t too hard to find and import, so I will leave that part to the reader. They exist in my script as the lists $Top10kFirstNames and $Top10kLastNames.

Let’s start by re-using some of the control code from the last script:

foreach ($Unit in $csvStructure) {
  $UnitDN = "ou=Accounts"
  $UnitDNFullyQualifiedPath = $Unit.domain.split('.')
  $UnitDNFullyQualifiedPath | foreach { $UnitDN += ",dc=$_" }

  $UnitDN = "ou=$($Unit.businessName),$UnitDN"

  Foreach ($Department in $Departments ) {
    if($Unit.$Department -eq 0) { Continue; }
    $DepartmentDN = "ou=$Department,$UnitDN"

And right about here we’ll drop in some new code to add users. We will iterate through the Departments in that Unit, and for each one generate a random username. Passwords are generated using the New-Guid command. We will then add this user to the correct security group for that Department.


    $usersPath = "ou=UserAccounts,$DepartmentDN"
    
    1..$Unit.$Department | Foreach {

      $FirstName = ($Top10kFirstNames | Get-Random)
      $LastName = ($Top10kLastNames | Get-Random)
      $UPN = "$FirstName.$LastName@$($Unit.Domain)"
      $SamAccountName = (($FirstName[0..10] -join '') `
          + ($LastName[0..1] -join '')).ToLower()
      $GroupName = "cn=$($Unit.BusinessName)-$Department"

      New-ADUser -GivenName "$FirstName" `
        -Surname "$LastName" ` 
        -Name "$FirstName $LastName" `
        -AccountPassword:$(New-Guid) `
        -Path $usersPath 
        -UPN $UPN 
        -SamAccountName $SamAccountName
        -Server $Unit.DomainControllerFQDN

      $userID = Get-ADUser "cn=$FirstName $LastName,$usersPath" `
        -Server $Unit.DomainControllerFQDN

      $groupID = Get-ADGroup "$GroupName,$DepartmentDN" `
        -Server $Unit.DomainControllerFQDN

      Add-ADGroupMember -Identity $groupID  `
        -Members $userID `
        -Server $Unit.domainControllerFQDN
    }
  }
}

…And that’s it! After a couple minutes, our domain controllers are toasty hot, our replication is going nuts, and most importantly, we’re a little less lonely here in the testing environment.