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
    }
}