Skip to content

Commit dff842e

Browse files
Added new article
Updated configuration to re-enable comments
1 parent e732cf3 commit dff842e

2 files changed

Lines changed: 255 additions & 1 deletion

File tree

_config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ teaser : # path of fallback teaser image, e.g. "/assets/images
2727
# breadcrumbs : false # true, false (default)
2828
words_per_minute : 200
2929
comments:
30-
provider : "false" # false (default), "disqus", "discourse", "facebook", "google-plus", "staticman", "staticman_v2", "utterances", "custom"
30+
provider : "disqus" # false (default), "disqus", "discourse", "facebook", "google-plus", "staticman", "staticman_v2", "utterances", "custom"
3131
disqus:
3232
shortname : PsCustomObject # https://help.disqus.com/customer/portal/articles/466208-what-s-a-shortname-
3333
discourse:
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
---
2+
title: "PowerShell - Generate Unique UPN"
3+
excerpt: "In this short I will share two new cmdlets I have developed to generate forest wide unique UPN handling the different edge cases."
4+
categories:
5+
- PowerShell
6+
- Howto
7+
8+
tags:
9+
- PowerShell
10+
- Tips
11+
- HowTo
12+
13+
toc: true
14+
header:
15+
teaser: "/assets/images/PowerShell_Logo.png"
16+
---
17+
18+
## Generating Unique User Principal Names (UPNs) in PowerShell
19+
20+
When managing Active Directory (AD) environments, creating unique User Principal Names (UPNs) is a common, and challenging!, task.
21+
22+
This blog post covers two PowerShell functions that help ensure UPN uniqueness: **Test-UPNExist** and **Get-UniqueUPN**. These functions can be particularly useful for automating user creation processes and can be integrated into your automation projects and solutions.
23+
24+
**Note** Both cmdlets are still under active development so do expect some changes, as I progres with development I will update the post. Feel free to report any issue, idea or suggestion so that I can integrate it into the final cmdlet version.
25+
26+
### Function 1: Test-UPNExist
27+
28+
This is a support that is used to check if given UPN already exists in the AD environment.
29+
30+
```powershell
31+
function Test-UPNExist
32+
{
33+
[CmdletBinding()]
34+
param (
35+
[Parameter(Mandatory = $true)]
36+
[string]$UPN,
37+
38+
[string]$Server
39+
)
40+
41+
try
42+
{
43+
if ($Server)
44+
{
45+
$ldapPath = "LDAP://$Server"
46+
}
47+
else
48+
{
49+
$forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
50+
$gc = $forest.FindGlobalCatalog()
51+
$ldapPath = "GC://$($gc.Name)"
52+
}
53+
$domain = New-Object System.DirectoryServices.DirectoryEntry($ldapPath)
54+
$searcher = New-Object System.DirectoryServices.DirectorySearcher($domain)
55+
$searcher.SearchScope = "Subtree"
56+
$searcher.PageSize = 1000
57+
$searcher.Filter = "(&(objectCategory=person)(userPrincipalName=$UPN))"
58+
[void]($searcher.PropertiesToLoad.Add("userPrincipalName"))
59+
60+
$result = $searcher.FindOne()
61+
return $null -ne $result
62+
}
63+
catch
64+
{
65+
Write-Error "Error checking UPN existence: $_"
66+
throw
67+
}
68+
}
69+
70+
```
71+
72+
Here's a summary explanation of the parameters:
73+
74+
- **Parameters**
75+
- *$UPN* - A string representing the UPN to check
76+
- *$Server* - A string representing the name of the LDAP server to query. Parameter is optional and if omitted function will automatically select the closest global catalog server
77+
78+
Here's an example usage:
79+
80+
```powershell
81+
$upnExists = Test-UPNExist -UPN "john.doe@example.com"
82+
83+
if ($upnExists -eq $true) # Redundant I know but I like to make code redeable :-)
84+
{
85+
Write-Host "The UPN exists."
86+
}
87+
else
88+
{
89+
Write-Host "The UPN does not exist."
90+
}
91+
92+
```
93+
94+
### Function 1: Test-UPNExist
95+
96+
This function generates a unique UPN based on given name components and ensures it doesn't already exist in the AD forest.
97+
98+
```powershell
99+
function Get-UniqueUPN
100+
{
101+
[CmdletBinding()]
102+
param (
103+
[Parameter(Mandatory = $true, ParameterSetName = "ADObject")]
104+
[object]$ADObject,
105+
106+
[Parameter(Mandatory = $true, ParameterSetName = "Strings")]
107+
[string]$FirstName,
108+
109+
[Parameter(Mandatory = $true, ParameterSetName = "Strings")]
110+
[string]$LastName,
111+
112+
[Parameter(ParameterSetName = "Strings")]
113+
[string]$MiddleName,
114+
115+
[Parameter(Mandatory = $true)]
116+
[string]$UPNSuffix,
117+
118+
[string]$FirstNameFormat = "Full",
119+
[switch]$IncludeMiddleName,
120+
[string]$DuplicateSuffix = "Numeric",
121+
[string]$CustomDuplicateSuffix,
122+
[string]$Server,
123+
[string]$Separator = "."
124+
)
125+
126+
try
127+
{
128+
if ($PSCmdlet.ParameterSetName -eq "ADObject")
129+
{
130+
Write-Verbose "Processing AD Object"
131+
switch ($ADObject.GetType().FullName)
132+
{
133+
"Microsoft.ActiveDirectory.Management.ADUser"
134+
{
135+
$firstName = $ADObject.GivenName
136+
$lastName = $ADObject.Surname
137+
$middleName = $ADObject.MiddleName
138+
break
139+
}
140+
"System.DirectoryServices.DirectoryEntry"
141+
{
142+
$firstName = $ADObject.Properties["givenName"][0]
143+
$lastName = $ADObject.Properties["sn"][0]
144+
$middleName = $ADObject.Properties["middleName"][0]
145+
break
146+
}
147+
"System.DirectoryServices.SearchResult"
148+
{
149+
$firstName = $ADObject.Properties["givenName"][0]
150+
$lastName = $ADObject.Properties["sn"][0]
151+
$middleName = $ADObject.Properties["middleName"][0]
152+
break
153+
}
154+
default
155+
{
156+
throw "Unsupported AD object type: $($ADObject.GetType().FullName)"
157+
}
158+
}
159+
}
160+
else
161+
{
162+
Write-Verbose "Processing string inputs"
163+
$firstName = $FirstName
164+
$lastName = $LastName
165+
$middleName = $MiddleName
166+
}
167+
168+
Write-Verbose "First Name: $firstName, Last Name: $lastName, Middle Name: $middleName"
169+
170+
$firstName = switch ($FirstNameFormat)
171+
{
172+
"Full" { $firstName }
173+
"FirstLetter" { $firstName.Substring(0, 1) }
174+
default { $firstName }
175+
}
176+
177+
$middleNamePart = if ($IncludeMiddleName -and $middleName)
178+
{
179+
"$Separator$middleName"
180+
}
181+
else { "" }
182+
183+
$baseUPN = "$firstName$middleNamePart$Separator$lastName@$UPNSuffix".ToLower()
184+
Write-Verbose "Base UPN: $baseUPN"
185+
186+
$uniqueUPN = $baseUPN
187+
$counter = 1
188+
189+
while (Test-UPNExist -UPN $uniqueUPN -Server $Server)
190+
{
191+
Write-Verbose "UPN $uniqueUPN already exists, generating alternative"
192+
if ($DuplicateSuffix -eq "Numeric")
193+
{
194+
$uniqueUPN = "{0}{1}@{2}" -f ($baseUPN.Split('@')[0]), $counter, $UPNSuffix
195+
}
196+
else
197+
{
198+
$uniqueUPN = "{0}{1}@{2}" -f ($baseUPN.Split('@')[0]), $CustomDuplicateSuffix, $UPNSuffix
199+
}
200+
$counter++
201+
}
202+
203+
Write-Verbose "Final Unique UPN: $uniqueUPN"
204+
return $uniqueUPN
205+
}
206+
catch
207+
{
208+
Write-Error "Error generating UPN: $_"
209+
throw
210+
}
211+
}
212+
```
213+
214+
Here's a summary explanation of the parameters:
215+
216+
- **Parameters**
217+
- *$ADObject* - We can pass an existing AD object to the function, for example output from the *Get-AdUser* cmdlet
218+
- *$FirstName* - A string representing the *First Name* of a given user
219+
- *$LastName* - A string representing the *Last Name* of a given user
220+
- *MiddleName* - A string representing the *Middle Name* of a given user (of course optional)
221+
- *$UPNSuffix* - A string representing the domain suffix for the UPN
222+
- *FirstNameFormat* - The format of the first name in the UPN (e.g. Full or First letter of name)
223+
- *IncludeMiddleName* - Switch parameter indicating to cmdlet if Middle Name should be used or not in the UPN generation
224+
- *DuplicateSuffix* - By default if a duplicate is found a progressive number is added to the UPN, using this paramter you can specify a *custom* character to use
225+
- *CustomDuplicateSuffix* - Allows us to specify the custom suffix for duplicates
226+
- *$Server* - A string representing the name of the LDAP server to query. Parameter is optional and if omitted function will automatically select the closest global catalog server
227+
- *$Separator* - The separator to use between the name parts. If not specified a '.' (dot) is used
228+
229+
Below you can find couple examples of functions at work
230+
231+
```powershell
232+
# Using string parameters
233+
$uniqueUPN = Get-UniqueUPN -FirstName "John" -LastName "Doe" -UPNSuffix "example.com" -FirstNameFormat "FirstLetter" -IncludeMiddleName -MiddleName "A"
234+
235+
Write-Host "Generated UPN: $uniqueUPN"
236+
237+
# Using an AD object
238+
$adUser = Get-ADUser -Identity "johndoe"
239+
$uniqueUPN = Get-UniqueUPN -ADObject $adUser -UPNSuffix "example.com"
240+
241+
Write-Host "Generated UPN: $uniqueUPN"
242+
```
243+
244+
## Conclusion
245+
246+
By using **Test-UPNExist** and **Get-UniqueUPN**, you can automate the process of generating unique UPNs for users in your Active Directory environment.
247+
248+
These functions ensure that each UPN is unique and adhere to the naming conventions you specify.
249+
250+
This approach minimizes the risk of conflicts and simplifies user management.
251+
252+
As mentioned both functions are still under development and I need to cleanup code and optimize some parts of it but the core functionality is there.
253+
254+
I would love to receive feedback, ideas or implemntation ideas for them!

0 commit comments

Comments
 (0)