Skip to content

Instantly share code, notes, and snippets.

@phyoewaipaing
Last active November 12, 2023 14:17
Show Gist options
  • Save phyoewaipaing/7eccf9f77b2cbfd6734982c2c157c3bb to your computer and use it in GitHub Desktop.
Save phyoewaipaing/7eccf9f77b2cbfd6734982c2c157c3bb to your computer and use it in GitHub Desktop.
Create AD Users in Nested OU v2.1
####################################################################################################################################################################
##*********************************** Script to create OUs and sub-OUs and Users in Active Directory ***************************************************************
## What this script will do:
## 1) It will read the csv file and trim start/end whitespaces.
## 2) create OUs and sub-OUs automatically as listed in 'OU' column. Be sure to use slash (/) between OU and sub-OUs
## 3) Before creating OUs and Users, it will test the domain names in csv file against the DC the script is running
## 4) It will throw an error:
## if user list csv file does not exists and does not have correct extension
## ActiveDirectory Powershell Module is not installed
## If the required column headers are missing in csv file
## If duplicate "Name" are found
## If the domain name is different from the one listed in csv file
## If the user's password policy doesn't meet the requirements
## and finally..
## If you do not have permissions to create users in Active Directory
## Author: Phyoe Wai Paing
## Country: Myanmar(Burma)
## Note: You can find this script and more at https://www.scriptinghouse.com/
## Version:
##[8/5/16, v1.1] Initial Release
##[1/25/18, v1.2] Added First Name, Last Name, Employee ID Attributes
##[4/29/18, v2.0] Can export the current users to csv file when no parameter is specified. Use -ImportCsvFile to create new users
## Prompt to inlude the new domain name & password to save in csv file.
## Change some properties: First Name, Last Name, Employee ID, Office Phone, Email, Job Title, OU Properties are changed to GivenName, SurName, EmployeeID.OfficePhone, EmailAddress, Title, Path: same as the output of get-aduser
## SamAccountName Property is added.
## OU name starting with letter 'U' or 'O' caused errors in early versions is fixed
##[5/17/18, v2.1] The issue that users 'Names' needed to be unique in domain level is fixed. It only needs to be unique in each OU level.
##
## Admin must specify the csv file path. Column names of CSV file should be as below.
## EmployeeID, DisplayName, OU, Description, Name, GivenName, SurName, SamAccountName, Title, Departement, Domain, Office, OfficePhone, Company, EmailAddress, Password ##
## 1, Mr. Phyo Wai, ICT/System Support, ICT System Administrator, phyo.wai, Phyo, Wai, phyo.wai, System Admin, System Support Section, contoso.com, Myanmar Office, 95-9-99999999, Consoto Airway, phyo@contoso.com, 12345678910
## ... ... ...
## ... ... ...
###########################################################################################################################################################
################## Validates csv headers so that it is not different from the input variable defined in script, but you can skip this check by removing the first lettet (#) of next line ##########
param([String]$ImportCsvFile)
$DomainName= (gwmi WIN32_ComputerSystem).Domain
If (!($ImportCsvFile))
{
Write-Host "No parameter is specified. The users and OUs of current domain will be exported to new domain."
$NewDomainName = Read-Host "Enter the new domain name(eg: contoso.com)"
$NewPasswords = Read-Host -AsSecureString "Enter new password (blank to insert manually in csv file)"
Write-Host "`nExporting Users of $DomainName domain. Please wait..."
$ExistingUsers = Get-aduser -Filter * -Properties * | select EmployeeID, DisplayName, Description, Name, GivenName, SurName, SamAccountName, Title, Department, Office, OfficePhone, Company, EmailAddress, @{N="OU";Exp={$Count=($_.DistinguishedName.split(',') -match 'dc=').count; $Arr1 = $_.DistinguishedName.split(','); [array]::reverse($Arr1); $Arr1[$Count..($Arr1.length-2)].substring(3) -join '/'}}, @{N="Domain";Exp={$NewDomainName}}, @{N="Password";Exp= { [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($NewPasswords)) }}
$NumOfOUs = (dsquery ou -limit 100000 | ? { $_ -NotLike "*OU=Domain Controllers,*" }).count
$ExistingUsers | export-csv -notype "AD_Users_Info_$DomainName.csv" ## Export current users
If (Test-Path AD_Users_Info_$DomainName.csv)
{
Write-Host "CSV file saved to $((gci AD_Users_Info_$DomainName.csv).FullName) Successfully. Please use this file with -ImportCsvFile option to create new AD users."
Write-Host "Number of Users Exported: $($ExistingUsers.count)"
Write-Host "Number of OUs Exported: $NumOfOUs"
}
Else
{ Write-Host -fore Red "Error occurred: Cannot export the csv file." }
}
elseif ((Test-Path $ImportCsvFile) -AND (Get-ChildItem $ImportCsvFile -EA SilentlyContinue) -like "*.csv") ## check if csv file exists and it has correct extension
{
$Total_OU_Created=0
$Total_User_Created=0
Import-module ActiveDirectory -EA SilentlyContinue
if(Get-Module | where {$_.Name -eq 'ActiveDirectory'}) ## check ActiveDirectory Powershell modules can be loaded.
{
$usersinfo=import-csv $ImportCsvFile
$validator_set=@("EmployeeID","DisplayName","OU","Description","Name","SamAccountName","Title","Department","Domain","Office","OfficePhone","Company","EmailAddress","Password","GivenName","SurName")
$input_headers= ($usersinfo | Get-Member -MemberType NoteProperty).Name
$Mismatches=$validator_set | where { $input_headers -Notcontains $_ }
If ($Mismatches -is [array])
{
[string]$Mismatches_Quoted_String = (($Mismatches | foreach { "$([char]34)$_$([char]34)"}) -join (','))
Write-Host -Fore Red "The following fields are invalid or blank in your csv columns. Please Check and correct."
Write-Host $Mismatches_Quoted_String
}
elseif ($Mismatches)
{
Write-Host -Fore Red "The following csv header is invalid or blank. Please check and correct"
Write-Host $Mismatches
}
else
{
$usersinfo | foreach { $_.PsObject.Properties | foreach { $_.value =$_.value.trim() } } ## Trim the whitespaces and group OUs
$ounames=($usersinfo | Group-Object OU).name
$Dup_Names = ($usersinfo | group -Property Name,OU | where { $_.count -gt 1 }).Name ## check if duplicate "Name" exists for each OU
$Dup_Names_Output = ($usersinfo | group -Property Name,OU | where { $_.count -gt 1 }) | % { "`'$($_.name.split(',')[0].Trim())`' in `'$($_.name.split(',')[1].Trim())`' OU" }
$DomainSplit = $DomainName.split('.')
$DomainJoin = ',DC='+($DomainSplit -join (',DC='))
$Domain_DN=$DomainJoin.TrimStart(',')
if ($Dup_Names) ### If duplicate user names are found, do not run the script
{
Write-Host -Fore Red "The following Name have duplicate entries. Please check and correct it."
$Dup_Names_Output;
}
else {
$Domains=($usersinfo | group-object -property Domain).Name ### Group the domain names listed in csv file
if ($Domains -isnot [array])
{
if( $Domains -match "$DomainName") ### check if domain name matches queried domain name, if not exits
{ Write-Host -Fore Green "Domain name matches:$DomainName"
######################### OU Operation, Create the nested sub-OUs ##########################
$ounames | foreach {
$OU_Split=$NULL
$Parent_OU=$NULL
$OU_Split = $_.split('/')
for($i=0;$i -lt $OU_Split.Length;$i++)
{
if($i -eq 0)
{
[string]$currentOU= ','+$OU_Split[$i] ###get the current OU and append ','
[string]$Parent_OU=$currentOU
[string]$Parent_Current_OU=$currentOU ### put the current OU as root OU for next loops
$OU_DN= ($Parent_OU -replace(",",",OU=")).TrimStart(',')
$Full_DN=$OU_DN+$DomainJoin
$ou_exists = [adsi]::Exists("LDAP://$Full_DN")
}
else
{
[string]$currentOU=$OU_Split[$i]
$Parent_Current_OU=','+$currentOU+$Parent_Current_OU ## get the current OU and append it to the previous OU path as stored in $Parent_OU
$OU_DN= ($Parent_Current_OU -replace(",",",OU=")).TrimStart(',')
$Full_DN=$OU_DN+$DomainJoin
$ou_exists = [adsi]::Exists("LDAP://$Full_DN")
}
if ($ou_exists) ## Check if OU exists and if not, create new OU
{
Write-Host -fore cyan "OU already created:$OU_DN"
}
else
{
$Full_DN_Split = $Full_DN.Split(',')
$FirstPart = $Full_DN_Split[0].substring($Full_DN_Split[0].indexof("=") +1 ) ## Remove "OU=" syntax from the pair
$Full_DN_Split[0] = $NULL
$SecondPart= ($Full_DN_Split -join(',')).trimstart(',')
try {
New-ADOrganizationalUnit -name $FirstPart -Path $SecondPart -ProtectedFromAccidentalDeletion $False | Out-Null
Write-Host -Fore Green "OU `'$FirstPart`' created successfully."
$Total_OU_Created++
}
catch [Microsoft.ActiveDirectory.Management.ADException]
{
Write-Host -Fore Red "Cannot create OU. The Object already exists:`'$FirstPart`'"
}
catch [System.UnauthorizedAccessException]
{
Write-Host -Fore "Access Denied. Make sure your have necessary permission to create OUs"
}
}
}
}
$usersinfo | foreach {
$OU_Path_Split=$_.ou.split('/')
[array]::reverse($OU_Path_Split)
If ($OU_Path_Split[0] -eq "Users" -OR $OU_Path_Split[0] -eq 'BuiltIn' ) ## If the OU is 'Users' or 'BuiltIn', then append as 'CN=' instead of 'OU='
{ $OU_Path_Join= 'CN='+($OU_Path_Split -join (',OU=')) }
else
{ $OU_Path_Join= 'OU='+($OU_Path_Split -join (',OU=')) }
$Domain_Split = $_.Domain.split('.')
$Domain_Path_Join = ',DC='+($Domain_Split -join (',DC='))
$Path=$OU_Path_Join + $Domain_Path_Join
################### Creating users in the respective OUs ##########################
$userprinc = $_.SamAccountName
try {
$Name=$_.Name
New-ADUser -Name $Name `
-SamAccountName $_.SamAccountName `
-UserPrincipalName "$userprinc@$DomainName" `
-DisplayName $_.DisplayName `
-Department $_.Department `
-AccountPassword (convertTo-SecureString $_.Password -asPlainText -Force) `
-Enabled $true `
-Description $_.Description `
-Title $_.Title `
-EmailAddress $_.EmailAddress `
-Office $_.Office `
-OfficePhone $_.OfficePhone `
-Company $_.Company `
-ChangePasswordAtLogon $false `
-PasswordNeverExpires $false `
-GivenName $_.GivenName `
-SurName $_.SurName `
-EmployeeID $_.EmployeeID `
-Path $Path | Out-Null
Write-Host -Fore Green "User has been created successfully:`'$Name`' in `'$($_.OU)`'"
$Total_User_Created++
}
catch [Microsoft.ActiveDirectory.Management.ADIdentityAlreadyExistsException]
{
Write-Host -Fore Red "Cannot add user. The account already exists:`'$Name`'"
}
catch [Microsoft.ActiveDirectory.Management.ADPasswordComplexityException]
{
Write-Host -Fore Red "Cannot add user due to password policy. Please use more complex one:`'$Name`'"
}
}
Write-Host "`nNumber of new OUs Created:$Total_OU_Created"
Write-Host "Number of new Users Created:$Total_User_Created"
}
else
{
Write-Host -Fore Red "Domain Name Mismatch. Please check your domain name and try again"
}
}
else {
Write-Host -Fore Red "There are some invalid domain names in csv file"
Write-Host -fore Red "Domains is $($Domains -join(' ,')) ?"
}
}
}
}
else
{ Write-Host -Fore Red "Active Directory Powershell Modules is not found. Please make sure you are running this script on domain Controller or `nRSAT is installed"
}
}
elseif ((Get-ChildItem $ImportCsvFile -EA SilentlyContinue) -notlike "*.csv")
{
Write-Host -Fore Red "The file name extension is invalid. Please ensure that you created user list file as csv(comma-separated) file."
}
else
{
Write-Host -Fore Red "CSV file not found. Please put the correct file name and path."
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment