Public/New-ADUserFromTemplate.ps1
|
function New-ADUserFromTemplate { <# .SYNOPSIS Creates a new Active Directory user based on a template account. .DESCRIPTION Provisions a new AD user by cloning group memberships, OU placement, and properties from an existing template account. Supports single-user creation or bulk provisioning from a CSV file. The function automatically: - Generates a unique SAMAccountName (last + first initial, incrementing if taken) - Copies group memberships from the template - Inherits OU placement from the template - Copies address properties from the template - Sets a temporary password requiring change at next logon - Logs all actions to a transcript file .PARAMETER FirstName The user's first name (GivenName). .PARAMETER LastName The user's last name (Surname). .PARAMETER Template The SAMAccountName of the template user to clone settings from. .PARAMETER CsvPath Path to a CSV file for bulk user creation. Expected columns: FirstName, LastName, Template, Department, Title, OfficePhone, Email .PARAMETER Department The user's department. If not specified, inherits from the template. .PARAMETER Title The user's job title. .PARAMETER OfficePhone The user's office phone number. .PARAMETER Email The user's email address. .PARAMETER TemporaryPassword A SecureString password for the new account. If not provided, a random complex password is generated. .PARAMETER EnableAccount If specified, the account is enabled immediately. Default is disabled (requiring manual enablement after verification). .PARAMETER LogPath Directory for transcript logs. Defaults to .\Logs. .EXAMPLE New-ADUserFromTemplate -FirstName "Jane" -LastName "Smith" -Template "Template.IT" Creates a new user jsmith (or jsmith2 if taken) based on the Template.IT account. .EXAMPLE New-ADUserFromTemplate -CsvPath ".\NewHires.csv" Bulk-creates users from a CSV file. .EXAMPLE New-ADUserFromTemplate -FirstName "Bob" -LastName "Jones" -Template "Template.HR" -EnableAccount -WhatIf Shows what would happen without making changes. .NOTES Requires: ActiveDirectory module, permission to create users in the target OU. #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Single')] param( [Parameter(Mandatory, ParameterSetName = 'Single')] [ValidateNotNullOrEmpty()] [string]$FirstName, [Parameter(Mandatory, ParameterSetName = 'Single')] [ValidateNotNullOrEmpty()] [string]$LastName, [Parameter(Mandatory, ParameterSetName = 'Single')] [ValidateNotNullOrEmpty()] [string]$Template, [Parameter(Mandatory, ParameterSetName = 'Bulk')] [ValidateScript({ Test-Path $_ -PathType Leaf })] [string]$CsvPath, [Parameter(ParameterSetName = 'Single')] [string]$Department, [Parameter(ParameterSetName = 'Single')] [string]$Title, [Parameter(ParameterSetName = 'Single')] [string]$OfficePhone, [Parameter(ParameterSetName = 'Single')] [string]$Email, [Parameter(ParameterSetName = 'Single')] [securestring]$TemporaryPassword, [switch]$EnableAccount, [string]$LogPath = ".\Logs" ) begin { Import-Module ActiveDirectory -ErrorAction Stop if (-not (Test-Path $LogPath)) { New-Item -Path $LogPath -ItemType Directory -Force | Out-Null } $timestamp = Get-Date -Format 'yyyyMMdd-HHmmss' $logFile = Join-Path $LogPath "NewUser-$timestamp.log" Start-Transcript -Path $logFile -Append $results = [System.Collections.Generic.List[PSCustomObject]]::new() } process { # Build user list from either single params or CSV if ($PSCmdlet.ParameterSetName -eq 'Bulk') { $users = Import-Csv -Path $CsvPath } else { $users = @([PSCustomObject]@{ FirstName = $FirstName LastName = $LastName Template = $Template Department = $Department Title = $Title OfficePhone = $OfficePhone Email = $Email }) } foreach ($user in $users) { try { Write-Verbose "Processing: $($user.FirstName) $($user.LastName)" # Validate template exists $templateObj = Get-ADUser -Identity $user.Template -Properties MemberOf, StreetAddress, City, State, PostalCode, Office, Company -ErrorAction Stop $targetOU = ($templateObj.DistinguishedName -replace '^CN=.+?(?<!\\),', '') # Generate unique SAMAccountName $samBase = ($user.LastName + $user.FirstName.Substring(0, 1)).ToLower() -replace '[^a-z0-9]', '' $sam = $samBase $index = 2 while (Get-ADUser -Filter "SAMAccountName -eq '$sam'" -ErrorAction SilentlyContinue) { $sam = "$samBase$index" $index++ if ($index -ge 100) { throw "Could not generate unique SAMAccountName for $samBase" } } # Build display name $displayName = "$($user.LastName), $($user.FirstName)" # Generate password if not provided if (-not $TemporaryPassword) { $password = _New-RandomPassword } else { $password = $TemporaryPassword } # Build New-ADUser parameters $newUserParams = @{ Name = $displayName DisplayName = $displayName GivenName = $user.FirstName Surname = $user.LastName SAMAccountName = $sam UserPrincipalName = "$sam@$((Get-ADDomain).DNSRoot)" Path = $targetOU AccountPassword = $password ChangePasswordAtLogon = $true Enabled = [bool]$EnableAccount Company = $templateObj.Company StreetAddress = $templateObj.StreetAddress City = $templateObj.City State = $templateObj.State PostalCode = $templateObj.PostalCode Office = $templateObj.Office } # Add optional properties if provided if ($user.Department) { $newUserParams['Department'] = $user.Department } if ($user.Title) { $newUserParams['Title'] = $user.Title } if ($user.OfficePhone) { $newUserParams['OfficePhone'] = $user.OfficePhone } if ($user.Email) { $newUserParams['EmailAddress'] = $user.Email } if ($PSCmdlet.ShouldProcess("$displayName ($sam) in $targetOU", 'Create AD User')) { New-ADUser @newUserParams # Copy group memberships from template $templateObj.MemberOf | ForEach-Object { Add-ADGroupMember -Identity $_ -Members $sam } $results.Add([PSCustomObject]@{ SAMAccountName = $sam DisplayName = $displayName OU = $targetOU Template = $user.Template Status = 'Created' }) Write-Verbose "Created: $sam ($displayName)" } } catch { Write-Warning "Failed to create $($user.FirstName) $($user.LastName): $_" $results.Add([PSCustomObject]@{ SAMAccountName = $sam DisplayName = "$($user.LastName), $($user.FirstName)" OU = $targetOU Template = $user.Template Status = "FAILED: $_" }) } } } end { Stop-Transcript $results } } |