April 16, 2010

Create users in Active Directory from a CVS file

[update august 23th, 2010] This is a new version (1.10) that allows you put an ‘#’ in the ‘password’ field to be able to update properties of an existing user without modifying his password.

#
# .SYNOPSIS 
#   Create users in Active Directory from a CVS file (';' delimited).
#	
# .DESCRIPTION
#   The CSV file used to create the users must have a special format.
#   The first line of the CVS file must contain columns names.
#   Each column name must correspond exactly to an attribute name of 
#   a user object in the Active Directory.
#   All Active Directory attributes containing a text value can be used.
#  
#   The following columns (attributes) are mandatory :
#
#      Name               If this field contain a '#', the line will
#                         ingnored (commented).
#      OU
#      Password           If this filed contain # '#', the password
#                         will not be set. Of course the user must
#                         already exist.
#
#   The follwing attributes (columns) cannot be used, they are automatically
#   generated by the script :
#
#      sAMAccountName
#      userPrincipalName
#
#   The following special columns (attributes) are handle by the script :
#
#      OU                  Organizational Unit containing the user to be created.
#                          The distingushedName of the domain will be appended.
#      sAMAccountName      Automicically set to the value of the Name column
#      userPrincipalName   Automicically set to the value of the Name column
#                          plus the canonicl name of the domain
#                         (ex: user1@domain.com)
#      MemberOf            List of group names separeted by a comma (,) do not
#                          specified the full distingushedName or the group
#                          only the name, the script will do the rest.
#      AccountExpires      Set the expiration date of the new account
#
#   If an attribute of the new user cannot be set, the new user account will
#   be left disabled.
#
#   If the user account already exists, the script will not end and will
#   try to set the attributes of the accounts.
#
#   Here's a sample CSV file :
#   Name;OU;Password;displayName;mail;description;AccountExpires;memberOf
#   PdelphiC1;ou=UtilisateursExternes;!42mj428;My First Poweshell User;chico@hotmail.com ;This is a test;2010-09-15;group 1,group 2
#   PdelphiC2;ou=UtilisateursExternes;$93me934;My second PowerShell User;binou@hotmail.com ;This is a test;2010-09-15;group 3
#   # This is a sample input file for Import-Users.ps1
#
#   Author   : Jean-Pierre.Paradis@fsa.ulval.ca
#   Date     : august 23,  2010
#   Version  : 1.10
#   Language : PowerShell 2.0
#    
# .PARAMETER CSVInputFile
#   Input file name.
#		
# .EXAMPLE
#  C:\PS> .\Import-users.ps1 -CSVInputFile "users.cvs"
#
# .LINK 
#  Inspire by Don Jones and Jeffrey Hicks in 'Windows powershell 2.0 TFM' from Sapien Press
#

#REQUIRES -version 2.0

param (
	[parameter(
	Mandatory=$true)]
	[String[]]$CSVInputFile
	) 
	
Set-StrictMode -Version 2.0

# Script parameters
# $CSVInputFile="newusers.csv"
$Delimiter=";"
$DirectoryServicesCOMException_ENTRY_EXISTS=-2147019886

function get-scriptdirectory {

# .SYNOPSIS 
# 	Return the current script directory path, compatible with PrimalScript 2009
# 	Equivalent to VBscript fso.GetParentFolderName(WScript.ScriptFullName)
# 	Requires PowerShell 2.0
#    
# .DESCRIPTION
#	Author   : Jean-Pierre.Paradis@fsa.ulaval.ca
#	Date     : March 31, 2010
#	Version  : 1.01
#
# .LINK 
# 	http://blog.sapien.com/index.php/2009/09/02/powershell-hosting-and-myinvocation/

    if (Test-Path variable:\hostinvocation) 
    	{$FullPath=$hostinvocation.MyCommand.Path}
    Else {
   		$FullPath=(get-variable myinvocation -scope script).value.Mycommand.Definition }  	
	if (Test-Path $FullPath) {
    	return (Split-Path $FullPath) 
		}
    Else {
		$FullPath=(Get-Location).path
		Write-Warning ("Get-ScriptDirectory: Powershell Host <" + $Host.name + "> may not be compatible with this function, the current directory <" + $FullPath + "> will be used.")
		return $FullPath
		}
}

function get-GroupDN {
# .SYNOPSIS 
# 	Return an Active Directory group distinguished name
param (
	[parameter(
	Mandatory=$true)]
	[String[]]$GroupShortName
	)
	
	$AdSearch = [ADSISearcher] ""
	$AdSearch.Filter = "(&(objectCategory=group)(cn=$($GroupShortName)))"
	$AdResult = $ADSearch.FindOne()
	if ($AdResult -ne $null) {
		return $AdResult.properties.distinguishedname
		}
	Else {
		Write-Warning ("get-GroupDN: Can't find group named <$($GroupShortName)>")
		return $null
		}
}


function add-UserToGroups {
# .SYNOPSIS 
# 	Add a user to an array of groups
param (
	[parameter(
	Mandatory=$true)]
	[String]$UserDistinguishedName,

	[parameter(
	Mandatory=$True)]
	[System.Array]$Groups
	
	)
	
	Foreach ($GroupName in $Groups) {
		$GroupDN = get-GroupDN($GroupName)
		if ($GroupDN -ne $null) {
			Write-host "   Adding to group '$($groupname)' ..."
			$GroupObj=[adsi]("LDAP://"+$GroupDN)
			$GroupObj.member.add($UserDistinguishedName) >$null
			
			Try {
				$GroupObj.SetInfo()
				}
			Catch [System.DirectoryServices.DirectoryServicesCOMException] {
				If ($_.exception.ErrorCode -eq $DirectoryServicesCOMException_ENTRY_EXISTS) {
					Write-Warning "The user is already a member."
					}
				else {
					Throw $_
					}
				}
			}
	}
}


# Read the data file and filter any commented line
$CSVInputFileFullPath = (get-scriptdirectory($CSVInputFile)) + "\" + $CSVInputFile
$imported=Import-Csv $CSVInputFileFullPath -Delimiter $Delimiter | where {$_.name -notlike '#*'}

# retrieve list of csv column headings
# Each column heading should correspond to an ADSI user property name
$properties=$imported | Get-Member -type noteproperty | `
where {$_.name -ne "OU" -and $_.name -ne "Password" `
-and $_.name -ne "Name" -and $_.name -ne "sAMAccountName" `
-and $_.name -ne "userPrincipalName"}

# Get the domain canonicalName (mydomain.com) & distinguishedname
$rootDomain=[ADSI]""
$rootDomain.RefreshCache(@("canonicalName"))
$rootDomainCanonicalName=($rootDomain.get("canonicalName") -replace "/","")
$rootDomainDistinguishedName=$rootDomain.distinguishedName

# Loop throuth the data
foreach ($user in $imported) {
 
 		# Create the account
        $UserAlreadyExist=$false
		$OUDistinguishedName = $user.OU+","+$rootDomainDistinguishedName
		Write-Host "Creating User '$($user.Name)' in '$($OUDistinguishedName)' ..."
		[ADSI]$OU="LDAP://"+$OUDistinguishedName
		$newUser=$OU.Create("user","CN="+$user.Name)
		$newUser.Put("sAMAccountName",$user.Name)
		
		# Get the domain canonicalName (mydomain.com)
		$rootDomain=[ADSI]""
		$rootDomain.RefreshCache(@("canonicalName"))
		# Set userPrincipalName
		$newUserPrincipalName=$user.Name + "@" + $rootDomainCanonicalName
		$newUser.Put("userPrincipalName",$newUserPrincipalName)
		
		# commit creation to Active Directory
		Try {
			$newUser.SetInfo()
			}
		Catch [System.DirectoryServices.DirectoryServicesCOMException] {
			If ($_.exception.ErrorCode -eq $DirectoryServicesCOMException_ENTRY_EXISTS) {
				Write-Warning "The object already exist, will try to reset properties."
				$newUser=[adsi]("LDAP://"+"CN="+$user.Name+","+$OUDistinguishedName)
                $UserAlreadyExist=$true
				}
			else {
				Throw $_
				}
			}
			
		# set a password 
        
        If ($user.password -notlike '#*') {
		  Write-Host ("   Setting password")
		  $newUser.SetPassword($user.Password)
		  $newUser.SetInfo()
        }

		# set additional properties
		foreach ($prop in $properties) {
		$value=$user.($prop.name)
		if (($value -ne $Null) -and ($value.length -gt 0)) {
			Switch ($prop.name ) {
				"MemberOf" {
					# Write-Host ("   Adding to groups $($Value)")
					add-UserToGroups $newUser.distinguishedname @($value -split ",")
					}
				"AccountExpires" {
					$newAccountExpires = ($value -as [datetime])
					if ($newAccountExpires -ne $null) {
						Write-Host ("   Setting '$($prop.name)' to $($newAccountExpires)")
						$newUser.InvokeSet("AccountExpirationDate",$newAccountExpires)
						}
					Else {
						Write-Warning "   Can't convert '$($value)' to acceptable format for AccountExpires"
						}
					}
				default {
					#only set properties that have values
					Write-Host ("   Setting '$($prop.name)' to '$($value)'")
					$newUser.put($prop.name,$value)
					}
				}
			}
		}
		$newUser.SetInfo()
		
		# Activate the account
        If ($UserAlreadyExist -ne $True) {
          Write-Host ("   Enabling account")
		  $newUser.Invokeset("AccountDisabled", "False")
		  $newUser.SetInfo()
        }
 }

No comments:

Post a Comment