# //*************************************************************************** # // Created by: Henk Hofs # // # // Microsoft Deployment Toolkit Solution Accelerator # // # // File: MDT-ZTI.ps1 # // # // Version: 1.0 # // # // Purpose: Provide ZTI Functionality to MDT without the use of SCCM/SMS. # // # // Usage: powershell.exe -command MDT-ZTI.ps1 # // # // History: # // Version Author Notes # // 0.7 HHO Initial version # // 0.8 HHO Added SQL Invoke-command function from the Powershell Cookbook by Lee Holmes # // 0.9 HHO Added Remove-netBootComputer account to fully clear PXE data on computers # // 1.0 HHO Final version # //*************************************************************************** # //*************************************************************************** # // Configuration: # // Notes: Set the database name and datasource for your environment heren. # // For SQLEXPRESS on the same server use ".\SQLEXPRESS" as datasource # //*************************************************************************** $Database = "MDT_DB" $DataSource = ".\SQLEXPRESS" # //---------------------------------------------------------------------------- # // Function: AD\Remove-netBootComputerAccount # // Purpose: Removes the DomainAdmins(n) computer account from AD to reset the PXE request # //---------------------------------------------------------------------------- Function Global:AD\Remove-netBootComputerAccount ([string]$UUID) { $strFilter = "(&(objectCategory=Computer))" $objDomain = New-Object System.DirectoryServices.DirectoryEntry $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objSearcher.SearchRoot = $objDomain $objSearcher.PageSize = 1000 $objSearcher.Filter = $strFilter $objSearcher.SearchScope = "Subtree" $colResults = $objSearcher.FindAll() ForEach ($objResult in $colResults) { If ($objResult.Properties.netbootguid -ne $Null) { $netbootguid = $objResult.GetDirectoryEntry().netbootguid $netbootguid = $netbootguid.ToString() $netbootguid.split(" ") | ForEach-Object { [int]$netbootguid = $_ $strNetboot = [Convert]::ToString($netbootguid, 16) If ($strNetboot.length -eq 1) {$strNetboot = $strNetboot.Insert(0, "0")} $strNetbootNew = $strNetbootNew + $strNetboot } If ($strNetBootNew.ToUpper() -eq $UUID) { $DN = $objResult.GetDirectoryEntry().distinguishedName dsrm.exe -noprompt "$DN" } } } } # //---------------------------------------------------------------------------- # // Function: SQL\Invoke-Command # // Purpose: Invoke a command in SQL syntax # // Notes: From Windows PowerShell Cookbook (O'Reilly) # // by Lee Holmes (http://www.leeholmes.com/guide) # //---------------------------------------------------------------------------- Function Global:SQL\Invoke-Command ([string] $DataSource, [string] $Database, [string] $SQLCommand, [System.Management.Automation.PsCredential] $Credential) { # // Prepare the authentication information. By default, we pick Windows authentication $authentication = "Integrated Security=SSPI;" # // If the user supplies a credential, then they want SQL authentication if($credential) { $plainCred = $credential.GetNetworkCredential() $authentication = ("uid={0};pwd={1};" -f $plainCred.Username,$plainCred.Password) } # // Prepare the connection string out of the information they provide $connectionString = "Provider=sqloledb; " + "Data Source=$dataSource; " + "Initial Catalog=$database; " + "$authentication; " # // Connect to the data source and open it $connection = New-Object System.Data.OleDb.OleDbConnection $connectionString $command = New-Object System.Data.OleDb.OleDbCommand $sqlCommand,$connection $connection.Open() # // Fetch the results, and close the connection $adapter = New-Object System.Data.OleDb.OleDbDataAdapter $command $dataset = New-Object System.Data.DataSet [void] $adapter.Fill($dataSet) $connection.Close() # // Return the dataset Return $dataset } # //---------------------------------------------------------------------------- # // Function: ZTI\Process-PendingDevicesData # // Purpose: Process the data to a usable format. # //---------------------------------------------------------------------------- Function Global:ZTI\Process-PendingDevicesData([hashtable]$PendingDevices, [int]$i) { $UUIDRaw = $PendingDevices.GetEnumerator() | Where-Object {$_.Value -eq $i -and $_.Key -match "UUID*"} | Select-Object Key $MACAddressRaw = $PendingDevices.GetEnumerator() | Where-Object {$_.Value -eq $i -and $_.Key -match "MAC address"} | Select-Object Key $RequestIDRaw = $PendingDevices.GetEnumerator() | Where-Object {$_.Value -eq $i -and $_.Key -match "Request*"} | Select-Object Key [string]$UUID = $UUIDRaw.Key [string]$MACAddress = $MACAddressRaw.Key [string]$RequestID = $RequestIDRaw.Key $UUID = $UUID.Split(":") | Select-Object -Last 1 $UUID = $UUID.TrimStart() $MACAddress = $MACAddress.Split(":") | Select-Object -Last 1 $MACAddress = $MACAddress.TrimStart() $MACAddress = $MACAddress.Substring(20) $MACAddress = $MACAddress.Insert(2, ":");$MACAddress = $MACAddress.Insert(5, ":");$MACAddress = $MACAddress.Insert(8, ":"); $MACAddress = $MACAddress.Insert(11, ":"); $MACAddress = $MACAddress.Insert(14, ":") $RequestID = $RequestID.Split(":") | Select-Object -Last 1 $RequestID = $RequestID.TrimStart() Return @{"RequestID" = $RequestID; "MACAddress" = $MACAddress; "UUID" = $UUID} } # //---------------------------------------------------------------------------- # // Function: ZTI\Get-PendingDevices # // Purpose: Get the pending devices from WDS # //---------------------------------------------------------------------------- Function Global:ZTI\Get-PendingDevices() { ZTI\Write-Log -LogLevel "Information" -Description "Getting pending devices from WDS" -EventID 4006 -Source "WDS Server" $PendingDevices = wdsutil.exe /Get-AutoAddDevices /DeviceType:PendingDevices $PendingDevices = $PendingDevices | Where-Object {$_ -ne ""} [hashtable]$PendingHashTable = @{0 = 0} [int]$i = 0 $PendingHashTable.Clear() $PendingDevices | ForEach-Object{ if($_ -match "UUID*"){$PendingHashTable.Add($_, $i)} if($_ -match "MAC add*"){$PendingHashTable.Add($_, $i)} if($_ -match "Request*"){$i++;$PendingHashTable.Add($_, $i)} } $TotalPending = $PendingHashTable.Count / 3 ZTI\Write-Log -LogLevel "Information" -Description "Found $TotalPending pending device(s)" -EventID 4007 -Source "WDS Server" Return $PendingHashTable } # //---------------------------------------------------------------------------- # // Function: ZTI\Write-Log # // Purpose: Write-Log entries to the event log and display on screen # //---------------------------------------------------------------------------- Function Global:ZTI\Write-Log ([string]$LogLevel, [string]$Description, [int]$EventID, [string]$Source) { Switch ($LogLevel) { "Error" {$ForegroundColor = "Red"} "Warning" {$ForegroundColor = "Yellow"} "Information" {$ForegroundColor = "White"} } New-EventLog -LogName MDT-ZeroTouch -Source MDT-ZeroTouch 2>1 Write-EventLog -LogName MDT-ZeroTouch -EntryType $LogLevel -EventId $EventID -Source $Source -Message $Description Write-Host -ForegroundColor $ForegroundColor $LogLevel ":" $Description } # //---------------------------------------------------------------------------- # // Main Routine # //---------------------------------------------------------------------------- # // Get the pending devices from WDS [hashtable]$PendingDevices = ZTI\Get-PendingDevices # // Calculate how many loops to make [int]$TotalPending = $PendingDevices.Count / 3 # // Initialize the loop $i = 0;Do { # // Start of Loop # // Iterate loop number $i++ # // Process data [hashtable]$PendingDevicesData = ZTI\Process-PendingDevicesData -PendingDevices $PendingDevices -i $i [string]$MACAddress = $PendingDevicesData.MACAddress [string]$RequestID = $PendingDevicesData.RequestID [string]$UUID = $PendingDevicesData.UUID Write-Host $UUID # // Check if Computer MAC address is found in MDT database $SQLCommand = "SELECT ID FROM dbo.ComputerIdentity WHERE MACAddress = '$MACAddress'" $SQLDataset = SQL\Invoke-Command -Database $Database -DataSource $DataSource -SQLCommand $SQLCommand $ID = $SQLDataSet.Tables | Select-Object -ExpandProperty Rows # // If computer is found in MDT database proceed If ($ID) { ZTI\Write-Log -LogLevel "Information" -Description "Computer entry found for $MACAddress, checking if device is allowed to netboot" -EventID 4008 -Source $MACAdress $ComputerID = $ID.Get_Item(0) $SQLCommand = "SELECT netBoot FROM dbo.Settings WHERE Type = 'C' AND ID = '$ComputerID'" $SQLDataset = SQL\Invoke-Command -Database $Database -DataSource $DataSource -SQLCommand $SQLCommand $netBoot = $SQLDataset.Tables | Select-Object -ExpandProperty Rows $netBoot = $netBoot.Get_Item(0) # // If the computer is allowed to netboot proceed If ($netBoot -ne "FALSE") { ZTI\Write-Log -LogLevel "Information" -Description "Computer is allowed to netboot, approving device" -EventID 4009 -Source $MACAddress wdsutil.exe /Approve-AutoAddDevices /RequestID:$RequestID # // Reset computer information in both WDS and MDT database so on next boot computer will be rejected Sleep -Seconds 10 wdsutil.exe /Delete-AutoAddDevices /DeviceType:ApprovedDevices $SQLCommand = "UPDATE dbo.Settings SET netBoot = 'FALSE' WHERE Type = 'C' AND ID = '$ComputerID'" $SQLDataset = SQL\Invoke-Command -Database $Database -DataSource $DataSource -SQLCommand $SQLCommand $RemoveComputer = AD\Remove-netBootComputerAccount -UUID $UUID # // If computer is not allowed to netboot, reject the device and clear the WDS database } Else { ZTI\Write-Log -LogLevel "Information" -Description "Computer is not allowed to netboot, rejecting device" -EventID 4010 -Source $MACAddress wdsutil.exe /Reject-AutoAddDevices /RequestID:$RequestID Sleep -Seconds 10 wdsutil.exe /Delete-AutoAddDevices /DeviceType:RejectedDevices } # // If computer is not found in MDT Database, reject the device and clear the WDS database } Else { ZTI\Write-Log -LogLevel "Information" -Description "No computer entries found for $MACAddress, rejecting device" -EventID 4011 -Source $MACAddress wdsutil.exe /Reject-AutoAddDevices /RequestID:$RequestID Sleep -Seconds 10 wdsutil.exe /Delete-AutoAddDevices /DeviceType:RejectedDevices } # // End of loop } until ($i -ge $TotalPending)