SCCM Importer: Mastering SCCM Deployment – A Tale of Script, GUI, and AutomationSCCM

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:

  1. Query the MAC address to check if it’s already in use by another machine.
  2. Check if the machine already exists in SCCM.
    1. If it does, verify if the MAC address matches.
    2. If it doesn’t match, produce an error.
    3. If the MAC is the same, delete the machine from SCCM.
  3. Add the machine back to SCCM and to the device collection.
  4. If the machine does not exist, add the machine to SCCM.
  5. Check if the machine is domain-joined.
    1. 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