Hey You!
I hope you’re having a fantastic week, just as I am. I’ve been scoring big time with all the challenges that have been laid out for me.
What happened?
I’ll tell you. You might remember my last post, “Optimizing SCCM Deployments: Remove device from collection after OSD” where I faced a significant challenge in removing a device from a device collection after OSD.
Well, that was just the beginning of my adventure with SCCM.
Most devices that are deployed are already registered in SCCM/MECM/MEM and are also domain-joined. This presents some issues:
- Many devices are members of device collections that clutter the system.
- Many devices are members of multiple AD groups that adds to the clutter.
Besides this, there are administrative issues:
- People managing the deployment sometimes provide a device name with a MAC address already in use. (HOW? I don’t know???)
- The same people might add a device, but it ends up having a completely different MAC address from what is registered in SCCM. (Why are they making my job harder than it already is?)
So, I can’t proceed with my work until I find a way to automate this process.
Solution
Here’s the plan. I need a PowerShell script that will:
- Query the MAC address to check if it’s already in use by another machine.
- Check if the machine already exists in SCCM.
- If it does, verify if the MAC address matches.
- If it doesn’t match, produce an error.
- If the MAC is the same, delete the machine from SCCM.
- Add the machine back to SCCM and to the device collection.
- If the machine does not exist, add the machine to SCCM.
- Check if the machine is domain-joined.
- If it is, remove it from the domain.
This process needs to be logged, and all errors must be displayed. There’s also the bonus of incorporating a real GUI.
*Side note: The TXT file needs to contain the computer names with MAC address. Something that looks like this:
COMP001,00:00:00:00:00:01
COMP002,00:00:00:00:00:02
etc
This is what the GUI eventually looks like.
Here is this script.
##########
#
# Version 2.3
# - Welcome text when starting
# - Added a cancel button
#
# Version 2.2
# - Pop-up that shows which computers are going to be added
#
# Version 2.1
# - Turned on for troubleshooting. Location: \\SCCMSRV01\software\OSD\Scripts\RemoveDeviceCollection\log\import
#
# Version 2.0
# - Created a GUI
#
# Version 1.5
# - Script check if MAC address is already registered with another machine
#
# Version 1.4.2
# - Added some lines to make the output more clearer
#
# Version 1.4.1
# - Message at the end
#
# Version 1.4
# - Added new variable $addToSCMM. If this is on $false it will not add the computer to SCCM.
#
# Version 1.3
# - If machine has a differet MAC address it will log this in a notepad
# - Added new variable $removeFromAD. If this is set to $false it will not remove from AD
#
# Version 1.2
# - Added a button "Browse" to open a TXT file
# - Each letter will be be uppercased
#
# Version 1.1
# - Machine that already exists in SCCM and MAC address is the same; machine will be removed.
# - Script goes back to local so that it doesn't get stuck at the SCCM server.
#
# Version 1.0
# - Machine that exist will be placed in de deployment collection.
# - Machine that exists and MAC address is correct will be added in de the deployment collection.
# - Machine that does not exists will be added to SCCM and placed in the deployment collection.
# - Machine that exists but has a different MAC address will be notified.
# - Machine will check if it's domain joined and if needed it will be removed from AD.
#
##########
# Versioning
$versionNumber = "v2.3"
# Adding some libraries so that the GUI can work
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# Variable for the collections
$allCollectionName = "All Systems"
$deploymentCollectionName = "Windows 10 x64 Deployment" # Still working on getting this automated
# Variabele for logging
$userName = $env:USERNAME
$date = Get-Date
# Location for logging
$logFile = "\\SCCMSRV01\software\OSD\Scripts\RemoveDeviceCollection\log\import\importComputers_$versionNumber.txt"
$logBackup = "\\SCCMSRV01\software\OSD\Scripts\RemoveDeviceCollection\log\import\importComputers_$versionNumber.bak"
# This part will check if the log size isn't too big
if (Test-Path -Path $logFile) {
$logFileSize = (Get-Item -Path $logFile).Length
# Check if log is bigger than 1 MB
if ($logFileSize -gt 1MB) {
# Check if a .bak already exists, if so that delete it first.
if (Test-Path -Path $logBackup) {
Remove-Item -Path $logBackup -Force
}
# Rename current log file extension to .bak
Rename-Item -Path $logFile -NewName $logBackup
}
}
# Here I define the main screen of the GUI including the title and the version number with the variable $versionNumber
$form = New-Object System.Windows.Forms.Form
$form.Text = "SCCM Computer Importer $versionNumber"
$form.Size = New-Object System.Drawing.Size(600,400)
$form.StartPosition = 'CenterScreen'
# Here I define the Browse button
$browseButton = New-Object System.Windows.Forms.Button
$browseButton.Location = New-Object System.Drawing.Point(10,10)
$browseButton.Size = New-Object System.Drawing.Size(100,23)
$browseButton.Text = 'Browse'
$browseButton.Add_Click({
$openFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$openFileDialog.InitialDirectory = [Environment]::GetFolderPath("Desktop")
$openFileDialog.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
if ($openFileDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
$global:txtFilePath = $openFileDialog.FileName
$startButton.Enabled = $true
}
})
# I have to add the Browse button to the GUI
$form.Controls.Add($browseButton)
# Start button
$startButton = New-Object System.Windows.Forms.Button
$startButton.Location = New-Object System.Drawing.Size(120,10)
$startButton.Size = New-Object System.Drawing.Size(100,23)
$startButton.Text = 'Start'
$startButton.Enabled = $false
$startButton.Add_Click({
$outputTextBox.Clear()
$outputTextBox.AppendText("Script started using file`r`n$global:txtFilePath`r`n`r`n")
# Need to check if a file is selected.
if ($global:txtFilePath -ne $null -and $global:txtFilePath -ne '') {
try{
# Every letter needs to be uppercase. The magic is done here
$fileContent = Get-Content -Path $global:txtFilePath
$computers = $fileContent | ForEach-Object {
# UPPERCASE!!!
$_.ToUpper()
$outputTextBox.AppendText("$upperCaseLine`r`n")
}
} catch {
# Error if the TXT file is corrupted.
$outputTextBox.AppendText("Error reading file: $_`r`n")
}
} else {
$outputTextBox.AppendText("No file selected.`r`n")
}
# I need it to confirm the computers that will be added. I also added a cancel button.
$confirmationMessage = "The following machines will be improted:`r`n`r`n$(($computers -join "`r`n"))`r`n`r`nClick OK to continue or CANCEL to go back."
$confirmationResult = [System.Windows.Forms.MessageBox]::Show($confirmationMessage, "Confirm", [System.Windows.Forms.MessageBoxButtons]::OKCancel, [System.Windows.Forms.MessageBoxIcon]::Information)
# Check what the user does
if ($confirmationResult -ne [System.Windows.Forms.DialogResult]::OK) {
# If user cancels report that
$outputTextBox.AppendText("Cancelled by user`r`n")
return
}
# Let's log before we begin!
"==========`r`n$userName started the script at $date.`r`n`r`n$(($computers -join "`r`n"))`r`n==========" | Add-Content -Path $logFile
# Import the SCCM-Module
try {
Import-Module (Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1)
$outputTextBox.AppendText("SCCM-Module imported.`r`n")
# Change to the SCCM path
Set-Location ((Get-PSDrive -PSProvider CMSite).Name + ":")
$outputTextBox.AppendText("Changed to SCCM path.`r`n")
} catch {
$outputTextBox.AppendText("Error importing SCCM-Module: $_`r`n")
}
# Get information from the deployment collection
$deploymentCollection = Get-CMDeviceCollection -Name $deploymentCollectionName
foreach($computer in $computers){
# Seperate the machine from MAC Address
$details = $computer.Split(',')
$name = $details[0].Trim()
$macAddress = $details[1].Trim()
# Check if machine already exists in SCCM
$sccmComputer = Get-CMDevice -Name $name
$sccmComputerMac = Get-CMDevice | Where-Object { $_.MACAddress -contains $macAddress }
# Variables for continueing with the script
$continueScript = $true
$addToSCCM = $true
$removeFromAD = $true
# Log location for the user
$tempPath = [System.Environment]::ExpandEnvironmentVariables("%TEMP%\importComputer.txt")
$outputTextBox.AppendText("===========================================================================`r`n")
$outputTextBox.AppendText("- Computer $name with MAC $macAddress -`r`n")
# Check if MAC already exists
if ($sccmComputerMac) {
# If the machine name and MAC address are not the same
if ($sccmComputerMac.Name -ne $name) {
$sccmComputerMacName = $sccmComputerMac.Name
$outputTextBox.AppendText("Mac: $macAddress is already registered with $sccmComputerMacName`r`n")
# Save Log
$tempMessage = "- Mac: $macAddress is already registered with computer $sccmComputerMacName. Please check."
Add-Content -Path $tempPath -Value $tempMessage
# Flag to not conintue with this machine
$continueScript = $false
$outputTextBox.AppendText("===========================================================================`r`n")
}
}
# Check if we can continue with the script
if ($continueScript) {
# Check if machine already exists in SCCM
if ($sccmComputer) {
$sccmMacAddress = $sccmComputer.MACAddress
# Machine exists, check MAC address
if ($sccmMacAddress -eq $macAddress) {
$outputTextBox.AppendText("Machine $name with MAC-address $macAddress exists in SCCM`r`n")
# Remove machine en add to the deployment collection
if ($deploymentCollection) {
Remove-CMDevice -DeviceName $name -Force
$outputTextBox.AppendText("Machine $name removed from SCCM.`r`n")
}
} else {
# MAC address is not the same
$outputTextBox.AppendText("Machine $name exists in SCCM, but MAC-address $macAddress is not the same.`r`n")
# Save Log
$tempMessage = "- $name exists in SCCM but MAC-Address $macAddress is not the same. Please check."
Add-Content -Path $tempPath -Value $tempMessage
# Variable for not removing from AD
$removeFromAD = $false
# Variable for not adding to SCCM
$addToSCCM = $false
}
}
# Check if we can add the machine to SCCM
if ($addToSCCM) {
# Machine will be added to SCCM and placed in the deployment collection
Import-CMComputerInformation -CollectionName $allCollectionName,$deploymentCollectionName -ComputerName $name -MacAddress $macAddress
$outputTextBox.AppendText("Machine $name added to SCCM and placed in the deployment collection.`r`n")
}
# Check if machine can be removed from AD
if ($removeFromAD) {
# Check if machine already exists in AD
if (Get-ADComputer -Filter { Name -eq $name }) {
# Remove the AD object
Remove-ADComputer -Identity $name -Confirm:$false
$outputTextBox.AppendText("Machine $name removed from AD.`r`n")
}
}
$outputTextBox.AppendText("===========================================================================`r`n")
}
}
# Update all collections
Invoke-CMCollectionUpdate -Name $allCollectionName
Invoke-CMCollectionUpdate -Name $deploymentCollectionName
# Party
$outputTextBox.AppendText("#############################################`r`n")
$outputTextBox.AppendText("# You are done! #`r`n")
$outputTextBox.AppendText("# Wait 3 minutes before you start #`r`n")
$outputTextBox.AppendText("# Time to PARTY!!! #`r`n")
$outputTextBox.AppendText("#############################################`r`n")
# Check if log file already exist
if (Test-Path -Path $tempPath) {
# Open log file
Invoke-Item -Path $tempPath
# Wait 2 seconds before the temporary log file gets deleted
Start-Sleep -Seconds 2
# Delete the log file
Remove-Item -Path $tempPath -Force
}
})
# Wow... all that and NOW we can add the start button? Damn...
$form.Controls.Add($startButton)
# Output screen
$outputTextBox = New-Object System.Windows.Forms.TextBox
$outputTextBox.Location = New-Object System.Drawing.Point(10,40)
$outputTextBox.Size = New-Object System.Drawing.Size(560,300)
$outputTextBox.Multiline = $true
$outputTextBox.ScrollBars = 'Vertical'
$outputTextBox.Font = New-Object System.Drawing.Font("Consolas", 10)
# Add some explanation in the output field
$introText = "Welcome to the SCCM importer version $versionNumber`r`n" +
"`r`nYou can use a TXT file to add the computers. The TXT file with all computers must look like this:`r`n" +
"`r`n`r`nCOM00001,00:00:00:00:00:01`r`nCOM00002,00:00:00:00:00:02`r`nCOM00003,00:00:00:00:00:03`r`netc.`r`n"
$outputTextBox.Text = $introText
$form.Controls.Add($outputTextBox)
# And here we show the GUI
$form.ShowDialog() | Out-Null
CD C:
Conclusion
Automation for the win! There are still a few tasks I need to complete, but the primary goal is to automate the deployment collection process.
Thank you for reading.
Cheers,
Engin Soysal