airplane

As promised.  Not really much “new” going on here that hasn’t been done by others.  But maybe, just maybe, there’s a slim, microscopic, chance that it might help someone somewhere at sometime in some way.

It takes a CSV file with the first row being the names of Active Directory User object attributes, including Exchange 2016 and/or Skype for Business, extended attributes, as well as custom, glue-sniffing, kitty-litter munching, underwear over the head attributes.  You know, the kind you might find at a keg party.

I manually declare the list(s) of attributes to read form the CSV input and apply to the user accounts.  The reason for this is I want to selfishly take control of the bus and steer it directly into the mountainside.  No, seriously, it’s because the input file might contain bogus attribute names, and I’d rather not lean on exception handling due to performance overhead.

For example, allowing all mushroom-chomping hallucinated attributes in the bar entrance, while the bouncer plays with his/her phone, with 100,000 users, slows the processing down by about 20-25% on a good day.  White-listing them, (having the bouncer taser them with some Krav Maga and a set of steak knives, at the door and piling their bodies on a wheelbarrow) cuts that margin out entirely.  Geez.  I’ve been watching the wrong movies for too long.

Anyhow, here goes…

<#
.DESCRIPTION
Import-ADUsers.ps1
Imports AD user accounts from a CSV input file. Accommodates custom
and extended attributes if requested.

.PARAMETER CsvFile
Required: Full path and filename of CSV input file.

Note that the CSV input file must have the following...

1. The top row contains AD schema attribute names
2. One of the headings is "path" for the LDAP path where accounts will be created
3. No read-only attributes are specified (e.g. msExtendedAttribute20)

.EXAMPLE
.\Import-ADUsers.ps1 -CsvFile ".\sad_angry_users.csv" -Verbose

.NOTES
Author = Skatterbrainz.wordpress.com
Date = 01/13/2017
Version = 2017.01.13.01

USE AT YOUR OWN RISK - NO WARRANTY PROVIDED
User accepts any and all risk and liability 
#>

param (
  [parameter(Mandatory=$True, Position=0)]
  [ValidateNotNullOrEmpty()]
  [string] $CsvFile
)
$ErrorActionPreference = "Stop"
$startTime = Get-Date
$ScriptVer = "2017.01.13.01"

function New-RandomPassword {
  [CmdletBinding(DefaultParameterSetName='Length')]
  param (
    [parameter(Mandatory=$False)][int] $Length = 15
  )
  <#
  .DESCRIPTION
  Generate a random password of a given length.
  
  .PARAMETER Length
  Optional: Length of password to generate (number of digits).
  Default: 15

  .EXAMPLE
  New-RandomPassword -Length 24

  .NOTES
  thanks to http://blog.oddbit.com/2012/11/04/powershell-random-passwords/
  #>

  $punc = 46..46
  $digits = 48..57
  $letters = 65..90 + 97..122
  $password = Get-Random -count $length `
    -Input ($punc + $digits + $letters) |
      % -Begin { $aa = $null } `
      -Process {$aa += [char]$_} `
      -End {$aa}
  return $password
}

function Update-ADUser {
  [CmdletBinding(DefaultParameterSetName='User')]
  param (
    [parameter(
      Position=0, 
      Mandatory=$True, 
      ValueFromPipeline=$True)
    ] 
    [Microsoft.ActiveDirectory.Management.ADUser]
    $User, 
    [parameter(
      Position=1, 
      Mandatory=$True)
    ]
    [PSCustomObject[]]
    $DataRow, 
    [parameter(
      Position=2, 
      Mandatory=$True)
    ]
    [string] $AttList
  )
  <#
  .DESCRIPTION
  Applies an array of attribute values to a specified AD user object.
  
  .PARAMETER User
  An object defined from Get-ADUser.
 
  .PARAMETER DataRow
  An array obtained from one row in a CSV input file.
 
  .PARAMETER AttList
  A comma-delimited string that contains the names of AD user schema attributes to apply.

  .EXAMPLE
  Update-ADUser -User $user -DataRow $csvRow -AttList $atts
  #>

  foreach ($att in $AttList.Split(',')) {
    write-verbose "internal: att = $att"
    if (!($excluded.Contains($att))) {
      $v = $DataRow."$att"
      write-verbose "internal: value = $v"
      if ($($v.Trim()).Length -gt 0) {
        write-verbose "info: applying input: $att"
        write-verbose "info: value = $v"
        Set-ADUser $User -replace @{"$att"="$v"} -Confirm:$False
      }
      else {
        write-verbose "info: ignoring null input: $att"
      }
    }
  }
}

write-output "info: script version $ScriptVer"

if (!(Test-Path $csvfile)) {
  write-host "error: $csvfile not found"
}
else {
  $rowcount = 0
  $excluded = ("name","path","samaccountname")
  write-verbose "info: reading input data file..."
  $csvData = Import-Csv -Path $csvfile
  foreach ($row in $csvData) {
    $upath = $row.Path
    $rpwd = New-RandomPassword -Length 24
    $sam = $row.sAMSccountName -replace "(?s)^.*\\", ""
    try {
      $user = Get-ADUser -Identity "$sam" -ErrorAction SilentlyContinue
      write-output "info: updating user: $sam"
    }
    catch {
      $user = $null
      write-output "info: creating user: $sam"
      New-ADUser -Name "$sam" -SamAccountName "$sam" -Path $upath -AccountPassword (ConvertTo-SecureString $rpwd -AsPlainText -Force) -Enabled:$True
      $user = Get-ADUser -Identity "$sam"
    } 
    if ($user -eq $null) {
      break
    }
    # pathetic manual declaration of lonely attributes...
    $atts1 = "employeeid,displayName,title,telephoneNumber,mobile,"
    $atts1 += "physicalDeliveryOfficeName,mail,initials,Description,"
    $atts1 += "company,facsimileTelephoneNumber,department,co,l,manager,"
    $atts1 += "st,streetAddress,postalCode,sn,givenName,userPrincipalName,"
    $atts1 += "employeeType,pager,MailNickName,roomNumber,businessCategory,"
    $atts1 += "departmentnumber,otherTelephone,street,adminDescription,"
    $atts1 += "countryCode,initials,ipPhone,localeID,c,proxyaddresses"
 
    Update-ADUser $user $row $atts1

    write-verbose "info: exchange attributes..."

    $atts3 = "destinationIndicator,ExtensionAttribute1,ExtensionAttribute3,"
    $atts3 += "extensionattribute5,extensionattribute6,extensionattribute8,"
    $atts3 += "extensionattribute12,ExtensionName"
 
    Update-ADUser $user $row $atts3

    write-verbose "info: custom attributes..."

    $atts4 = "xlisprimary,xlacctcd,xlsublob,xlDvision,xlsubdivision,"
    $atts4 += "xlSubDivDescr,xlSection,xlSectionDescr,xlsubSection,xlsubSectionDescr"

    Update-ADUser $user $row $atts4

    $rowcount += 1
  }
  write-host "info: completed $rowcount rows"
}
$StopTime = Get-Date
$RunTime= New-TimeSpan -Start $startTime -End $StopTime
write-host "info: runtime was $($RunTime.Seconds) seconds. You can make this faster! You must try!"

 

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s