Repository navigation
Expand file tree
/
Copy pathGet-UniqueUPN.ps1
More file actions
143 lines (125 loc) · 4.89 KB
/
Copy pathGet-UniqueUPN.ps1
File metadata and controls
143 lines (125 loc) · 4.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#Requires -Version 7.4
. (Join-Path $PSScriptRoot 'Test-UpnExist.ps1')
. (Join-Path $PSScriptRoot 'Private/Test-ITToolBoxDnsName.ps1')
function Get-UniqueUPN
{
<#
.SYNOPSIS
Finds an available user principal name in the selected directory scope.
.DESCRIPTION
Accepts explicit name strings or ADUser, DirectoryEntry and SearchResult
inputs. First and last name formatting are independent. Collisions append
an integer to the complete local part; the search has a bounded attempt count.
Availability is a preflight check, not a reservation or atomic uniqueness guarantee.
.PARAMETER MaxAttempts
Maximum candidates to check, including the initial unsuffixed name. Defaults to 1000.
.PARAMETER AdServer
Directory hostname, optionally with port. See Test-UPNExist for scope behaviour.
#>
[CmdletBinding(DefaultParameterSetName = 'Strings')]
[OutputType([string])]
param (
[Parameter(ParameterSetName = 'ADObject', Mandatory)]
[ValidateNotNull()]
[object]$ADObject,
[Parameter(ParameterSetName = 'Strings', Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$FirstName,
[Parameter(ParameterSetName = 'Strings', Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$LastName,
[Parameter(ParameterSetName = 'Strings')]
[string]$MiddleName,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$UPNSuffix,
[ValidateSet('FullName', 'FirstLetter')]
[string]$FirstNameFormat = 'FullName',
[ValidateSet('FullName', 'FirstLetter')]
[string]$LastNameFormat = 'FullName',
[switch]$IncludeMiddleName,
[Alias('Server')]
[ValidatePattern('^[A-Za-z0-9][A-Za-z0-9._-]*(?::[0-9]{1,5})?$')]
[string]$ADServer,
[ValidatePattern('^[^\s@\p{Cc}]*$')]
[AllowEmptyString()]
[string]$Separator = '.',
[ValidateSet('Forest', 'Domain')]
[string]$SearchScope = 'Forest',
[ValidateRange(1, 100000)]
[int]$MaxAttempts = 1000
)
$givenName = $FirstName
$surname = $LastName
$middleNameValue = $MiddleName
if ($PSCmdlet.ParameterSetName -eq 'ADObject')
{
if ($ADObject.PSObject.TypeNames -contains 'Microsoft.ActiveDirectory.Management.ADUser')
{
$givenName = [string]$ADObject.GivenName
$surname = [string]$ADObject.Surname
$middleNameValue = [string]$ADObject.MiddleName
}
elseif ($ADObject.PSObject.TypeNames -contains 'System.DirectoryServices.DirectoryEntry' -or
$ADObject.PSObject.TypeNames -contains 'System.DirectoryServices.SearchResult')
{
$givenName = [string](@($ADObject.Properties['givenName'])[0])
$surname = [string](@($ADObject.Properties['sn'])[0])
$middleNameValue = [string](@($ADObject.Properties['middleName'])[0])
}
else
{
throw "Unsupported AD object type: $($ADObject.GetType().FullName)"
}
}
foreach ($name in @($givenName, $surname))
{
if ([string]::IsNullOrWhiteSpace($name) -or $name.Trim() -match '[\s@\p{Cc}]')
{
throw 'First and last names must contain nonblank components without embedded whitespace, controls or @. Normalize compound names before calling.'
}
}
if (-not (Test-ITToolBoxDnsName -Name $UPNSuffix))
{
throw 'UPNSuffix must be a DNS/IDN name without whitespace or a trailing root dot.'
}
$first = $givenName.Trim()
$last = $surname.Trim()
if ($FirstNameFormat -eq 'FirstLetter')
{
$first = [System.Globalization.StringInfo]::GetNextTextElement($first)
}
if ($LastNameFormat -eq 'FirstLetter')
{
$last = [System.Globalization.StringInfo]::GetNextTextElement($last)
}
$parts = [System.Collections.Generic.List[string]]::new()
$parts.Add($first)
if ($IncludeMiddleName -and -not [string]::IsNullOrWhiteSpace($middleNameValue))
{
$middle = $middleNameValue.Trim()
if ($middle -match '[\s@\p{Cc}]')
{
throw 'Middle name must not contain embedded whitespace, controls or @.'
}
$parts.Add($middle)
}
$parts.Add($last)
$localPart = ($parts -join $Separator).ToLowerInvariant()
$suffix = $UPNSuffix.ToLowerInvariant()
$lookup = @{ SearchScope = $SearchScope; ErrorAction = 'Stop' }
if ($PSBoundParameters.ContainsKey('ADServer'))
{
$lookup.AdServer = $ADServer
}
for ($attempt = 0; $attempt -lt $MaxAttempts; $attempt++)
{
$counter = if ($attempt -eq 0) { '' } else { [string]$attempt }
$candidate = '{0}{1}@{2}' -f $localPart, $counter, $suffix
if (-not (Test-UPNExist -UPN $candidate @lookup))
{
return $candidate
}
}
throw "No available UPN found after $MaxAttempts candidates."
}