It’s a new dance, and it goes like this…
Step 1 – Left foot forward: Image the device with a generic name, unplug, place on a shelf
Step 2 – Right foot to the side: Fetch from shelf, run script to assign to a user, hand device to user, go back to surfing Twitter
What could possibly go wrong?
Caveat Stuff
This “procedure”, if you will, is predicated on a scenario where the devices are NOT going to retain the auto-generated name when going into production. They will instead use a unique name based on whomever they are assigned to (e.g. SAMaccountName, etc.). If you can, I strongly recommend NOT doing this, which would seem strange that I’m essentially negating all of the remainder of this stupid blog post and telling you to just follow step 1, sort of. However, if you insist on using “JSMITH”, or some other ad hoc data entry value, for the device name, then by all means, drink up, snort up, shoot up, and continue reading. Thank you!
Errata / Disclaimer / Legal Stuff
At no point in any time in inter-galactic history, for any purpose or interstellar war or planetary conflict, shall anything mentioned herein be provided with any semblance of a warranty, guarantee, or promise that it will be error-free or suitable for your needs. Nor shall this brainless author assume any liability, or responsibility for any direct, indirect, or alleged damages or loss of productivity, possibly attributed to the direct or indirect use of any information provided herein, for any purpose, explicit or implied, notwithstanding hereinafter for any jurisdiction of human societal or governmental law, or any group of suits on a golf course, related therein. Golf carts and Martinis are not included.
…and One More Thing
Many blog posts / articles tend to portray a tone of “this is how it’s done”. This blog post is different for two reason: (a) It’s just ONE example of dealing with ONE common scenario, out of quadrillions of bazillions and kadrillions of possible scenarios, and (b) it’s likely to be the dumbest article you’ve read today.
Step 1 – Image and Stage Device
This step is all about imaging a new device (or wipe/reload an existing device) whereby it isn’t immediately assigned to some whiney complainer, oops, I mean user. It goes on a shelf, gathering dust, while it awaits being assigned to someone.
- Create / Copy / Hallucinate a PowerShell script:
> It derives a name using available data (ex. Serial number, MAC, etc.).
> Save the script in a shared location to allow for making a Configuration Manager Package.
> Refer to horrifically inept script example further below.
- Create a new Package in Configuration Manager
> Note: if you already have a OSD-related package for bundling your script goodies, just toss it in with the rest and they’ll play like over-caffeinated kids in one of those gooey McDonald’s Playland ball pits.
> Distribute or Update Distribution on the Package
- Add a step to your OSD Task Sequence
> Insert just before “Apply Operating System”
> Run PowerShell Script –> Choose the Package, and enter the script name and parameters/arguments, select “ByPass”
> Note: If you want to assign a common OU just assign it in the Task Sequence “Apply Network Settings” step, or add your own “Join Domain or Workgroup” step.
- Deploy the Task Sequence
> If you target “All Unknown Computers”, make sure the collection does not have the “OSDComputerName” Collection Variable attached
Step 2 – Provision and Assign to Hapless User
This step is all about getting up from your desk, grunting and complaining the entire way, maybe knocking over your cup of cold coffee, to shuffle slowly over to the dust-covered shelf, fetching a pre-imaged device, and doing some doodling on it so it can be handed to a bitchy customer, oops, again, I mean user. Okay, in all seriousness, you may be lucky today, and the user is actually a cool person. But you’re reading my blog, which means you’re probably not that lucky.
- Plug device into your network
- Find something to talk about while you wait for it to boot up
- Log in using your magical omniscient IT wizard power account
- Run a crappy half-baked PowerShell script which renames the device and moves it to a special AD Organizational Unit (OU) to suit the user’s department, etc.
- Wait for the reboot
- Unplug the device
- Throw at the user as hard as you can
- Go back to reading Facebook and Twitter
- Wait for Security to arrive and escort you out of the building
Horrifically Inept Script Examples
I told you they were going to be horrific and inept, but you didn’t think I was serious.
Script 1 – Assign a “Temporary” Device Name during OSD Task Sequence
Save this mess to a file named “Set-DeviceName.ps1”
[CmdletBinding()]
param (
[parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[string] $Prefix = "TMP"
)
$SerialNum = Get-WmiObject -Class Win32_SystemEnclosure | Select-Object -ExpandProperty SerialNumber
$NewName = "$Prefix-$SerialNum"
# in case you're imaging a VM with a stupid-long serial number...
if ($NewName.Length -gt 15) {
$SerialNum = $SerialNum.Substring(0,15-($Prefix.Length+1))
}
$NewName = "$Prefix-$SerialNum"
try {
Write-Verbose "new device name = $NewName"
$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$tsenv.Value("OSDComputerName") = $NewName
Write-Verbose "OSDComputerName = $NewName"
}
catch {
Write-Verbose "not running in a task sequence environment"
Write-Host "new device name = $NewName"
}
Script 2 – Provision Device for Assigned User
Note: The following chunk of PowerShell code might look impressive, but that’s because I didn’t create all of it. I just modified original examples shared by John Warnken and Stephen Owen. Save this mess to a file named “Assign-UserDevice.ps1”. This script relies on the “Locations.csv” file to provide the list of locations and department codes for the popup form.
[CmdletBinding()]
param (
[parameter(Mandatory=$False, HelpMessage="CSV input file path")]
[string] $CsvFile = "",
[parameter(Mandatory=$False, HelpMessage="Form Title")]
[ValidateNotNullOrEmpty()]
[string] $FormTitle = "Contoso - Provision Device",
[parameter(Mandatory=$False, HelpMessage="Maximum UserName character length")]
[ValidateRange(1,15)]
[int] $MaxUserNameLength = 11,
[parameter(Mandatory=$False, HelpMessage="Force Upper Case username")]
[switch] $IgnoreCase,
[parameter(Mandatory=$False, HelpMessage="Keep existing OU location")]
[switch] $KeepOuLocation,
[parameter(Mandatory=$False, HelpMessage="Apply Changes")]
[switch] $Apply,
[parameter(Mandatory=$False, HelpMessage="Do not force a restart")]
[switch] $NoRestart
)
$ScriptPath = Split-Path -Parent $PSCommandPath
if ($CsvFile -eq "") {
$CsvFile = Join-Path -Path $ScriptPath -ChildPath "Locations.csv"
}
function Move-ComputerOU {
param (
[parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[string] $TargetOU
)
$ComputerName = $env:COMPUTERNAME
$ads=[adsi]''
$adssearch = New-Object DirectoryServices.DirectorySearcher
$adssearch.searchroot = $ads
$adssearch.filter="(objectclass=computer)"
$adc1 = $adssearch.findall() | Where-Object {$_.properties.item("cn") -like $ComputerName}
$ComputerDN = $adc1.properties.item("distinguishedname")
Write-Verbose "distinguishedName = $ComputerDN"
$adc = [adsi]"LDAP://$ComputerDN"
$targetOU="LDAP://$targetOU"
Write-Verbose "target path = $targetOU"
$adc.psbase.MoveTo($targetOU)
}
if ($MaxUserNameLength -gt 9) {
Write-Warning "UserName portion cannot be longer than 9 characters when the prefix is 6 characters long"
break
}
if (!(Test-Path $CsvFile)) {
Write-Warning "CSV Input file not found: $CsvFile"
break
}
$LocData = Import-Csv -Path $CsvFile
[xml]$XAML = @'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title=""
Height="200" Width="320" Topmost="True" WindowStyle="ToolWindow"
WindowStartupLocation="Manual" Top="200" Left="200"
FocusManager.FocusedElement="{Binding ElementName=ComputerName_text}">
<Grid>
<Label Name="Label_Warn" Content="" HorizontalAlignment="Left" Foreground="#ff0000" Height="27" Margin="15,0,0,0" VerticalAlignment="Top" Width="300" />
<Label Name="Label_Loc" Content="Loc+Dept" Foreground="#000000" HorizontalAlignment="Left" Height="27" Margin="15,20,0,0" VerticalAlignment="Top" />
<Label Name="Label_Dlm" Content="-" Foreground="#000000" HorizontalAlignment="Left" Height="27" Margin="125,50,0,0" VerticalAlignment="Top" />
<Label Name="Label_Num" Content="UserName" Foreground="#000000" HorizontalAlignment="Left" Height="27" Margin="150,20,0,0" VerticalAlignment="Top" />
<ComboBox Name="Combo_Loc" Margin="20,50,0,0" Height="27" Width="90" HorizontalAlignment="Left" VerticalAlignment="Top" VerticalContentAlignment="Center">
</ComboBox>
<TextBox Name="Text_User" Margin="150,50,0,0" Height="27" Width="90" HorizontalAlignment="Left" VerticalAlignment="Top" VerticalContentAlignment="Center" Text="" MaxLength="20" CharacterCasing="Lower" />
<Button Name="Button_Continue" Content="Continue" Margin="90,100,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Height="27" Width="100"/>
</Grid>
</Window>
'@
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
# Read XAML string and convert into a form object
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
$Form = [Windows.Markup.XamlReader]::Load( $reader )
# Add Form objects as script variables
$xaml.SelectNodes("//*[@Name]") | ForEach-Object {Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)}
foreach ($loc in $LocData) {
$LocDept = "$($loc.Loc)$($loc.Dept)"
$Combo_Loc.AddChild($LocDept)
}
$Form.Title = $FormTitle
$Text_User.Maxlength = $MaxUserNameLength
if (!($IgnoreCase)) {
$Text_User.CharacterCasing = "Upper"
}
# add form handler for pressing Enter on UserName text box
$Text_User.add_KeyDown({
if ($args[1].key -eq 'Return') {
Write-Verbose "action -> user pressed Enter on username textbox"
$Location = $Combo_Loc.SelectedValue
$UserName = $Text_User.Text.ToString()
Write-Verbose "selection -> $Location"
Write-Verbose "username -> $UserName"
if (!([string]::IsNullOrEmpty($Location))) {
$Script:LocIndex = $Combo_Loc.SelectedIndex
$Script:NewName = $Location+'-'+$UserName
$Script:Ready = $True
}
$Form.Close()
}
})
# add form handler for clicking Continue button on exit
$Button_Continue.add_Click({
Write-Verbose "action -> pressed Continue button"
$Location = $Combo_Loc.SelectedValue
$UserName = $Text_User.Text.ToString()
Write-Verbose "selection -> $Location"
Write-Verbose "username -> $UserName"
if (!([string]::IsNullOrEmpty($Location))) {
$Script:LocIndex = $Combo_Loc.SelectedIndex
$Script:NewName = $Location+'-'+$UserName
$Script:Ready = $True
}
$Form.Close()
})
# display the form for the user to interact with
$Form.ShowDialog() | Out-Null
if (!($Script:Ready)) {
Write-Warning "No selection or entry. Nothing to do."
break
}
$RowSet = $LocData[$Script:LocIndex]
$OuPath = $RowSet.DeviceOU
if ($Apply) {
Write-Host "New Name...: $NewName" -ForegroundColor Green
if (-not ($KeepOuLocation)) {
Write-Host "OU Path....: $OuPath" -ForegroundColor Green
Move-ComputerOU -TargetOU $OuPath
}
Write-Verbose "renaming computer to $NewName"
Rename-Computer -NewName $NewName -Force
if (!($NoRestart)) {
Restart-Computer -Force
}
}
else {
Write-Host "Test Mode (No changes were applied)" -ForegroundColor Cyan
Write-Host "New Name...: $NewName" -ForegroundColor Cyan
if (-not ($KeepOuLocation)) {
Write-Host "OU Path....: $OuPath" -ForegroundColor Cyan
}
}
Locations.csv File for Assign-UserDevice.ps1
Note: “Loc” can be a building, campus, city, or whatever. The ADGroup column is for future/optional/possible/potential use for adding the computer to an AD security group as well.
Loc,Dept,DeviceOU,ADGroup
BOS,HR,"OU=Workstations,OU=HR,OU=Boston,DC=Contoso,DC=local",
BOS,RD,"OU=Workstations,OU=Research,OU=Boston,DC=Contoso,DC=local",
MIA,HR,"OU=Workstations,OU=HR,OU=Miami,DC=Contoso,DC=local",
MIA,MK,"OU=Workstations,OU=Marketing,OU=Miami,DC=Contoso,DC=local",
SFO,FN,"OU=Workstations,OU=Finance,OU=SanFrancisco,DC=Contoso,DC=local",
SFO,HR,"OU=Workstations,OU=HR,OU=SanFrancisco,DC=Contoso,DC=local",
SFO,RD,"OU=Workstations,OU=Research,OU=SanFrancisco,DC=Contoso,DC=local",
TMP,HR,"OU=Workstations,OU=HR,OU=Tampa,DC=Contoso,DC=local",
Cheesy Examples
Example: Assign-UserDevice.ps1 -MaxUserNameLength 9 -Verbose
Summary and Conclusion
As you may have surmised by now, everything you’ve read above is completely stupid and useless. You’re shaking your head in disbelief that you skipped some other opportunity to read this, and you should have chosen otherwise, even if that other opportunity was a prostate exam. You are now dumber for having read this.
You’re welcome.
You must be logged in to post a comment.