From 183e78b6d531904ae7c0a659c333f0e673cef2e3 Mon Sep 17 00:00:00 2001 From: maurice-daly Date: Sun, 3 Dec 2017 23:03:38 +0000 Subject: [PATCH 01/57] Create Invoke-MSIntuneDriverUpdate.ps1 --- .../Invoke-MSIntuneDriverUpdate.ps1 | 742 ++++++++++++++++++ 1 file changed, 742 insertions(+) create mode 100644 Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 diff --git a/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 new file mode 100644 index 0000000..6db9537 --- /dev/null +++ b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 @@ -0,0 +1,742 @@ +<# +.SYNOPSIS + + The purpose of this script is to automate the driver update process when enrolling devices through + Microsoft Intune. + +.DESCRIPTION + + This script will determine the model of the computer, manufacturer and operating system used then download, + extract Q install the latest driver package from the manufacturer. At present Dell, HP and Lenovo devices + are supported. + +.NOTES + + FileName: Invoke-MSIntuneDriverUpdate.ps1 + + Author: Maurice Daly + Contact: @MoDaly_IT + Created: 2017-03-27 + Updated: 2017-11-07 + + Version history: + + 1.0.0 - (2017-12-03) Script created +#> + +# // =================== GLOBAL VARIABLES ====================== // + +$TempLocation = Join-Path $env:SystemDrive "Temp\SCConfigMgr" + +# Set Temp & Log Location +[string]$TempDirectory = Join-Path $TempLocation "\Temp" +[string]$LogDirectory = Join-Path $TempLocation "\Logs" + +# Create Temp Folder +if ((Test-Path -Path $TempDirectory) -eq $false) { + New-Item -Path $TempDirectory -ItemType Dir +} + +# Create Logs Folder +if ((Test-Path -Path $LogDirectory) -eq $false) { + New-Item -Path $LogDirectory -ItemType Dir +} + +# Logging Function +function global:Write-CMLogEntry { + param ( + [parameter(Mandatory = $true, HelpMessage = "Value added to the log file.")] + [ValidateNotNullOrEmpty()] + [string] + $Value, + [parameter(Mandatory = $true, HelpMessage = "Severity for the log entry. 1 for Informational, 2 for Warning and 3 for Error.")] + [ValidateNotNullOrEmpty()] + [ValidateSet("1", "2", "3")] + [string] + $Severity, + [parameter(Mandatory = $false, HelpMessage = "Name of the log file that the entry will written to.")] + [ValidateNotNullOrEmpty()] + [string] + $FileName = "Invoke-MSIntuneDriverUpdate.log" + ) + # Determine log file location + $LogFilePath = Join-Path -Path $LogDirectory -ChildPath $FileName + # Construct time stamp for log entry + $Time = -join @((Get-Date -Format "HH:mm:ss.fff"), "+", (Get-WmiObject -Class Win32_TimeZone | Select-Object -ExpandProperty Bias)) + # Construct date for log entry + $Date = (Get-Date -Format "MM-dd-yyyy") + # Construct context for log entry + $Context = $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name) + # Construct final log entry + $LogText = "" + # Add value to log file + try { + Add-Content -Value $LogText -LiteralPath $LogFilePath -ErrorAction Stop + } + catch [System.Exception] { + Write-Warning -Message "Unable to append log entry to Invoke-DriverUpdate.log file. Error message: $($_.Exception.Message)" + } +} + +# // =================== DELL VARIABLES ================ // + +# Define Dell Download Sources +$DellDownloadList = "http://downloads.dell.com/published/Pages/index.html" +$DellDownloadBase = "http://downloads.dell.com" +$DellDriverListURL = "http://en.community.dell.com/techcenter/enterprise-client/w/wiki/2065.dell-command-deploy-driver-packs-for-enterprise-client-os-deployment" +$DellBaseURL = "http://en.community.dell.com" + +# Define Dell Download Sources +$DellXMLCabinetSource = "http://downloads.dell.com/catalog/DriverPackCatalog.cab" +$DellCatalogSource = "http://downloads.dell.com/catalog/CatalogPC.cab" + +# Define Dell Cabinet/XL Names and Paths +$DellCabFile = [string]($DellXMLCabinetSource | Split-Path -Leaf) +$DellCatalogFile = [string]($DellCatalogSource | Split-Path -Leaf) +$DellXMLFile = $DellCabFile.Trim(".cab") +$DellXMLFile = $DellXMLFile + ".xml" +$DellCatalogXMLFile = $DellCatalogFile.Trim(".cab") + ".xml" + +# Define Dell Global Variables +$DellCatalogXML = $null +$DellModelXML = $null +$DellModelCabFiles = $null + +# // =================== HP VARIABLES ================ // + +# Define HP Download Sources +$HPXMLCabinetSource = "http://ftp.hp.com/pub/caps-softpaq/cmit/HPClientDriverPackCatalog.cab" +$HPSoftPaqSource = "http://ftp.hp.com/pub/softpaq/" +$HPPlatFormList = "http://ftp.hp.com/pub/caps-softpaq/cmit/imagepal/ref/platformList.cab" + +# Define HP Cabinet/XL Names and Paths +$HPCabFile = [string]($HPXMLCabinetSource | Split-Path -Leaf) +$HPXMLFile = $HPCabFile.Trim(".cab") +$HPXMLFile = $HPXMLFile + ".xml" +$HPPlatformCabFile = [string]($HPPlatFormList | Split-Path -Leaf) +$HPPlatformXMLFile = $HPPlatformCabFile.Trim(".cab") +$HPPlatformXMLFile = $HPPlatformXMLFile + ".xml" + +# Define HP Global Variables +$global:HPModelSoftPaqs = $null +$global:HPModelXML = $null +$global:HPPlatformXML = $null + +# // =================== LENOVO VARIABLES ================ // + +# Define Lenovo Download Sources +$global:LenovoXMLSource = "https://download.lenovo.com/cdrt/td/catalog.xml" + +# Define Lenovo Cabinet/XL Names and Paths +$global:LenovoXMLFile = [string]($global:LenovoXMLSource | Split-Path -Leaf) + +# Define Lenovo Global Variables +$global:LenovoModelDrivers = $null +$global:LenovoModelXML = $null +$global:LenovoModelType = $null +$global:LenovoSystemSKU = $null + +# // =================== COMMON VARIABLES ================ // + +# ArrayList to store models in +$DellProducts = New-Object -TypeName System.Collections.ArrayList +$HPProducts = New-Object -TypeName System.Collections.ArrayList +$global:LenovoProducts = New-Object -TypeName System.Collections.ArrayList + +# Determine manufacturer +$ComputerManufacturer = (Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty Manufacturer).Trim() +Write-CMLogEntry -Value "Manufacturer determined as: $($ComputerManufacturer)" -Severity 1 + +# Determine manufacturer name and hardware information +switch -Wildcard ($ComputerManufacturer) { + "*HP*" { + $ComputerManufacturer = "Hewlett-Packard" + $ComputerModel = Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty Model + $SystemSKU = (Get-CIMInstance -ClassName MS_SystemInformation -NameSpace root\WMI).BaseBoardProduct + } + "*Hewlett-Packard*" { + $ComputerManufacturer = "Hewlett-Packard" + $ComputerModel = Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty Model + $SystemSKU = (Get-CIMInstance -ClassName MS_SystemInformation -NameSpace root\WMI).BaseBoardProduct + } + "*Dell*" { + $ComputerManufacturer = "Dell" + $ComputerModel = Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty Model + $SystemSKU = (Get-CIMInstance -ClassName MS_SystemInformation -NameSpace root\WMI).SystemSku + } + "*Lenovo*" { + $ComputerManufacturer = "Lenovo" + $ComputerModel = Get-WmiObject -Class Win32_ComputerSystemProduct | Select-Object -ExpandProperty Version + $SystemSKU = ((Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty Model).SubString(0, 4)).Trim() + } +} +Write-CMLogEntry -Value "Computer model determined as: $($ComputerModel)" -Severity 1 + +if (-not [string]::IsNullOrEmpty($SystemSKU)) { + Write-CMLogEntry -Value "Computer SKU determined as: $($SystemSKU)" -Severity 1 +} + +# Get operating system name from version +switch -wildcard (Get-WmiObject -Class Win32_OperatingSystem | Select-Object -ExpandProperty Version) { + "10.0*" { + $OSName = "Windows 10" + } + "6.3*" { + $OSName = "Windows 8.1" + } + "6.1*" { + $OSName = "Windows 7" + } +} +Write-CMLogEntry -Value "Operating system determined as: $OSName" -Severity 1 + +$OSArchitecture = (Get-CimInstance Win32_operatingsystem).OSArchitecture +Write-CMLogEntry -Value "Architecture determined as: $OSArchitecture" -Severity 1 + +$WindowsVersion = ($OSName).Split(" ")[1] + +function DownloadDriverList { + global:Write-CMLogEntry -Value "======== Download Model Link Information ========" -Severity 1 + if ($ComputerManufacturer -eq "Hewlett-Packard") { + if ((Test-Path -Path $TempDirectory\$HPCabFile) -eq $false) { + global:Write-CMLogEntry -Value "======== Downloading HP Product List ========" -Severity 1 + # Download HP Model Cabinet File + global:Write-CMLogEntry -Value "Info: Downloading HP driver pack cabinet file from $HPXMLCabinetSource" -Severity 1 + try { + Start-BitsTransfer -Source $HPXMLCabinetSource -Destination $TempDirectory + # Expand Cabinet File + global:Write-CMLogEntry -Value "Info: Expanding HP driver pack cabinet file: $HPXMLFile" -Severity 1 + Expand "$TempDirectory\$HPCabFile" -F:* "$TempDirectory\$HPXMLFile" + } + catch { + global:Write-CMLogEntry -Value "Error: $($_.Exception.Message)" -Severity 3 + } + } + # Read XML File + if ($global:HPModelSoftPaqs -eq $null) { + global:Write-CMLogEntry -Value "Info: Reading driver pack XML file - $TempDirectory\$HPXMLFile" -Severity 1 + [xml]$global:HPModelXML = Get-Content -Path $TempDirectory\$HPXMLFile + # Set XML Object + $global:HPModelXML.GetType().FullName | Out-Null + $global:HPModelSoftPaqs = $HPModelXML.NewDataSet.HPClientDriverPackCatalog.ProductOSDriverPackList.ProductOSDriverPack + } + } + if ($ComputerManufacturer -eq "Dell") { + if ((Test-Path -Path $TempDirectory\$DellCabFile) -eq $false) { + global:Write-CMLogEntry -Value "Info: Downloading Dell product list" -Severity 1 + global:Write-CMLogEntry -Value "Info: Downloading Dell driver pack cabinet file from $DellXMLCabinetSource" -Severity 1 + # Download Dell Model Cabinet File + try { + Start-BitsTransfer -Source $DellXMLCabinetSource -Destination $TempDirectory + # Expand Cabinet File + global:Write-CMLogEntry -Value "Info: Expanding Dell driver pack cabinet file: $DellXMLFile" -Severity 1 + Expand "$TempDirectory\$DellCabFile" -F:* "$TempDirectory\$DellXMLFile" + } + catch { + global:Write-CMLogEntry -Value "Error: $($_.Exception.Message)" -Severity 3 + } + } + if ($DellModelXML -eq $null) { + # Read XML File + global:Write-CMLogEntry -Value "Info: Reading driver pack XML file - $TempDirectory\$DellXMLFile" -Severity 1 + [xml]$DellModelXML = (Get-Content -Path $TempDirectory\$DellXMLFile) + # Set XML Object + $DellModelXML.GetType().FullName | Out-Null + } + $DellModelCabFiles = $DellModelXML.driverpackmanifest.driverpackage + + } + if ($ComputerManufacturer -eq "Lenovo") { + if ($global:LenovoModelDrivers -eq $null) { + try { + [xml]$global:LenovoModelXML = Invoke-WebRequest -Uri $global:LenovoXMLSource + } + catch { + global:Write-CMLogEntry -Value "Error: $($_.Exception.Message)" -Severity 3 + } + + # Read Web Site + global:Write-CMLogEntry -Value "Info: Reading driver pack URL - $global:LenovoXMLSource" -Severity 1 + + # Set XML Object + $global:LenovoModelXML.GetType().FullName | Out-Null + $global:LenovoModelDrivers = $global:LenovoModelXML.Products + } + } +} + +function FindLenovoDriver { + +<# + # This powershell file will extract the link for the specified driver pack or application + # param $URI The string version of the URL + # param $64bit A boolean to determine what version to pick if there are multiple + # param $os A string containing 7, 8, or 10 depending on the os we are deploying + # i.e. 7, Win7, Windows 7 etc are all valid os strings + #> + param ( + [parameter(Mandatory = $true, HelpMessage = "Provide the URL to parse.")] + [ValidateNotNullOrEmpty()] + [string] + $URI, + [parameter(Mandatory = $true, HelpMessage = "Specify the operating system.")] + [ValidateNotNullOrEmpty()] + [string] + $OS, + [string] + $Architecture + ) + + #Case for direct link to a zip file + if ($URI.EndsWith(".zip")) { + return $URI + } + + $err = @() + + #Get the content of the website + try { + $html = Invoke-WebRequest –Uri $URI + } + catch { + global:Write-CMLogEntry -Value "Error: $($_.Exception.Message)" -Severity 3 + } + + #Create an array to hold all the links to exe files + $Links = @() + $Links.Clear() + + #determine if the URL resolves to the old download location + if ($URI -like "*olddownloads*") { + #Quickly grab the links that end with exe + $Links = (($html.Links | Where-Object { + $_.href -like "*exe" + }) | Where class -eq "downloadBtn").href + } + + $Links = ((Select-string '(http[s]?)(:\/\/)([^\s,]+.exe)(?=")' -InputObject ($html).Rawcontent -AllMatches).Matches.Value) + + if ($Links.Count -eq 0) { + return $null + } + + # Switch OS architecture + switch -wildcard ($Architecture) { + "*64*" { + $Architecture = "64" + } + "*86*" { + $Architecture = "32" + } + } + + #if there are multiple links then narrow down to the proper arc and os (if needed) + if ($Links.Count -gt 0) { + #Second array of links to hold only the ones we want to target + $MatchingLink = @() + $MatchingLink.clear() + foreach ($Link in $Links) { + if ($Link -like "*w$($OS)$($Architecture)_*" -or $Link -like "*w$($OS)_$($Architecture)*") { + $MatchingLink += $Link + } + } + } + + if ($MatchingLink -ne $null) { + return $MatchingLink + } + else { + return "badLink" + } +} + +function Get-RedirectedUrl { + Param ( + [Parameter(Mandatory = $true)] + [String] + $URL + ) + + $Request = [System.Net.WebRequest]::Create($URL) + $Request.AllowAutoRedirect = $false + $Request.Timeout = 3000 + $Response = $Request.GetResponse() + + if ($Response.ResponseUri) { + $Response.GetResponseHeader("Location") + } + $Response.Close() +} + +function LenovoModelTypeFinder { + param ( + [parameter(Mandatory = $false, HelpMessage = "Enter Lenovo model to query")] + [string] + $ComputerModel, + [parameter(Mandatory = $false, HelpMessage = "Enter Operating System")] + [string] + $OS, + [parameter(Mandatory = $false, HelpMessage = "Enter Lenovo model type to query")] + [string] + $ComputerModelType + ) + try { + if ($global:LenovoModelDrivers -eq $null) { + [xml]$global:LenovoModelXML = Invoke-WebRequest -Uri $global:LenovoXMLSource + # Read Web Site + global:Write-CMLogEntry -Value "Info: Reading driver pack URL - $global:LenovoXMLSource" -Severity 1 + + # Set XML Object + $global:LenovoModelXML.GetType().FullName | Out-Null + $global:LenovoModelDrivers = $global:LenovoModelXML.Products + } + } + catch { + global:Write-CMLogEntry -Value "Error: $($_.Exception.Message)" -Severity 3 + } + + if ($ComputerModel.Length -gt 0) { + $global:LenovoModelType = ($global:LenovoModelDrivers.Product | Where-Object { + $_.Queries.Version -match "$ComputerModel" + }).Queries.Types | Select -ExpandProperty Type | Select -first 1 + $global:LenovoSystemSKU = ($global:LenovoModelDrivers.Product | Where-Object { + $_.Queries.Version -match "$ComputerModel" + }).Queries.Types | select -ExpandProperty Type | Get-Unique + } + + if ($ComputerModelType.Length -gt 0) { + $global:LenovoModelType = (($global:LenovoModelDrivers.Product.Queries) | Where-Object { + ($_.Types | Select -ExpandProperty Type) -match $ComputerModelType + }).Version | Select -first 1 + } + Return $global:LenovoModelType +} + +function InitiateDownloads { + + $Product = "Intune Driver Automation" + + # Driver Download ScriptBlock + $DriverDownloadJob = { + Param ([string] + $TempDirectory, + [string] + $ComputerModel, + [string] + $DriverCab, + [string] + $DriverDownloadURL + ) + + try { + # Start Driver Download + Start-BitsTransfer -DisplayName "$ComputerModel-DriverDownload" -Source $DriverDownloadURL -Destination "$($TempDirectory + '\Driver Cab\' + $DriverCab)" + } + catch [System.Exception] { + global:Write-CMLogEntry -Value "Error: $($_.Exception.Message)" -Severity 3 + } + } + + global:Write-CMLogEntry -Value "======== Starting Download Processes ========" -Severity 1 + global:Write-CMLogEntry -Value "Info: Operating System specified: Windows $($WindowsVersion)" -Severity 1 + global:Write-CMLogEntry -Value "Info: Operating System architecture specified: $($Architecture)" -Severity 1 + + # Operating System Version + $OperatingSystem = ("Windows " + $($WindowsVersion)) + + # Vendor Make + $ComputerModel = $ComputerModel.Trim() + + # Get Windows Version Number + switch -Wildcard ((Get-WmiObject -Class Win32_OperatingSystem).Version) { + "*10.0.16*" { + $OSBuild = "1709" + } + "*10.0.15*" { + $OSBuild = "1703" + } + "*10.0.14*" { + $OSBuild = "1607" + } + } + global:Write-CMLogEntry -Value "Info: Windows 10 build $OSBuild identified for driver match" -Severity 1 + + # Start driver import processes + global:Write-CMLogEntry -Value "Info: Starting Download,Extract And Import Processes For $ComputerManufacturer Model: $($ComputerModel)" -Severity 1 + + # =================== DEFINE VARIABLES ===================== + + if ($ComputerManufacturer -eq "Dell") { + global:Write-CMLogEntry -Value "Info: Setting Dell variables" -Severity 1 + if ($DellModelCabFiles -eq $null) { + [xml]$DellModelXML = Get-Content -Path $TempDirectory\$DellXMLFile + # Set XML Object + $DellModelXML.GetType().FullName | Out-Null + $DellModelCabFiles = $DellModelXML.driverpackmanifest.driverpackage + } + if ($SystemSKU -ne $null) { + global:Write-CMLogEntry -Value "Info: SystemSKU value is present, attempting match based on SKU - $SystemSKU)" -Severity 1 + + $ComputerModelURL = $DellDownloadBase + "/" + ($DellModelCabFiles | Where-Object { + ((($_.SupportedOperatingSystems).OperatingSystem).osCode -like "*$WindowsVersion*") -and ($_.SupportedSystems.Brand.Model.SystemID -eq $SystemSKU) + }).delta + $ComputerModelURL = $ComputerModelURL.Replace("\", "/") + $DriverDownload = $DellDownloadBase + "/" + ($DellModelCabFiles | Where-Object { + ((($_.SupportedOperatingSystems).OperatingSystem).osCode -like "*$WindowsVersion*") -and ($_.SupportedSystems.Brand.Model.SystemID -eq $SystemSKU) + }).path + $DriverCab = (($DellModelCabFiles | Where-Object { + ((($_.SupportedOperatingSystems).OperatingSystem).osCode -like "*$WindowsVersion*") -and ($_.SupportedSystems.Brand.Model.SystemID -eq $SystemSKU) + }).path).Split("/") | select -Last 1 + } + elseif ($SystemSKU -eq $null -or $DriverCab -eq $null) { + global:Write-CMLogEntry -Value "Info: Falling back to matching based on model name" -Severity 1 + + $ComputerModelURL = $DellDownloadBase + "/" + ($DellModelCabFiles | Where-Object { + ((($_.SupportedOperatingSystems).OperatingSystem).osCode -like "*$WindowsVersion*") -and ($_.SupportedSystems.Brand.Model.Name -like "*$ComputerModel*") + }).delta + $ComputerModelURL = $ComputerModelURL.Replace("\", "/") + $DriverDownload = $DellDownloadBase + "/" + ($DellModelCabFiles | Where-Object { + ((($_.SupportedOperatingSystems).OperatingSystem).osCode -like "*$WindowsVersion*") -and ($_.SupportedSystems.Brand.Model.Name -like "*$ComputerModel") + }).path + $DriverCab = (($DellModelCabFiles | Where-Object { + ((($_.SupportedOperatingSystems).OperatingSystem).osCode -like "*$WindowsVersion*") -and ($_.SupportedSystems.Brand.Model.Name -like "*$ComputerModel") + }).path).Split("/") | select -Last 1 + } + $DriverRevision = (($DriverCab).Split("-")[2]).Trim(".cab") + $DellSystemSKU = ($DellModelCabFiles.supportedsystems.brand.model | Where-Object { + $_.Name -eq $ComputerModel + } | Get-Unique).systemID + if ($DellSystemSKU.count -gt 1) { + $DellSystemSKU = [string]($DellSystemSKU -join ";") + } + global:Write-CMLogEntry -Value "Info: Dell System Model ID is : $DellSystemSKU" -Severity 1 + } + if ($ComputerManufacturer -eq "Hewlett-Packard") { + global:Write-CMLogEntry -Value "Info: Setting HP variables" -Severity 1 + if ($global:HPModelSoftPaqs -eq $null) { + [xml]$global:HPModelXML = Get-Content -Path $TempDirectory\$HPXMLFile + # Set XML Object + $global:HPModelXML.GetType().FullName | Out-Null + $global:HPModelSoftPaqs = $global:HPModelXML.NewDataSet.HPClientDriverPackCatalog.ProductOSDriverPackList.ProductOSDriverPack + } + if ($SystemSKU -ne $null) { + $HPSoftPaqSummary = $global:HPModelSoftPaqs | Where-Object { + ($_.SystemID -match $SystemSKU) -and ($_.OSName -like "$OSName*$OSArchitecture*$OSBuild*") + } | Sort-Object -Descending | select -First 1 + } + else { + $HPSoftPaqSummary = $global:HPModelSoftPaqs | Where-Object { + ($_.SystemName -like "*$ComputerModel*") -and ($_.OSName -like "$OSName*$OSArchitecture*$OSBuild*") + } | Sort-Object -Descending | select -First 1 + } + $HPSoftPaq = $HPSoftPaqSummary.SoftPaqID + $HPSoftPaqDetails = $global:HPModelXML.newdataset.hpclientdriverpackcatalog.softpaqlist.softpaq | Where-Object { + $_.ID -eq "$HPSoftPaq" + } + $ComputerModelURL = $HPSoftPaqDetails.URL + # Replace FTP for HTTP for Bits Transfer Job + $DriverDownload = ($HPSoftPaqDetails.URL).TrimStart("ftp:") + $DriverCab = $ComputerModelURL | Split-Path -Leaf + $DriverRevision = "$($HPSoftPaqDetails.Version)" + } + if ($ComputerManufacturer -eq "Lenovo") { + global:Write-CMLogEntry -Value "Info: Setting Lenovo variables" -Severity 1 + $global:LenovoModelType = LenovoModelTypeFinder -ComputerModel $ComputerModel -OS $WindowsVersion + global:Write-CMLogEntry -Value "Info: $ComputerManufacturer $ComputerModel matching model type: $global:LenovoModelType" -Severity 1 + + if ($global:LenovoModelDrivers -ne $null) { + [xml]$global:LenovoModelXML = (New-Object System.Net.WebClient).DownloadString("$global:LenovoXMLSource") + # Set XML Object + $global:LenovoModelXML.GetType().FullName | Out-Null + $global:LenovoModelDrivers = $global:LenovoModelXML.Products + if ($SystemSKU -ne $null) { + $ComputerModelURL = (($global:LenovoModelDrivers.Product | Where-Object { + ($_.Queries.smbios -match $SystemSKU -and $_.OS -match $WindowsVersion) + }).driverPack | Where-Object { + $_.id -eq "SCCM" + })."#text" + } + else { + $ComputerModelURL = (($global:LenovoModelDrivers.Product | Where-Object { + ($_.Queries.Version -like "*ComputerModel*" -and $_.OS -match $WindowsVersion) + }).driverPack | Where-Object { + $_.id -eq "SCCM" + })."#text" + } + global:Write-CMLogEntry -Value "Info: Model URL determined as $ComputerModelURL" -Severity 1 + $DriverDownload = FindLenovoDriver -URI $ComputerModelURL -os $WindowsVersion -Architecture $OSArchitecture + If ($DriverDownload -ne $null) { + $DriverCab = $DriverDownload | Split-Path -Leaf + $DriverRevision = ($DriverCab.Split("_") | Select -Last 1).Trim(".exe") + global:Write-CMLogEntry -Value "Info: Driver cabinet download determined as $DriverDownload" -Severity 1 + } + else { + global:Write-CMLogEntry -Value "Error: Unable to find driver for $Make $Model" -Severity 1 + } + } + } + + # Driver location variables + $DriverSourceCab = ($TempDirectory + "\Driver Cab\" + $DriverCab) + $DriverExtractDest = ("$TempDirectory" + "\Driver Files") + global:Write-CMLogEntry -Value "Info: Driver extract location set - $DriverExtractDest" -Severity 1 + + # =================== INITIATE DOWNLOADS =================== + + global:Write-CMLogEntry -Value "======== $Product - $ComputerManufacturer $ComputerModel DRIVER PROCESSING STARTED ========" -Severity 1 + + # =============== ConfigMgr Driver Cab Download ================= + global:Write-CMLogEntry -Value "$($Product): Retrieving ConfigMgr driver pack site For $ComputerManufacturer $ComputerModel" -Severity 1 + global:Write-CMLogEntry -Value "$($Product): URL found: $ComputerModelURL" -Severity 1 + + if (($ComputerModelURL -ne $null) -and ($DriverDownload -ne "badLink")) { + # Cater for HP / Model Issue + $ComputerModel = $ComputerModel -replace '/', '-' + $ComputerModel = $ComputerModel.Trim() + Set-Location -Path $TempDirectory + # Check for destination directory, create if required and download the driver cab + if ((Test-Path -Path $($TempDirectory + "\Driver Cab\" + $DriverCab)) -eq $false) { + if ((Test-Path -Path $($TempDirectory + "\Driver Cab")) -eq $false) { + New-Item -ItemType Directory -Path $($TempDirectory + "\Driver Cab") + } + global:Write-CMLogEntry -Value "$($Product): Downloading $DriverCab driver cab file" -Severity 1 + global:Write-CMLogEntry -Value "$($Product): Downloading from URL: $DriverDownload" -Severity 1 + Start-Job -Name "$ComputerModel-DriverDownload" -ScriptBlock $DriverDownloadJob -ArgumentList ($TempDirectory, $ComputerModel, $DriverCab, $DriverDownload) + sleep -Seconds 5 + $BitsJob = Get-BitsTransfer | Where-Object { + $_.DisplayName -match "$ComputerModel-DriverDownload" + } + while (($BitsJob).JobState -eq "Connecting") { + global:Write-CMLogEntry -Value "$($Product): Establishing connection to $DriverDownload" -Severity 1 + sleep -seconds 30 + } + while (($BitsJob).JobState -eq "Transferring") { + if ($BitsJob.BytesTotal -ne $null) { + $PercentComplete = [int](($BitsJob.BytesTransferred * 100)/$BitsJob.BytesTotal); + global:Write-CMLogEntry -Value "$($Product): Downloaded $([int]((($BitsJob).BytesTransferred)/ 1MB)) MB of $([int]((($BitsJob).BytesTotal)/ 1MB)) MB ($PercentComplete%). Next update in 30 seconds." -Severity 1 + sleep -seconds 30 + } + else { + global:Write-CMLogEntry -Value "$($Product): Download issues detected. Cancelling download process" -Severity 2 + Get-BitsTransfer | Where-Object { + $_.DisplayName -eq "$ComputerModel-DriverDownload" + } | Remove-BitsTransfer + } + } + Get-BitsTransfer | Where-Object { + $_.DisplayName -eq "$ComputerModel-DriverDownload" + } | Complete-BitsTransfer + global:Write-CMLogEntry -Value "$($Product): Driver revision: $DriverRevision" -Severity 1 + } + else { + global:Write-CMLogEntry -Value "$($Product): Skipping $DriverCab. Driver pack already downloaded." -Severity 1 + } + + # Cater for HP / Model Issue + $ComputerModel = $ComputerModel -replace '/', '-' + + if (((Test-Path -Path "$($TempDirectory + '\Driver Cab\' + $DriverCab)") -eq $true) -and ($DriverCab -ne $null)) { + global:Write-CMLogEntry -Value "$($Product): $DriverCab File exists - Starting driver update process" -Severity 1 + # =============== Extract Drivers ================= + + if ((Test-Path -Path "$DriverExtractDest") -eq $false) { + New-Item -ItemType Directory -Path "$($DriverExtractDest)" + } + if ((Get-ChildItem -Path "$DriverExtractDest" -Recurse -Filter *.inf -File).Count -eq 0) { + global:Write-CMLogEntry -Value "==================== $PRODUCT DRIVER EXTRACT ====================" -Severity 1 + global:Write-CMLogEntry -Value "$($Product): Expanding driver CAB source file: $DriverCab" -Severity 1 + global:Write-CMLogEntry -Value "$($Product): Driver CAB destination directory: $DriverExtractDest" -Severity 1 + if ($ComputerManufacturer -eq "Dell") { + global:Write-CMLogEntry -Value "$($Product): Extracting $ComputerManufacturer drivers to $DriverExtractDest" -Severity 1 + Expand "$DriverSourceCab" -F:* "$DriverExtractDest" + } + if ($ComputerManufacturer -eq "Hewlett-Packard") { + global:Write-CMLogEntry -Value "$($Product): Extracting $ComputerManufacturer drivers to $HPTemp" -Severity 1 + # Driver Silent Extract Switches + $HPSilentSwitches = "-PDF -F" + "$DriverExtractDest" + " -S -E" + global:Write-CMLogEntry -Value "$($Product): Using $ComputerManufacturer silent switches: $HPSilentSwitches" -Severity 1 + Start-Process -FilePath "$($TempDirectory + '\Driver Cab\' + $DriverCab)" -ArgumentList $HPSilentSwitches -Verb RunAs + $DriverProcess = ($DriverCab).Substring(0, $DriverCab.length - 4) + + # Wait for HP SoftPaq Process To Finish + While ((Get-Process).name -contains $DriverProcess) { + global:Write-CMLogEntry -Value "$($Product): Waiting for extract process (Process: $DriverProcess) to complete.. Next check in 30 seconds" -Severity 1 + sleep -Seconds 30 + } + } + if ($ComputerManufacturer -eq "Lenovo") { + # Driver Silent Extract Switches + $global:LenovoSilentSwitches = "/VERYSILENT /DIR=" + '"' + $DriverExtractDest + '"' + ' /Extract="Yes"' + global:Write-CMLogEntry -Value "$($Product): Using $ComputerManufacturer silent switches: $global:LenovoSilentSwitches" -Severity 1 + global:Write-CMLogEntry -Value "$($Product): Extracting $ComputerManufacturer drivers to $DriverExtractDest" -Severity 1 + Unblock-File -Path $($TempDirectory + '\Driver Cab\' + $DriverCab) + Start-Process -FilePath "$($TempDirectory + '\Driver Cab\' + $DriverCab)" -ArgumentList $global:LenovoSilentSwitches -Verb RunAs + $DriverProcess = ($DriverCab).Substring(0, $DriverCab.length - 4) + # Wait for Lenovo Driver Process To Finish + While ((Get-Process).name -contains $DriverProcess) { + global:Write-CMLogEntry -Value "$($Product): Waiting for extract process (Process: $DriverProcess) to complete.. Next check in 30 seconds" -Severity 1 + sleep -seconds 30 + } + } + } + else { + global:Write-CMLogEntry -Value "Skipping. Drivers already extracted." -Severity 1 + } + } + else { + global:Write-CMLogEntry -Value "$($Product): $DriverCab file download failed" -Severity 3 + } + } + elseif ($DriverDownload -eq "badLink") { + global:Write-CMLogEntry -Value "$($Product): Operating system driver package download path not found.. Skipping $ComputerModel" -Severity 3 + } + else { + global:Write-CMLogEntry -Value "$($Product): Driver package not found for $ComputerModel running Windows $WindowsVersion $Architecture. Skipping $ComputerModel" -Severity 2 + } + global:Write-CMLogEntry -Value "======== $PRODUCT - $ComputerManufacturer $ComputerModel DRIVER PROCESSING FINISHED ========" -Severity 1 + + + if ($ValidationErrors -eq 0) { + + } +} + +function Update-Drivers { + $DriverPackagePath = Join-Path $TempDirectory "Driver Files" + Write-CMLogEntry -Value "Driver package location is $DriverPackagePath" -Severity 1 + Write-CMLogEntry -Value "Starting driver installation process" -Severity 1 + Write-CMLogEntry -Value "Reading drivers from $DriverPackagePath" -Severity 1 + # Apply driver maintenance package + try { + if ((Get-ChildItem -Path $DriverPackagePath -Filter *.inf -Recurse).count -gt 0) { + try { + Start-Process "$env:WINDIR\sysnative\windowspowershell\v1.0\powershell.exe" -WorkingDirectory $DriverPackagePath -ArgumentList "pnputil /add-driver *.inf /subdirs /install | Out-File -FilePath (Join-Path $LogDirectory '\Install-Drivers.txt') -Append" -NoNewWindow -Wait + Write-CMLogEntry -Value "Driver installation complete. Restart required" -Severity 1 + } + catch [System.Exception] + { + Write-CMLogEntry -Value "An error occurred while attempting to apply the driver maintenance package. Error message: $($_.Exception.Message)" -Severity 3; exit 1 + } + } + else { + Write-CMLogEntry -Value "No driver inf files found in $DriverPackagePath." -Severity 3; exit 1 + } + } + catch [System.Exception] { + Write-CMLogEntry -Value "An error occurred while attempting to apply the driver maintenance package. Error message: $($_.Exception.Message)" -Severity 3; exit 1 + } + Write-CMLogEntry -Value "Finished driver maintenance." -Severity 1 + Return $LastExitCode +} + +if ($OSName -eq "Windows 10") { + # Download manufacturer lists for driver matching + DownloadDriverList + # Initiate matched downloads + InitiateDownloads + # Update driver repository and install drivers + Update-Drivers +} +else { + Write-CMLogEntry -Value "An upsupported OS was detected. This script only supports Windows 10." -Severity 3; exit 1 +} From 8d38932df551243e690653ee4c5180c882c4f16a Mon Sep 17 00:00:00 2001 From: maurice-daly Date: Sun, 3 Dec 2017 23:03:57 +0000 Subject: [PATCH 02/57] Update Invoke-MSIntuneDriverUpdate.ps1 --- Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 index 6db9537..a761956 100644 --- a/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 +++ b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 @@ -7,7 +7,7 @@ .DESCRIPTION This script will determine the model of the computer, manufacturer and operating system used then download, - extract Q install the latest driver package from the manufacturer. At present Dell, HP and Lenovo devices + extract & install the latest driver package from the manufacturer. At present Dell, HP and Lenovo devices are supported. .NOTES From ab1973fb176eeeb09b532b6972c6015fa700227f Mon Sep 17 00:00:00 2001 From: maurice-daly Date: Sun, 3 Dec 2017 23:47:54 +0000 Subject: [PATCH 03/57] Update Invoke-MSIntuneDriverUpdate.ps1 --- Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 index a761956..6a4c7da 100644 --- a/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 +++ b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 @@ -439,7 +439,7 @@ function InitiateDownloads { global:Write-CMLogEntry -Value "======== Starting Download Processes ========" -Severity 1 global:Write-CMLogEntry -Value "Info: Operating System specified: Windows $($WindowsVersion)" -Severity 1 - global:Write-CMLogEntry -Value "Info: Operating System architecture specified: $($Architecture)" -Severity 1 + global:Write-CMLogEntry -Value "Info: Operating System architecture specified: $($OSArchitecture)" -Severity 1 # Operating System Version $OperatingSystem = ("Windows " + $($WindowsVersion)) From 0a6ac596ec8a21a95603e115db715a710d97a6fe Mon Sep 17 00:00:00 2001 From: maurice-daly Date: Mon, 4 Dec 2017 09:11:33 +0000 Subject: [PATCH 04/57] Update Invoke-MSIntuneDriverUpdate.ps1 --- Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 index 6a4c7da..5713e38 100644 --- a/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 +++ b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 @@ -16,8 +16,8 @@ Author: Maurice Daly Contact: @MoDaly_IT - Created: 2017-03-27 - Updated: 2017-11-07 + Created: 2017-12-03 + Updated: 2017-12-03 Version history: From 137a9a016da331873bb56c645433f0044c49b0fa Mon Sep 17 00:00:00 2001 From: maurice-daly Date: Tue, 5 Dec 2017 14:42:03 +0000 Subject: [PATCH 05/57] Update Invoke-MSIntuneDriverUpdate.ps1 --- .../Invoke-MSIntuneDriverUpdate.ps1 | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 index 5713e38..160cf63 100644 --- a/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 +++ b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 @@ -17,11 +17,12 @@ Author: Maurice Daly Contact: @MoDaly_IT Created: 2017-12-03 - Updated: 2017-12-03 + Updated: 2017-12-05 Version history: 1.0.0 - (2017-12-03) Script created + 1.0.1 - (2017-12-05) Updated Lenovo matching SKU value and added regex matching for Computer Model values #> # // =================== GLOBAL VARIABLES ====================== // @@ -138,11 +139,6 @@ $global:LenovoSystemSKU = $null # // =================== COMMON VARIABLES ================ // -# ArrayList to store models in -$DellProducts = New-Object -TypeName System.Collections.ArrayList -$HPProducts = New-Object -TypeName System.Collections.ArrayList -$global:LenovoProducts = New-Object -TypeName System.Collections.ArrayList - # Determine manufacturer $ComputerManufacturer = (Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty Manufacturer).Trim() Write-CMLogEntry -Value "Manufacturer determined as: $($ComputerManufacturer)" -Severity 1 @@ -167,7 +163,7 @@ switch -Wildcard ($ComputerManufacturer) { "*Lenovo*" { $ComputerManufacturer = "Lenovo" $ComputerModel = Get-WmiObject -Class Win32_ComputerSystemProduct | Select-Object -ExpandProperty Version - $SystemSKU = ((Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty Model).SubString(0, 4)).Trim() + $SystemSKU = ((Get-CIMInstance -ClassName MS_SystemInformation -NameSpace root\WMI | Select-Object -ExpandProperty BIOSVersion).SubString(0, 4)).Trim() } } Write-CMLogEntry -Value "Computer model determined as: $($ComputerModel)" -Severity 1 @@ -504,7 +500,7 @@ function InitiateDownloads { } $DriverRevision = (($DriverCab).Split("-")[2]).Trim(".cab") $DellSystemSKU = ($DellModelCabFiles.supportedsystems.brand.model | Where-Object { - $_.Name -eq $ComputerModel + $_.Name -match ("^" + $ComputerModel + "$") } | Get-Unique).systemID if ($DellSystemSKU.count -gt 1) { $DellSystemSKU = [string]($DellSystemSKU -join ";") @@ -526,18 +522,23 @@ function InitiateDownloads { } else { $HPSoftPaqSummary = $global:HPModelSoftPaqs | Where-Object { - ($_.SystemName -like "*$ComputerModel*") -and ($_.OSName -like "$OSName*$OSArchitecture*$OSBuild*") + ($_.SystemName -match ("^" + $ComputerModel + "$")) -and ($_.OSName -like "$OSName*$OSArchitecture*$OSBuild*") } | Sort-Object -Descending | select -First 1 } - $HPSoftPaq = $HPSoftPaqSummary.SoftPaqID - $HPSoftPaqDetails = $global:HPModelXML.newdataset.hpclientdriverpackcatalog.softpaqlist.softpaq | Where-Object { - $_.ID -eq "$HPSoftPaq" + if ($HPSoftPaqSummary -ne $null) { + $HPSoftPaq = $HPSoftPaqSummary.SoftPaqID + $HPSoftPaqDetails = $global:HPModelXML.newdataset.hpclientdriverpackcatalog.softpaqlist.softpaq | Where-Object { + $_.ID -eq "$HPSoftPaq" + } + $ComputerModelURL = $HPSoftPaqDetails.URL + # Replace FTP for HTTP for Bits Transfer Job + $DriverDownload = ($HPSoftPaqDetails.URL).TrimStart("ftp:") + $DriverCab = $ComputerModelURL | Split-Path -Leaf + $DriverRevision = "$($HPSoftPaqDetails.Version)" + } + else{ + Write-CMLogEntry -Value "Unsupported model / operating system combination found. Exiting." -Severity 3; exit 1 } - $ComputerModelURL = $HPSoftPaqDetails.URL - # Replace FTP for HTTP for Bits Transfer Job - $DriverDownload = ($HPSoftPaqDetails.URL).TrimStart("ftp:") - $DriverCab = $ComputerModelURL | Split-Path -Leaf - $DriverRevision = "$($HPSoftPaqDetails.Version)" } if ($ComputerManufacturer -eq "Lenovo") { global:Write-CMLogEntry -Value "Info: Setting Lenovo variables" -Severity 1 @@ -558,7 +559,7 @@ function InitiateDownloads { } else { $ComputerModelURL = (($global:LenovoModelDrivers.Product | Where-Object { - ($_.Queries.Version -like "*ComputerModel*" -and $_.OS -match $WindowsVersion) + ($_.Queries.Version -match ("^" + $ComputerModel + "$") -and $_.OS -match $WindowsVersion) }).driverPack | Where-Object { $_.id -eq "SCCM" })."#text" From 38ae0317238916396121b104285d009eb2a6d526 Mon Sep 17 00:00:00 2001 From: maurice-daly Date: Tue, 5 Dec 2017 15:07:38 +0000 Subject: [PATCH 06/57] Update Invoke-MSIntuneDriverUpdate.ps1 --- Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 index 160cf63..e8a5306 100644 --- a/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 +++ b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 @@ -522,7 +522,7 @@ function InitiateDownloads { } else { $HPSoftPaqSummary = $global:HPModelSoftPaqs | Where-Object { - ($_.SystemName -match ("^" + $ComputerModel + "$")) -and ($_.OSName -like "$OSName*$OSArchitecture*$OSBuild*") + ($_.SystemName -match $ComputerModel) -and ($_.OSName -like "$OSName*$OSArchitecture*$OSBuild*") } | Sort-Object -Descending | select -First 1 } if ($HPSoftPaqSummary -ne $null) { From 72c0c92be818629e6dce44c4f374a534ca961660 Mon Sep 17 00:00:00 2001 From: maurice-daly Date: Tue, 5 Dec 2017 15:12:44 +0000 Subject: [PATCH 07/57] Update Invoke-MSIntuneDriverUpdate.ps1 --- Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 index e8a5306..1bd067b 100644 --- a/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 +++ b/Driver Automation/Invoke-MSIntuneDriverUpdate.ps1 @@ -22,7 +22,8 @@ Version history: 1.0.0 - (2017-12-03) Script created - 1.0.1 - (2017-12-05) Updated Lenovo matching SKU value and added regex matching for Computer Model values + 1.0.1 - (2017-12-05) Updated Lenovo matching SKU value and added regex matching for Computer Model values. + 1.0.2 - (2017-12-05) Updated to cater for language differences in OS architecture returned #> # // =================== GLOBAL VARIABLES ====================== // @@ -186,7 +187,16 @@ switch -wildcard (Get-WmiObject -Class Win32_OperatingSystem | Select-Object -Ex } Write-CMLogEntry -Value "Operating system determined as: $OSName" -Severity 1 -$OSArchitecture = (Get-CimInstance Win32_operatingsystem).OSArchitecture +# Get operating system architecture +switch -wildcard ((Get-CimInstance Win32_operatingsystem).OSArchitecture) { + "64-*" { + $OSArchitecture = "64-Bit" + } + "32-*" { + $OSArchitecture = "32-Bit" + } +} + Write-CMLogEntry -Value "Architecture determined as: $OSArchitecture" -Severity 1 $WindowsVersion = ($OSName).Split(" ")[1] From 36fc08c470f47701eac6a068501df043ab588346 Mon Sep 17 00:00:00 2001 From: Nickolaj Andersen Date: Thu, 22 Mar 2018 16:08:04 +0100 Subject: [PATCH 08/57] 1.0.0 --- .../Get-AppleMDMPushCertificateExpiration.ps1 | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 Automation/Get-AppleMDMPushCertificateExpiration.ps1 diff --git a/Automation/Get-AppleMDMPushCertificateExpiration.ps1 b/Automation/Get-AppleMDMPushCertificateExpiration.ps1 new file mode 100644 index 0000000..c575ff1 --- /dev/null +++ b/Automation/Get-AppleMDMPushCertificateExpiration.ps1 @@ -0,0 +1,127 @@ +# Functions +function Send-O365MailMessage { + param ( + [parameter(Mandatory=$true)] + [string]$Credential, + [parameter(Mandatory=$false)] + [string]$Body, + [parameter(Mandatory=$false)] + [string]$Subject, + [parameter(Mandatory=$true)] + [string]$Recipient, + [parameter(Mandatory=$true)] + [string]$From + ) + # Get Azure Automation credential for authentication + $PSCredential = Get-AutomationPSCredential -Name $Credential + + # Construct the MailMessage object + $MailMessage = New-Object -TypeName System.Net.Mail.MailMessage + $MailMessage.From = $From + $MailMessage.ReplyTo = $From + $MailMessage.To.Add($Recipient) + $MailMessage.Body = $Body + $MailMessage.BodyEncoding = ([System.Text.Encoding]::UTF8) + $MailMessage.IsBodyHtml = $true + $MailMessage.SubjectEncoding = ([System.Text.Encoding]::UTF8) + + # Attempt to set the subject + try { + $MailMessage.Subject = $Subject + } + catch [System.Management.Automation.SetValueInvocationException] { + Write-Warning -InputObject "An exception occurred while setting the message subject" + } + + # Construct SMTP Client object + $SMTPClient = New-Object -TypeName System.Net.Mail.SmtpClient -ArgumentList @("smtp.office365.com", 587) + $SMTPClient.Credentials = $PSCredential + $SMTPClient.EnableSsl = $true + + # Send mail message + $SMTPClient.Send($MailMessage) +} + +# Define email information details +$AzureAutomationCredentialName = "MailUser" +$MailRecipient = "recipient@domain.com" +$MailFrom = "user@domain.com" + +# Define Azure Automation variables +$AzureAutomationCredentialName = "MSIntuneAutomationUser" +$AzureAutomationVariableAppClientID = "AppClientID" +$AzureAutomationVariableTenantName = "TenantName" + +# Define monitoring options +$AppleMDMPushCertificateNotificationRange = 7 + +try { + # Import required modules + Write-Output -InputObject "Importing required modules" + Import-Module -Name AzureAD -ErrorAction Stop + Import-Module -Name PSIntuneAuth -ErrorAction Stop + + try { + # Read credentials and variables + Write-Output -InputObject "Reading automation variables" + $Credential = Get-AutomationPSCredential -Name $AzureAutomationCredentialName -ErrorAction Stop + $AppClientID = Get-AutomationVariable -Name $AzureAutomationVariableAppClientID -ErrorAction Stop + $TenantName = Get-AutomationVariable -Name $AzureAutomationVariableTenantName -ErrorAction Stop + + try { + # Retrieve authentication token + Write-Output -InputObject "Attempting to retrieve authentication token" + $AuthToken = Get-MSIntuneAuthToken -TenantName $TenantName -ClientID $AppClientID -Credential $Credential -ErrorAction Stop + if ($AuthToken -ne $null) { + Write-Output -InputObject "Successfully retrieved authentication token" + + try { + # Get Apple MDM Push certificates + $AppleMDMPushResource = "https://graph.microsoft.com/v1.0/devicemanagement/applePushNotificationCertificate" + $AppleMDMPushCertificate = Invoke-RestMethod -Uri $AppleMDMPushResource -Method Get -Headers $AuthToken -ErrorAction Stop + + if ($AppleMDMPushCertificate -ne $null) { + Write-Output -InputObject "Successfully retrieved Apple MDM Push certificate" + + # Parse the JSON date time string into an DateTime object + $AppleMDMPushCertificateExpirationDate = [System.DateTime]::Parse($AppleMDMPushCertificate.expirationDateTime) + + # Validate that the MDM Push certificate has not already expired + if ($AppleMDMPushCertificateExpirationDate -lt (Get-Date)) { + Write-Output -InputObject "Apple MDM Push certificate has already expired, sending notification email" + Send-O365MailMessage -Credential $AzureAutomationCredentialName -Body "ACTION REQUIRED: Apple MDM Push certificate has expired" -Subject "MSIntune: IMPORTANT - Apple MDM Push certificate has expired" -Recipient $MailRecipient -From $MailFrom + } + else { + $AppleMDMPushCertificateDaysLeft = ($AppleMDMPushCertificateExpirationDate - (Get-Date)) + if ($AppleMDMPushCertificateDaysLeft.Days -le $AppleMDMPushCertificateNotificationRange) { + Write-Output -InputObject "Apple MDM Push certificate has not expired, but is within the given expiration notification range" + Send-O365MailMessage -Credential $AzureAutomationCredentialName -Body "Please take action before the Apple MDM Push certificate expires" -Subject "MSIntune: Apple MDM Push certificate expires in $($AppleMDMPushCertificateDaysLeft.Days) days" -Recipient $MailRecipient -From $MailFrom + } + else { + Write-Output -InputObject "Apple MDM Push certificate has not expired and is outside of the specified expiration notification range" + } + } + } + else { + Write-Output -InputObject "Query for Apple MDM Push certificates returned empty" + } + } + catch [System.Exception] { + Write-Warning -Message "An error occurred. Error message: $($_.Exception.Message)" + } + } + else { + Write-Warning -Message "An error occurred while attempting to retrieve an authentication token" + } + } + catch [System.Exception] { + Write-Warning -Message "Failed to retrieve authentication token" + } + } + catch [System.Exception] { + Write-Warning -Message "Failed to read automation variables" + } +} +catch [System.Exception] { + Write-Warning -Message "Failed to import modules" +} \ No newline at end of file From b9e33c05b8ddf5cbb032f0ff66748333a2f78d53 Mon Sep 17 00:00:00 2001 From: Nickolaj Andersen Date: Thu, 21 Jun 2018 15:12:29 +0200 Subject: [PATCH 09/57] Install-MSIntuneNDESServer.ps1 version 1.0.0 --- Certificates/Install-MSIntuneNDESServer.ps1 | 439 ++++++++++++++++++++ 1 file changed, 439 insertions(+) create mode 100644 Certificates/Install-MSIntuneNDESServer.ps1 diff --git a/Certificates/Install-MSIntuneNDESServer.ps1 b/Certificates/Install-MSIntuneNDESServer.ps1 new file mode 100644 index 0000000..e1b27e6 --- /dev/null +++ b/Certificates/Install-MSIntuneNDESServer.ps1 @@ -0,0 +1,439 @@ +<# +.SYNOPSIS + Prepare a Windows server for SCEP certificate distribution using NDES for Microsoft Intune. + +.DESCRIPTION + This script will prepare and configure a Windows server for SCEP certificate distribution using NDES for Microsoft Intune. + For running this script, permissions to set service principal names are required including local administrator privileges on the server where the script is executed on. + +.PARAMETER CertificateAuthorityConfig + Define the Certificate Authority configuration using the following format: \. + +.PARAMETER NDESTemplateName + Define the name of the certificate template that will be used by NDES to issue certificates to mobile devices. Don't specify the display name. + +.PARAMETER NDESExternalFQDN + Define the external FQDN of the NDES service published through an application proxy, e.g. ndes-tenantname.msappproxy.net. + +.PARAMETER RegistrationAuthorityName + Define the Registration Authority name information used by NDES. + +.PARAMETER RegistrationAuthorityCompany + Define the Registration Authority company information used by NDES. + +.PARAMETER RegistrationAuthorityDepartment + Define the Registration Authority department information used by NDES. + +.PARAMETER RegistrationAuthorityCity + Define the Registration Authority city information used by NDES. + +.EXAMPLE + # Install and configure NDES with verbose output: + .\Install-MSIntuneNDESServer.ps1 -CertificateAuthorityConfig "CA01.domain.com\DOMAIN-CA01-CA" -NDESTemplateName "NDESIntune" -NDESExternalFQDN "ndes-tenantname.msappproxy.net" -RegistrationAuthorityName "Name" -RegistrationAuthorityCompany "CompanyName" -RegistrationAuthorityDepartment "Department" -RegistrationAuthorityCity "City" -Verbose + +.NOTES + FileName: Install-MSIntuneNDESServer.ps1 + Author: Nickolaj Andersen + Contact: @NickolajA + Created: 2018-06-17 + Updated: 2018-06-17 + + Version history: + 1.0.0 - (2018-06-17) Script created +#> +[CmdletBinding(SupportsShouldProcess=$true)] +param( + [parameter(Mandatory=$true, HelpMessage="Define the Certificate Authority configuration using the following format: \.")] + [ValidateNotNullOrEmpty()] + [string]$CertificateAuthorityConfig, + + [parameter(Mandatory=$true, HelpMessage="Define the name of the certificate template that will be used by NDES to issue certificates to mobile devices. Don't specify the display name.")] + [ValidateNotNullOrEmpty()] + [string]$NDESTemplateName, + + [parameter(Mandatory=$true, HelpMessage="Define the external FQDN of the NDES service published through an application proxy, e.g. ndes-tenantname.msappproxy.net.")] + [ValidateNotNullOrEmpty()] + [string]$NDESExternalFQDN, + + [parameter(Mandatory=$true, HelpMessage="Define the Registration Authority name information used by NDES.")] + [ValidateNotNullOrEmpty()] + [string]$RegistrationAuthorityName, + + [parameter(Mandatory=$true, HelpMessage="Define the Registration Authority company information used by NDES.")] + [ValidateNotNullOrEmpty()] + [string]$RegistrationAuthorityCompany, + + [parameter(Mandatory=$true, HelpMessage="Define the Registration Authority department information used by NDES.")] + [ValidateNotNullOrEmpty()] + [string]$RegistrationAuthorityDepartment, + + [parameter(Mandatory=$true, HelpMessage="Define the Registration Authority city information used by NDES.")] + [ValidateNotNullOrEmpty()] + [string]$RegistrationAuthorityCity +) +Begin { + # Ensure that running PowerShell version is 5.1 + #Requires -Version 5.1 + + # Init verbose logging for environment gathering process phase + Write-Verbose -Message "Initiating environment gathering process phase" + + # Add additional variables required for installation and configuration + Write-Verbose -Message "- Configuring additional variables required for installation and configuration" + $ServerFQDN = -join($env:COMPUTERNAME, ".", $env:USERDNSDOMAIN.ToLower()) + Write-Verbose -Message "- Variable ServerFQDN has been assigned value: $($ServerFQDN)" + $ServerNTAccountName = -join($env:USERDOMAIN.ToUpper(), "\", $env:COMPUTERNAME, "$") + Write-Verbose -Message "- Variable ServerNTAccountName has been assigned value: $($ServerNTAccountName)" + + # Get Server Authentication certificate for IIS binding + try { + $ServerAuthenticationCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -ErrorAction Stop | Where-Object { ($_.Subject -match $NDESExternalFQDN) -and ($_.Extensions["2.5.29.37"].EnhancedKeyUsages.FriendlyName.Contains("Server Authentication")) } + if ($ServerAuthenticationCertificate -eq $null) { + Write-Warning -Message "Unable to locate required Server Authentication certificate matching external NDES FQDN"; break + } + else { + Write-Verbose -Message "- Successfully located required Server Authentication certificate matching external NDES FQDN" + } + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while attempting to locate required Server Authentication certificate matching external NDES FQDN"; break + } + + # Get Client Authentication certifcate for Intune Certificate Connector + try { + $ClientAuthenticationCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -ErrorAction Stop | Where-Object { ($_.Subject -match $ServerFQDN) -and ($_.Extensions["2.5.29.37"].EnhancedKeyUsages.FriendlyName.Contains("Client Authentication")) } + if ($ClientAuthenticationCertificate -eq $null) { + Write-Warning -Message "Unable to locate required Client Authentication certificate matching internal NDES server FQDN"; break + } + else { + Write-Verbose -Message "- Successfully located required Client Authentication certificate matching internal NDES server FQDN" + } + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while attempting to locate required Client Authentication certificate matching internal NDES server FQDN"; break + } + + # Completed verbose logging for environment gathering process phase + Write-Verbose -Message "Completed environment gathering process phase" +} +Process { + # Functions + function Test-PSCredential { + param ( + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [System.Management.Automation.PSCredential]$Credential + ) + Process { + $ErrorActionPreference = "Stop" + try { + Add-Type -AssemblyName System.DirectoryServices.AccountManagement -ErrorAction Stop + $ContextType = [System.DirectoryServices.AccountManagement.ContextType]::Domain + $PrincipalContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ContextType, $env:USERDNSDOMAIN.ToLower() + $ContextOptions = [System.DirectoryServices.AccountManagement.ContextOptions]::Negotiate + if (-not($PrincipalContext.ValidateCredentials($Credential.GetNetworkCredential().UserName, $Credential.GetNetworkCredential().Password)) -eq $true) { + return $false + } + else { + return $true + } + } + catch [System.Exception] { + if (-not($PrincipalContext.ValidateCredentials($Credential.GetNetworkCredential().UserName, $Credential.GetNetworkCredential().Password, $ContextOptions)) -eq $true) { + return $false + } + else { + return $true + } + } + } + } + + # Configure main script error action preference + $ErrorActionPreference = "Stop" + + # Initiate main script function + Write-Verbose -Message "Initiating main script engine to install and configure NDES on server: $($env:COMPUTERNAME)" + + # Init verbose logging for credentials phase + Write-Verbose -Message "Initiating credentials gathering process phase" + + # Get local administrator credential + Write-Verbose -Message "- Prompting for credential input for Enterprise Administrator domain credential" + $AdministratorPSCredential = (Get-Credential -Message "Specify a Enterprise Administrator domain credential with the following formatting 'DOMAIN\useraccount'") + if (-not(Test-PSCredential -Credential $AdministratorPSCredential)) { + Write-Warning -Message "Unable to validate specified Enterprise Administrator domain credentials"; break + } + else { + # Validate local administrator privileges + Write-Verbose -Message "- Given credentials was validated successfully, checking for Enterprise Administrator privileges for current user" + if (-not([Security.Principal.WindowsIdentity]::GetCurrent().Groups | Select-String -Pattern "S-1-5-32-544")) { + Write-Warning -Message "Current user context is not a local administrator on this server"; break + } + } + + # Get service account credential + Write-Verbose -Message "- Prompting for credential input for NDES service account domain credential" + $NDESServiceAccountCredential = (Get-Credential -Message "Specify the NDES service account domain credential with the following formatting 'DOMAIN\useraccount'") + if (-not(Test-PSCredential -Credential $NDESServiceAccountCredential)) { + Write-Warning -Message "Unable to validate specified NDES service account domain credentials"; break + } + $NDESServiceAccountName = -join($NDESServiceAccountCredential.GetNetworkCredential().Domain, "\" ,$NDESServiceAccountCredential.GetNetworkCredential().UserName) + $NDESServiceAccountPassword = $NDESServiceAccountCredential.GetNetworkCredential().SecurePassword + Write-Verbose -Message "- Successfully gathered NDES service account credentials" + + # Completed verbose logging for credentials phase + Write-Verbose -Message "Completed credentials gathering process phase" + + # Init verbose logging for pre-configuration phase + Write-Verbose -Message "Initiating pre-configuration phase" + + # Give computer account read permissions on Client Authentication certificate private key + try { + Write-Verbose -Message "- Attempting to give the NDES server computer account permissions on the Client Authentication certificate private key" + $ClientAuthenticationKeyContainerName = $ClientAuthenticationCertificate.PrivateKey.CspKeyContainerInfo.KeyContainerName + $ClientAuthenticationKeyFilePath = Join-Path -Path $env:ProgramData -ChildPath "Microsoft\Crypto\RSA\MachineKeys\$($ClientAuthenticationKeyContainerName)" + Write-Verbose -Message "- Retrieving existing access rules for private key container" + $ClientAuthenticationACL = Get-Acl -Path $ClientAuthenticationKeyFilePath + + # Check if existing ACL exist matching computer account with read permissions + $ServerAccessRule = $ClientAuthenticationACL.Access | Where-Object { ($_.IdentityReference -like $ServerNTAccountName) -and ($_.FileSystemRights -match "Read") } + if ($ServerAccessRule -eq $null) { + Write-Verbose -Message "- Could not find existing access rule for computer account with read permission on private key, attempting to delegate permissions" + $NTAccountUser = New-Object -TypeName System.Security.Principal.NTAccount($ServerNTAccountName) -ErrorAction Stop + $FileSystemAccessRule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule($NTAccountUser, "Read", "None", "None", "Allow") -ErrorAction Stop + $ClientAuthenticationACL.AddAccessRule($FileSystemAccessRule) + Set-Acl -Path $ClientAuthenticationKeyFilePath -AclObject $ClientAuthenticationACL -ErrorAction Stop + Write-Verbose -Message "- Successfully delegated the NDES server computer account permissions on the Client Authentication certificate private key" + } + else { + Write-Verbose -Message "- Found an existing access rule for computer account with read permission on private key, will skip configuration" + } + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while attempting to give the NDES server computer account permissions on the Client Authentication certificate private key"; break + } + + try { + # Configure service account SPN for local server + Write-Verbose -Message "- Attempting to configure service princal names for NDES service account: $($NDESServiceAccountName)" + Write-Verbose -Message "- Configuring service principal name HTTP/$($ServerFQDN) on $($NDESServiceAccountName)" + $ServerFQDNInvocation = Invoke-Expression -Command "cmd.exe /c setspn.exe -s HTTP/$($ServerFQDN) $($NDESServiceAccountName)" -ErrorAction Stop + if ($ServerFQDNInvocation -match "Updated object") { + Write-Verbose -Message "- Successfully configured service principal name for NDES service account" + } + Write-Verbose -Message "- Configuring service principal name HTTP/$($env:COMPUTERNAME) on $($NDESServiceAccountName)" + $ServerInvocation = Invoke-Expression -Command "cmd.exe /c setspn.exe -s HTTP/$($env:COMPUTERNAME) $($NDESServiceAccountName)" -ErrorAction Stop + if ($ServerInvocation -match "Updated object") { + Write-Verbose -Message "- Successfully configured service principal name for NDES service account" + } + Write-Verbose -Message "- Successfully configured service principal names for NDES service account" + } + catch [System.Exception] { + Write-Warning -Message "Failed to configure service princal names for NDES service account"; break + } + + # Completed verbose logging for pre-configuration phase + Write-Verbose -Message "Completed pre-configuration phase" + + # Init verbose logging for Windows feature installation phase + Write-Verbose -Message "Initiating Windows feature installation phase" + + # Install required Windows features for NDES + $NDESWindowsFeatures = @("ADCS-Device-Enrollment", "Web-Filtering", "Web-Asp-Net", "NET-Framework-Core", "NET-HTTP-Activation", "Web-Asp-Net45", "NET-Framework-45-Core", "NET-Framework-45-ASPNET", "NET-WCF-HTTP-Activation45", "Web-Metabase", "Web-WMI", "Web-Mgmt-Console", "NET-Non-HTTP-Activ") + foreach ($WindowsFeature in $NDESWindowsFeatures) { + Write-Verbose -Message "- Checking installation state for feature: $($WindowsFeature)" + if (((Get-WindowsFeature -Name $WindowsFeature -Verbose:$false).InstallState -ne "Installed")) { + Write-Verbose -Message "- Attempting to install Windows feature: $($WindowsFeature)" + Add-WindowsFeature -Name $WindowsFeature -ErrorAction Stop -Verbose:$false | Out-Null + Write-Verbose -Message "- Successfully installed Windows feature: $($WindowsFeature)" + } + else { + Write-Verbose -Message "- Windows feature is already installed: $($WindowsFeature)" + } + } + + # Completed verbose logging for Windows feature installation phase + Write-Verbose -Message "Completed Windows feature installation phase" + + # Init verbose logging for NDES server role installation phase + Write-Verbose -Message "Initiating NDES server role installation phase" + + # Add NDES service account to the IIS_IUSRS group + try { + Write-Verbose -Message "- Checking if NDES service account is a member of the IIS_IUSRS group" + $IISIUSRSMembers = Get-LocalGroupMember -Group "IIS_IUSRS" -Member $NDESServiceAccountName -ErrorAction SilentlyContinue + if ($IISIUSRSMembers -eq $null) { + Write-Verbose -Message "- Attempting to add NDES service account to the IIS_IUSRS group" + Add-LocalGroupMember -Group "IIS_IUSRS" -Member $NDESServiceAccountName -ErrorAction Stop + Write-Verbose -Message "- Successfully added NDES service account to the IIS_IUSRS group" + } + else { + Write-Verbose -Message "- NDES service account is already a member of the IIS_IUSRS group" + } + } + catch [System.Exception] { + Write-Warning -Message "An error occurred when attempting to add NDES service account to the IIS_IUSRS group"; break + } + + # Set NDES install parameters + $InstallNDESParams = @{ + "Credential" = $AdministratorPSCredential + "CAConfig" = $CertificateAuthorityConfig + "RAName" = $RegistrationAuthorityName + "RACompany" = $RegistrationAuthorityCompany + "RADepartment" = $RegistrationAuthorityDepartment + "RACity" = $RegistrationAuthorityCity + "ServiceAccountName" = $NDESServiceAccountName + "ServiceAccountPassword" = $NDESServiceAccountPassword + } + + # Install and configure NDES server role + try { + Write-Verbose -Message "- Starting NDES server role installation, this could take some time" + Install-AdcsNetworkDeviceEnrollmentService @InstallNDESParams -Force -ErrorAction Stop -Verbose:$false | Out-Null + Write-Verbose -Message "- Successfully installed and configured NDES server role" + } + catch [System.Exception] { + Write-Warning -Message "An error occurred. Error message: $($_.Exception.Message)"; break + } + + # Completed verbose logging for NDES server role installation phase + Write-Verbose -Message "Completed NDES server role installation phase" + + # Init verbose logging for NDES server role post-installation phase + Write-Verbose -Message "Initiating NDES server role post-installation phase" + + # Configure NDES certificate template in registry + try { + Write-Verbose -Message "- Attempting to configure EncryptionTemplate registry name with value: $($NDESTemplateName)" + Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP" -Name "EncryptionTemplate" -Value $NDESTemplateName -ErrorAction Stop + Write-Verbose -Message "- Successfully configured EncryptionTemplate registry name" + Write-Verbose -Message "- Attempting to configure GeneralPurposeTemplate registry name with value: $($NDESTemplateName)" + Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP" -Name "GeneralPurposeTemplate" -Value $NDESTemplateName -ErrorAction Stop + Write-Verbose -Message "- Successfully configured GeneralPurposeTemplate registry name" + Write-Verbose -Message "- Attempting to configure SignatureTemplate registry name with value: $($NDESTemplateName)" + Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP" -Name "SignatureTemplate" -Value $NDESTemplateName -ErrorAction Stop + Write-Verbose -Message "- Successfully configured SignatureTemplate registry name" + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while configuring NDES certificate template in registry"; break + } + + # Completed verbose logging for NDES server role installation phase + Write-Verbose -Message "Completed NDES server role post-installation phase" + + # Init verbose logging for IIS configuration phase + Write-Verbose -Message "Initiating IIS configuration phase" + + # Import required IIS module + try { + Write-Verbose -Message "- Import required IIS module" + Import-Module -Name "WebAdministration" -ErrorAction Stop -Verbose:$false + Write-Verbose -Message "- Successfully imported required IIS module" + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while importing the required IIS module"; break + } + + # Configure HTTP parameters in registry + try { + Write-Verbose -Message "- Attempting to configure HTTP parameters in registry, setting MaxFieldLength to value: 65534" + Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters" -Name "MaxFieldLength" -Value 65534 -ErrorAction Stop + Write-Verbose -Message "- Successfully configured HTTP parameter in registry for MaxFieldLength" + Write-Verbose -Message "- Attempting to configure HTTP parameters in registry, setting MaxRequestBytes to value: 65534" + Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters" -Name "MaxRequestBytes" -Value 65534 -ErrorAction Stop + Write-Verbose -Message "- Successfully configured HTTP parameter in registry for MaxRequestBytes" + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while configuring HTTP parameters in registry"; break + } + + # Add new HTTPS binding for Default Web Site + try { + Write-Verbose -Message "- Attempting to create new HTTPS binding for Default Web Site" + $HTTPSWebBinding = Get-WebBinding -Name "Default Web Site" -IPAddress "*" -Port 443 -ErrorAction Stop + if ($HTTPSWebBinding -eq $null) { + New-WebBinding -Name "Default Web Site" -IPAddress "*" -Port 443 -Protocol Https -ErrorAction Stop | Out-Null + Write-Verbose -Message "- Successfully creating new HTTPS binding for Default Web Site" + Write-Verbose -Message "- Attempting to set Server Authentication certificate for HTTPS binding" + $ServerAuthenticationCertificate | New-Item -Path "IIS:\SslBindings\*!443" -ErrorAction Stop | Out-Null + Write-Verbose -Message "- Successfully set Server Authentication certificate for HTTPS binding" + } + else { + Write-Verbose -Message "- Existing HTTPS binding found for Default Web Site, attempting to set Server Authentication certificate" + if (-not(Get-Item -Path "IIS:\SslBindings\*!443" -ErrorAction SilentlyContinue)) { + $ServerAuthenticationCertificate | New-Item -Path "IIS:\SslBindings\*!443" -ErrorAction Stop | Out-Null + Write-Verbose -Message "- Successfully set Server Authentication certificate for HTTPS binding" + } + else { + Write-Verbose -Message "- Existing HTTPS binding already has a certificate selected, removing it" + Remove-Item -Path "IIS:\SslBindings\*!443" -Force -ErrorAction Stop | Out-Null + Write-Verbose -Message "- Successfully removed certificate for existing HTTPS binding" + Write-Verbose -Message "- Attempting to set new Server Authentication certificate for HTTPS binding" + $ServerAuthenticationCertificate | New-Item -Path "IIS:\SslBindings\*!443" -ErrorAction Stop | Out-Null + Write-Verbose -Message "- Successfully set Server Authentication certificate for HTTPS binding" + } + } + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while attempting to create new or update existing HTTPS binding and set certificate selection for Default Web Site"; break + } + + # Configure Default Web Site to require SSL + try { + Write-Verbose -Message "- Attempting to set Default Web Site to require SSL" + Set-WebConfigurationProperty -PSPath "IIS:\" -Filter "/system.webServer/security/access" -Name "sslFlags" -Value "Ssl" -Location "Default Web Site" -ErrorAction Stop + Write-Verbose -Message "- Successfully set Default Web Site to require SSL" + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while attempting to set Default Web Site to require SSL"; break + } + + # Set Default Web Site request limits + try { + Write-Verbose -Message "- Attempting to set Default Web Site request filtering maximum URL length with value: 65534" + Set-WebConfiguration -PSPath "IIS:\Sites\Default Web Site" -Filter "/system.webServer/security/requestFiltering/requestLimits/@maxUrl" -Value 65534 -ErrorAction Stop + Write-Verbose -Message "- Successfully set Default Web Site request filtering maximum URL length" + Write-Verbose -Message "- Attempting to set Default Web Site request filtering maximum query string with value: 65534" + Set-WebConfiguration -PSPath "IIS:\Sites\Default Web Site" -Filter "/system.webServer/security/requestFiltering/requestLimits/@maxQueryString" -Value 65534 -ErrorAction Stop + Write-Verbose -Message "- Successfully set Default Web Site request filtering maximum query string" + Write-Verbose -Message "- Attempting to set Default Web Site request filtering for double escaping with value: False" + Set-WebConfiguration -PSPath "IIS:\Sites\Default Web Site" -Filter "/system.webServer/security/requestFiltering/@allowDoubleEscaping" -Value "False" -ErrorAction Stop + Write-Verbose -Message "- Successfully set Default Web Site request filtering for double escaping" + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while attempting to set Default Web Site request filtering configuration"; break + } + + # Configure Default Web Site authentication + try { + # Enable anonymous authentication + Write-Verbose -Message "- Attempting to set Default Web Site anonymous authentication to: Enabled" + Set-WebConfiguration -Location "Default Web Site" -Filter "/system.webServer/security/authentication/anonymousAuthentication/@Enabled" -Value "True" -ErrorAction Stop + Write-Verbose -Message "- Successfully set Default Web Site anonymous authentication" + + # Disable windows authentication + Write-Verbose -Message "- Attempting to set Default Web Site Windows authentication to: Disabled" + Set-WebConfiguration -Location "Default Web Site" -Filter "/system.webServer/security/authentication/windowsAuthentication/@Enabled" -Value "False" -ErrorAction Stop + Write-Verbose -Message "- Successfully set Default Web Site Windows authentication" + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while attempting to set Default Web Site authentication configuration"; break + } + + # Disable IE Enhanced Security Configuration for administrators + try { + Write-Verbose -Message "- Attempting to disable IE Enhanced Security Configuration for administrators" + Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" -Name "IsInstalled" -Value 0 -ErrorAction Stop + Write-Verbose -Message "- Successfully disabled IE Enhanced Security Configuration for administrators" + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while attempting to disable IE Enhanced Security Configuration for administrators"; break + } + + # Completed verbose logging for IIS configuration phase + Write-Verbose -Message "Completed IIS configuration phase" + Write-Verbose -Message "Successfully installed and configured this server with NDES for Intune Certificate Connector to be installed" + Write-Verbose -Message "IMPORTANT: Restart the server at this point before installing the Intune Certificate Connector" +} \ No newline at end of file From ee5b7e3f10507f544b0ef1ff84b0f2a85f8bd6aa Mon Sep 17 00:00:00 2001 From: Nickolaj Andersen Date: Wed, 7 Nov 2018 14:11:43 +0100 Subject: [PATCH 10/57] Scripts added --- Templates/Script-IntunePSTemplate.ps1 | 27 +++++++++++++ Windows 10/Enable-OneDriveADAL.ps1 | 13 ++++++ Windows 10/Set-OneDriveConfig.ps1 | 56 ++++++++++++++++++++++++++ Windows 10/Set-WindowsSystemConfig.ps1 | 28 +++++++++++++ Windows 10/Set-WindowsUserConfig.ps1 | 2 + 5 files changed, 126 insertions(+) create mode 100644 Templates/Script-IntunePSTemplate.ps1 create mode 100644 Windows 10/Enable-OneDriveADAL.ps1 create mode 100644 Windows 10/Set-OneDriveConfig.ps1 create mode 100644 Windows 10/Set-WindowsSystemConfig.ps1 create mode 100644 Windows 10/Set-WindowsUserConfig.ps1 diff --git a/Templates/Script-IntunePSTemplate.ps1 b/Templates/Script-IntunePSTemplate.ps1 new file mode 100644 index 0000000..cbdbedf --- /dev/null +++ b/Templates/Script-IntunePSTemplate.ps1 @@ -0,0 +1,27 @@ +# Check if we're running as a 64-bit process or not +if (-not[System.Environment]::Is64BitProcess) { + # Get the sysnative path for powershell.exe + $SysNativePowerShell = Join-Path -Path ($PSHOME.ToLower().Replace("syswow64", "sysnative")) -ChildPath "powershell.exe" + + # Construct new ProcessStartInfo object to restart powershell.exe as a 64-bit process and re-run scipt + $ProcessStartInfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo + $ProcessStartInfo.FileName = $SysNativePowerShell + $ProcessStartInfo.Arguments = "-ExecutionPolicy Bypass -File ""$($PSCommandPath)""" + $ProcessStartInfo.RedirectStandardOutput = $true + $ProcessStartInfo.RedirectStandardError = $true + $ProcessStartInfo.UseShellExecute = $false + $ProcessStartInfo.WindowStyle = "Hidden" + $ProcessStartInfo.CreateNoWindow = $true + + # Instatiate the new 64-bit process + $Process = [System.Diagnostics.Process]::Start($ProcessStartInfo) + + # Read standard error output to determine if the 64-bit script process somehow failed + $ErrorOutput = $Process.StandardError.ReadToEnd() + if ($ErrorOutput) { + Write-Error -Message $ErrorOutput + } +} +else { + # Code to be executed as 64-bit +} \ No newline at end of file diff --git a/Windows 10/Enable-OneDriveADAL.ps1 b/Windows 10/Enable-OneDriveADAL.ps1 new file mode 100644 index 0000000..9a57f91 --- /dev/null +++ b/Windows 10/Enable-OneDriveADAL.ps1 @@ -0,0 +1,13 @@ +# Define registry path, key and value +$Path = "HKCU:\SOFTWARE\Microsoft\OneDrive" +$Name = "EnableADAL" +$Value = 1 + +# Verify if registry path exists, if not create key including value +if (-not(Test-Path -Path $Path)) { + New-Item -Path $Path -Force | Out-Null + New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null +} +else { + New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType DWORD -Force | Out-Null +} \ No newline at end of file diff --git a/Windows 10/Set-OneDriveConfig.ps1 b/Windows 10/Set-OneDriveConfig.ps1 new file mode 100644 index 0000000..b5e2b8c --- /dev/null +++ b/Windows 10/Set-OneDriveConfig.ps1 @@ -0,0 +1,56 @@ +# Check if we're running as a 64-bit process or not +if (-not[System.Environment]::Is64BitProcess) { + # Get the sysnative path for powershell.exe + $SysNativePowerShell = Join-Path -Path ($PSHOME.ToLower().Replace("syswow64", "sysnative")) -ChildPath "powershell.exe" + + # Construct new ProcessStartInfo object to restart powershell.exe as a 64-bit process and re-run scipt + $ProcessStartInfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo + $ProcessStartInfo.FileName = $SysNativePowerShell + $ProcessStartInfo.Arguments = "-ExecutionPolicy Bypass -File ""$($PSCommandPath)""" + $ProcessStartInfo.RedirectStandardOutput = $true + $ProcessStartInfo.RedirectStandardError = $true + $ProcessStartInfo.UseShellExecute = $false + $ProcessStartInfo.WindowStyle = "Hidden" + $ProcessStartInfo.CreateNoWindow = $true + + # Instatiate the new 64-bit process + $Process = [System.Diagnostics.Process]::Start($ProcessStartInfo) + + # Read standard error output to determine if the 64-bit script process somehow failed + $ErrorOutput = $Process.StandardError.ReadToEnd() + if ($ErrorOutput) { + Write-Error -Message $ErrorOutput + } +} +else { + # Construct a list of custom objects to configure OneDrive + $RegistryList = New-Object -TypeName System.Collections.ArrayList + + # Construct custom object for enabling silent account configuration + $RegistryItem = [PSCustomObject]@{ + "Path" = "HKLM:\SOFTWARE\Policies\Microsoft\OneDrive" + "Name" = "SilentAccountConfig" + "Value" = "1" + } + $RegistryList.Add($RegistryItem) | Out-Null + + # Construct custom object for enabling Files on Demand + $RegistryItem = [PSCustomObject]@{ + "Path" = "HKLM:\SOFTWARE\Policies\Microsoft\OneDrive" + "Name" = "FilesOnDemandEnabled" + "Value" = "1" + } + $RegistryList.Add($RegistryItem) | Out-Null + + # Process each item in the array list + foreach ($Item in $RegistryList) { + # Verify if registry path exists, if not create key including value + if (-not(Test-Path -Path $Item.Path)) { + New-Item -Path $Item.Path -Force | Out-Null + New-ItemProperty -Path $Item.Path -Name $Item.Name -Value $Item.Value -PropertyType DWORD -Force | Out-Null + } + else { + New-ItemProperty -Path $Item.Path -Name $Item.Name -Value $Item.Value -PropertyType DWORD -Force | Out-Null + } + } +} \ No newline at end of file diff --git a/Windows 10/Set-WindowsSystemConfig.ps1 b/Windows 10/Set-WindowsSystemConfig.ps1 new file mode 100644 index 0000000..be0f718 --- /dev/null +++ b/Windows 10/Set-WindowsSystemConfig.ps1 @@ -0,0 +1,28 @@ +# Check if we're running as a 64-bit process or not +if (-not[System.Environment]::Is64BitProcess) { + # Get the sysnative path for powershell.exe + $SysNativePowerShell = Join-Path -Path ($PSHOME.ToLower().Replace("syswow64", "sysnative")) -ChildPath "powershell.exe" + + # Construct new ProcessStartInfo object to restart powershell.exe as a 64-bit process and re-run scipt + $ProcessStartInfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo + $ProcessStartInfo.FileName = $SysNativePowerShell + $ProcessStartInfo.Arguments = "-ExecutionPolicy Bypass -File ""$($PSCommandPath)""" + $ProcessStartInfo.RedirectStandardOutput = $true + $ProcessStartInfo.RedirectStandardError = $true + $ProcessStartInfo.UseShellExecute = $false + $ProcessStartInfo.WindowStyle = "Hidden" + $ProcessStartInfo.CreateNoWindow = $true + + # Instatiate the new 64-bit process + $Process = [System.Diagnostics.Process]::Start($ProcessStartInfo) + + # Read standard error output to determine if the 64-bit script process somehow failed + $ErrorOutput = $Process.StandardError.ReadToEnd() + if ($ErrorOutput) { + Write-Error -Message $ErrorOutput + } +} +else { + # Remove Edge icon on desktop + New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer" -Name "DisableEdgeDesktopShortcutCreation" -Value 1 -PropertyType DWORD -Force | Out-Null +} \ No newline at end of file diff --git a/Windows 10/Set-WindowsUserConfig.ps1 b/Windows 10/Set-WindowsUserConfig.ps1 new file mode 100644 index 0000000..fe48b4d --- /dev/null +++ b/Windows 10/Set-WindowsUserConfig.ps1 @@ -0,0 +1,2 @@ +# Change Explorer home screen back to "This PC" +Set-ItemProperty -Path HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced -Name LaunchTo -Type DWord -Value 1 \ No newline at end of file From 6454abd888e74e581c890114736291ef90889b01 Mon Sep 17 00:00:00 2001 From: Nickolaj Andersen Date: Wed, 7 Nov 2018 14:11:52 +0100 Subject: [PATCH 11/57] Script moved --- .../Script-TemplateWithAuth.ps1 | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Script-TemplateWithAuth.ps1 => Templates/Script-TemplateWithAuth.ps1 (100%) diff --git a/Script-TemplateWithAuth.ps1 b/Templates/Script-TemplateWithAuth.ps1 similarity index 100% rename from Script-TemplateWithAuth.ps1 rename to Templates/Script-TemplateWithAuth.ps1 From 86c8d871a4138e5537f4a1bf26c1db886f8564a9 Mon Sep 17 00:00:00 2001 From: Nickolaj Andersen Date: Tue, 14 May 2019 15:51:33 +0200 Subject: [PATCH 12/57] Get-StoreAppInformation.ps1 version 1.0.1 --- Apps/Get-StoreAppInformation.ps1 | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Apps/Get-StoreAppInformation.ps1 b/Apps/Get-StoreAppInformation.ps1 index 07f9a1c..48e72f8 100644 --- a/Apps/Get-StoreAppInformation.ps1 +++ b/Apps/Get-StoreAppInformation.ps1 @@ -1,21 +1,32 @@ <# .SYNOPSIS Search the iTunes or Google Play stores for the app links + .DESCRIPTION This script can search for any app available in either iTunes or Google Play store + .PARAMETER Store Specify which Store to search within + .PARAMETER AppName Specify the app name to search for within the Store + .PARAMETER Limit Limit search results to the specified number (only valid for iTunes Store) + .EXAMPLE .\Get-StoreAppInformation.ps1 -Store iTunes -AppName "Microsoft Word" -Limit 1 + .NOTES - Script name: Get-StoreAppInformation.ps1 + FileName: Get-StoreAppInformation.ps1 Author: Nickolaj Andersen Contact: @NickolajA - DateCreated: 2015-08-19 + Created: 2015-08-19 + Updated: 2019-05-14 + + Version history: + 1.0.0 - (2015-08-19) Script created + 1.0.1 - (2019-05-14) Added BundleId property returned from store search #> [CmdletBinding(SupportsShouldProcess=$true)] param( @@ -23,10 +34,12 @@ param( [ValidateNotNullOrEmpty()] [ValidateSet("iTunes","GooglePlay")] [string]$Store, + [parameter(Mandatory=$true, HelpMessage="Specify the app name to search for within the Store")] [ValidateNotNullOrEmpty()] [ValidatePattern("^[A-Za-z\s]*$")] [string]$AppName, + [parameter(Mandatory=$false, HelpMessage="Limit search results to the specified number (only valid for iTunes Store)")] [ValidateNotNullOrEmpty()] [string]$Limit = "1" @@ -57,6 +70,7 @@ Process { $PSObject = [PSCustomObject]@{ "AppName" = $Object.trackCensoredName "StoreLink" = $Object.trackViewUrl + "BundleId" = $Object.bundleId } Write-Output -InputObject $PSObject } @@ -70,6 +84,7 @@ Process { $PSObject = [PSCustomObject]@{ "AppName" = $Object.innerText "StoreLink" = "https://play.google.com" + $Object.href + "BundleId" = ($Object.href).Split("=")[1] } Write-Output -InputObject $PSObject } From 5afb7b161568080fc2a58b3970538be2b6cbd523 Mon Sep 17 00:00:00 2001 From: Nickolaj Andersen Date: Tue, 14 May 2019 15:52:02 +0200 Subject: [PATCH 13/57] Get-StoreAppInformation.ps1 version 1.0.1 --- Apps/Get-StoreAppInformation.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Apps/Get-StoreAppInformation.ps1 b/Apps/Get-StoreAppInformation.ps1 index 48e72f8..39c658d 100644 --- a/Apps/Get-StoreAppInformation.ps1 +++ b/Apps/Get-StoreAppInformation.ps1 @@ -18,7 +18,7 @@ .\Get-StoreAppInformation.ps1 -Store iTunes -AppName "Microsoft Word" -Limit 1 .NOTES - FileName: Get-StoreAppInformation.ps1 + FileName: Get-StoreAppInformation.ps1 Author: Nickolaj Andersen Contact: @NickolajA Created: 2015-08-19 From 26284cdf9b2db79b234e60e661256676353ee4e1 Mon Sep 17 00:00:00 2001 From: Nickolaj Andersen Date: Mon, 3 Jun 2019 10:36:07 +0200 Subject: [PATCH 14/57] Upload-WindowsAutopilotDeviceInfo.ps1 version 1.0.0 --- .../Upload-WindowsAutopilotDeviceInfo.ps1 | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 Autopilot/Upload-WindowsAutopilotDeviceInfo.ps1 diff --git a/Autopilot/Upload-WindowsAutopilotDeviceInfo.ps1 b/Autopilot/Upload-WindowsAutopilotDeviceInfo.ps1 new file mode 100644 index 0000000..3878764 --- /dev/null +++ b/Autopilot/Upload-WindowsAutopilotDeviceInfo.ps1 @@ -0,0 +1,205 @@ +<# +.SYNOPSIS + Gather device hash from local machine and automatically upload it to Autopilot. + +.DESCRIPTION + This script automatically gathers the device hash, serial number, manufacturer and model and uploads that data into Autopilot. + Authentication is required within this script and required permissions for creating Autopilot device identities are needed. + +.PARAMETER TenantName + Specify the tenant name, e.g. tenantname.onmicrosoft.com. + +.PARAMETER ApplicationID + Specify the Application ID of the app registration in Azure AD. By default, the script will attempt to use well known Microsoft Intune PowerShell app registration (d1ddf0e4-d672-4dae-b554-9d5bdfd93547). + +.PARAMETER OrderIdentifier + Specify the order identifier, e.g. 'Purchase'. + +.EXAMPLE + # Gather device hash from local computer and upload to Autopilot using Intune Graph API's with a given order identifier as 'AADUserDriven': + .\Upload-WindowsAutopilotDeviceInfo.ps1 -TenantName "tenant.onmicrosoft.com" -OrderIdentifier "AADUserDriven" + +.NOTES + FileName: Upload-WindowsAutopilotDeviceInfo.ps1 + Author: Nickolaj Andersen + Contact: @NickolajA + Created: 2019-03-21 + Updated: 2019-03-21 + + Version history: + 1.0.0 - (2019-03-21) Script created + + Required modules: + AzureAD (Install-Module -Name AzureAD) + PSIntuneAuth (Install-Module -Name PSIntuneAuth) +#> +[CmdletBinding(SupportsShouldProcess=$true)] +param( + [parameter(Mandatory=$true, HelpMessage="Specify the tenant name, e.g. tenantname.onmicrosoft.com.")] + [ValidateNotNullOrEmpty()] + [string]$TenantName, + + [parameter(Mandatory=$false, HelpMessage="Specify the Application ID of the app registration in Azure AD. By default, the script will attempt to use well known Microsoft Intune PowerShell app registration (d1ddf0e4-d672-4dae-b554-9d5bdfd93547).")] + [ValidateNotNullOrEmpty()] + [string]$ApplicationID = "d1ddf0e4-d672-4dae-b554-9d5bdfd93547", + + [parameter(Mandatory=$true, HelpMessage="Specify the order identifier, e.g. 'Purchase'.")] + [ValidateNotNullOrEmpty()] + [string]$OrderIdentifier +) +Begin { + # Determine if the AzureAD module needs to be installed + try { + Write-Verbose -Message "Attempting to locate AzureAD module" + $AzureADModule = Get-InstalledModule -Name AzureAD -ErrorAction Stop -Verbose:$false + if ($AzureADModule -ne $null) { + Write-Verbose -Message "AzureAD module detected, checking for latest version" + $LatestModuleVersion = (Find-Module -Name AzureAD -ErrorAction Stop -Verbose:$false).Version + if ($LatestModuleVersion -gt $AzureADModule.Version) { + Write-Verbose -Message "Latest version of AzureAD module is not installed, attempting to install: $($LatestModuleVersion.ToString())" + $UpdateModuleInvocation = Update-Module -Name AzureAD -Force -ErrorAction Stop -Confirm:$false -Verbose:$false + } + } + } + catch [System.Exception] { + Write-Warning -Message "Unable to detect AzureAD module, attempting to install from PSGallery" + try { + # Install NuGet package provider + $PackageProvider = Install-PackageProvider -Name NuGet -Force -Verbose:$false + + # Install PSIntuneAuth module + Install-Module -Name AzureAD -Force -ErrorAction Stop -Confirm:$false -Verbose:$false + Write-Verbose -Message "Successfully installed AzureAD" + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while attempting to install AzureAD module. Error message: $($_.Exception.Message)" ; break + } + } + + # Determine if the PSIntuneAuth module needs to be installed + try { + Write-Verbose -Message "Attempting to locate PSIntuneAuth module" + $PSIntuneAuthModule = Get-InstalledModule -Name PSIntuneAuth -ErrorAction Stop -Verbose:$false + if ($PSIntuneAuthModule -ne $null) { + Write-Verbose -Message "Authentication module detected, checking for latest version" + $LatestModuleVersion = (Find-Module -Name PSIntuneAuth -ErrorAction Stop -Verbose:$false).Version + if ($LatestModuleVersion -gt $PSIntuneAuthModule.Version) { + Write-Verbose -Message "Latest version of PSIntuneAuth module is not installed, attempting to install: $($LatestModuleVersion.ToString())" + $UpdateModuleInvocation = Update-Module -Name PSIntuneAuth -Scope CurrentUser -Force -ErrorAction Stop -Confirm:$false -Verbose:$false + } + } + } + catch [System.Exception] { + Write-Warning -Message "Unable to detect PSIntuneAuth module, attempting to install from PSGallery" + try { + # Install NuGet package provider + $PackageProvider = Install-PackageProvider -Name NuGet -Force -Verbose:$false + + # Install PSIntuneAuth module + Install-Module -Name PSIntuneAuth -Scope AllUsers -Force -ErrorAction Stop -Confirm:$false -Verbose:$false + Write-Verbose -Message "Successfully installed PSIntuneAuth" + } + catch [System.Exception] { + Write-Warning -Message "An error occurred while attempting to install PSIntuneAuth module. Error message: $($_.Exception.Message)" ; break + } + } + + # Check if token has expired and if, request a new + Write-Verbose -Message "Checking for existing authentication token" + if ($Global:AuthToken -ne $null) { + $UTCDateTime = (Get-Date).ToUniversalTime() + $TokenExpireMins = ($Global:AuthToken.ExpiresOn.datetime - $UTCDateTime).Minutes + Write-Verbose -Message "Current authentication token expires in (minutes): $($TokenExpireMins)" + if ($TokenExpireMins -le 0) { + Write-Verbose -Message "Existing token found but has expired, requesting a new token" + $Global:AuthToken = Get-MSIntuneAuthToken -TenantName $TenantName -ClientID $ApplicationID + } + else { + Write-Verbose -Message "Existing authentication token has not expired, will not request a new token" + } + } + else { + Write-Verbose -Message "Authentication token does not exist, requesting a new token" + $Global:AuthToken = Get-MSIntuneAuthToken -TenantName $TenantName -ClientID $ApplicationID + } +} +Process { + # Functions + function Get-ErrorResponseBody { + param( + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [System.Exception]$Exception + ) + + # Read the error stream + $ErrorResponseStream = $Exception.Response.GetResponseStream() + $StreamReader = New-Object System.IO.StreamReader($ErrorResponseStream) + $StreamReader.BaseStream.Position = 0 + $StreamReader.DiscardBufferedData() + $ResponseBody = $StreamReader.ReadToEnd(); + + # Handle return object + return $ResponseBody + } + + # Gather device hash data + Write-Verbose -Message "Gather device hash data from local machine" + $CimSession = New-CimSession -Verbose:$false + $DeviceHashData = (Get-CimInstance -CimSession $CimSession -Namespace "root/cimv2/mdm/dmmap" -Class "MDM_DevDetail_Ext01" -Filter "InstanceID='Ext' AND ParentID='./DevDetail'" -Verbose:$false).DeviceHardwareData + $SerialNumber = (Get-CimInstance -CimSession $CimSession -ClassName "Win32_BIOS" -Verbose:$false).SerialNumber + + # Construct Graph variables + $GraphVersion = "beta" + $GraphResource = "deviceManagement/importedWindowsAutopilotDeviceIdentities" + $GraphURI = "https://graph.microsoft.com/$($GraphVersion)/$($GraphResource)" + + # Construct hash table for new Autopilot device identity and convert to JSON + Write-Verbose -Message "Constructing required JSON body based upon parameter input data for device hash upload" + $AutopilotDeviceIdentity = [ordered]@{ + '@odata.type' = '#microsoft.graph.importedWindowsAutopilotDeviceIdentity' + 'orderIdentifier' = "$($OrderIdentifier)" + 'serialNumber' = "$($SerialNumber)" + 'productKey' = '' + 'hardwareIdentifier' = "$($DeviceHashData)" + 'state' = @{ + '@odata.type' = 'microsoft.graph.importedWindowsAutopilotDeviceIdentityState' + 'deviceImportStatus' = 'pending' + 'deviceRegistrationId' = '' + 'deviceErrorCode' = 0 + 'deviceErrorName' = '' + } + } + $AutopilotDeviceIdentityJSON = $AutopilotDeviceIdentity | ConvertTo-Json + + try { + # Call Graph API and post JSON data for new Autopilot device identity + Write-Verbose -Message "Attempting to post data for hardware hash upload" + $AutopilotDeviceIdentityResponse = Invoke-RestMethod -Uri $GraphURI -Headers $AuthToken -Method Post -Body $AutopilotDeviceIdentityJSON -ContentType "application/json" -ErrorAction Stop -Verbose:$false + $AutopilotDeviceIdentityResponse + } + catch [System.Exception] { + # Construct stream reader for reading the response body from API call + $ResponseBody = Get-ErrorResponseBody -Exception $_.Exception + + # Handle response output and error message + Write-Output -InputObject "Response content:`n$ResponseBody" + Write-Warning -Message "Failed to upload hardware hash. Request to $($GraphURI) failed with HTTP Status $($_.Exception.Response.StatusCode) and description: $($_.Exception.Response.StatusDescription)" + } + + try { + # Call Graph API and post Autopilot devices sync command + Write-Verbose -Message "Attempting to perform a sync action in Autopilot" + $GraphResource = "deviceManagement/windowsAutopilotSettings/sync" + $GraphURI = "https://graph.microsoft.com/$($GraphVersion)/$($GraphResource)" + (Invoke-RestMethod -Uri $GraphURI -Headers $AuthToken -Method Post -ErrorAction Stop -Verbose:$false).Value + } + catch [System.Exception] { + # Construct stream reader for reading the response body from API call + $ResponseBody = Get-ErrorResponseBody -Exception $_.Exception + + # Handle response output and error message + Write-Output -InputObject "Response content:`n$ResponseBody" + Write-Warning -Message "Request to $GraphURI failed with HTTP Status $($_.Exception.Response.StatusCode) and description: $($_.Exception.Response.StatusDescription)" + } +} \ No newline at end of file From 2a7e713a781885c69675b621f10afed08f0a805a Mon Sep 17 00:00:00 2001 From: Nickolaj Andersen Date: Mon, 3 Jun 2019 11:03:49 +0200 Subject: [PATCH 15/57] Upload-WindowsAutopilotDeviceInfo.ps1 v. 1.0.0 --- .../Upload-WindowsAutopilotDeviceInfo.ps1 | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Autopilot/Upload-WindowsAutopilotDeviceInfo.ps1 b/Autopilot/Upload-WindowsAutopilotDeviceInfo.ps1 index 3878764..a6bce3c 100644 --- a/Autopilot/Upload-WindowsAutopilotDeviceInfo.ps1 +++ b/Autopilot/Upload-WindowsAutopilotDeviceInfo.ps1 @@ -1,3 +1,21 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID 8d3532b3-ff9f-4031-b06f-25fcab76c626 +.AUTHOR NickolajA +.DESCRIPTION Gather device hash from local machine and automatically upload it to Autopilot +.COMPANYNAME SCConfigMgr +.COPYRIGHT +.TAGS Autopilot Windows Intune +.LICENSEURI +.PROJECTURI https://github.com/SCConfigMgr/Intune/blob/master/Autopilot/Upload-WindowsAutopilotDeviceInfo.ps1 +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES +#> +#Requires -Module AzureAD +#Requires -Module PSIntuneAuth <# .SYNOPSIS Gather device hash from local machine and automatically upload it to Autopilot. From d17010f0a8f189cb35a3f400d363c2dd8b4e6b49 Mon Sep 17 00:00:00 2001 From: Nickolaj Andersen Date: Tue, 22 Oct 2019 14:12:39 +0200 Subject: [PATCH 16/57] 1.0.0 --- Templates/Script-TemplateWithAuth.ps1 | 98 +++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 15 deletions(-) diff --git a/Templates/Script-TemplateWithAuth.ps1 b/Templates/Script-TemplateWithAuth.ps1 index bde9f84..d090f45 100644 --- a/Templates/Script-TemplateWithAuth.ps1 +++ b/Templates/Script-TemplateWithAuth.ps1 @@ -5,11 +5,14 @@ .DESCRIPTION -.PARAMETER Param - Param description. +.PARAMETER TenantName + Specify the tenant name, e.g. domain.onmicrosoft.com. -.PARAMETER ShowProgress - Show a progressbar displaying the current operation. +.PARAMETER ApplicationID + Specify the Application ID of the app registration in Azure AD. By default, the script will attempt to use well known Microsoft Intune PowerShell app registration. + +.PARAMETER PromptBehavior + Set the prompt behavior when acquiring a token. .EXAMPLE @@ -18,24 +21,30 @@ FileName: