├── .gitattributes ├── .gitignore ├── Create-IntuneExtendedLogging.ps1 ├── Create-ScopeTags.ps1 ├── Extract-File.ps1 ├── Get-MDMDiagnosticsRegEntries.ps1 ├── Get-SSRSCert.ps1 ├── Get-Win32AppResults.ps1 ├── Install-CMTrace.ps1 ├── Intune ├── Check-UpdateComplianceSetting.ps1 ├── Convert-AzureAdObjectIdToSid.ps1 ├── CustomInventory.ps1 ├── Get-AutopilotCSV.ps1 ├── Get-DeviceManagementScripts.ps1 ├── Get-IntuneManagementExtensionDiagnostics.ps1 ├── Get-IntuneScripts.ps1 ├── ImpersonateLoggedOnUser.ps1 ├── Intune Reports │ └── Get-IntuneReport │ │ ├── Get-IntuneReport.ps1 │ │ └── README.md ├── Invoke-BitLockerCompliance.ps1 ├── Invoke-IntuneBackupDeviceConfiguration.txt ├── Invoke-RemoveBuiltinApps.ps1 ├── Invoke-TenantSecurity.ps1 ├── Remove-Appx.ps1 ├── Remove-AppxUserPackages.ps1 ├── Remove-Appx_Detection.ps1 ├── Remove-DirectAccess.ps1 ├── Remove-DirectAccess_Detect.ps1 ├── Remove-WorkplaceUserJoinRegistration.ps1 ├── Test-UpdateCompliance.ps1 ├── TestScript.ps1 └── Windows_AntiMalware_Audit .ps1 ├── MBAM_Update.ps1 ├── PRs ├── Detect-JavaApps.ps1 ├── Remediate-JavaApps.ps1 ├── Windows Updates │ ├── Add-MSIGraphPermission.ps1 │ ├── Invoke-WUInventory.ps1 │ ├── Invoke-WUInventory_Funky.ps1 │ ├── Invoke-WUInventory_testdata.ps1 │ ├── Windows Update Device Settings.workbook │ └── testdata.csv ├── basic_detect.ps1 ├── basic_detect_UTF8BOM.ps1 ├── basic_remediate.ps1 └── ninja-banner.png ├── README.md ├── Reset-Appx.ps1 ├── Set-IconSize.png └── Set-IconSize.ps1 /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store -------------------------------------------------------------------------------- /Create-IntuneExtendedLogging.ps1: -------------------------------------------------------------------------------- 1 | # Define the registry path 2 | $regPath = "HKLM:\SOFTWARE\Microsoft\IntuneWindowsAgent\Logging" 3 | $regValues = @("LogMaxHistory,4", "LogMaxSize,4194304") # 4 logs at 4MB each 4 | 5 | # Check if the path exists, if not, create it 6 | if (-not (Test-Path -Path $regPath)) { 7 | Try { 8 | New-Item -Path $regPath -Force -ErrorAction Stop 9 | } 10 | Catch { 11 | Write-Warning ("Unable to create registry path {0}: {1}" -f $regPath, $_.Exception.Message) 12 | } 13 | } 14 | 15 | # Create or set the registry values 16 | Try { 17 | Foreach ($reg in $regValues) { 18 | $name, $value = $reg -split ',' 19 | New-ItemProperty -Path $regPath -Name $name -Value $value -PropertyType "String" -Force -ErrorAction Stop 20 | } 21 | } 22 | Catch { 23 | Write-Warning ("Unable to create registry value {0} at {1}: {2}" -f $name, $regPath, $_.Exception.Message) 24 | } -------------------------------------------------------------------------------- /Create-ScopeTags.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | Requires MSAL.PS module 4 | 5 | API permisions required:- 6 | 7 | DeviceManagementConfiguration.ReadWrite.All 8 | DeviceManagementRBAC.ReadWrite.All 9 | 10 | #> 11 | 12 | $authParams = @{ 13 | ClientId = "" #App Registration Client ID here 14 | TenantId = "" #Tenant here 15 | Interactive = $true 16 | } 17 | 18 | $authToken = Get-MsalToken @authParams 19 | 20 | $headers = @{ 21 | "Content-Type" = "application/json" 22 | "Authorization" = "$($authToken.accessToken)" 23 | } 24 | 25 | $scopeTags = Import-CSV .\scopeTags.csv 26 | 27 | Foreach ($scopeTag in $scopeTags) { 28 | 29 | $tag = $scopeTag.Name 30 | $payload = @{ 31 | "displayName" = $tag 32 | } 33 | 34 | $body = $payload | ConvertTo-Json 35 | 36 | $uri = "https://graph.microsoft.com/beta/deviceManagement/roleScopeTags" 37 | Invoke-RestMethod -Method Post -Uri $uri -Headers $headers -Body $body 38 | } -------------------------------------------------------------------------------- /Extract-File.ps1: -------------------------------------------------------------------------------- 1 | try { $installer = Get-ChildItem -Path (Get-Location) -Filter "*.exe" -ErrorAction Stop | Sort-Object -Property Length -Descending | Select-Object -First 1 2 | if ( -not $installer) { throw } else { Start-Process -FilePath $installer.FullName -ArgumentList "-sfx_o`"$($path)`" -sfx_ne -sfx_nu" -NoNewWindow -Wait } } catch { throw } -------------------------------------------------------------------------------- /Get-MDMDiagnosticsRegEntries.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Retrieves and displays registry entries from the MdmDiagnostics area. 4 | 5 | .DESCRIPTION 6 | This script iterates through the registry entries under the MdmDiagnostics area in the Windows registry, 7 | collects the values, and outputs them in a formatted table. It is useful for understanding which items are 8 | collected for diagnosing and troubleshooting MDM related issues. 9 | 10 | .EXAMPLE 11 | .\Get-MDMDiagnosticsRegEntries.ps1 12 | This command runs the script and outputs the registry entries from the MdmDiagnostics area. 13 | 14 | .NOTES 15 | Author: Ben Whitmore 16 | Date: 25/01/2025 17 | 18 | .OUTPUTS 19 | System.Management.Automation.PSCustomObject 20 | The script outputs custom objects containing the registry path, area, item type, name, registry type, and data. 21 | #> 22 | 23 | 24 | # Define the base registry path 25 | $basePath = "HKLM:\SOFTWARE\Microsoft\MdmDiagnostics\Area" 26 | 27 | # Initialize an array to store results 28 | $results = @() 29 | 30 | # Iterate through each area under the base path 31 | Get-ChildItem -Path $basePath | ForEach-Object { 32 | $area = $_.PSChildName 33 | 34 | # Iterate through each folder under the area 35 | Get-ChildItem -Path $_.PSPath | ForEach-Object { 36 | $folder = $_.PSChildName 37 | $keyPath = $_.PSPath 38 | 39 | # Retrieve all values from the folder 40 | $key = Get-Item -Path $keyPath 41 | $values = @() 42 | foreach ($name in $key.Property) { 43 | 44 | $type = $key.GetValueKind($name) 45 | $data = $key.GetValue($name) 46 | 47 | $values += [PSCustomObject]@{ 48 | Name = $name 49 | Reg_Type = $type.ToString() 50 | Data = $data 51 | } 52 | } 53 | 54 | # Add the result as a custom object 55 | $results += [PSCustomObject]@{ 56 | BasePath = $basePath 57 | Area = $area 58 | Type = $folder 59 | HashTable = $values 60 | } 61 | } 62 | } 63 | 64 | # Initialize an array to collect the output 65 | $finalResults = @() 66 | 67 | # Process the results and add to the array 68 | foreach ($result in $results) { 69 | foreach ($entry in $result.HashTable) { 70 | $finalResults += [PSCustomObject]@{ 71 | BasePath = $result.BasePath 72 | Area = $result.Area 73 | ItemType = $result.Type 74 | Name = $entry.Name 75 | Reg_Type = $entry.Reg_Type 76 | Data = $entry.Data 77 | } 78 | } 79 | } 80 | 81 | # Output the collected results 82 | $finalResults | Format-Table -AutoSize -------------------------------------------------------------------------------- /Get-SSRSCert.ps1: -------------------------------------------------------------------------------- 1 | $Server = "SQL-SCCM" 2 | netsh -r $Server http show sslcert IPPort=0.0.0.0:444 3 | 4 | #$cert = (Get-ChildItem cert:\LocalMachine\My | where-object { $_.Subject -like "*$hostname*" } | Select-Object -First 1).Thumbprint -------------------------------------------------------------------------------- /Get-Win32AppResults.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Get Win32app state from the local registry 4 | 5 | .DESCRIPTION 6 | This script will get the state of Win32apps from the local registry 7 | It will return the compliance state and enforcement state of the app, as well as the state results for each 8 | 9 | ##Example using the returned $stateMessages array to find apps that are applicable, required but not installed 10 | $stateMessages | where-Object {$_.complianceStateMessage.Applicability -eq 'Applicable' -and $_.complianceStateMessage.ComplianceState -eq 'NotInstalled' -and $_.complianceStateMessage.DesiredState -eq 'Present'} 11 | 12 | .NOTES 13 | FileName: Get-Win32AppResults.ps1 14 | Author: Ben Whitmore 15 | Date: 28th August 2023 16 | 17 | .PARAMETER Win32appKey 18 | The registry key to look for Win32apps under. Default is HKLM:SOFTWARE\Microsoft\IntuneManagementExtension\Win32Apps 19 | 20 | .PARAMETER DontTranslateStates 21 | If set, the script will not attempt to translate the numerical values of the state messages to the string equivilent 22 | 23 | .EXAMPLE 24 | .\Get-Win32AppResults.ps1 25 | 26 | .EXAMPLE 27 | .\Get-Win32AppResults.ps1 -DontTranslateStates 28 | 29 | #> 30 | 31 | [CmdletBinding()] 32 | param( 33 | [Parameter(Mandatory = $false)] 34 | [string]$Win32appKey = "HKLM:SOFTWARE\Microsoft\IntuneManagementExtension\Win32Apps", 35 | [switch]$DontTranslateStates 36 | ) 37 | 38 | # declare state message compliance state message hashtables 39 | $stateMessageEnforcement = @{ 40 | 1000 = "Success" 41 | 1003 = "SuccessFastNotify" 42 | 1004 = "SuccessButDependencyFailedToInstall" 43 | 1005 = "SuccessButDependencyWithRequirementsNotMet" 44 | 1006 = "SuccessButDependencyPendingReboot" 45 | 1007 = "SuccessButDependencyWithAutoInstallOff" 46 | 1008 = "SuccessButIOSAppStoreUpdateFailedToInstall" 47 | 1009 = "SuccessVPPAppHasUpdateAvailable" 48 | 1010 = "SuccessButUserRejectedUpdate" 49 | 1011 = "SuccessUninstallPendingReboot" 50 | 1012 = "SuccessSupersededAppUninstallFailed" 51 | 1013 = "SuccessSupersededAppUninstallPendingReboot" 52 | 1014 = "SuccessSupersedingAppsDetected" 53 | 1015 = "SuccessSupersededAppsDetected" 54 | 1016 = "SuccessAppRemovedBySupersedence" 55 | 1017 = "SuccessButDependencyBlockedByManagedInstallerPolicy" 56 | 1018 = "SuccessUninstallingSupersededApps" 57 | 2000 = "InProgress" 58 | 2007 = "InProgressDependencyInstalling" 59 | 2008 = "InProgressPendingReboot" 60 | 2009 = "InProgressDownloadCompleted" 61 | 2010 = "InProgressPendingUninstallOfSupersededApps" 62 | 2011 = "InProgressUninstallPendingReboot" 63 | 2012 = "InProgressPendingManagedInstaller" 64 | 3000 = "RequirementsNotMet" 65 | 4000 = "Unknown" 66 | 5000 = "Error" 67 | 5003 = "ErrorDownloadingContent" 68 | 5006 = "ErrorConflictsPreventInstallation" 69 | 5015 = "ErrorManagedInstallerAppLockerPolicyNotApplied" 70 | 5999 = "ErrorWithImmeadiateRetry" 71 | 6000 = "NotAttempted" 72 | 6001 = "NotAttemptedDependencyWithFailure" 73 | 6002 = "NotAttemptedPendingReboot" 74 | 6003 = "NotAttemptedDependencyWithRequirementsNotMet" 75 | 6004 = "NotAttemptedAutoInstallOff" 76 | 6005 = "NotAttemptedDependencyWithAutoInstallOff" 77 | 6006 = "NotAttemptedWithManagedAppNoLongerPresent" 78 | 6007 = "NotAttemptedBecauseUserRejectedInstall" 79 | 6008 = "NotAttemptedBecauseUserIsNotLoggedIntoAppStore" 80 | 6009 = "NotAttemptedSupersededAppUninstallFailed" 81 | 6010 = "NotAttemptedSupersededAppUninstallPendingReboot" 82 | 6011 = "NotAttemptedUntargetedSupersedingAppsDetected" 83 | 6012 = "NotAttemptedDependencyBlockedByManagedInstallerPolicy" 84 | 6013 = "NotAttemptedUnsupportedOrIndeterminateSupersededApp" 85 | } 86 | 87 | $stateMessageApplicability = @{ 88 | 0 = "Applicable" 89 | 1 = "RequirementsNotMet" 90 | 3 = "HostPlatformNotApplicable" 91 | 1000 = "ProcessorArchitectureNotApplicable" 92 | 1001 = "MinimumDiskSpaceNotMet" 93 | 1002 = "MinimumOSVersionNotMet" 94 | 1003 = "MinimumPhysicalMemoryNotMet" 95 | 1004 = "MinimumLogicalProcessorCountNotMet" 96 | 1005 = "MinimumCPUSpeedNotMet" 97 | 1006 = "FileSystemRequirementRuleNotMet" 98 | 1007 = "RegistryRequirementRuleNotMet" 99 | 1008 = "ScriptRequirementRuleNotMet" 100 | 1009 = "NotTargetedAndSupersedingAppsNotApplicable" 101 | 1010 = "AssignmentFiltersCriteriaNotMet" 102 | 1011 = "AppUnsupportedDueToUnknownReason" 103 | 1012 = "UserContextAppNotSupportedDuringDeviceOnlyCheckin" 104 | 2000 = "COSUMinimumApiLevelNotMet" 105 | 2001 = "COSUManagementMode" 106 | 2002 = "COSUUnsupported" 107 | 2003 = "COSUAppIncompatible" 108 | } 109 | 110 | $stateMessageComplianceState = @{ 111 | 1 = "Installed" 112 | 2 = "NotInstalled" 113 | 4 = "Error" 114 | 5 = "Unknown" 115 | 100 = "Cleanup" 116 | } 117 | 118 | $stateMessageDesiredState = @{ 119 | 0 = "None" 120 | 1 = "Not Present" 121 | 2 = "Present" 122 | 3 = "Unknown" 123 | 4 = "Available" 124 | } 125 | 126 | $stateMessageTargetingMethod = @{ 127 | 0 = "EgatTargetedApplication" 128 | 1 = "DependencyOfEgatTargetedApplication" 129 | } 130 | 131 | $stateMessageInstallContext = @{ 132 | 1 = "User" 133 | 2 = "System" 134 | } 135 | 136 | $stateMessageTargetType = @{ 137 | 0 = "None" 138 | 1 = "User" 139 | 2 = "Device" 140 | 3 = "Both Device and User" 141 | } 142 | 143 | # declare state message enforcement state message hashtables 144 | $stateMessageEnforcementState = @{ 145 | 1000 = "Success" 146 | 1003 = "SuccessFastNotify" 147 | 1004 = "SuccessButDependencyFailedToInstall" 148 | 1005 = "SuccessButDependencyWithRequirementsNotMet" 149 | 1006 = "SuccessButDependencyPendingReboot" 150 | 1007 = "SuccessButDependencyWithAutoInstallOff" 151 | 1008 = "SuccessButIOSAppStoreUpdateFailedToInstall" 152 | 1009 = "SuccessVPPAppHasUpdateAvailable" 153 | 1010 = "SuccessButUserRejectedUpdate" 154 | 1011 = "SuccessUninstallPendingReboot" 155 | 1012 = "SuccessSupersededAppUninstallFailed" 156 | 1013 = "SuccessSupersededAppUninstallPendingReboot" 157 | 1014 = "SuccessSupersedingAppsDetected" 158 | 1015 = "SuccessSupersededAppsDetected" 159 | 1016 = "SuccessAppRemovedBySupersedence" 160 | 1017 = "SuccessButDependencyBlockedByManagedInstallerPolicy" 161 | 1018 = "SuccessUninstallingSupersededApps" 162 | 2000 = "InProgress" 163 | 2007 = "InProgressDependencyInstalling" 164 | 2008 = "InProgressPendingReboot" 165 | 2009 = "InProgressDownloadCompleted" 166 | 2010 = "InProgressPendingUninstallOfSupersededApps" 167 | 2011 = "InProgressUninstallPendingReboot" 168 | 2012 = "InProgressPendingManagedInstaller" 169 | 3000 = "RequirementsNotMet" 170 | 4000 = "Unknown" 171 | 5000 = "Error" 172 | 5003 = "ErrorDownloadingContent" 173 | 5006 = "ErrorConflictsPreventInstallation" 174 | 5015 = "ErrorManagedInstallerAppLockerPolicyNotApplied" 175 | 5999 = "ErrorWithImmeadiateRetry" 176 | 6000 = "NotAttempted" 177 | 6001 = "NotAttemptedDependencyWithFailure" 178 | 6002 = "NotAttemptedPendingReboot" 179 | 6003 = "NotAttemptedDependencyWithRequirementsNotMet" 180 | 6004 = "NotAttemptedAutoInstallOff" 181 | 6005 = "NotAttemptedDependencyWithAutoInstallOff" 182 | 6006 = "NotAttemptedWithManagedAppNoLongerPresent" 183 | 6007 = "NotAttemptedBecauseUserRejectedInstall" 184 | 6008 = "NotAttemptedBecauseUserIsNotLoggedIntoAppStore" 185 | 6009 = "NotAttemptedSupersededAppUninstallFailed" 186 | 6010 = "NotAttemptedSupersededAppUninstallPendingReboot" 187 | 6011 = "NotAttemptedUntargetedSupersedingAppsDetected" 188 | 6012 = "NotAttemptedDependencyBlockedByManagedInstallerPolicy" 189 | 6013 = "NotAttemptedUnsupportedOrIndeterminateSupersededApp" 190 | } 191 | 192 | # get valid Contexts(GUIDs) in the Win32Apps key 193 | $regKeys = Get-ChildItem -Path $win32appKey -Force -ErrorAction SilentlyContinue 194 | $getValidContexts = $regKeys | Where-Object { ([System.Guid]::TryParse((Split-Path -Path $_.Name -Leaf), [System.Management.Automation.PSReference]([guid]::empty))) } | Select-Object -ExpandProperty Name 195 | 196 | # initialize $appKeyResults array to hold results of win32app policies found under each context (for both device and users) 197 | $appKeyResults = @() 198 | 199 | Foreach ($getApp in $getValidContexts) { 200 | $leaf = Split-Path -Path $getApp -Leaf 201 | $regToWork = "$win32appKey\$leaf" 202 | 203 | # get apps under each context 204 | $appKeys = Get-ChildItem -Path $regToWork -Force -ErrorAction SilentlyContinue 205 | $evaluatedApps = $appKeys | ForEach-Object { 206 | $app = $_.Name 207 | $appGuid = Split-Path -Path $app -Leaf 208 | $appReg = "$win32appKey\$leaf\$appGuid" 209 | $appRegValues = Get-ItemProperty -Path $appReg 210 | $appRegValues | Select-Object -Property @{Name = "App"; Expression = { $appGuid -replace '_\d+$' } }, @{Name = "AppReg"; Expression = { $appReg } }, @{Name = "UserObjectID"; Expression = { Split-Path -Path $regToWork -Leaf } } 211 | } 212 | 213 | # store app results in $appKeyResults array 214 | $appKeyResults += $evaluatedApps 215 | } 216 | 217 | # initialize $stateMessages array to hold results 218 | $stateMessages = @() 219 | 220 | Foreach ($appToWorkWith in $appKeyResults) { 221 | 222 | # continue if either state message key is not found 223 | $complianceStateKey = (Get-ItemProperty -Path "$($appToWorkWith.AppReg)\ComplianceStateMessage" -Name 'ComplianceStateMessage' -ErrorAction SilentlyContinue ).ComplianceStateMessage 224 | $enforcementStateKey = (Get-ItemProperty -Path "$($appToWorkWith.AppReg)\EnforcementStateMessage" -Name 'EnforcementStateMessage' -ErrorAction SilentlyContinue ).EnforcementStateMessage 225 | 226 | # if the DontTranslateStates switch is not set, attempt to translate the numerical values of the state messages to the string equivilent 227 | 228 | If (-not $DontTranslateStates) { 229 | #if the compliance state key is not found, try and replace the numerical value with the string state message equivilent 230 | 231 | If ($complianceStateKey) { 232 | 233 | # convert json object 234 | $complianceStateObjects = @("Applicability", "ComplianceState", "DesiredState", "TargetingMethod", "InstallContext", "TargetType") 235 | $complianceStateKey = $complianceStateKey | ConvertFrom-Json 236 | 237 | # convert values to int 238 | 239 | ForEach ($complianceStateObject in $complianceStateObjects) { 240 | $intVal = [int]$complianceStateKey.$complianceStateObject 241 | $tempVariable = "stateMessage$complianceStateObject" 242 | $stateMessageObject = (Get-Variable -Name $tempVariable).Value 243 | 244 | # replace values with equivalent key from state message hashtables at the top of the script 245 | 246 | If ($stateMessageObject[$intVal]) { 247 | $complianceStateKey.$complianceStateObject = $stateMessageObject[$intVal] 248 | } 249 | } 250 | } 251 | If ($enforcementStateKey) { 252 | 253 | # convert json object 254 | $enforcementStateObjects = @("EnforcementState", "TargetingMethod") 255 | $enforcementStateKey = $enforcementStateKey | ConvertFrom-Json 256 | 257 | # convert values to int 258 | 259 | ForEach ($enforcementStateObject in $enforcementStateObjects) { 260 | $intVal2 = [int]$enforcementStateKey.$enforcementStateObject 261 | $tempVariable2 = "stateMessage$enforcementStateObject" 262 | $stateMessageObject2 = (Get-Variable -Name $tempVariable2).Value 263 | 264 | # replace values with equivalent key from state message hashtables at the top of the script 265 | 266 | If ($stateMessageObject2[$intVal2]) { 267 | $enforcementStateKey.$enforcementStateObject = $stateMessageObject2[$intVal2] 268 | } 269 | } 270 | } 271 | } 272 | 273 | # add results to $stateMessages array 274 | $stateMessages += $appToWorkWith | Select-Object -Property @{Name = "UserObjectID"; Expression = { $appToWorkWith.UserObjectID } }, @{Name = "AppID"; Expression = { $appToWorkWith.App } }, @{Name = "ComplianceStateMessage"; Expression = { $complianceStateKey } }, @{Name = "EnforcementStateMessage"; Expression = { $enforcementStateKey } }, @{Name = "StateMessagesRegKey"; Expression = { $appToWorkWith.AppReg } } 275 | } 276 | 277 | $stateMessages -------------------------------------------------------------------------------- /Install-CMTrace.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Download and Install the Microsoft ConfigMgr 2012 Toolkit R2 4 | 5 | .DESCRIPTION 6 | This script will download and install the Microsoft ConfigMgr 2012 Toolkit R2. 7 | The intention was to use the scrtipt for Intune managed devices to easily read log files in the absence of the ConfigMgr client and C:\Windows\CCM\CMTrace.exe 8 | 9 | .EXAMPLE 10 | .\Install-CMTrace.exe 11 | 12 | .NOTES 13 | FileName: Install-CMTrace.ps1 14 | Author: Ben Whitmore 15 | Date: 9th July 2022 16 | Thanks: @PMPC colleague(s) for "Current user" identitity and role functions 17 | 18 | #> 19 | 20 | [CmdletBinding()] 21 | param( 22 | [Parameter(Mandatory = $false)] 23 | [string]$URL = "https://download.microsoft.com/download/5/0/8/508918E1-3627-4383-B7D8-AA07B3490D21/ConfigMgrTools.msi", 24 | [Parameter(Mandatory = $false)] 25 | [string]$DownloadDir = $env:temp, 26 | [Parameter(Mandatory = $false)] 27 | [string]$DownloadFileName = "ConfigMgrTools.msi", 28 | [Parameter(Mandatory = $false)] 29 | [string]$InstallDest = "C:\Program Files (x86)\ConfigMgr 2012 Toolkit R2\ClientTools\CMTrace.exe" 30 | ) 31 | 32 | ##Set Verbose Level## 33 | $VerbosePreference = "Continue" 34 | #$VerbosePreference = "SilentlyContinue" 35 | 36 | Function Get-URLHashInfo { 37 | [CmdletBinding()] 38 | param( 39 | [Parameter(Mandatory = $true)] 40 | [string]$URLPath 41 | ) 42 | #Initialize WebClient class and return file hash of URI passed to the function 43 | $WebClient = [System.Net.WebClient]::new() 44 | $URLHash = Get-FileHash -Algorithm MD5 -InputStream ($WebClient.OpenRead($URLPath)) 45 | return $URLHash 46 | } 47 | 48 | Function Get-FileHashInfo { 49 | [CmdletBinding()] 50 | param( 51 | [Parameter(Mandatory = $true)] 52 | [string]$FilePath 53 | ) 54 | #Return file hash of file passed to the function 55 | $FileHash = Get-FileHash -Algorithm MD5 -Path $FilePath 56 | return $FileHash 57 | } 58 | 59 | Function Test-ElevatedSession { 60 | 61 | #Get the current user 62 | $CurrentUser = [Security.Principal.WindowsIdentity]::GetCurrent() 63 | $IsAdmin = (New-Object Security.Principal.WindowsPrincipal $CurrentUser).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) 64 | $IsSystem = $CurrentUser.User -eq 'S-1-5-18' 65 | Write-Verbose "Current User = $($CurrentUser.Name)" 66 | Write-Verbose "Current user is an administrator? $IsAdmin" 67 | Write-Verbose "Running as SYSTEM? $IsSystem" 68 | 69 | If ($IsAdmin -or $IsSystem) { 70 | Write-Verbose "Session elevated? $true" 71 | $ElevatedSession = $true 72 | } 73 | 74 | return $ElevatedSession 75 | } 76 | 77 | Function Get-FileFromInternet { 78 | [CmdletBinding()] 79 | Param ( 80 | [Parameter(Mandatory = $true)] 81 | [ValidateScript({ $_.StartsWith("https://") })] 82 | [String]$URL, 83 | [Parameter(Mandatory = $true)] 84 | [String]$Destination 85 | ) 86 | 87 | #Test the URL is valid 88 | Try { 89 | $URLRequest = Invoke-WebRequest -UseBasicParsing -URI $URL -ErrorAction SilentlyContinue 90 | $StatusCode = $URLRequest.StatusCode 91 | } 92 | Catch { 93 | Write-Verbose "It looks like the URL is invalid. Please try again" 94 | $StatusCode = $_.Exception.Response.StatusCode.value__ 95 | } 96 | 97 | #If the URL is valid, attempt to download the file 98 | If ($StatusCode -eq 200) { 99 | Try { 100 | Invoke-WebRequest -UseBasicParsing -Uri $URL -OutFile $Destination -ErrorAction SilentlyContinue 101 | 102 | If (Test-Path $Destination) { 103 | Write-Verbose "File download Successfull. File saved to $Destination" 104 | } 105 | else { 106 | Write-Verbose "The download was interrupted or an error occured moving the file to the destination you specified" 107 | break 108 | } 109 | } 110 | Catch { 111 | Write-Verbose "Error downloading file: $URL" 112 | $_ 113 | break 114 | } 115 | } 116 | else { 117 | Write-Verbose "URL Does not exists or the website is down. Status Code: $($StatusCode)" 118 | break 119 | } 120 | } 121 | 122 | #Continue if the user running the script is a local administrator or SYSTEM 123 | If (!(Test-ElevatedSession)) { 124 | Write-Verbose "The script has not been run by an administrator or the SYSTEM account. Please re-run this script with administrator credentials or in the SYSTEM context." 125 | } 126 | else { 127 | 128 | #Check the CLient Tools are not already installed 129 | If (-not(Test-Path -Path $InstallDest)) { 130 | 131 | #Set Download destination full path 132 | $FilePath = (Join-Path -Path $DownloadDir -ChildPath $DownloadFileName) 133 | 134 | #Download the MSI 135 | Get-FileFromInternet -URL $URL -Destination $FilePath 136 | 137 | #Test if the download was successful 138 | If (-not (Test-Path -Path $FilePath)) { 139 | Write-Verbose "There was an error downloading the file to the file system. $($FilePath) does not exist." 140 | break 141 | } 142 | else { 143 | 144 | #Check the hash from Microsoft matches the hash of the file saved to disk 145 | $URLHash = (Get-URLHashInfo -URLPath $URL).hash 146 | $FileHash = (Get-FileHashInfo -FilePath $FilePath).hash 147 | Write-Verbose "Checking Hash.." 148 | 149 | #Warn if the hash is different 150 | If (($URLHash -ne $FileHash) -or ([string]::IsNullOrWhitespace($URLHash)) -or ([string]::IsNullOrWhitespace($FileHash))) { 151 | Write-Verbose "URL Hash = $($URLHash)" 152 | Write-Verbose "File Hash = $($FileHash)" 153 | Write-Verbose "There was an error checking the hash value of the downloaded file. The file hash for ""$($FilePath)"" does not match the hash at ""$($URL)"". Aborting installation" 154 | break 155 | } 156 | else { 157 | Write-Verbose "Hash match confirmed. Continue installation.." 158 | Try { 159 | 160 | #Attempt to install the MSI 161 | $MSIArgs = @( 162 | "/i" 163 | $FilePath 164 | "ADDLOCAL=ClientTools" 165 | "/qn" 166 | ) 167 | Start-Process "$env:SystemRoot\System32\msiexec.exe" -args $MSIArgs -Wait -NoNewWindow 168 | 169 | #Check the installation was successful 170 | If (Test-Path -Path $InstallDest) { 171 | Write-Verbose "CMTrace installed succesfully at $($InstallDest) " 172 | } 173 | } 174 | Catch { 175 | Write-Verbose "There was an error installing the CMTrace" 176 | $_ 177 | } 178 | } 179 | } 180 | } 181 | else { 182 | Write-Verbose "CMTrace is already installed at $($InstallDest). Installation will not continue." 183 | } 184 | } -------------------------------------------------------------------------------- /Intune/Convert-AzureAdObjectIdToSid.ps1: -------------------------------------------------------------------------------- 1 | function Convert-AzureAdObjectIdToSid { 2 | <# 3 | .SYNOPSIS 4 | Convert an Azure AD Object ID to SID 5 | 6 | .DESCRIPTION 7 | Converts an Azure AD Object ID to a SID. 8 | Author: Oliver Kieselbach (oliverkieselbach.com) 9 | The script is provided "AS IS" with no warranties. 10 | 11 | .PARAMETER ObjectID 12 | The Object ID to convert 13 | #> 14 | 15 | param([String] $ObjectId) 16 | 17 | $bytes = [Guid]::Parse($ObjectId).ToByteArray() 18 | $array = New-Object 'UInt32[]' 4 19 | 20 | [Buffer]::BlockCopy($bytes, 0, $array, 0, 16) 21 | $sid = "S-1-12-1-$array".Replace(' ', '-') 22 | 23 | return $sid 24 | } 25 | 26 | $objectId = "73d664e4-0886-4a73-b745-c694da45ddb4" 27 | $sid = Convert-AzureAdObjectIdToSid -ObjectId $objectId 28 | Write-Output $sid 29 | 30 | # Output: 31 | 32 | # S-1-12-1-1943430372-1249052806-2496021943-3034400218 -------------------------------------------------------------------------------- /Intune/CustomInventory.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Collect custom device inventory and upload to Log Analytics for further processing. 4 | 5 | .DESCRIPTION 6 | This script will collect device hardware and / or app inventory and upload this to a Log Analytics Workspace. This allows you to easily search in device hardware and installed apps inventory. 7 | The script is meant to be runned on a daily schedule either via Proactive Remediations (RECOMMENDED) in Intune or manually added as local schedule task on your Windows 10 Computer. 8 | 9 | .EXAMPLE 10 | Invoke-CustomInventoryWithAzureFunction.ps1 (Required to run as System or Administrator) 11 | 12 | .NOTES 13 | FileName: Invoke-CustomInventory.ps1 14 | Author: Jan Ketil Skanke 15 | Contributor: Sandy Zeng / Maurice Daly 16 | Contact: @JankeSkanke 17 | Created: 2021-01-02 18 | Updated: 2021-21-10 19 | 20 | Version history: 21 | 0.9.0 - (2021 - 01 - 02) Script created 22 | 1.0.0 - (2021 - 01 - 02) Script polished cleaned up. 23 | 1.0.1 - (2021 - 04 - 05) Added NetworkAdapter array and fixed typo 24 | 2.0 - (2021 - 08 - 29) Moved secrets out of code - now running via Azure Function 25 | 2.0.1 (2021-09-01) Removed all location information for privacy reasons 26 | 2.1 - (2021-09-08) Added section to cater for BIOS release version information, for HP, Dell and Lenovo and general bugfixes 27 | 2.1.1 - (2021-21-10) Added MACAddress to the inventory for each NIC. 28 | #> 29 | 30 | #region initialize 31 | # Define your azure function URL: 32 | # Example 'https://.azurewebsites.net/api/' 33 | 34 | $AzureFunctionURL = "https://appcwlogrestapi.azurewebsites.net/api/IngestLogsToLA" 35 | 36 | # Enable TLS 1.2 support 37 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 38 | #Control if you want to collect App or Device Inventory or both (True = Collect) 39 | $CollectAppInventory = $true 40 | $CollectDeviceInventory = $true 41 | #Set Log Analytics Log Name 42 | 43 | $AppLogName = "AppInventory" 44 | $DeviceLogName = "DeviceInventory" 45 | $Date=(Get-Date) 46 | #endregion initialize 47 | 48 | #region functions 49 | # Function to get Azure AD DeviceID 50 | function Get-AzureADDeviceID { 51 | <# 52 | .SYNOPSIS 53 | Get the Azure AD device ID from the local device. 54 | 55 | .DESCRIPTION 56 | Get the Azure AD device ID from the local device. 57 | 58 | .NOTES 59 | Author: Nickolaj Andersen 60 | Contact: @NickolajA 61 | Created: 2021-05-26 62 | Updated: 2021-05-26 63 | 64 | Version history: 65 | 1.0.0 - (2021-05-26) Function created 66 | #> 67 | Process { 68 | # Define Cloud Domain Join information registry path 69 | $AzureADJoinInfoRegistryKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\JoinInfo" 70 | 71 | # Retrieve the child key name that is the thumbprint of the machine certificate containing the device identifier guid 72 | $AzureADJoinInfoThumbprint = Get-ChildItem -Path $AzureADJoinInfoRegistryKeyPath | Select-Object -ExpandProperty "PSChildName" 73 | if ($AzureADJoinInfoThumbprint -ne $null) { 74 | # Retrieve the machine certificate based on thumbprint from registry key 75 | $AzureADJoinCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Thumbprint -eq $AzureADJoinInfoThumbprint } 76 | if ($AzureADJoinCertificate -ne $null) { 77 | # Determine the device identifier from the subject name 78 | $AzureADDeviceID = ($AzureADJoinCertificate | Select-Object -ExpandProperty "Subject") -replace "CN=", "" 79 | # Handle return value 80 | return $AzureADDeviceID 81 | } 82 | } 83 | } 84 | } #endfunction 85 | function Get-AzureADJoinDate { 86 | <# 87 | .SYNOPSIS 88 | Get the Azure AD device ID from the local device. 89 | 90 | .DESCRIPTION 91 | Get the Azure AD device ID from the local device. 92 | 93 | .NOTES 94 | Author: Nickolaj Andersen 95 | Contact: @NickolajA 96 | Created: 2021-05-26 97 | Updated: 2021-05-26 98 | 99 | Version history: 100 | 1.0.0 - (2021-05-26) Function created 101 | #> 102 | Process { 103 | # Define Cloud Domain Join information registry path 104 | $AzureADJoinInfoRegistryKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\JoinInfo" 105 | 106 | # Retrieve the child key name that is the thumbprint of the machine certificate containing the device identifier guid 107 | $AzureADJoinInfoThumbprint = Get-ChildItem -Path $AzureADJoinInfoRegistryKeyPath | Select-Object -ExpandProperty "PSChildName" 108 | if ($AzureADJoinInfoThumbprint -ne $null) { 109 | # Retrieve the machine certificate based on thumbprint from registry key 110 | $AzureADJoinCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Thumbprint -eq $AzureADJoinInfoThumbprint } 111 | if ($AzureADJoinCertificate -ne $null) { 112 | # Determine the device identifier from the subject name 113 | $AzureADJoinDate = ($AzureADJoinCertificate | Select-Object -ExpandProperty "NotBefore") 114 | # Handle return value 115 | return $AzureADJoinDate 116 | } 117 | } 118 | } 119 | } #endfunction 120 | #Function to get AzureAD TenantID 121 | function Get-AzureADTenantID { 122 | # Cloud Join information registry path 123 | $AzureADTenantInfoRegistryKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\TenantInfo" 124 | # Retrieve the child key name that is the tenant id for AzureAD 125 | $AzureADTenantID = Get-ChildItem -Path $AzureADTenantInfoRegistryKeyPath | Select-Object -ExpandProperty "PSChildName" 126 | return $AzureADTenantID 127 | } 128 | # Function to download files (speedtest) 129 | function Start-DownloadFile { 130 | param ( 131 | [parameter(Mandatory = $true)] 132 | [ValidateNotNullOrEmpty()] 133 | [string]$URL, 134 | [parameter(Mandatory = $true)] 135 | [ValidateNotNullOrEmpty()] 136 | [string]$Path, 137 | [parameter(Mandatory = $true)] 138 | [ValidateNotNullOrEmpty()] 139 | [string]$Name 140 | ) 141 | Begin { 142 | # Construct WebClient object 143 | $WebClient = New-Object -TypeName System.Net.WebClient 144 | } 145 | Process { 146 | # Create path if it doesn't exist 147 | if (-not (Test-Path -Path $Path)) { 148 | New-Item -Path $Path -ItemType Directory -Force | Out-Null 149 | } 150 | 151 | # Start download of file 152 | $WebClient.DownloadFile($URL, (Join-Path -Path $Path -ChildPath $Name)) 153 | } 154 | End { 155 | # Dispose of the WebClient object 156 | $WebClient.Dispose() 157 | } 158 | } #endfunction 159 | # Function to get all Installed Application 160 | function Get-InstalledApplications() { 161 | param ( 162 | [string]$UserSid 163 | ) 164 | 165 | New-PSDrive -PSProvider Registry -Name "HKU" -Root HKEY_USERS | Out-Null 166 | $regpath = @("HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*") 167 | $regpath += "HKU:\$UserSid\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" 168 | if (-not ([IntPtr]::Size -eq 4)) { 169 | $regpath += "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" 170 | $regpath += "HKU:\$UserSid\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" 171 | } 172 | $propertyNames = 'DisplayName', 'DisplayVersion', 'Publisher', 'UninstallString' 173 | $Apps = Get-ItemProperty $regpath -Name $propertyNames -ErrorAction SilentlyContinue | . { process { if ($_.DisplayName) { $_ } } } | Select-Object DisplayName, DisplayVersion, Publisher, UninstallString, PSPath | Sort-Object DisplayName 174 | Remove-PSDrive -Name "HKU" | Out-Null 175 | Return $Apps 176 | } 177 | 178 | function Start-PowerShellSysNative { 179 | param ( 180 | [parameter(Mandatory = $false, HelpMessage = "Specify arguments that will be passed to the sysnative PowerShell process.")] 181 | [ValidateNotNull()] 182 | [string]$Arguments 183 | ) 184 | 185 | # Get the sysnative path for powershell.exe 186 | $SysNativePowerShell = Join-Path -Path ($PSHOME.ToLower().Replace("syswow64", "sysnative")) -ChildPath "powershell.exe" 187 | 188 | # Construct new ProcessStartInfo object to run scriptblock in fresh process 189 | $ProcessStartInfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo 190 | $ProcessStartInfo.FileName = $SysNativePowerShell 191 | $ProcessStartInfo.Arguments = $Arguments 192 | $ProcessStartInfo.RedirectStandardOutput = $true 193 | $ProcessStartInfo.RedirectStandardError = $true 194 | $ProcessStartInfo.UseShellExecute = $false 195 | $ProcessStartInfo.WindowStyle = "Hidden" 196 | $ProcessStartInfo.CreateNoWindow = $true 197 | 198 | # Instatiate the new 64-bit process 199 | $Process = [System.Diagnostics.Process]::Start($ProcessStartInfo) 200 | 201 | # Read standard error output to determine if the 64-bit script process somehow failed 202 | $ErrorOutput = $Process.StandardError.ReadToEnd() 203 | if ($ErrorOutput) { 204 | Write-Error -Message $ErrorOutput 205 | } 206 | } #endfunction 207 | #endregion functions 208 | 209 | #region script 210 | 211 | #Get Common data for App and Device Inventory: 212 | #Get Intune DeviceID and ManagedDeviceName 213 | if (@(Get-ChildItem HKLM:SOFTWARE\Microsoft\Enrollments\ -Recurse | Where-Object { $_.PSChildName -eq 'MS DM Server' })) { 214 | $MSDMServerInfo = Get-ChildItem HKLM:SOFTWARE\Microsoft\Enrollments\ -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -eq 'MS DM Server' } 215 | $ManagedDeviceInfo = Get-ItemProperty -LiteralPath "Registry::$($MSDMServerInfo)" -ErrorAction SilentlyContinue 216 | } 217 | $ManagedDeviceName = $ManagedDeviceInfo.EntDeviceName 218 | $ManagedDeviceID = $ManagedDeviceInfo.EntDMID 219 | $AzureADDeviceID = Get-AzureADDeviceID 220 | $AzureADTenantID = Get-AzureADTenantID 221 | 222 | #Get Computer Info 223 | 224 | $ComputerInfo = Get-CimInstance -ClassName Win32_ComputerSystem 225 | $ComputerName = $ComputerInfo.Name 226 | $ComputerManufacturer = $ComputerInfo.Manufacturer 227 | 228 | if ($ComputerManufacturer -match "HP|Hewlett-Packard") { 229 | $ComputerManufacturer = "HP" 230 | } 231 | 232 | #region DEVICEINVENTORY 233 | if ($CollectDeviceInventory) { 234 | 235 | # Get Windows Update Service Settings 236 | $DefaultAUService = (New-Object -ComObject "Microsoft.Update.ServiceManager").Services | Where-Object { $_.isDefaultAUService -eq $True } | Select-Object Name 237 | $AUMeteredNetwork = (Get-ItemProperty -Path HKLM:\Software\Microsoft\WindowsUpdate\UX\Settings\).AllowAutoWindowsUpdateDownloadOverMeteredNetwork 238 | if ($AUMeteredNetwork -eq "0") { 239 | $AUMetered = "false" 240 | } else { $AUMetered = "true" } 241 | 242 | 243 | # Get Computer Inventory Information 244 | $ComputerOSInfo = Get-CimInstance -ClassName Win32_OperatingSystem 245 | $ComputerBiosInfo = Get-CimInstance -ClassName Win32_Bios 246 | $ComputerModel = $ComputerInfo.Model 247 | $ComputerLastBoot = $ComputerOSInfo.LastBootUpTime 248 | $ComputerUptime = [int](New-TimeSpan -Start $ComputerLastBoot -End $Date).Days 249 | $ComputerInstallDate = $ComputerOSInfo.InstallDate 250 | $DisplayVersion = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name DisplayVersion -ErrorAction SilentlyContinue).DisplayVersion 251 | if ([string]::IsNullOrEmpty($DisplayVersion)) { 252 | $ComputerWindowsVersion = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ReleaseId).ReleaseId 253 | } else { 254 | $ComputerWindowsVersion = $DisplayVersion 255 | } 256 | $ComputerOSName = $ComputerOSInfo.Caption 257 | $ComputerSystemSkuNumber = $ComputerInfo.SystemSKUNumber 258 | $ComputerSerialNr = $ComputerBiosInfo.SerialNumber 259 | $ComputerBiosUUID = Get-CimInstance Win32_ComputerSystemProduct | Select-Object -ExpandProperty UUID 260 | $ComputerBiosVersion = $ComputerBiosInfo.SMBIOSBIOSVersion 261 | $ComputerBiosDate = $ComputerBiosInfo.ReleaseDate 262 | $ComputerFirmwareType = $env:firmware_type 263 | $PCSystemType = $ComputerInfo.PCSystemType 264 | switch ($PCSystemType){ 265 | 0 {$ComputerPCSystemType = "Unspecified"} 266 | 1 {$ComputerPCSystemType = "Desktop"} 267 | 2 {$ComputerPCSystemType = "Laptop"} 268 | 3 {$ComputerPCSystemType = "Workstation"} 269 | 4 {$ComputerPCSystemType = "EnterpriseServer"} 270 | 5 {$ComputerPCSystemType = "SOHOServer"} 271 | 6 {$ComputerPCSystemType = "AppliancePC"} 272 | 7 {$ComputerPCSystemType = "PerformanceServer"} 273 | 8 {$ComputerPCSystemType = "Maximum"} 274 | default {$ComputerPCSystemType = "Unspecified"} 275 | } 276 | $PCSystemTypeEx = $ComputerInfo.PCSystemTypeEx 277 | switch ($PCSystemTypeEx){ 278 | 0 {$ComputerPCSystemTypeEx = "Unspecified"} 279 | 1 {$ComputerPCSystemTypeEx = "Desktop"} 280 | 2 {$ComputerPCSystemTypeEx = "Laptop"} 281 | 3 {$ComputerPCSystemTypeEx = "Workstation"} 282 | 4 {$ComputerPCSystemTypeEx = "EnterpriseServer"} 283 | 5 {$ComputerPCSystemTypeEx = "SOHOServer"} 284 | 6 {$ComputerPCSystemTypeEx = "AppliancePC"} 285 | 7 {$ComputerPCSystemTypeEx = "PerformanceServer"} 286 | 8 {$ComputerPCSystemTypeEx = "Slate"} 287 | 9 {$ComputerPCSystemTypeEx = "Maximum"} 288 | default {$ComputerPCSystemTypeEx = "Unspecified"} 289 | } 290 | 291 | $ComputerPhysicalMemory = [Math]::Round(($ComputerInfo.TotalPhysicalMemory / 1GB)) 292 | $ComputerOSBuild = $ComputerOSInfo.BuildNumber 293 | $ComputerOSRevision = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name UBR).UBR 294 | $ComputerCPU = Get-CimInstance win32_processor | Select-Object Name, Manufacturer, NumberOfCores, NumberOfLogicalProcessors 295 | $ComputerProcessorManufacturer = $ComputerCPU.Manufacturer | Get-Unique 296 | $ComputerProcessorName = $ComputerCPU.Name | Get-Unique 297 | $ComputerNumberOfCores = $ComputerCPU.NumberOfCores | Get-Unique 298 | $ComputerNumberOfLogicalProcessors = $ComputerCPU.NumberOfLogicalProcessors | Get-Unique 299 | $ComputerSystemSKU = (Get-CIMInstance -ClassName MS_SystemInformation -NameSpace root\WMI).SystemSku.Trim() 300 | 301 | try { 302 | $TPMValues = Get-Tpm -ErrorAction SilentlyContinue | Select-Object -Property TPMReady, TPMPresent, TPMEnabled, TPMActivated, ManagedAuthLevel 303 | } catch { 304 | $TPMValues = $null 305 | } 306 | 307 | try { 308 | $ComputerTPMThumbprint = (Get-TpmEndorsementKeyInfo).AdditionalCertificates.Thumbprint 309 | } catch { 310 | $ComputerTPMThumbprint = $null 311 | } 312 | 313 | try { 314 | $BitLockerInfo = Get-BitLockerVolume -MountPoint $env:SystemDrive | Select-Object -Property * 315 | } catch { 316 | $BitLockerInfo = $null 317 | } 318 | 319 | $ComputerTPMReady = $TPMValues.TPMReady 320 | $ComputerTPMPresent = $TPMValues.TPMPresent 321 | $ComputerTPMEnabled = $TPMValues.TPMEnabled 322 | $ComputerTPMActivated = $TPMValues.TPMActivated 323 | 324 | $ComputerBitlockerCipher = $BitLockerInfo.EncryptionMethod 325 | $ComputerBitlockerStatus = $BitLockerInfo.VolumeStatus 326 | $ComputerBitlockerProtection = $BitLockerInfo.ProtectionStatus 327 | $ComputerDefaultAUService = $DefaultAUService.Name 328 | $ComputerAUMetered = $AUMetered 329 | 330 | # Get BIOS information 331 | # Determine manufacturer specific information 332 | switch -Wildcard ($ComputerManufacturer) { 333 | "*Microsoft*" { 334 | $ComputerManufacturer = "Microsoft" 335 | $ComputerModel = (Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() 336 | $ComputerSystemSKU = Get-WmiObject -Namespace root\wmi -Class MS_SystemInformation | Select-Object -ExpandProperty SystemSKU 337 | } 338 | "*HP*" { 339 | $ComputerModel = (Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() 340 | $ComputerSystemSKU = (Get-CIMInstance -ClassName MS_SystemInformation -NameSpace root\WMI).BaseBoardProduct.Trim() 341 | 342 | # Obtain current BIOS release 343 | $CurrentBIOSProperties = (Get-WmiObject -Class Win32_BIOS | Select-Object -Property *) 344 | 345 | # Detect new versus old BIOS formats 346 | switch -wildcard ($($CurrentBIOSProperties.SMBIOSBIOSVersion)) { 347 | "*ver*" { 348 | if ($CurrentBIOSProperties.SMBIOSBIOSVersion -match '.F.\d+$') { 349 | $ComputerBiosVersion = ($CurrentBIOSProperties.SMBIOSBIOSVersion -split "Ver.")[1].Trim() 350 | } else { 351 | $ComputerBiosVersion = [System.Version]::Parse(($CurrentBIOSProperties.SMBIOSBIOSVersion).TrimStart($CurrentBIOSProperties.SMBIOSBIOSVersion.Split(".")[0]).TrimStart(".").Trim().Split(" ")[0]) 352 | } 353 | } 354 | default { 355 | $ComputerBiosVersion = "$($CurrentBIOSProperties.SystemBiosMajorVersion).$($CurrentBIOSProperties.SystemBiosMinorVersion)" 356 | } 357 | } 358 | } 359 | "*Dell*" { 360 | $ComputerManufacturer = "Dell" 361 | $ComputerModel = (Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() 362 | $ComputerSystemSKU = (Get-CIMInstance -ClassName MS_SystemInformation -NameSpace root\WMI).SystemSku.Trim() 363 | 364 | # Obtain current BIOS release 365 | $ComputerBiosVersion = (Get-WmiObject -Class Win32_BIOS | Select-Object -ExpandProperty SMBIOSBIOSVersion).Trim() 366 | 367 | } 368 | "*Lenovo*" { 369 | $ComputerManufacturer = "Lenovo" 370 | $ComputerModel = (Get-WmiObject -Class Win32_ComputerSystemProduct | Select-Object -ExpandProperty Version).Trim() 371 | $ComputerSystemSKU = ((Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty Model).SubString(0, 4)).Trim() 372 | 373 | # Obtain current BIOS release 374 | $CurrentBIOSProperties = (Get-WmiObject -Class Win32_BIOS | Select-Object -Property *) 375 | 376 | # Obtain current BIOS release 377 | #$ComputerBiosVersion = ((Get-WmiObject -Class Win32_BIOS | Select-Object -Property *).SMBIOSBIOSVersion).SubString(0, 8) 378 | $ComputerBiosVersion = "$($CurrentBIOSProperties.SystemBiosMajorVersion).$($CurrentBIOSProperties.SystemBiosMinorVersion)" 379 | } 380 | } 381 | 382 | #Get network adapters 383 | $NetWorkArray = @() 384 | 385 | $CurrentNetAdapters = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' } 386 | 387 | foreach ($CurrentNetAdapter in $CurrentNetAdapters) { 388 | $IPConfiguration = Get-NetIPConfiguration -InterfaceIndex $CurrentNetAdapter[0].ifIndex 389 | $ComputerNetInterfaceDescription = $CurrentNetAdapter.InterfaceDescription 390 | $ComputerNetProfileName = $IPConfiguration.NetProfile.Name 391 | $ComputerNetIPv4Adress = $IPConfiguration.IPv4Address.IPAddress 392 | $ComputerNetInterfaceAlias = $CurrentNetAdapter.InterfaceAlias 393 | $ComputerNetIPv4DefaultGateway = $IPConfiguration.IPv4DefaultGateway.NextHop 394 | $ComputerNetMacAddress = $CurrentNetAdapter.MacAddress 395 | 396 | $tempnetwork = New-Object -TypeName PSObject 397 | $tempnetwork | Add-Member -MemberType NoteProperty -Name "NetInterfaceDescription" -Value "$ComputerNetInterfaceDescription" -Force 398 | $tempnetwork | Add-Member -MemberType NoteProperty -Name "NetProfileName" -Value "$ComputerNetProfileName" -Force 399 | $tempnetwork | Add-Member -MemberType NoteProperty -Name "NetIPv4Adress" -Value "$ComputerNetIPv4Adress" -Force 400 | $tempnetwork | Add-Member -MemberType NoteProperty -Name "NetInterfaceAlias" -Value "$ComputerNetInterfaceAlias" -Force 401 | $tempnetwork | Add-Member -MemberType NoteProperty -Name "NetIPv4DefaultGateway" -Value "$ComputerNetIPv4DefaultGateway" -Force 402 | $tempnetwork | Add-Member -MemberType NoteProperty -Name "MacAddress" -Value "$ComputerNetMacAddress" -Force 403 | $NetWorkArray += $tempnetwork 404 | } 405 | [System.Collections.ArrayList]$NetWorkArrayList = $NetWorkArray 406 | 407 | # Get Disk Health 408 | $DiskArray = @() 409 | $Disks = Get-PhysicalDisk | Where-Object { $_.BusType -match "NVMe|SATA|SAS|ATAPI|RAID" } 410 | 411 | # Loop through each disk 412 | foreach ($Disk in ($Disks | Sort-Object DeviceID)) { 413 | # Obtain disk health information from current disk 414 | $DiskHealth = Get-PhysicalDisk -UniqueId $($Disk.UniqueId) | Get-StorageReliabilityCounter | Select-Object -Property Wear, ReadErrorsTotal, ReadErrorsUncorrected, WriteErrorsTotal, WriteErrorsUncorrected, Temperature, TemperatureMax 415 | 416 | # Obtain media type 417 | $DriveDetails = Get-PhysicalDisk -UniqueId $($Disk.UniqueId) | Select-Object MediaType, HealthStatus 418 | $DriveMediaType = $DriveDetails.MediaType 419 | $DriveHealthState = $DriveDetails.HealthStatus 420 | $DiskTempDelta = [int]$($DiskHealth.Temperature) - [int]$($DiskHealth.TemperatureMax) 421 | 422 | # Create custom PSObject 423 | $DiskHealthState = new-object -TypeName PSObject 424 | 425 | # Create disk entry 426 | $DiskHealthState | Add-Member -MemberType NoteProperty -Name "Disk Number" -Value $Disk.DeviceID 427 | $DiskHealthState | Add-Member -MemberType NoteProperty -Name "FriendlyName" -Value $($Disk.FriendlyName) 428 | $DiskHealthState | Add-Member -MemberType NoteProperty -Name "HealthStatus" -Value $DriveHealthState 429 | $DiskHealthState | Add-Member -MemberType NoteProperty -Name "MediaType" -Value $DriveMediaType 430 | $DiskHealthState | Add-Member -MemberType NoteProperty -Name "Disk Wear" -Value $([int]($DiskHealth.Wear)) 431 | $DiskHealthState | Add-Member -MemberType NoteProperty -Name "Disk $($Disk.DeviceID) Read Errors" -Value $([int]($DiskHealth.ReadErrorsTotal)) 432 | $DiskHealthState | Add-Member -MemberType NoteProperty -Name "Disk $($Disk.DeviceID) Temperature Delta" -Value $DiskTempDelta 433 | $DiskHealthState | Add-Member -MemberType NoteProperty -Name "Disk $($Disk.DeviceID) ReadErrorsUncorrected" -Value $($Disk.ReadErrorsUncorrected) 434 | $DiskHealthState | Add-Member -MemberType NoteProperty -Name "Disk $($Disk.DeviceID) ReadErrorsTotal" -Value $($Disk.ReadErrorsTotal) 435 | $DiskHealthState | Add-Member -MemberType NoteProperty -Name "Disk $($Disk.DeviceID) WriteErrorsUncorrected" -Value $($Disk.WriteErrorsUncorrected) 436 | $DiskHealthState | Add-Member -MemberType NoteProperty -Name "Disk $($Disk.DeviceID) WriteErrorsTotal" -Value $($Disk.WriteErrorsTotal) 437 | 438 | $DiskArray += $DiskHealthState 439 | [System.Collections.ArrayList]$DiskHealthArrayList = $DiskArray 440 | } 441 | 442 | 443 | # Create JSON to Upload to Log Analytics 444 | $Inventory = New-Object System.Object 445 | $Inventory | Add-Member -MemberType NoteProperty -Name "ManagedDeviceName" -Value "$ManagedDeviceName" -Force 446 | $Inventory | Add-Member -MemberType NoteProperty -Name "ManagedDeviceID" -Value "$ManagedDeviceID" -Force 447 | $Inventory | Add-Member -MemberType NoteProperty -Name "AzureADDeviceID" -Value "$AzureADDeviceID" -Force 448 | $Inventory | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value "$ComputerName" -Force 449 | $Inventory | Add-Member -MemberType NoteProperty -Name "Model" -Value "$ComputerModel" -Force 450 | $Inventory | Add-Member -MemberType NoteProperty -Name "Manufacturer" -Value "$ComputerManufacturer" -Force 451 | $Inventory | Add-Member -MemberType NoteProperty -Name "PCSystemType" -Value "$ComputerPCSystemType" -Force 452 | $Inventory | Add-Member -MemberType NoteProperty -Name "PCSystemTypeEx" -Value "$ComputerPCSystemTypeEx" -Force 453 | $Inventory | Add-Member -MemberType NoteProperty -Name "ComputerUpTime" -Value "$ComputerUptime" -Force 454 | $Inventory | Add-Member -MemberType NoteProperty -Name "LastBoot" -Value "$ComputerLastBoot" -Force 455 | $Inventory | Add-Member -MemberType NoteProperty -Name "InstallDate" -Value "$ComputerInstallDate" -Force 456 | $Inventory | Add-Member -MemberType NoteProperty -Name "WindowsVersion" -Value "$ComputerWindowsVersion" -Force 457 | $Inventory | Add-Member -MemberType NoteProperty -Name "DefaultAUService" -Value "$ComputerDefaultAUService" -Force 458 | $Inventory | Add-Member -MemberType NoteProperty -Name "AUMetered" -Value "$ComputerAUMetered" -Force 459 | $Inventory | Add-Member -MemberType NoteProperty -Name "SystemSkuNumber" -Value "$ComputerSystemSkuNumber" -Force 460 | $Inventory | Add-Member -MemberType NoteProperty -Name "SerialNumber" -Value "$ComputerSerialNr" -Force 461 | $Inventory | Add-Member -MemberType NoteProperty -Name "SMBIOSUUID" -Value "$ComputerBiosUUID" -Force 462 | $Inventory | Add-Member -MemberType NoteProperty -Name "BiosVersion" -Value "$ComputerBiosVersion" -Force 463 | $Inventory | Add-Member -MemberType NoteProperty -Name "BiosDate" -Value "$ComputerBiosDate" -Force 464 | $Inventory | Add-Member -MemberType NoteProperty -Name "SystemSKU" -Value "$ComputerSystemSKU" -Force 465 | $Inventory | Add-Member -MemberType NoteProperty -Name "FirmwareType" -Value "$ComputerFirmwareType" -Force 466 | $Inventory | Add-Member -MemberType NoteProperty -Name "Memory" -Value "$ComputerPhysicalMemory" -Force 467 | $Inventory | Add-Member -MemberType NoteProperty -Name "OSBuild" -Value "$ComputerOSBuild" -Force 468 | $Inventory | Add-Member -MemberType NoteProperty -Name "OSRevision" -Value "$ComputerOSRevision" -Force 469 | $Inventory | Add-Member -MemberType NoteProperty -Name "OSName" -Value "$ComputerOSName" -Force 470 | $Inventory | Add-Member -MemberType NoteProperty -Name "CPUManufacturer" -Value "$ComputerProcessorManufacturer" -Force 471 | $Inventory | Add-Member -MemberType NoteProperty -Name "CPUName" -Value "$ComputerProcessorName" -Force 472 | $Inventory | Add-Member -MemberType NoteProperty -Name "CPUCores" -Value "$ComputerNumberOfCores" -Force 473 | $Inventory | Add-Member -MemberType NoteProperty -Name "CPULogical" -Value "$ComputerNumberOfLogicalProcessors" -Force 474 | $Inventory | Add-Member -MemberType NoteProperty -Name "TPMReady" -Value "$ComputerTPMReady" -Force 475 | $Inventory | Add-Member -MemberType NoteProperty -Name "TPMPresent" -Value "$ComputerTPMPresent" -Force 476 | $Inventory | Add-Member -MemberType NoteProperty -Name "TPMEnabled" -Value "$ComputerTPMEnabled" -Force 477 | $Inventory | Add-Member -MemberType NoteProperty -Name "TPMActived" -Value "$ComputerTPMActivated" -Force 478 | $Inventory | Add-Member -MemberType NoteProperty -Name "TPMThumbprint" -Value "$ComputerTPMThumbprint" -Force 479 | $Inventory | Add-Member -MemberType NoteProperty -Name "BitlockerCipher" -Value "$ComputerBitlockerCipher" -Force 480 | $Inventory | Add-Member -MemberType NoteProperty -Name "BitlockerVolumeStatus" -Value "$ComputerBitlockerStatus" -Force 481 | $Inventory | Add-Member -MemberType NoteProperty -Name "BitlockerProtectionStatus" -Value "$ComputerBitlockerProtection" -Force 482 | $Inventory | Add-Member -MemberType NoteProperty -Name "NetworkAdapters" -Value $NetWorkArrayList -Force 483 | $Inventory | Add-Member -MemberType NoteProperty -Name "DiskHealth" -Value $DiskHealthArrayList -Force 484 | 485 | 486 | $DevicePayLoad = $Inventory 487 | 488 | } 489 | #endregion DEVICEINVENTORY 490 | 491 | #region APPINVENTORY 492 | if ($CollectAppInventory) { 493 | #$AppLog = "AppInventory" 494 | 495 | #Get SID of current interactive users 496 | $CurrentLoggedOnUser = (Get-CimInstance win32_computersystem).UserName 497 | if (-not ([string]::IsNullOrEmpty($CurrentLoggedOnUser))) { 498 | $AdObj = New-Object System.Security.Principal.NTAccount($CurrentLoggedOnUser) 499 | $strSID = $AdObj.Translate([System.Security.Principal.SecurityIdentifier]) 500 | $UserSid = $strSID.Value 501 | } else { 502 | $UserSid = $null 503 | } 504 | 505 | #Get Apps for system and current user 506 | $MyApps = Get-InstalledApplications -UserSid $UserSid 507 | $UniqueApps = ($MyApps | Group-Object Displayname | Where-Object { $_.Count -eq 1 }).Group 508 | $DuplicatedApps = ($MyApps | Group-Object Displayname | Where-Object { $_.Count -gt 1 }).Group 509 | $NewestDuplicateApp = ($DuplicatedApps | Group-Object DisplayName) | ForEach-Object { $_.Group | Sort-Object [version]DisplayVersion -Descending | Select-Object -First 1 } 510 | $CleanAppList = $UniqueApps + $NewestDuplicateApp | Sort-Object DisplayName 511 | 512 | $AppArray = @() 513 | foreach ($App in $CleanAppList) { 514 | $tempapp = New-Object -TypeName PSObject 515 | $tempapp | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value "$ComputerName" -Force 516 | $tempapp | Add-Member -MemberType NoteProperty -Name "ManagedDeviceName" -Value "$ManagedDeviceName" -Force 517 | $tempapp | Add-Member -MemberType NoteProperty -Name "ManagedDeviceID" -Value "$ManagedDeviceID" -Force 518 | $tempapp | Add-Member -MemberType NoteProperty -Name "AzureADDeviceID" -Value "$AzureADDeviceID" -Force 519 | $tempapp | Add-Member -MemberType NoteProperty -Name "AppName" -Value $App.DisplayName -Force 520 | $tempapp | Add-Member -MemberType NoteProperty -Name "AppVersion" -Value $App.DisplayVersion -Force 521 | $tempapp | Add-Member -MemberType NoteProperty -Name "AppInstallDate" -Value $App.InstallDate -Force -ErrorAction SilentlyContinue 522 | $tempapp | Add-Member -MemberType NoteProperty -Name "AppPublisher" -Value $App.Publisher -Force 523 | $tempapp | Add-Member -MemberType NoteProperty -Name "AppUninstallString" -Value $App.UninstallString -Force 524 | $tempapp | Add-Member -MemberType NoteProperty -Name "AppUninstallRegPath" -Value $app.PSPath.Split("::")[-1] 525 | $AppArray += $tempapp 526 | } 527 | 528 | $AppPayLoad = $AppArray 529 | } 530 | #endregion APPINVENTORY 531 | 532 | #Randomize over 50 minutes to spread load on Azure Function - disabled on date of enrollment 533 | $JoinDate = Get-AzureADJoinDate 534 | $DelayDate = $JoinDate.AddDays(1) 535 | $CompareDate = ($DelayDate - $JoinDate) 536 | if ($CompareDate.Days -ge 1){ 537 | Write-Output "Randomzing execution time" 538 | #$ExecuteInSeconds = (Get-Random -Maximum 3000 -Minimum 1) 539 | #Start-Sleep -Seconds $ExecuteInSeconds 540 | } 541 | #Report back status 542 | $date = Get-Date -Format "dd-MM HH:mm" 543 | $OutputMessage = "InventoryDate:$date " 544 | 545 | $PayLoad = [PSCustomObject]@{ 546 | AppLogName = $AppLogName 547 | DeviceLogName = $DeviceLogName 548 | AzureADTenantID = $AzureADTenantID 549 | AzureADDeviceID = $AzureADDeviceID 550 | AppPayload = $AppPayload 551 | DevicePayload = $DevicePayload 552 | } 553 | 554 | $PayloadJSON = $PayLoad | ConvertTo-Json -Depth 9 555 | 556 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 557 | $headers.Add("Content-Type", "application/json") 558 | 559 | try { 560 | $ResponseInventory = Invoke-RestMethod $AzureFunctionURL -Method 'POST' -Headers $headers -Body $PayloadJSON 561 | } catch { 562 | $ResponseInventory = "Error Code: $($_.Exception.Response.StatusCode.value__)" 563 | $ResponseMessage = $_.Exception.Message 564 | } 565 | 566 | if ($ResponseInventory-match "200"){ 567 | $AppResponse = $ResponseInventory.Split(",") | Where-Object { $_ -match "App:" } 568 | $DeviceResponse = $ResponseInventory.Split(",") | Where-Object { $_ -match "Device:" } 569 | if ($CollectDeviceInventory) { 570 | if ($DeviceResponse -match "Device:200") { 571 | $OutputMessage = $OutPutMessage + "DeviceInventory:OK " + $DeviceResponse 572 | } else 573 | { 574 | $OutputMessage = $OutPutMessage + "DeviceInventory:Fail " + $DeviceResponse 575 | } 576 | } 577 | if ($CollectAppInventory) { 578 | if ($AppResponse -match "App:200") { 579 | 580 | $OutputMessage = $OutPutMessage + " AppInventory:OK " + $AppResponse 581 | } else { 582 | $OutputMessage = $OutPutMessage + " AppInventory:Fail " + $AppResponse 583 | } 584 | } 585 | Write-Output $OutputMessage 586 | if (($DeviceResponse -notmatch "Device:200") -or ($AppResponse -notmatch "App:200")) { 587 | Exit 1 588 | } else { 589 | Exit 0 590 | } 591 | } else { 592 | Write-Output "Error: $($ResponseInventory), Message: $($ResponseMessage)" 593 | Exit 1 594 | } 595 | #endregion script 596 | -------------------------------------------------------------------------------- /Intune/Get-AutopilotCSV.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byteben/MEM/1cd804b002274748fde9bfc342f6e0b46cde916e/Intune/Get-AutopilotCSV.ps1 -------------------------------------------------------------------------------- /Intune/Get-DeviceManagementScripts.ps1: -------------------------------------------------------------------------------- 1 | #################################################### 2 | 3 | #region Initialization code 4 | 5 | $m = Get-Module -Name Microsoft.Graph.Intune -ListAvailable 6 | if (-not $m) 7 | { 8 | Install-Module NuGet -Force 9 | Install-Module Microsoft.Graph.Intune 10 | } 11 | Import-Module Microsoft.Graph.Intune -Global 12 | 13 | #endregion 14 | 15 | #################################################### 16 | 17 | Function Get-DeviceManagementScripts(){ 18 | <# 19 | .SYNOPSIS 20 | Get all or individual Intune PowerShell scripts and save them in specified folder. 21 | 22 | .DESCRIPTION 23 | The Get-DeviceManagementScripts cmdlet downloads all or individual PowerShell scripts from Intune to a specified folder. 24 | Initial Author: Oliver Kieselbach (oliverkieselbach.com) 25 | The script is provided "AS IS" with no warranties. 26 | 27 | .PARAMETER FolderPath 28 | The folder where the script(s) are saved. 29 | 30 | .PARAMETER FileName 31 | An optional parameter to specify an explicit PowerShell script to download. 32 | 33 | .EXAMPLE 34 | Download all Intune PowerShell scripts to the specified folder 35 | 36 | Get-DeviceManagementScripts -FolderPath C:\temp 37 | 38 | .EXAMPLE 39 | Download an individual PowerShell script to the specified folder 40 | 41 | Get-DeviceManagementScripts -FolderPath C:\temp -FileName myScript.ps1 42 | 43 | #> 44 | 45 | [CmdletBinding()] 46 | Param( 47 | [Parameter(Mandatory=$true)][String] $FolderPath, 48 | [Parameter(Mandatory=$false)][String] $FileName 49 | ) 50 | 51 | $graphApiVersion = "Beta" 52 | $graphUrl = "https://graph.microsoft.com/$graphApiVersion" 53 | 54 | $result = Invoke-MSGraphRequest -Url "$graphUrl/deviceManagement/deviceManagementScripts" -HttpMethod GET 55 | 56 | if ($FileName){ 57 | $scriptIds = $result.value | Select-Object id,fileName | Where-Object -Property fileName -eq $FileName 58 | $script = Invoke-MSGraphRequest -Url "$graphUrl/deviceManagement/deviceManagementScripts/$($scriptId.id)" -HttpMethod GET 59 | [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($($script.scriptContent))) | Out-File -Encoding ASCII -FilePath $(Join-Path $FolderPath $($script.fileName)) 60 | } 61 | else{ 62 | $scriptIds = $result.value | Select-Object id,fileName 63 | foreach($scriptId in $scriptIds){ 64 | $script = Invoke-MSGraphRequest -Url "$graphUrl/deviceManagement/deviceManagementScripts/$($scriptId.id)" -HttpMethod GET 65 | [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($($script.scriptContent))) | Out-File -Encoding ASCII -FilePath $(Join-Path $FolderPath $($script.fileName)) 66 | } 67 | } 68 | } 69 | 70 | Connect-MSGraph | Out-Null 71 | 72 | Get-DeviceManagementScripts -FolderPath C:\temp 73 | #Get-DeviceManagementScripts -FolderPath C:\temp -FileName myScript.ps1 -------------------------------------------------------------------------------- /Intune/Get-IntuneScripts.ps1: -------------------------------------------------------------------------------- 1 | 2 | $Path = "C:\temp\scriptbackup" 3 | Connect-MSGraph 4 | $Stream = Invoke-MSGraphRequest -Url "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts" -HttpMethod GET 5 | 6 | $ScriptData = $Stream.value | Select-Object id, fileName, displayname 7 | $TotalScripts = ($ScriptData).count 8 | 9 | if ($TotalScripts -gt 0) { 10 | foreach ($ScriptItem in $ScriptData) { 11 | $Script = Invoke-MSGraphRequest -Url "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts/$($scriptItem.id)" -HttpMethod GET 12 | [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($($Script.ScriptContent))) | Out-File -FilePath $(Join-Path $Path $($Script.fileName)) -Encoding ASCII 13 | } 14 | } -------------------------------------------------------------------------------- /Intune/ImpersonateLoggedOnUser.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | function StartImpersonatingLoggedOnUser { 4 | 5 | Try { 6 | add-type @" 7 | namespace mystruct { 8 | using System; 9 | using System.Runtime.InteropServices; 10 | [StructLayout(LayoutKind.Sequential)] 11 | public struct WTS_SESSION_INFO 12 | { 13 | public Int32 SessionID; 14 | 15 | [MarshalAs(UnmanagedType.LPStr)] 16 | public String pWinStationName; 17 | 18 | public WTS_CONNECTSTATE_CLASS State; 19 | } 20 | 21 | public enum WTS_CONNECTSTATE_CLASS 22 | { 23 | WTSActive, 24 | WTSConnected, 25 | WTSConnectQuery, 26 | WTSShadow, 27 | WTSDisconnected, 28 | WTSIdle, 29 | WTSListen, 30 | WTSReset, 31 | WTSDown, 32 | WTSInit 33 | } 34 | } 35 | "@ 36 | 37 | $wtsEnumerateSessions = @" 38 | [DllImport("wtsapi32.dll", SetLastError=true)] 39 | public static extern int WTSEnumerateSessions( 40 | System.IntPtr hServer, 41 | int Reserved, 42 | int Version, 43 | ref System.IntPtr ppSessionInfo, 44 | ref int pCount); 45 | "@ 46 | 47 | $wtsenum = add-type -MemberDefinition $wtsEnumerateSessions -Name PSWTSEnumerateSessions -Namespace GetLoggedOnUsers -PassThru 48 | 49 | 50 | $wtsqueryuserToken = @" 51 | [DllImport("wtsapi32.dll", SetLastError = true)] 52 | public static extern bool WTSQueryUserToken(UInt32 sessionId, out System.IntPtr Token); 53 | "@ 54 | 55 | $wtsQuery = add-type -MemberDefinition $wtsqueryuserToken -Name PSWTSQueryServer -Namespace GetLoggedOnUsers -PassThru 56 | 57 | 58 | [long]$count = 0 59 | [long]$sessionInfo = 0 60 | [long]$returnValue = $wtsenum::WTSEnumerateSessions(0, 0, 1, [ref]$sessionInfo, [ref]$count) 61 | $datasize = [system.runtime.interopservices.marshal]::SizeOf([System.Type][mystruct.WTS_SESSION_INFO]) 62 | $userSessionID = $null 63 | if ($returnValue -ne 0) { 64 | for ($i = 0; $i -lt $count; $i++) { 65 | $element = [system.runtime.interopservices.marshal]::PtrToStructure($sessionInfo + ($datasize * $i), [System.type][mystruct.WTS_SESSION_INFO]) 66 | 67 | if ($element.State -eq [mystruct.WTS_CONNECTSTATE_CLASS]::WTSActive) { 68 | $userSessionID = $element.SessionID 69 | } 70 | } 71 | 72 | if ($userSessionID -eq $null) { 73 | Write-Host "Could not impersonate Logged on user. Continuing as System. Data will be sent when a user Write-Hosts on." "Error" "41" "StartImpersonatingLoggedOnUser" 74 | return 75 | } 76 | 77 | $userToken = [System.IntPtr]::Zero 78 | $wtsQuery::WTSQueryUserToken($userSessionID, [ref]$userToken) 79 | 80 | 81 | $advapiImpersonate = @' 82 | [DllImport("advapi32.dll", SetLastError=true)] 83 | public static extern bool ImpersonateLoggedOnUser(System.IntPtr hToken); 84 | '@ 85 | 86 | $impersonateUser = add-type -MemberDefinition $advapiImpersonate -Name PSImpersonategedOnUser -PassThru 87 | $impersonateUser::ImpersonategedOnUser($UserToken) 88 | $global:isImpersonatedUser = $true 89 | 90 | Write-Host "Passed: StartImpersonatingLoggedOnUser. Connected as Logged on user" 91 | } 92 | else { 93 | Write-Host "Could not impersonate Logged on user. Continuing as System. Data will be sent when a user Logs on." "Error" "41" "StartImpersonatingLoggedOnUser" 94 | } 95 | } 96 | Catch { 97 | Write-Host "StartImpersonatingLoggedOnUser failed with unexpected exception. Continuing as System. Data will be sent when a user Logs on." "Error" "42" "StartImpersonatingLoggedOnUser" $_.Exception.HResult $_.Exception.Message 98 | } 99 | } 100 | 101 | StartImpersonatingLoggedOnUser -------------------------------------------------------------------------------- /Intune/Intune Reports/Get-IntuneReport/README.md: -------------------------------------------------------------------------------- 1 | # Get-IntuneReport 2 | 3 | ![PowerShell](https://img.shields.io/badge/PowerShell-v5.1+-blue?logo=powershell) 4 | ![Status](https://img.shields.io/badge/Status-Preview-yellow) 5 | 6 | ## 📝 Synopsis 7 | 8 | The Get-IntuneReport script allows you to generate and download reports from Microsoft Intune. It supports various report types with filtering capabilities and authentication methods. 9 | 10 | ![Out-GridView](https://byteben.com/bb/Downloads/GitHub/Get-IntuneReport_SelectReports.png) 11 | 12 | ## 📋 Requirements 13 | 14 | - PowerShell 5.1 or later 15 | - Microsoft.Graph.Authentication module 16 | - Entra ID App Registration with appropriate permissions 17 | - Internet connectivity to access Microsoft Graph API 18 | 19 | ## 📖 Description 20 | 21 | This script allows you to interactively select and fetch Intune reports using the Microsoft Graph API. These reports are documented at [Microsoft Learn](https://learn.microsoft.com/en-us/mem/intune/fundamentals/reports-export-graph-available-reports). 22 | Reports are grouped by categories, enabling you to select relevant reports conveniently. Reports are saved in the specified format (CSV by default) to the designated path ($SavePath). 23 | The script supports delegated and application-based authentication flows. 24 | 25 | ## 📄 Log File 26 | 27 | The script logs its activities in CMTrace format, which is a log format used by Configuration Manager. 28 | The log file is saved in the specified `$SavePath` directory with the name `Get-IntuneReport.log`. 29 | 30 | ![Log File](https://byteben.com/bb/Downloads/GitHub/Get-IntuneReport_Log.png) 31 | 32 | ## 📂 JSON File Structure 33 | 34 | The script creates JSON files in the following folder structure: 35 | 36 | - `$SavePath\_.json` 37 | 38 | These JSON files can be updated to reflect the filters you want to apply to the reports. Each JSON file contains the following structure: 39 | 40 | - `reportName`: the name of the report 41 | - `category`: the category of the report 42 | - `requiredScope`: the minimum scope required to call the report. If using the `Connect-MgGraph` cmdlet, this scope is tested to ensure the connection has at least this level of access. 43 | - `filters`: an array of available filters. Each filter contains the following structure: 44 | - `name`: the name of the filter 45 | - `presence`: Optional or Required. If Required, the filter must be present in the JSON with a value. 46 | - `operator`: the operator to use when applying the filter. Can be EQ, NE, GT, GE, LT, LE, IN, or CONTAINS. 47 | - `value`: the value to use when applying the filter. 48 | - `properties`: the properties to include in the report. Each property is a string with the name of the property. These will be sent as the Select element in the JSON body of the request. You can remove properties that you do not want returned in the report. 49 | 50 | ![JSON Structure](https://byteben.com/bb/Downloads/GitHub/Get-IntuneReport_JSONExample.png) 51 | 52 | The image below indicates the user is prompted for a filter value if it is required and not supplied in the JSON. 53 | 54 | ![Required Filters](https://byteben.com/bb/Downloads/GitHub/Get-IntuneReport_RequiredFilter.png) 55 | 56 | ## 🔄 Resetting JSON Files 57 | 58 | To reset the JSON files to their default values, pass the `-OverwriteRequestBodies` switch. 59 | 60 | ## 🗑️ Removing Older Reports 61 | 62 | To remove older reports from the reports directory, pass the `-CleanupOldReports` switch. 63 | 64 | ## 🚀 Script Usage 65 | 66 | To use the `Get-IntuneReport` script, follow the examples below. These examples demonstrate how to run the script with various parameters and options. 67 | By default, all properties are returned in the report. To customize the properties returned and the filters used in the request, edit the JSON files in the `$SavePath\Category_ReportName` directory. 68 | 69 | ### Quick Start Run 70 | `Get-IntuneReport.ps1 -TenantId "your-tenant-id" -ClientId "your-client-id"` to connect interactively to the Microsoft Graph API and display an interactive menu to select reports you wish to request. 71 | 72 | The example below indicates the host output when running the script and selecting reports 2 reports. 73 | 74 | ![Example Host Output](https://byteben.com/bb/Downloads/GitHub/Get-IntuneReport_ExampleRun.png) 75 | 76 | 77 | 78 | ## ⏲️ Scheduled Task 79 | 80 | To automate the execution of this script using a scheduled task, follow these steps. As this will not be an interactive session, consider using a client certificate for authentication. For more information on how to use a certificate, visit [Deep Diving Microsoft Graph SDK Authentication Methods](https://msendpointmgr.com/2025/01/12/deep-diving-microsoft-graph-sdk-authentication-methods/#certificates-client-assertion). 81 | 82 | Ensure the JSON files in the `$SavePath\Category_ReportName` are updated with the required filters before creating the scheduled task. 83 | 84 | 1. **Edit the script parameters**: 85 | Update the script parameters in the `Run-IntuneReport.ps1` script to match your environment. The following parameters are required: 86 | 87 | ```yaml 88 | $TenantId = "your-tenant-id" 89 | $ClientId = "your-client-id" 90 | $ClientCertificateThumbprint = "your-client-certificate-thumbprint" 91 | $SavePath = "C:\Reports\Intune" 92 | $FormatChoice = "csv" 93 | $ReportNames = ("AllAppsList", "AppInvByDevice") 94 | ``` 95 | 96 | 2. **Create a Scheduled Task**: 97 | Use the `New-ScheduledTaskAction`, `New-ScheduledTaskTrigger`, and `Register-ScheduledTask` cmdlets to create a scheduled task that runs the `Run-IntuneReport.ps1` script at a specified interval. 98 | 99 | ```yaml 100 | # Define the action to run the PowerShell script 101 | $action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Path\To\Get-IntuneReport.ps1" 102 | 103 | # Define the trigger to run the task daily at 2 AM 104 | $trigger = New-ScheduledTaskTrigger -Daily -At 2:00AM 105 | 106 | # Define the principal (user) under which the task will run 107 | $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest 108 | 109 | # Register the scheduled task 110 | Register-ScheduledTask -Action $action -Trigger $trigger -Principal $principal -TaskName "Get-IntuneReports" -Description "Scheduled task to get Intune reports daily" 111 | ``` 112 | 113 | 3. **Verify the Scheduled Task**: 114 | Ensure the scheduled task is created successfully and verify its properties using the Task Scheduler GUI or the `Get-ScheduledTask` cmdlet. 115 | ```powershell 116 | # Get the details of the scheduled task 117 | Get-ScheduledTask -TaskName "Get-IntuneReports" 118 | ``` 119 | 120 | ## 📍 Locations of Reports in the Intune Admin Centre 121 | For more information on available reports, visit [Microsoft Learn](https://learn.microsoft.com/en-us/mem/intune/fundamentals/reports-export-graph-available-reports). 122 | 123 | ### Applications 124 | 125 | - **AllAppsList**: Found under Apps > All Apps 126 | - **AppInstallStatusAggregate**: Found under Apps > Monitor > App install status 127 | - **DeviceInstallStatusByApp**: Found under Apps > All Apps > Select an individual app (Required Filter: ApplicationId) 128 | - **UserInstallStatusAggregateByApp**: Found under Apps > All Apps > Select an individual app (Required Filter: ApplicationId) 129 | - **DevicesByAppInv**: Found under Apps > Monitor > Discovered apps > Discovered app > Export (Required Filter: ApplicationKey) 130 | - **AppInvByDevice**: Found under Devices > All Devices > Device > Discovered Apps (Required Filter: DeviceId) 131 | - **AppInvAggregate**: Found under Apps > Monitor > Discovered apps > Export 132 | - **AppInvRawData**: Found under Apps > Monitor > Discovered apps > Export 133 | 134 | ### Apps Protection 135 | 136 | - **MAMAppProtectionStatus**: Found under Apps > Monitor > App protection status > App protection report: iOS, Android 137 | - **MAMAppConfigurationStatus**: Found under Apps > Monitor > App protection status > App configuration report 138 | 139 | ### Cloud Attached Devices 140 | 141 | - **ComanagedDeviceWorkloads**: Found under Reports > Cloud attached devices > Reports > Co-Managed Workloads 142 | - **ComanagementEligibilityTenantAttachedDevices**: Found under Reports > Cloud attached devices > Reports > Co-Management Eligibility 143 | 144 | ### Device Compliance 145 | 146 | - **DeviceCompliance**: Found under Device Compliance Org 147 | - **DeviceNonCompliance**: Found under Non-compliant devices 148 | 149 | ### Device Management 150 | 151 | - **Devices**: Found under All devices list 152 | - **DevicesWithInventory**: Found under Devices > All Devices > Export 153 | - **DeviceFailuresByFeatureUpdatePolicy**: Found under Devices > Monitor > Failure for feature updates > Click on error (Required Filter: PolicyId) 154 | - **FeatureUpdatePolicyFailuresAggregate**: Found under Devices > Monitor > Failure for feature updates 155 | 156 | ### Endpoint Analytics 157 | 158 | - **DeviceRunStatesByProactiveRemediation**: Found under Reports > Endpoint Analytics > Proactive remediations > Select a remediation > Device status (Required Filter: PolicyId) 159 | 160 | ### Endpoint Security 161 | 162 | - **UnhealthyDefenderAgents**: Found under Endpoint Security > Antivirus > Win10 Unhealthy Endpoints 163 | - **DefenderAgents**: Found under Reports > Microsoft Defender > Reports > Agent Status 164 | - **ActiveMalware**: Found under Endpoint Security > Antivirus > Win10 detected malware 165 | - **Malware**: Found under Reports > Microsoft Defender > Reports > Detected malware 166 | - **FirewallStatus**: Found under Reports > Firewall > MDM Firewall status for Windows 10 and later 167 | 168 | ### Group Policy Analytics 169 | 170 | - **GPAnalyticsSettingMigrationReadiness**: Found under Reports > Group policy analytics > Reports > Group policy migration readiness 171 | 172 | ### Windows Updates 173 | 174 | - **FeatureUpdateDeviceState**: Found under Reports > Windows Updates > Reports > Windows Feature Update Report (Required Filter: PolicyId) 175 | - **QualityUpdateDeviceErrorsByPolicy**: Found under Devices > Monitor > Windows Expedited update failures > Select a profile (Required Filter: PolicyId) 176 | - **QualityUpdateDeviceStatusByPolicy**: Found under Reports > Windows updates > Reports > Windows Expedited Update Report (Required Filter: PolicyId) 177 | 178 | ### Example 1: Basic Usage 179 | 180 | This example shows how to run the script with the default parameters. The reports will be saved in the default path (`$env:TEMP\IntuneReports`) in CSV format. 181 | 182 | ```powershell 183 | .\Get-IntuneReport.ps1 -TenantId "your-tenant-id" -ClientId "your-client-id" -ClientSecret "your-client-secret" 184 | ``` 185 | 186 | ### Example 2: Specify Save Path and Format 187 | 188 | This example demonstrates how to specify a custom save path and output format (JSON). 189 | 190 | ```powershell 191 | .\Get-IntuneReport.ps1 -TenantId "your-tenant-id" -ClientId "your-client-id" -ClientSecret "your-client-secret" -SavePath "C:\CustomReports\Intune" -FormatChoice "json" 192 | ``` 193 | 194 | ### Example 3: Use Client Certificate for Authentication 195 | 196 | This example shows how to use a client certificate for authentication. 197 | 198 | ```powershell 199 | .\Get-IntuneReport.ps1 -TenantId "your-tenant-id" -ClientId "your-client-id" -ClientCertificateThumbprint "your-client-certificate-thumbprint" 200 | ``` 201 | 202 | ### Example 4: Run Specific Reports 203 | 204 | This example demonstrates how to run specific reports by specifying their names. 205 | 206 | ```powershell 207 | .\Get-IntuneReport.ps1 -TenantId "your-tenant-id" -ClientId "your-client-id" -ClientSecret "your-client-secret" -ReportNames "AllAppsList", "AppInvByDevice" 208 | ``` 209 | 210 | ### Example 5: Overwrite Existing JSON Files 211 | 212 | This example shows how to overwrite existing JSON files with default values. This is useful if you edit the JSON files and want to reset them. 213 | 214 | ```powershell 215 | .\Get-IntuneReport.ps1 -TenantId "your-tenant-id" -ClientId "your-client-id" -ClientSecret "your-client-secret" -OverwriteRequestBodies 216 | ``` 217 | 218 | ### Example 6: Cleanup Old Reports 219 | 220 | This example demonstrates how to remove older reports from the reports directory. 221 | 222 | ```powershell 223 | .\Get-IntuneReport.ps1 -TenantId "your-tenant-id" -ClientId "your-client-id" -ClientSecret "your-client-secret" -CleanupOldReports 224 | ``` 225 | 226 | ### Example 7: Use Device Authentication 227 | 228 | This example shows how to use device authentication for Microsoft Graph API. 229 | 230 | ```powershell 231 | .\Get-IntuneReport.ps1 -TenantId "your-tenant-id" -ClientId "your-client-id" -UseDeviceAuthentication 232 | ``` 233 | 234 | These examples cover common use cases for the `Get-IntuneReport` script. Adjust the parameters as needed to fit your specific requirements. 235 | 236 | ## 🔢 Parameters 237 | 238 | ### -SavePath 239 | ```yaml 240 | Type: String 241 | Parameter Sets: (All) 242 | Required: False 243 | Position: 0 244 | Default value: "$env:TEMP\IntuneReports" 245 | Description: The path where the reports will be saved. Default is $env:TEMP\IntuneReports 246 | ``` 247 | 248 | ### -FormatChoice 249 | 250 | ```yaml 251 | Type: String 252 | Parameter Sets: (All) 253 | Required: False 254 | Position: 1 255 | Default value: csv 256 | Description: The format of the report. Valid formats are csv and json. Default is csv 257 | ``` 258 | 259 | ### -EndpointVersion 260 | 261 | ```yaml 262 | Type: String 263 | Parameter Sets: (All) 264 | Required: False 265 | Position: 2 266 | Default value: beta 267 | Description: The Microsoft Graph API version to use. Valid endpoints are v1.0 and beta. Default is beta 268 | ``` 269 | 270 | ### -ModuleNames 271 | 272 | ```yaml 273 | Type: Object 274 | Parameter Sets: (All) 275 | Required: False 276 | Position: 3 277 | Default value: Microsoft.Graph.Authentication 278 | Description: Module Name to connect to Graph. Default is Microsoft.Graph.Authentication 279 | ``` 280 | 281 | ### -PackageProvider 282 | 283 | ```yaml 284 | Type: String 285 | Parameter Sets: (All) 286 | Required: False 287 | Position: 4 288 | Default value: NuGet 289 | Description: If not specified, the default value NuGet is used for PackageProvider 290 | ``` 291 | 292 | ### -TenantId 293 | 294 | ```yaml 295 | Type: String 296 | Parameter Sets: ClientSecret, ClientCertificateThumbprint, UseDeviceAuthentication, Interactive 297 | Required: True 298 | Position: 5 299 | Description: Tenant Id or name to connect to 300 | ``` 301 | 302 | ### -ClientId 303 | 304 | ```yaml 305 | Type: String 306 | Parameter Sets: ClientSecret, ClientCertificateThumbprint, UseDeviceAuthentication, Interactive 307 | Required: True 308 | Position: 6 309 | Description: Client Id (App Registration) to connect to 310 | ``` 311 | 312 | ### -ClientSecret 313 | 314 | ```yaml 315 | Type: String 316 | Parameter Sets: ClientSecret 317 | Required: True 318 | Position: 7 319 | Description: Client secret for authentication 320 | ``` 321 | 322 | ### -ClientCertificateThumbprint 323 | 324 | ```yaml 325 | Type: String 326 | Parameter Sets: ClientCertificateThumbprint 327 | Required: True 328 | Position: 8 329 | Description: Client certificate thumbprint for authentication 330 | ``` 331 | 332 | ### -UseDeviceAuthentication 333 | 334 | ```yaml 335 | Type: Switch 336 | Parameter Sets: UseDeviceAuthentication 337 | Required: True 338 | Position: 9 339 | Description: Use device authentication for Microsoft Graph API 340 | ``` 341 | 342 | ### -RequiredScopes 343 | 344 | ```yaml 345 | Type: String[] 346 | Parameter Sets: (All) 347 | Required: False 348 | Position: 10 349 | Default value: Reports.Read.All 350 | Description: The scopes required for Microsoft Graph API access. Default is Reports.Read.All 351 | ``` 352 | 353 | ### -ModuleScope 354 | 355 | ```yaml 356 | Type: String 357 | Parameter Sets: (All) 358 | Required: False 359 | Position: 11 360 | Default value: CurrentUser 361 | Description: Specifies the scope for installing the module. Default is CurrentUser 362 | ``` 363 | 364 | ### -OverwriteRequestBodies 365 | 366 | ```yaml 367 | Type: Switch 368 | Parameter Sets: (All) 369 | Required: False 370 | Position: 12 371 | Description: Always overwrite existing JSON file 372 | ``` 373 | 374 | ### -MaxRetries 375 | 376 | ```yaml 377 | Type: Int 378 | Parameter Sets: (All) 379 | Required: False 380 | Position: 13 381 | Default value: 10 382 | Description: Number of retries for polling report status. Default is 10 383 | ``` 384 | 385 | ### -SecondsToWait 386 | 387 | ```yaml 388 | Type: Int 389 | Parameter Sets: (All) 390 | Required: False 391 | Position: 14 392 | Default value: 5 393 | Description: Seconds to wait between polling report status. Default is 5 394 | ``` 395 | 396 | ### -CleanupOldReports 397 | 398 | ```yaml 399 | Type: Switch 400 | Parameter Sets: (All) 401 | Required: False 402 | Position: 15 403 | Description: Cleanup old reports from the reports directory 404 | ``` 405 | 406 | ### -ReportNames 407 | 408 | ```yaml 409 | Type: String[] 410 | Parameter Sets: (All) 411 | Required: False 412 | Position: 16 413 | Description: List of report names to run 414 | ``` 415 | 416 | ### -LogId 417 | 418 | ```yaml 419 | Type: String 420 | Parameter Sets: (All) 421 | Required: False 422 | Position: 17 423 | Default value: $($MyInvocation.MyCommand).Name 424 | Description: Component name for logging 425 | ``` 426 | 427 | ### -ResetLog 428 | 429 | ```yaml 430 | Type: Switch 431 | Parameter Sets: (All) 432 | Required: False 433 | Position: 18 434 | Description: ResetLog: Pass this parameter to reset the log file 435 | ``` 436 | -------------------------------------------------------------------------------- /Intune/Invoke-BitLockerCompliance.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 21/8/2020 11:00 PM 5 | Created by: Maurice Daly 6 | Organization: CloudWay 7 | Filename: Invoke-BitLockerCompliance.ps1 8 | =========================================================================== 9 | .DESCRIPTION 10 | Enforce BitLocker compliance through detection and remediation functions 11 | #> 12 | 13 | function Write-LogEntry { 14 | param ( 15 | [parameter(Mandatory = $true, HelpMessage = "Value added to the log file.")] 16 | [ValidateNotNullOrEmpty()] 17 | [string]$Value, 18 | [parameter(Mandatory = $true, HelpMessage = "Severity for the log entry. 1 for Informational, 2 for Warning and 3 for Error.")] 19 | [ValidateNotNullOrEmpty()] 20 | [ValidateSet("1", "2", "3")] 21 | [string]$Severity, 22 | [parameter(Mandatory = $false, HelpMessage = "Name of the log file that the entry will written to.")] 23 | [ValidateNotNullOrEmpty()] 24 | [string]$FileName = "Invoke-BitLockerCompliance.log" 25 | ) 26 | # Determine log file location 27 | $LogFilePath = Join-Path -Path $env:WinDir -ChildPath "Temp\$FileName" 28 | 29 | # Construct time stamp for log entry 30 | $Time = -join @((Get-Date -Format "HH:mm:ss.fff"), " ", (Get-WmiObject -Class Win32_TimeZone | Select-Object -ExpandProperty Bias)) 31 | 32 | # Construct date for log entry 33 | $Date = (Get-Date -Format "MM-dd-yyyy") 34 | 35 | # Construct context for log entry 36 | $Context = $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name) 37 | 38 | # Construct final log entry 39 | $LogText = "" 40 | 41 | # Add value to log file 42 | try { 43 | Out-File -InputObject $LogText -Append -NoClobber -Encoding Default -FilePath $LogFilePath -ErrorAction Stop 44 | if ($Severity -eq 1) { 45 | Write-Verbose -Message $Value 46 | } elseif ($Severity -eq 3) { 47 | Write-Warning -Message $Value 48 | } 49 | } catch [System.Exception] { 50 | Write-Warning -Message "Unable to append log entry to InvokeBitLockerBackup.log file. Error message at line $($_.InvocationInfo.ScriptLineNumber): $($_.Exception.Message)" 51 | } 52 | } 53 | 54 | function Enforce-BitLocker { 55 | try { 56 | 57 | $ComputerSystemType = Get-WmiObject -Class "Win32_ComputerSystem" | Select-Object -ExpandProperty "Model" 58 | if ($ComputerSystemType -notin @("Virtual Machine", "VMware Virtual Platform", "VirtualBox", "HVM domU", "KVM", "VMWare7,1")) { 59 | 60 | # Check if machine is a member of a domain 61 | Write-LogEntry "Checking domain membership" -Severity 1 62 | $DomainMember = Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty PartOfDomain 63 | 64 | # Obtain BitLocker information 65 | Write-LogEntry "Obtaining BitLocker volume information for C:\" -Severity 1 66 | $BitLockerVolume = Get-BitLockerVolume -MountPoint C: | Select-Object -Property * 67 | 68 | # Obtain TPM information 69 | $TPMValues = Get-Tpm -ErrorAction SilentlyContinue | Select-Object -Property TPMReady, TPMPresent, TPMEnabled, TPMActivated, TPMOwned, ManagedAuthLevel 70 | 71 | if ($BitLockerVolume.VolumeStatus -match "encrypt" -and $BitLockerVolume.ProtectionStatus -eq "On") { 72 | Write-LogEntry "Drive encryption is in place. Starting backup process." -Severity 1 73 | 74 | # Set current protector to backup 75 | $CurrentProtector = 0 76 | $TotalProtectorKeys = $($BitLockerVolume.KeyProtector | Where-Object { 77 | $_.RecoveryPassword -gt $null 78 | }).Count 79 | $KeyProtectors = $($BitLockerVolume.KeyProtector | Where-Object { 80 | (-not ([string]::IsNullOrEmpty($_.RecoveryPassword))) 81 | }) 82 | Write-LogEntry "Backing up $TotalProtectorKeys recovey keys" -Severity 1 83 | 84 | foreach ($KeyProtector in $KeyProtectors) { 85 | if ($DomainMember -eq $true) { 86 | # Backup to Active Directory 87 | Write-LogEntry "Backing up recovery key $CurrentProtector to Active Directory" -Severity 1 88 | Write-LogEntry "Key protector ID is $($KeyProtector.KeyProtectorID)" -Severity 1 89 | Backup-BitLockerKeyProtector -MountPoint "C:" -KeyProtectorId $KeyProtector.KeyProtectorID -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | Out-Null 90 | } 91 | # Backup to Azure Active Directory 92 | Write-LogEntry "Backing up recovery key $CurrentProtector to Azure Active Directory" -Severity 1 93 | Write-LogEntry "Key protector ID is $($KeyProtector.KeyProtectorID)" -Severity 1 94 | BackupToAAD-BitLockerKeyProtector -MountPoint "C:" -KeyProtectorId $KeyProtector.KeyProtectorID | Out-Null 95 | $CurrentProtector++ 96 | } 97 | 98 | Write-LogEntry "Recovery key backup process complete." -Severity 1 99 | exit 0 100 | 101 | } else { 102 | Write-LogEntry "Drive is currently not protected by BitLocker. Starting remediation process.." -Severity 2 103 | if ($BitLockerVolume.EncryptionPercentage -ne '100' -and $BitLockerVolume.EncryptionPercentage -ne '0') { 104 | Write-LogEntry "BitLocked detected, drive is not fully encrypted. Finishing encryption process" -Severity 2 105 | Write-Output "BitLocked detected, drive is not fully encrypted. Finishing encryption process" 106 | Resume-BitLocker -MountPoint "C:" | Out-Null 107 | $ErrorMessage = "Failed to resume BitLocker protection" 108 | 109 | } elseif ($BitLockerVolume.VolumeStatus -eq 'FullyEncrypted' -and $BitLockerVolume.ProtectionStatus -eq 'Off') { 110 | Write-LogEntry "BitLocked detected, but disabled. Re-enabling BitLocker protection." -Severity 2 111 | Write-Output "BitLocked detected, but disabled. Re-enabling BitLocker protection." 112 | Enable-BitLocker -MountPoint "C:" -EncryptionMethod XtsAES256 -UsedSpaceOnly -SkipHardwareTest -TpmProtector | Out-Null 113 | Enable-BitLocker -MountPoint "C:" -EncryptionMethod XtsAES256 -UsedSpaceOnly -SkipHardwareTest -RecoveryPasswordProtector | Out-Null 114 | Resume-BitLocker -MountPoint "C:" | Out-Null 115 | $ErrorMessage = "Failed to resume BitLocker protection" 116 | 117 | } elseif ($BitLockerVolume.VolumeStatus -eq 'FullyDecrypted' -and $TPMValues.TPMEnabled -eq $true) { 118 | Write-LogEntry "BitLocked encryption not present. Setting to enabled with XTSAES256 encryption cipher specified." -Severity 2 119 | Write-Output "BitLocked encryption not present. Setting to enabled with XTSAES256 encryption cipher specified." 120 | Enable-BitLocker -MountPoint "C:" -EncryptionMethod XtsAES256 -UsedSpaceOnly -SkipHardwareTest -TpmProtector | Out-Null 121 | Enable-BitLocker -MountPoint "C:" -EncryptionMethod XtsAES256 -UsedSpaceOnly -SkipHardwareTest -RecoveryPasswordProtector | Out-Null 122 | $ErrorMessage = "Failed to enable BitLocker to encrypt drive using TPM protector" 123 | } 124 | 125 | Start-Sleep -Seconds 10 126 | $BitLockerVolume = Get-BitLockerVolume -MountPoint "C:" | Select-Object * 127 | if ($BitLockerVolume.ProtectionStatus -eq "On") { 128 | Write-LogEntry "BitLocker enabled on drive C:" -Severity 1 129 | Write-LogEntry "Backing up BitLocker encryption key" -Severity 1 130 | BackupToAAD-BitLockerKeyProtector -MountPoint "C:" -KeyProtectorId $BitLockerVolume.KeyProtector[1].KeyProtectorId 131 | # Refresh BitLocker drive information 132 | Write-LogEntry "BitLocker encryption method : $($BitLockerVolume.EncryptionMethod)" -Severity 1 133 | Write-LogEntry "BitLocker volume status : $($BitLockerVolume.VolumeStatus)" -Severity 1 134 | Write-LogEntry "BitLocker key protector types : $($BitLockerVolume.KeyProtector)" -Severity 1 135 | Write-LogEntry "Drive capacity : $($BitLockerVolume.CapacityGB)GB" -Severity 1 136 | Write-Output "BitLocker protection status OK"; exit 0 137 | } else { 138 | Write-LogEntry "$ErrorMessage" -Severity 3 139 | Write-Output "$ErrorMessage"; exit 1 140 | } 141 | } 142 | } else { 143 | Write-LogEntry "Virtual machine detected. Skipping BitLocker enforcement" -Severity 2 144 | Write-Output "Virtual machine detected. Skipping BitLocker enforcement"; exit 0 145 | } 146 | } catch { 147 | Write-LogEntry -Value "Issues occured during the key recovery backup process $($_.Exception.Message)" -Severity 3 148 | Write-Output "Issues occured during the key recovery backup process $($_.Exception.Message)"; exit 1 149 | } 150 | } 151 | Enforce-BitLocker 152 | 153 | 154 | -------------------------------------------------------------------------------- /Intune/Invoke-IntuneBackupDeviceConfiguration.txt: -------------------------------------------------------------------------------- 1 | 2 | Install-Module -Name IntuneBackupAndRestore 3 | 4 | Connect-MSGraph -ForceInteractive 5 | 6 | Invoke-IntuneBackupDeviceConfiguration 7 | Invoke-IntuneBackupConfigurationPolicy 8 | 9 | -------------------------------------------------------------------------------- /Intune/Invoke-RemoveBuiltinApps.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Remove built-in apps (modern apps) from Windows 10. 4 | .DESCRIPTION 5 | This script will remove all built-in apps with a provisioning package that's not specified in the 'white-list' in this script. 6 | It supports MDT and ConfigMgr usage, but only for online scenarios, meaning it can't be executed during the WinPE phase. 7 | For a more detailed list of applications available in each version of Windows 10, refer to the documentation here: 8 | https://docs.microsoft.com/en-us/windows/application-management/apps-in-windows-10 9 | .EXAMPLE 10 | .\Invoke-RemoveBuiltinApps.ps1 11 | .NOTES 12 | FileName: Invoke-RemoveBuiltinApps.ps1 13 | Author: Nickolaj Andersen 14 | Contact: @NickolajA 15 | Created: 2019-03-10 16 | Updated: 2021-02-02 17 | Version history: 18 | 1.0.0 - (2019-03-10) Initial script updated with help section and a fix for randomly freezing 19 | 1.1.0 - (2019-05-03) Added support for Windows 10 version 1903 (19H1) 20 | 1.1.1 - (2019-08-13) Removed the part where it was disabling/enabling configuration for Store updates, as it's not needed 21 | 1.1.2 - (2019-10-03) Removed unnecessary left over functions and updated catch statements so that they actually log the current app that could not be removed 22 | 1.2.0 - (2021-02-02) Added support for Windows 10 version 2004 (20H1) and 20H2 23 | #> 24 | Begin { 25 | # Black List of Features On Demand V2 packages 26 | $BlackListOnDemand = "NetFX3|DirectX|Tools.DeveloperMode.Core|Language|InternetExplorer|ContactSupport|OneCoreUAP|WindowsMediaPlayer|Hello.Face|Notepad|MSPaint|PowerShell.ISE|ShellComponents" 27 | 28 | # Black List of appx packages to keep installed 29 | $BlackListedApps = New-Object -TypeName System.Collections.ArrayList 30 | $BlackListedApps.AddRange(@( 31 | "Microsoft.DesktopAppInstaller", 32 | "Microsoft.Office.OneNote", 33 | "Microsoft.Messaging", 34 | "Microsoft.MSPaint", 35 | "Microsoft.Windows.Photos", 36 | "Microsoft.StorePurchaseApp", 37 | "Microsoft.MicrosoftOfficeHub", 38 | "Microsoft.MicrosoftStickyNotes", 39 | "Microsoft.WindowsAlarms", 40 | "Microsoft.WindowsCalculator", 41 | "Microsoft.WindowsCommunicationsApps", # Mail, Calendar etc 42 | "Microsoft.WindowsSoundRecorder", 43 | "Microsoft.WindowsStore" 44 | )) 45 | 46 | # Windows 10 version 1809 47 | $BlackListedApps.AddRange(@( 48 | "Microsoft.ScreenSketch", 49 | "Microsoft.HEIFImageExtension", 50 | "Microsoft.VP9VideoExtensions", 51 | "Microsoft.WebMediaExtensions", 52 | "Microsoft.WebpImageExtension" 53 | )) 54 | 55 | # Windows 10 version 1903 56 | # No new apps 57 | 58 | # Windows 10 version 1909 59 | $BlackListedApps.AddRange(@( 60 | "Microsoft.Outlook.DesktopIntegrationServicess" 61 | )) 62 | 63 | # Windows 10 version 2004 64 | $BlackListedApps.AddRange(@( 65 | "Microsoft.VCLibs.140.00" 66 | )) 67 | 68 | # Windows 10 version 20H2 69 | $BlackListedApps.AddRange(@( 70 | "Microsoft.MicrosoftEdge.Stable" 71 | )) 72 | } 73 | Process { 74 | # Functions 75 | function Write-LogEntry { 76 | param( 77 | [parameter(Mandatory=$true, HelpMessage="Value added to the RemovedApps.log file.")] 78 | [ValidateNotNullOrEmpty()] 79 | [string]$Value, 80 | 81 | [parameter(Mandatory=$false, HelpMessage="Name of the log file that the entry will written to.")] 82 | [ValidateNotNullOrEmpty()] 83 | [string]$FileName = "RemovedApps.log" 84 | ) 85 | # Determine log file location 86 | $LogFilePath = Join-Path -Path $env:windir -ChildPath "Temp\$($FileName)" 87 | 88 | # Add value to log file 89 | try { 90 | Out-File -InputObject $Value -Append -NoClobber -Encoding Default -FilePath $LogFilePath -ErrorAction Stop 91 | } 92 | catch [System.Exception] { 93 | Write-Warning -Message "Unable to append log entry to $($FileName) file" 94 | } 95 | } 96 | 97 | # Initial logging 98 | Write-LogEntry -Value "Starting built-in AppxPackage, AppxProvisioningPackage and Feature on Demand V2 removal process" 99 | 100 | # Determine provisioned apps 101 | $AppArrayList = Get-AppxProvisionedPackage -Online | Select-Object -ExpandProperty DisplayName 102 | 103 | # Loop through the list of appx packages 104 | foreach ($App in $AppArrayList) { 105 | Write-LogEntry -Value "Processing appx package: $($App)" 106 | 107 | # If application name not in appx package Black List, remove AppxPackage and AppxProvisioningPackage 108 | if (($App -in $BlackListedApps)) { 109 | Write-LogEntry -Value "Skipping excluded application package: $($App)" 110 | } 111 | else { 112 | # Gather package names 113 | $AppPackageFullName = Get-AppxPackage -Name $App | Select-Object -ExpandProperty PackageFullName -First 1 114 | $AppProvisioningPackageName = Get-AppxProvisionedPackage -Online | Where-Object { $_.DisplayName -like $App } | Select-Object -ExpandProperty PackageName -First 1 115 | 116 | # Attempt to remove AppxPackage 117 | if ($AppPackageFullName -ne $null) { 118 | try { 119 | Write-LogEntry -Value "Removing AppxPackage: $($AppPackageFullName)" 120 | Remove-AppxPackage -Package $AppPackageFullName -ErrorAction Stop | Out-Null 121 | } 122 | catch [System.Exception] { 123 | Write-LogEntry -Value "Removing AppxPackage '$($AppPackageFullName)' failed: $($_.Exception.Message)" 124 | } 125 | } 126 | else { 127 | Write-LogEntry -Value "Unable to locate AppxPackage for current app: $($App)" 128 | } 129 | 130 | # Attempt to remove AppxProvisioningPackage 131 | if ($AppProvisioningPackageName -ne $null) { 132 | try { 133 | Write-LogEntry -Value "Removing AppxProvisioningPackage: $($AppProvisioningPackageName)" 134 | Remove-AppxProvisionedPackage -PackageName $AppProvisioningPackageName -Online -ErrorAction Stop | Out-Null 135 | } 136 | catch [System.Exception] { 137 | Write-LogEntry -Value "Removing AppxProvisioningPackage '$($AppProvisioningPackageName)' failed: $($_.Exception.Message)" 138 | } 139 | } 140 | else { 141 | Write-LogEntry -Value "Unable to locate AppxProvisioningPackage for current app: $($App)" 142 | } 143 | } 144 | } 145 | 146 | Write-LogEntry -Value "Starting Features on Demand V2 removal process" 147 | 148 | # Get Features On Demand that should be removed 149 | try { 150 | $OSBuildNumber = Get-WmiObject -Class "Win32_OperatingSystem" | Select-Object -ExpandProperty BuildNumber 151 | 152 | # Handle cmdlet limitations for older OS builds 153 | if ($OSBuildNumber -le "16299") { 154 | $OnDemandFeatures = Get-WindowsCapability -Online -ErrorAction Stop | Where-Object { $_.Name -notmatch $BlackListOnDemand -and $_.State -like "Installed" } | Select-Object -ExpandProperty Name 155 | } 156 | else { 157 | $OnDemandFeatures = Get-WindowsCapability -Online -LimitAccess -ErrorAction Stop | Where-Object { $_.Name -notmatch $BlackListOnDemand -and $_.State -like "Installed" } | Select-Object -ExpandProperty Name 158 | } 159 | 160 | foreach ($Feature in $OnDemandFeatures) { 161 | try { 162 | Write-LogEntry -Value "Removing Feature on Demand V2 package: $($Feature)" 163 | 164 | # Handle cmdlet limitations for older OS builds 165 | if ($OSBuildNumber -le "16299") { 166 | Get-WindowsCapability -Online -ErrorAction Stop | Where-Object { $_.Name -like $Feature } | Remove-WindowsCapability -Online -ErrorAction Stop | Out-Null 167 | } 168 | else { 169 | Get-WindowsCapability -Online -LimitAccess -ErrorAction Stop | Where-Object { $_.Name -like $Feature } | Remove-WindowsCapability -Online -ErrorAction Stop | Out-Null 170 | } 171 | } 172 | catch [System.Exception] { 173 | Write-LogEntry -Value "Removing Feature on Demand V2 package failed: $($_.Exception.Message)" 174 | } 175 | } 176 | } 177 | catch [System.Exception] { 178 | Write-LogEntry -Value "Attempting to list Feature on Demand V2 packages failed: $($_.Exception.Message)" 179 | } 180 | 181 | # Complete 182 | Write-LogEntry -Value "Completed built-in AppxPackage, AppxProvisioningPackage and Feature on Demand V2 removal process" 183 | } -------------------------------------------------------------------------------- /Intune/Invoke-TenantSecurity.ps1: -------------------------------------------------------------------------------- 1 | ???<# 2 | .NOTES 3 | =========================================================================== 4 | Created with: SAPIEN Technologies, Inc., PowerShell Studio 2021 v5.8.188 5 | Created on: 31/05/2021 10:01 6 | Created by: MauriceDaly 7 | Organization: CloudWay 8 | Filename: Invoke-TenantSecurity.ps1 9 | =========================================================================== 10 | .DESCRIPTION 11 | Sets recommendations from the Microsoft Security Center 12 | #> 13 | 14 | # Enable 'Local Security Authority (LSA) protection 15 | New-ItemProperty -Path HKLM:\System\CurrentControlSet\Control\Lsa -Name "RunAsPPL" -Value 1 -PropertyType DWORD -Force 16 | 17 | #Setting registry key to block AAD Registration to 3rd party tenants. 18 | $RegistryLocation = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WorkplaceJoin\" 19 | $keyname = "BlockAADWorkplaceJoin" 20 | 21 | #Test if path exists and create if missing 22 | if (!(Test-Path -Path $RegistryLocation)) { 23 | Write-Output "Registry location missing. Creating" 24 | New-Item $RegistryLocation | Out-Null 25 | } 26 | 27 | #Force create key with value 1 28 | New-ItemProperty -Path $RegistryLocation -Name $keyname -PropertyType DWord -Value 1 -Force | Out-Null 29 | Write-Output "Registry key set" 30 | 31 | # Adobe Acrobat 32 | $AdobeRegKey = "HKLM:\SOFTWARE\Policies\Adobe" 33 | if (Test-Path -Path $AdobeRegKey -eq $true) { 34 | if (Test-Path -Path "HKLM:\SOFTWARE\Policies\Adobe\Acrobat Reader\DC\FeatureLockDown" -eq $true) { 35 | New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Adobe\Acrobat Reader\DC\FeatureLockDown" -Name "bDisableJavaScript" -PropertyType DWord -Value 1 -Force | Out-Null 36 | New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Adobe\Acrobat Reader\DC\FeatureLockDown" -Name "bEnableFlash" -PropertyType DWord -Value 0 -Force | Out-Null 37 | } 38 | if (Test-Path -Path "HKLM\SOFTWARE\Policies\Adobe\Adobe Acrobat\DC\FeatureLockDown" -eq $true) { 39 | New-ItemProperty -Path "HKLM\SOFTWARE\Policies\Adobe\Adobe Acrobat\DC\FeatureLockDown" -Name "bDisableJavaScript" -PropertyType DWord -Value 0 -Force | Out-Null 40 | 41 | } 42 | } 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Intune/Remove-Appx.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Remove built-in apps (modern apps) from Windows 10. 4 | .DESCRIPTION 5 | This script will remove all built-in apps with a provisioning package that are specified in the 'black-list' in this script. 6 | .EXAMPLE 7 | .\Remove-Appx.ps1 8 | .NOTES 9 | 10 | Based on original script / Credit to:- 11 | FileName: Invoke-RemoveBuiltinApps.ps1 12 | Author: Nickolaj Andersen 13 | 14 | Modifications to original script to Black list Appx instead of Whitelist 15 | 16 | FileName: Remove-Apps.ps1 17 | Author: Ben Whitmore 18 | Contact: @byteben 19 | 20 | ################################ 21 | Appx Packages as of 27/08/2021 22 | ################################ 23 | 24 | Full Package List: https://docs.microsoft.com/en-us/windows/application-management/apps-in-windows-10 25 | 26 | ## 1809 - 21H1 ## 27 | Microsoft.BingWeather 28 | Microsoft.DesktopAppInstaller 29 | Microsoft.GetHelp 30 | Microsoft.GetStarted 31 | Microsoft.HEIFImageExtension 32 | Microsoft.Microsoft3DViewer 33 | Microsoft.MicrosoftOfficeHub 34 | Microsoft.MicrosoftSolitaireCollection 35 | Microsoft.MicrosoftStickyNotes 36 | Microsoft.MixedReality.Portal 37 | Microsoft.MSPaint 38 | Microsoft.Office.OneNote 39 | Microsoft.People 40 | Microsoft.ScreenSketch 41 | Microsoft.SkypeApp 42 | Microsoft.StorePurchaseApp 43 | Microsoft.VP9VideoExtensions 44 | Microsoft.Wallet 45 | Microsoft.WebMediaExtensions 46 | Microsoft.Windows.Photos 47 | Microsoft.WindowsAlarms 48 | Microsoft.WindowsCalculator 49 | Microsoft.WindowsCamera 50 | Microsoft.WindowsCommunicationsApps 51 | Microsoft.WindowsFeedbackHub 52 | Microsoft.WindowsMaps 53 | Microsoft.WindowsSoundRecorder 54 | Microsoft.WindowsStore 55 | Microsoft.Xbox.TCUI 56 | Microsoft.XboxApp 57 | Microsoft.XboxGameOverlay 58 | Microsoft.XboxGamingOverlay 59 | Microsoft.XboxIdentityProvider 60 | Microsoft.XboxSpeechToTextOverlay 61 | Microsoft.YourPhone 62 | Microsoft.ZuneMusic 63 | Microsoft.ZuneVideo 64 | 65 | ## 1809, 1903, 1909, 20H2, 21H1 ## 66 | Microsoft.Messaging 67 | Microsoft.OneConnect 68 | Microsoft.Print3D 69 | 70 | ## 1909, 20H2, 21H1## 71 | Microsoft.Outlook.DesktopIntegrationServices 72 | 73 | ## 21H1 ## 74 | Microsoft.3DBuilder 75 | #> 76 | Begin { 77 | 78 | # Black list of appx packages to remove 79 | $BlackListedApps = New-Object -TypeName System.Collections.ArrayList 80 | $BlackListedApps.AddRange(@( 81 | "Microsoft.GetHelp", 82 | "Microsoft.GetStarted", 83 | "Microsoft.MicrosoftOfficeHub", 84 | "Microsoft.MixedReality.Portal", 85 | "Microsoft.SkypeApp", 86 | "Microsoft.WindowsFeedbackHub", 87 | "Microsoft.XboxApp", 88 | "Microsoft.XboxGameOverlay", 89 | "Microsoft.XboxGamingOverlay", 90 | "Microsoft.YourPhone", 91 | "Microsoft.ZuneMusic", 92 | "Microsoft.ZuneVideo" 93 | )) 94 | } 95 | Process { 96 | # Functions 97 | function Write-LogEntry { 98 | param( 99 | [parameter(Mandatory=$true, HelpMessage="Value added to the RemovedApps.log file.")] 100 | [ValidateNotNullOrEmpty()] 101 | [string]$Value, 102 | 103 | [parameter(Mandatory=$false, HelpMessage="Name of the log file that the entry will written to.")] 104 | [ValidateNotNullOrEmpty()] 105 | [string]$FileName = "RemovedApps.log" 106 | ) 107 | # Determine log file location 108 | $LogFilePath = Join-Path -Path $env:windir -ChildPath "Temp\$($FileName)" 109 | 110 | # Add value to log file 111 | try { 112 | Out-File -InputObject $Value -Append -NoClobber -Encoding Default -FilePath $LogFilePath -ErrorAction Stop 113 | } 114 | catch [System.Exception] { 115 | Write-Warning -Message "Unable to append log entry to $($FileName) file" 116 | } 117 | } 118 | 119 | # Initial logging 120 | Write-LogEntry -Value "Starting built-in AppxPackage, AppxProvisioningPackage and Feature on Demand V2 removal process" 121 | 122 | # Determine provisioned apps 123 | $AppArrayList = Get-AppxProvisionedPackage -Online | Select-Object -ExpandProperty DisplayName 124 | 125 | # Loop through the list of appx packages 126 | foreach ($App in $AppArrayList) { 127 | Write-LogEntry -Value "Processing appx package: $($App)" 128 | 129 | # If application name not in appx package black list, remove AppxPackage and AppxProvisioningPackage 130 | if (($App -Notin $BlackListedApps)) { 131 | Write-LogEntry -Value "Skipping excluded application package: $($App)" 132 | } 133 | else { 134 | # Gather package names 135 | $AppPackageFullName = Get-AppxPackage -Name $App | Select-Object -ExpandProperty PackageFullName -First 1 136 | $AppProvisioningPackageName = Get-AppxProvisionedPackage -Online | Where-Object { $_.DisplayName -like $App } | Select-Object -ExpandProperty PackageName -First 1 137 | 138 | # Attempt to remove AppxPackage 139 | if ($AppPackageFullName -ne $null) { 140 | try { 141 | Write-LogEntry -Value "Removing AppxPackage: $($AppPackageFullName)" 142 | Remove-AppxPackage -Package $AppPackageFullName -ErrorAction Stop | Out-Null 143 | } 144 | catch [System.Exception] { 145 | Write-LogEntry -Value "Removing AppxPackage '$($AppPackageFullName)' failed: $($_.Exception.Message)" 146 | } 147 | } 148 | else { 149 | Write-LogEntry -Value "Unable to locate AppxPackage for current app: $($App)" 150 | } 151 | 152 | # Attempt to remove AppxProvisioningPackage 153 | if ($AppProvisioningPackageName -ne $null) { 154 | try { 155 | Write-LogEntry -Value "Removing AppxProvisioningPackage: $($AppProvisioningPackageName)" 156 | Remove-AppxProvisionedPackage -PackageName $AppProvisioningPackageName -Online -ErrorAction Stop | Out-Null 157 | } 158 | catch [System.Exception] { 159 | Write-LogEntry -Value "Removing AppxProvisioningPackage '$($AppProvisioningPackageName)' failed: $($_.Exception.Message)" 160 | } 161 | } 162 | else { 163 | Write-LogEntry -Value "Unable to locate AppxProvisioningPackage for current app: $($App)" 164 | } 165 | } 166 | } 167 | 168 | # Complete 169 | Write-LogEntry -Value "Completed built-in AppxPackage, AppxProvisioningPackage and Feature on Demand V2 removal process" 170 | } -------------------------------------------------------------------------------- /Intune/Remove-AppxUserPackages.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created with: SAPIEN Technologies, Inc., PowerShell Studio 2021 v5.8.188 5 | Created on: 24/05/2021 15:46 6 | Created by: MauriceDaly 7 | Organization: CloudWay 8 | Filename: Remove-AppxUserPackages.ps1 9 | =========================================================================== 10 | .DESCRIPTION 11 | Removes unwanted Appx packages 12 | #> 13 | 14 | $BlackListedUserApps = New-Object -TypeName System.Collections.ArrayList 15 | $BlackListedUserApps.AddRange(@( 16 | "Microsoft.BingWeather", 17 | "Microsoft.Xbox", 18 | "Microsoft.People", 19 | "Microsoft.Microsoft3DViewer", 20 | "Microsoft.OfficeHub", 21 | "Microsoft.SolitaireCollection", 22 | "Microsoft.Microsoft3DViewer", 23 | "Microsoft.SkypeApp", 24 | "Microsoft.WindowsCommunicationsApps" 25 | )) 26 | 27 | $AppArrayList = Get-AppxProvisionedPackage -Online | Select-Object -ExpandProperty DisplayName 28 | foreach ($App in $BlackListedUserApps) { 29 | if (($AppArrayList -match $App)) { 30 | Get-AppxPackage -AllUsers | Where-Object {$_.Name -match $App } | Remove-AppxPackage -ErrorAction SilentlyContinue 31 | } 32 | } 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Intune/Remove-Appx_Detection.ps1: -------------------------------------------------------------------------------- 1 | 2 | Begin { 3 | 4 | $BlackListedApps = New-Object -TypeName System.Collections.ArrayList 5 | $BlackListedApps.AddRange(@( 6 | "Microsoft.GetHelp", 7 | "Microsoft.GetStarted", 8 | "Microsoft.MicrosoftOfficeHub", 9 | "Microsoft.MixedReality.Portal", 10 | "Microsoft.SkypeApp", 11 | "Microsoft.WindowsFeedbackHub", 12 | "Microsoft.XboxApp", 13 | "Microsoft.XboxGameOverlay", 14 | "Microsoft.XboxGamingOverlay", 15 | "Microsoft.YourPhone", 16 | "Microsoft.ZuneMusic", 17 | "Microsoft.ZuneVideo" 18 | )) 19 | } 20 | 21 | Process { 22 | 23 | $AppArrayList = Get-AppxProvisionedPackage -Online | Select-Object -ExpandProperty DisplayName 24 | 25 | foreach ($App in $AppArrayList) { 26 | if (($App -in $BlackListedApps)) { 27 | $AppExists = $True 28 | } 29 | } 30 | 31 | If ($AppExists) { 32 | Write-Output "All appx packages were not removed" 33 | Exit 1 34 | } 35 | else { 36 | Write-Output "All appx packages were removed" 37 | Exit 0 38 | } 39 | } -------------------------------------------------------------------------------- /Intune/Remove-DirectAccess.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Created on: 10/08/2021 4 | Created by: Ben Whitmore @ CloudWay 5 | Filename: Remove-DirectAccess.ps1 6 | 7 | .Description 8 | Script to remove Direct Access client settings 9 | #> 10 | 11 | $Path = 'HKLM:\Software\Policies\Microsoft\Windows NT\DNSClient\DnsPolicyConfig' 12 | If (Test-Path $Path) { 13 | Write-Output "Direct Access DnsPolicyConfig registry key found.." 14 | Try { 15 | Write-Output "Attempting to delete Direct Access DnsPolicyConfig registry key.." 16 | Get-Item -Path $Path | Remove-Item -Recurse -Confirm:$False 17 | If (Test-Path $Path) { 18 | Write-Output "There was a problem removing the registry key" 19 | Exit 1 20 | } 21 | else { 22 | Write-Output "Direct Access DnsPolicyConfig registry key deleted successfully" 23 | Exit 0 24 | } 25 | } 26 | Catch { 27 | Write-Output "There was a problem removing the registry key" 28 | Exit 1 29 | } 30 | } 31 | else { 32 | Write-Output "Direct Access DnsPolicyConfig registry key not found" 33 | Exit 0 34 | } -------------------------------------------------------------------------------- /Intune/Remove-DirectAccess_Detect.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Created on: 10/08/2021 4 | Created by: Ben Whitmore @ CloudWay 5 | Filename: Remove-DirectAccess_Detect.ps1 6 | 7 | .Description 8 | Script to remove Direct Access client settings 9 | #> 10 | 11 | $Path = 'HKLM:\Software\Policies\Microsoft\Windows NT\DNSClient\DnsPolicyConfig' 12 | If (Test-Path $Path) { 13 | Write-Output "Direct Access DnsPolicyConfig registry key found. Return 1" 14 | #Exit 1 15 | } 16 | else { 17 | Write-Output "Direct Access DnsPolicyConfig registry key not found. Return 0" 18 | #Exit 0 19 | } -------------------------------------------------------------------------------- /Intune/Remove-WorkplaceUserJoinRegistration.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | [Parameter(Mandatory = $False)] 3 | [Switch]$Remove, 4 | [Parameter(Mandatory = $False)] 5 | [String[]] $Domains = @("tawkr.agency", "appcogroupnederland.com") 6 | ) 7 | 8 | Function Get-WorkplaceJoinUserReg { 9 | $RegPath = "HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WorkplaceJoin\JoinInfo" 10 | 11 | Try { 12 | $RegKeys = (Get-ChildItem $RegPath).PSChildName 13 | } 14 | Catch { 15 | Write-Warning "Unable to get corresponding Registry Keys for Workplace Join User Registrations" 16 | } 17 | 18 | If ($RegKeys) { 19 | $Keys = @() 20 | Foreach ($Reg in $Regkeys) { 21 | $Tenant = $Null 22 | Try { 23 | $WorkPlaceReg = Join-Path -Path $RegPath -Childpath $Reg 24 | Foreach ($Domain in $Domains) { 25 | $Tenant = Get-ItemProperty $WorkPlaceReg | Select-Object -ExpandProperty Useremail | Where-Object { $_ -like "*$Domain" } -ErrorAction SilentlyContinue 26 | If ($tenant) { 27 | If (!($Remove)) { 28 | Write-Host "Workplace Join User Registration Registry key found for $Tenant and ID $Reg" 29 | } 30 | $Keys += $Reg 31 | 32 | } 33 | } 34 | } 35 | Catch { 36 | Write-Warning "Unable to get corresponding Registry Keys for Workplace Join User Registrations" 37 | } 38 | } 39 | } 40 | 41 | Return $Keys 42 | 43 | } 44 | 45 | Function Get-WorkplaceJoinUserCert { 46 | $Registrations = Get-WorkplaceJoinUserReg 47 | $Certs = @() 48 | Foreach ($Registration in $Registrations) { 49 | Try { 50 | $AAD_Cert = Get-ChildItem -path Cert:\CurrentUser\My | Where-Object { $_.ThumbPrint -eq $Registration } -ErrorAction SilentlyContinue 51 | If ($AAD_Cert) { 52 | If (!($Remove)) { 53 | Write-Host "Found AAD Workplace Join User Certificate with matching ThumbPrint ""$($Registration)""" 54 | } 55 | $Certs += $Registration 56 | } 57 | else { 58 | Write-Warning "No AAD Workplace Join User Certificate found with matching ThumbPrint ""$($Registration)""" 59 | } 60 | } 61 | Catch { 62 | Write-Warning "Unable to find Certificate with Thumbprint ""$($Registration)""" 63 | } 64 | } 65 | 66 | Return $certs 67 | } 68 | 69 | Function Get-WorkplaceJoinUserInfo { 70 | $Info = Get-WorkplaceJoinUserCert 71 | } 72 | 73 | Function Remove-WorkplaceJoinUserRegistration { 74 | 75 | $CertsToRemove = Get-WorkplaceJoinUserCert 76 | Foreach ($CertToRemove in $CertsToRemove) { 77 | Try { 78 | $AAD_Cert = Get-ChildItem -path Cert:\CurrentUser\My | Where-Object { $_.ThumbPrint -eq $Registration } 79 | Write-Host "Deleting AAD Workplace Join User Certificate with matching ThumbPrint ""$($Registration)""" 80 | $AAD_Cert | Remove-Item -Force -Recurse -ErrorAction SilentlyContinue 81 | Write-Host "Done" 82 | } 83 | Catch { 84 | Write-Warning "Unable to find Certificate with Thumbprint ""$($Registration)""" 85 | } 86 | } 87 | 88 | $RegsToRemove = Get-WorkplaceJoinUserReg 89 | 90 | $RegPath = "HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WorkplaceJoin\JoinInfo" 91 | 92 | Foreach ($RegToRemove in $RegsToRemove) { 93 | Try { 94 | $WorkPlaceReg = Join-Path -Path $RegPath -Childpath $RegToRemove 95 | Write-Host "Deleting AAD Workplace Join User Registry Key ""$($WorkPlaceReg)""" 96 | Remove-Item -Path $WorkPlaceReg -Recurse -Force -ErrorAction SilentlyContinue 97 | Write-Host "Done" 98 | } 99 | Catch { 100 | Write-Warning "Error Removing Registry Key ""$WorkPlaceReg""" 101 | 102 | } 103 | } 104 | } 105 | 106 | If ($Remove) { 107 | Remove-WorkplaceJoinUserRegistration 108 | } 109 | else { 110 | Get-WorkplaceJoinUserInfo 111 | } -------------------------------------------------------------------------------- /Intune/Test-UpdateCompliance.ps1: -------------------------------------------------------------------------------- 1 | $global:VortexURL = "https://v10c.events.data.microsoft.com/ping" 2 | $global:operatingSystemName = (Get-WmiObject Win32_OperatingSystem).Name 3 | 4 | function CheckMSAService { 5 | Try { 6 | $serviceInfo = Get-WmiObject win32_service -Filter "Name='wlidsvc'" 7 | $serviceStartMode = $serviceInfo.StartMode 8 | $serviceState = $serviceInfo.State 9 | 10 | if ($serviceStartMode.ToString().ToLower() -eq "disabled") { 11 | Write-Output "CheckMSAService Failed: Microsoft Account Sign In Assistant Service is Disabled." 12 | Exit 1 13 | } 14 | else { 15 | $isManualTriggeredStart = $false 16 | 17 | if ($serviceStartMode.ToString().TOLower() -eq "manual") { 18 | if (Test-Path -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\wlidsvc\TriggerInfo\') { 19 | $Result_CheckMSAService = "CheckMSAService: Passed. Microsoft Account Sign In Assistant Service is Manual(Triggered Start)." 20 | $Exit_CheckMSAService = 0 21 | $isManualTriggeredStart = $true 22 | } 23 | } 24 | 25 | if ($isManualTriggeredStart -eq $false) { 26 | Write-Output"CheckMSAService: Failed: Microsoft Account Sign In Assistant Service is not running." 27 | Exit 1 28 | } 29 | } 30 | } 31 | Catch { 32 | Write-Output "CheckMSAService: Failed. Exception. $($_.Exception.HResult) $($_.Exception.Message)" 33 | Exit 1 34 | } 35 | } 36 | 37 | function CheckUtcCsp { 38 | 39 | Try { 40 | #Check the WMI-CSP bridge (must be local system) 41 | $ClassName = "MDM_Win32CompatibilityAppraiser_UniversalTelemetryClient01" 42 | $BridgeNamespace = "root\cimv2\mdm\dmmap" 43 | $FieldName = "UtcConnectionReport" 44 | 45 | $CspInstance = get-ciminstance -Namespace $BridgeNamespace -ClassName $ClassName 46 | 47 | $Data = $CspInstance.$FieldName 48 | 49 | #Parse XML data to extract the DataUploaded field. 50 | $XmlData = [xml]$Data 51 | 52 | if (0 -eq $XmlData.ConnectionReport.ConnectionSummary.DataUploaded) { 53 | Write-Output "CheckUtcCsp: Failed. The only recent data uploads all failed." 54 | Exit 1 55 | } 56 | else { 57 | Write-Output "CheckUtcCsp: Passed" 58 | } 59 | } 60 | Catch { 61 | Write-Output "CheckUtcCsp: Failed. Exception. $($_.Exception.HResult) $($_.Exception.Message)" 62 | Exit 1 63 | } 64 | } 65 | 66 | function CheckVortexConnectivity { 67 | 68 | Try { 69 | 70 | $Request = [System.Net.WebRequest]::Create($VortexURL) 71 | $Response = $Request.getResponse() 72 | 73 | If ($Response.StatusCode -eq 'OK') { 74 | Write-Output "CheckVortexConnectivity: Passed. URL $VortexURL is accessible." 75 | } 76 | Else { 77 | Write-Output "CheckVortexConnectivity: Failed. URL $VortexURL not accessible." 78 | Exit 1 79 | 80 | } 81 | } 82 | Catch { 83 | Write-Output "CheckVortexConnectivity failed with unexpected exception. CheckVortexConnectivity. $($_.Exception.HResult) $($_.Exception.Message)" 84 | Exit 1 85 | } 86 | } 87 | 88 | function CheckTelemetryOptIn { 89 | 90 | $vCommercialIDPathPri1 = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection" 91 | $vCommercialIDPathPri2 = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection" 92 | 93 | Try { 94 | if (($global:operatingSystemName.ToLower() -match ("^(.*?(windows )[1][0-1]*?)")) -eq $true) { 95 | $allowTelemetryPropertyPri1 = (Get-ItemProperty -Path $vCommercialIDPathPri1 -Name AllowTelemetry -ErrorAction SilentlyContinue).AllowTelemetry 96 | $allowTelemetryPropertyPri2 = (Get-ItemProperty -Path $vCommercialIDPathPri2 -Name AllowTelemetry -ErrorAction SilentlyContinue).AllowTelemetry 97 | 98 | if ($allowTelemetryPropertyPri1 -ne $null) { 99 | Write-Output "CheckTelemetryOptIn: Passed. AllowTelemetry property value at registry key path $vCommercialIDPathPri1 : $allowTelemetryPropertyPri1" 100 | 101 | $allowTelemetryPropertyType1 = (Get-ItemProperty -Path $vCommercialIDPathPri1 -Name AllowTelemetry -ErrorAction SilentlyContinue).AllowTelemetry.gettype().Name 102 | if ($allowTelemetryPropertyType1 -ne "Int32") { 103 | Write-Output "AllowTelemetry property value at registry key path $vCommercialIDPathPri1 is not of type REG_DWORD. It should be of type REG_DWORD." 104 | Exit 1 105 | } 106 | 107 | if (-not ([int]$allowTelemetryPropertyPri1 -ge 1 -and [int]$allowTelemetryPropertyPri1 -le 3)) { 108 | Write-Output "Please set the Windows telemetry level (AllowTelemetry property) to Basic (1) or above at path $vCommercialIDPathPri1. Please check https://aka.ms/uc-enrollment for more information." 109 | Exit 1 110 | } 111 | } 112 | 113 | if ($allowTelemetryPropertyPri2 -ne $null) { 114 | Write-Output "CheckTelemetryOptIn: Passed. AllowTelemetry property value at registry key path $vCommercialIDPathPri2 : $allowTelemetryPropertyPri2" 115 | 116 | $allowTelemetryPropertyType2 = (Get-ItemProperty -Path $vCommercialIDPathPri2 -Name AllowTelemetry -ErrorAction SilentlyContinue).AllowTelemetry.gettype().Name 117 | if ($allowTelemetryPropertyType2 -ne "Int32") { 118 | Write-Output "AllowTelemetry property value at registry key path $vCommercialIDPathPri2 is not of type REG_DWORD. It should be of type REG_DWORD." 119 | Exit 1 120 | } 121 | 122 | if (-not ([int]$allowTelemetryPropertyPri2 -ge 1 -and [int]$allowTelemetryPropertyPri2 -le 3)) { 123 | Write-Output "Please set the Windows telemetry level (AllowTelemetry property) to Basic (1) or above at path $vCommercialIDPathPri2. Check https://aka.ms/uc-enrollment for more information." 124 | Exit 1 125 | } 126 | } 127 | } 128 | else { 129 | Write-Output "Device must be Windows 10 or 11 to use Update Compliance." 130 | Exit 1 131 | } 132 | } 133 | Catch { 134 | Write-Output "CheckTelemetryOptIn failed with unexpected exception. $($_.Exception.HResult) $($_.Exception.Messag)" 135 | Exit 1 136 | } 137 | } 138 | 139 | function CheckCommercialId { 140 | Try { 141 | Write-Host "Start: CheckCommercialId" 142 | 143 | if (($commercialIDValue -eq $null) -or ($commercialIDValue -eq [string]::Empty)) { 144 | Write-Host "The commercialID parameter is incorrect. Please edit runConfig.bat and set the CommercialIDValue and rerun the script" "Error" "6" "SetupCommercialId" 145 | Write-Host "Script finished with error(s)" "Failure" "$global:errorCode" "ScriptEnd" 146 | [System.Environment]::Exit($global:errorCode) 147 | } 148 | 149 | [System.Guid]::Parse($commercialIDValue) | Out-Null 150 | 151 | } 152 | Catch { 153 | If (($commercialIDValue -match ("^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$")) -eq $false) { 154 | Write-Host "CommercialID mentioned in RunConfig.bat should be a GUID. It currently set to '$commercialIDValue'" "Error" "48" "CheckCommercialId" 155 | Write-Host "Script finished with error(s)" "Failure" "$global:errorCode" "ScriptEnd" 156 | [System.Environment]::Exit($global:errorCode) 157 | } 158 | } 159 | 160 | Write-Host "Passed: CheckCommercialId" 161 | } 162 | 163 | CheckMSAService 164 | CheckUtcCsp 165 | CheckVortexConnectivity 166 | CheckTelemetryOptIn -------------------------------------------------------------------------------- /Intune/TestScript.ps1: -------------------------------------------------------------------------------- 1 | Try { 2 | $folderContent = Get-ChildItem - Path 'C:\IDontExist' -ErrorAction SilentlyContinue 3 | } 4 | Catch { 5 | #Dont tell a soul 6 | } 7 | If (-not $folderContent) { 8 | Try { 9 | New-Item -Path 'C:\IDontExist' -ItemType 'Directory' -Force 10 | } 11 | Catch { 12 | #I failed miserably 13 | } 14 | } 15 | 16 | 17 | 18 | $newShell = New-Object -ComObject Shell.Application 19 | $newShell.open("intunemanagementextension://syncapp") 20 | -------------------------------------------------------------------------------- /Intune/Windows_AntiMalware_Audit .ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 11/01/2021 8:00 PM 5 | Created by: Maurice Daly 6 | Organization: CloudWay 7 | Filename: Invoke-WinAVAudit.ps1 8 | =========================================================================== 9 | .DESCRIPTION 10 | Gather Windows antimalware primary product 11 | #> 12 | 13 | # Obtain Antivirus information from WMI 14 | $AntivirusProduct = Get-WmiObject -Namespace "root\SecurityCenter2" -Query "SELECT * FROM AntiVirusProduct" -ErrorAction 'SilentlyContinue' 15 | # Check for returned values, if null, write output and exit 1 16 | if ($AntiVirusProduct -gt $null) { 17 | # Check for antivirus display name value, if null, write output and exit 1 18 | if (-not ([string]::IsNullOrEmpty($($AntivirusProduct.Displayname)))) { 19 | # Write antivirus product name out for proactive remediations display purposes and set exit success 20 | Write-Output $AntivirusProduct.displayName 21 | Exit 0 22 | } else { 23 | Write-Output "Antivirus product not found" 24 | Exit 1 25 | } 26 | } else { 27 | Write-Output "Could not query root\SecurityCenter2 WMI namespace" 28 | Exit 1 29 | } -------------------------------------------------------------------------------- /MBAM_Update.ps1: -------------------------------------------------------------------------------- 1 | #Set Current Directory 2 | $ScriptPath = $MyInvocation.MyCommand.Path 3 | $CurrentDir = Split-Path $ScriptPath 4 | 5 | #Set Variables 6 | $MDOPUpdateRequired = $False 7 | $MDOPMSI = Join-Path $CurrentDir "MbamClientSetup-2.5.1100.0.msi" 8 | $MDOPMSP = Join-Path $CurrentDir "MBAM2.5_Client_x64_KB4586232.msp" 9 | $MDOPVersion = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{AEC5BCA3-A2C5-46D7-9873-7698E6D3CAA4}" -ErrorAction SilentlyContinue).DisplayVersion 10 | 11 | If ($MDOPVersion) { 12 | 13 | Switch ($MDOPVersion) { 14 | "2.5.1100.0" { 15 | Write-Output "MDOP is installed, the version is $MDOPVersion, 2.5SP1 Initial Package" 16 | $MDOPUpdateRequired = $True 17 | } 18 | 19 | "2.5.1126.0" { 20 | Write-Output "MDOP is installed, the version is $MDOPVersion, 2.5SP1 December 2016 Package" 21 | $MDOPUpdateRequired = $True 22 | } 23 | 24 | "2.5.1133.0" { 25 | Write-Output "MDOP is installed, the version is $MDOPVersion, 2.5SP1 March 2017 Package" 26 | $MDOPUpdateRequired = $True 27 | } 28 | 29 | "2.5.1134.0" { 30 | Write-Output "MDOP is installed, the version is $MDOPVersion, 2.5SP1 June 2017 Package" 31 | $MDOPUpdateRequired = $True 32 | } 33 | 34 | "2.5.1143.0" { 35 | Write-Output "MDOP is installed, the version is $MDOPVersion, 2.5SP1 July 2018 Package" 36 | $MDOPUpdateRequired = $True 37 | } 38 | 39 | "2.5.1147.0" { 40 | Write-Output "MDOP is installed, the version is $MDOPVersion, 2.5SP1 March 2019 Package" 41 | $MDOPUpdateRequired = $True 42 | } 43 | 44 | "2.5.1152.0" { 45 | Write-Output "MDOP is installed, the version is $MDOPVersion, 2.5SP1 October 2020 Package" 46 | $MDOPUpdateRequired = $False 47 | } 48 | 49 | Default { 50 | Write-Warning "MDOP Version could not be read" 51 | Exit 1 52 | } 53 | } 54 | } 55 | else { 56 | 57 | Write-Output "MDOP Doesnt Exist. Installing MDOP 2.5SP1 Base 2.5.1100.0...." 58 | 59 | #Install MDOP 2.5SP1 Base 2.5.1100.0 60 | $Args = @( 61 | "/i" 62 | """$MDOPMSI""" 63 | "/qn" 64 | ) 65 | Start-Process msiexec -ArgumentList $Args -Wait -ErrorAction Stop | Out-Null 66 | $MDOPUpdateRequired = $True 67 | } 68 | 69 | If ($MDOPUpdateRequired -eq $True) { 70 | 71 | Write-Output "Installing MDOP 2.5SP1 October 2020 Package 2.5.1152.0...." 72 | 73 | #Install MDOP 2.5SP1 October 2020 Patch 74 | $Args = @( 75 | "/p" 76 | """$MDOPMSP""" 77 | "/qn" 78 | ) 79 | Start-Process msiexec -ArgumentList $Args -Wait -ErrorAction Stop | Out-Null 80 | } -------------------------------------------------------------------------------- /PRs/Detect-JavaApps.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Detection Script for unsupported applications 4 | .DESCRIPTION 5 | This script is designed to be run as a Proactive Remediation. 6 | The BadApps array contains application names that are considered unsupported by the company. 7 | If no BadApps are found, the exit code of the script is 0 (Do not remediate). If BadApps are found, the exit code is 1 (Remediate) 8 | .EXAMPLE 9 | Detect-JavaApps.ps1 (Run in the User Context) 10 | .NOTES 11 | FileName: Detect-JavaApps.ps1 12 | Author: Ben Whitmore 13 | Contributor: Jan Ketil Skanke 14 | Contact: @byteben 15 | Created: 2022-11-Mar 16 | 17 | Version history: 18 | 2.0.0 - (2022-03-01) Split Detection/Remeiation scripts and used Invoke-WebRequest instead of WebClient .NET http download 19 | #> 20 | 21 | #region SCRIPTVARIABLES 22 | $BadApps = @( 23 | "Java 6" 24 | "Java SE Development Kit 6" 25 | "Java(TM) SE Development Kit 6" 26 | "Java(TM) 6" 27 | "Java 7" 28 | "Java SE Development Kit 17" 29 | "Java(TM) SE Development Kit 17" 30 | "Java(TM) 7" 31 | ) 32 | #endregion 33 | 34 | # Function to get all Installed Applications 35 | function Get-InstalledApplications() { 36 | param( 37 | [string]$UserSid 38 | ) 39 | 40 | New-PSDrive -PSProvider Registry -Name "HKU" -Root HKEY_USERS | Out-Null 41 | $regpath = @("HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*") 42 | $regpath += "HKU:\$UserSid\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" 43 | if (-not ([IntPtr]::Size -eq 4)) { 44 | $regpath += "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" 45 | $regpath += "HKU:\$UserSid\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" 46 | } 47 | $PropertyNames = 'DisplayName', 'DisplayVersion', 'Publisher', 'UninstallString' 48 | $Apps = Get-ItemProperty $regpath -Name $PropertyNames -ErrorAction SilentlyContinue | . { process { if ($_.DisplayName) { $_ } } } | Select-Object DisplayName, DisplayVersion, Publisher | Sort-Object DisplayName 49 | Remove-PSDrive -Name "HKU" | Out-Null 50 | Return $Apps 51 | } 52 | #end function 53 | 54 | #region GETSID 55 | #Get SID of current interactive users 56 | $CurrentLoggedOnUser = (Get-CimInstance win32_computersystem).UserName 57 | if (-not ([string]::IsNullOrEmpty($CurrentLoggedOnUser))) { 58 | $AdObj = New-Object System.Security.Principal.NTAccount($CurrentLoggedOnUser) 59 | $strSID = $AdObj.Translate([System.Security.Principal.SecurityIdentifier]) 60 | $UserSid = $strSID.Value 61 | } 62 | else { 63 | $UserSid = $null 64 | } 65 | #endregion 66 | 67 | #region APPINVENTORY 68 | #Get Apps for system and current user 69 | $MyApps = Get-InstalledApplications -UserSid $UserSid 70 | $UniqueApps = ($MyApps | Group-Object Displayname | Where-Object { $_.Count -eq 1 }).Group 71 | $DuplicatedApps = ($MyApps | Group-Object Displayname | Where-Object { $_.Count -gt 1 }).Group 72 | $NewestDuplicateApp = ($DuplicatedApps | Group-Object DisplayName) | ForEach-Object { $_.Group | Sort-Object [version]DisplayVersion -Descending | Select-Object -First 1 } 73 | $CleanAppList = $UniqueApps + $NewestDuplicateApp | Sort-Object DisplayName 74 | 75 | #Build App Array 76 | $AppArray = @() 77 | foreach ($App in $CleanAppList) { 78 | $tempapp = New-Object -TypeName PSObject 79 | $tempapp | Add-Member -MemberType NoteProperty -Name "AppName" -Value $App.DisplayName -Force 80 | $tempapp | Add-Member -MemberType NoteProperty -Name "AppVersion" -Value $App.DisplayVersion -Force 81 | $tempapp | Add-Member -MemberType NoteProperty -Name "AppPublisher" -Value $App.Publisher -Force 82 | $AppArray += $tempapp 83 | } 84 | $AppPayLoad = $AppArray 85 | #endregion APPINVENTORY 86 | 87 | #region Find Bad Apps 88 | $BadAppFound = $Null 89 | $BadAppArray = @() 90 | 91 | Foreach ($App in $AppPayLoad) { 92 | Foreach ($BadApp in $BadApps) { 93 | If ($App.AppName -like "*$BadApp*") { 94 | $tempbadapp = New-Object -TypeName PSObject 95 | $tempbadapp | Add-Member -MemberType NoteProperty -Name "AppName" -Value $App.AppName -Force 96 | $tempbadapp | Add-Member -MemberType NoteProperty -Name "AppVersion" -Value $App.AppVersion -Force 97 | $tempbadapp | Add-Member -MemberType NoteProperty -Name "AppPublisher" -Value $App.AppPublisher -Force 98 | $BadAppArray += $tempbadapp 99 | $BadAppFound = $True 100 | } 101 | } 102 | } 103 | $BadAppPayLoad = $BadAppArray 104 | 105 | If ($BadAppFound) { 106 | #Write-Output for Proactive Remediation 107 | $BadAppPayLoadOutput = $BadAppPayLoad | ConvertTo-Json -Compress 108 | Write-Output $BadAppPayLoadOutput 109 | Exit 1 110 | } 111 | else { 112 | Write-Output "OK" 113 | Exit 0 114 | } 115 | #endregion -------------------------------------------------------------------------------- /PRs/Remediate-JavaApps.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Remediation Script for unsupported applications 4 | .DESCRIPTION 5 | This script is designed to be run as a Proactive Remediation. 6 | The BadApps array contains application names that are considered unsupported by the company. 7 | If BadApps are found, a Toast Notification is displayed to the user informing them to remove the unsupported applications. 8 | .EXAMPLE 9 | RemediateJavaApps.ps1 (Run in the User Context) 10 | .NOTES 11 | FileName: Remediate-JavaApps.ps1 12 | Author: Ben Whitmore 13 | Contributor: Jan Ketil Skanke 14 | Contact: @byteben 15 | Created: 2022-11-Mar 16 | 17 | Version history: 18 | 2.0.0 - (2022-03-01) Split Detection/Remeiation scripts and used Invoke-WebRequest instead of WebClient .NET http download 19 | #> 20 | 21 | #region SCRIPTVARIABLES 22 | 23 | $BadApps = @( 24 | "Java 6" 25 | "Java SE Development Kit 6" 26 | "Java(TM) SE Development Kit 6" 27 | "Java(TM) 6" 28 | "Java 7" 29 | "Java SE Development Kit 17" 30 | "Java(TM) SE Development Kit 17" 31 | "Java(TM) 7" 32 | ) 33 | 34 | $CustomHandlerDisplayName = "Ninja Notifications" 35 | $CustomHandlerAppID = "Ninja_Warning_Monitor" 36 | $GoodMorning = "Good Morning" 37 | $GoodAfternoon = "Good Afternoon" 38 | $GoodEvening = "Good Evening" 39 | $ToastImageSource = "https://github.com/byteben/MEM/raw/main/PRs/ninja-banner.png" #ToastImage should be 364px x 180px 40 | $ToastImage = Join-Path -Path $ENV:temp -ChildPath "ToastImage.jpg" #ToastImageSource is downloaded to this location 41 | $ToastDuration = "long" #ToastDuration: Short = 7s, Long = 25s 42 | $ToastScenario = "reminder" #ToastScenario: Default | Reminder | Alarm 43 | $ToastTitle = "Java apps found on your computer" 44 | $ToastText = "If not required, please uninstall the following Java applications at your earliest convenience as they pose a security risk to your computer:-" 45 | $SnoozeTitle = "Set Reminder" 46 | $SnoozeMessage = "Remind me again in" 47 | $LogFile = Join-Path -Path $env:TEMP -ChildPath "UnsupportedAppsFound-Java.log" 48 | #endregion 49 | 50 | # Function to get all Installed Applications 51 | function Get-InstalledApplications() { 52 | param( 53 | [string]$UserSid 54 | ) 55 | 56 | New-PSDrive -PSProvider Registry -Name "HKU" -Root HKEY_USERS | Out-Null 57 | $regpath = @("HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*") 58 | $regpath += "HKU:\$UserSid\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" 59 | if (-not ([IntPtr]::Size -eq 4)) { 60 | $regpath += "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" 61 | $regpath += "HKU:\$UserSid\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" 62 | } 63 | $PropertyNames = 'DisplayName', 'DisplayVersion', 'Publisher', 'UninstallString' 64 | $Apps = Get-ItemProperty $regpath -Name $PropertyNames -ErrorAction SilentlyContinue | . { process { if ($_.DisplayName) { $_ } } } | Select-Object DisplayName, DisplayVersion, Publisher | Sort-Object DisplayName 65 | Remove-PSDrive -Name "HKU" | Out-Null 66 | Return $Apps 67 | } 68 | #end function 69 | # Function Write Log Entry 70 | function Write-LogEntry { 71 | param ( 72 | [parameter(Mandatory = $true)] 73 | [ValidateNotNullOrEmpty()] 74 | [string]$Value, 75 | [parameter(Mandatory = $false)] 76 | [ValidateNotNullOrEmpty()] 77 | [string]$FileName = $($LogName), 78 | [switch]$Stamp 79 | ) 80 | 81 | #Build Log File appending System Date/Time to output 82 | $Time = -join @((Get-Date -Format "HH:mm:ss.fff"), " ", (Get-WmiObject -Class Win32_TimeZone | Select-Object -ExpandProperty Bias)) 83 | $Date = (Get-Date -Format "MM-dd-yyyy") 84 | 85 | If ($Stamp) { 86 | $LogText = "<$($Value)> " 87 | } 88 | else { 89 | $LogText = "$($Value)" 90 | } 91 | 92 | Try { 93 | Out-File -InputObject $LogText -Append -NoClobber -Encoding Default -FilePath $LogFile -ErrorAction Stop 94 | } 95 | Catch [System.Exception] { 96 | Write-Warning -Message "Unable to add log entry to $LogFile.log file. Error message at line $($_.InvocationInfo.ScriptLineNumber): $($_.Exception.Message)" 97 | } 98 | } 99 | #end function 100 | 101 | #region RESETLOG 102 | If (Test-Path -Path $LogFile) { 103 | Remove-Item $LogFile -Force | Out-Null 104 | } 105 | #endregion 106 | 107 | #region FETCHIMAGE 108 | New-Object uri $ToastImageSource 109 | 110 | #Test if URL is valid 111 | Try { 112 | #Attempt URL get and set Status Code variable 113 | $URLRequest = Invoke-WebRequest -UseBasicParsing -URI $ToastImageSource -ErrorAction SilentlyContinue 114 | $StatusCode = $URLRequest.StatusCode 115 | Write-LogEntry -Stamp -Value "Attempting to download $ToastImageSource" 116 | Write-LogEntry -Stamp -Value "Download status code is $StatusCode" 117 | } 118 | Catch { 119 | #Catch Status Code on error 120 | $StatusCode = $_.Exception.Response.StatusCode.value__ 121 | Exit 1 122 | } 123 | 124 | #If URL exists 125 | If ($StatusCode -eq 200) { 126 | #Attempt File download 127 | Try { 128 | Invoke-WebRequest -UseBasicParsing -Uri $ToastImageSource -OutFile $ToastImage -ErrorAction SilentlyContinue 129 | #If download was successful, test the file was saved to the correct directory 130 | If (!(Test-Path -Path $ToastImage)) { 131 | Write-LogEntry -Stamp -Value "The download was interrupted or an error occured moving the file to the destination specified" 132 | Write-Output "The download was interrupted or an error occured moving the file to the destination specified" 133 | Exit 1 134 | } 135 | } 136 | Catch { 137 | #Catch any errors during the file download 138 | Write-LogEntry -Stamp -Value "Error downloading file: $ToastImageSource" 139 | write-Output "Error downloading file: $ToastImageSource" 140 | Exit 1 141 | } 142 | } 143 | else { 144 | #For anything other than status 200 (URL OK), throw a warning 145 | Write-LogEntry -Stamp -Value "URL Does not exists or the website is down. Status Code: $StatusCode" 146 | Write-Output "URL Does not exists or the website is down. Status Code: $StatusCode" 147 | Exit 1 148 | } 149 | #endregion 150 | 151 | #region GETSID 152 | #Get SID of current interactive users 153 | 154 | $CurrentLoggedOnUser = (Get-CimInstance win32_computersystem).UserName 155 | if (-not ([string]::IsNullOrEmpty($CurrentLoggedOnUser))) { 156 | $AdObj = New-Object System.Security.Principal.NTAccount($CurrentLoggedOnUser) 157 | $strSID = $AdObj.Translate([System.Security.Principal.SecurityIdentifier]) 158 | $UserSid = $strSID.Value 159 | } 160 | else { 161 | $UserSid = $null 162 | } 163 | #endregion 164 | 165 | #region APPINVENTORY 166 | #Get Apps for system and current user 167 | $MyApps = Get-InstalledApplications -UserSid $UserSid 168 | $UniqueApps = ($MyApps | Group-Object Displayname | Where-Object { $_.Count -eq 1 }).Group 169 | $DuplicatedApps = ($MyApps | Group-Object Displayname | Where-Object { $_.Count -gt 1 }).Group 170 | $NewestDuplicateApp = ($DuplicatedApps | Group-Object DisplayName) | ForEach-Object { $_.Group | Sort-Object [version]DisplayVersion -Descending | Select-Object -First 1 } 171 | $CleanAppList = $UniqueApps + $NewestDuplicateApp | Sort-Object DisplayName 172 | 173 | #Build App Array 174 | $AppArray = @() 175 | foreach ($App in $CleanAppList) { 176 | $tempapp = New-Object -TypeName PSObject 177 | $tempapp | Add-Member -MemberType NoteProperty -Name "AppName" -Value $App.DisplayName -Force 178 | $tempapp | Add-Member -MemberType NoteProperty -Name "AppVersion" -Value $App.DisplayVersion -Force 179 | $tempapp | Add-Member -MemberType NoteProperty -Name "AppPublisher" -Value $App.Publisher -Force 180 | $AppArray += $tempapp 181 | } 182 | 183 | $AppPayLoad = $AppArray 184 | $AppPayLoadLog = $AppPayLoad | Out-String 185 | Write-LogEntry -Value "################Unique Apps Found################" 186 | Write-LogEntry -Stamp -Value $AppPayLoadLog 187 | #endregion APPINVENTORY 188 | 189 | #region Find Bad Apps 190 | $BadAppsLog = $BadApps | Out-String 191 | Write-LogEntry -Value "################Unsupported Apps being searched for################" 192 | Write-LogEntry -Stamp -Value $BadAppsLog 193 | 194 | $BadAppArray = @() 195 | 196 | Foreach ($App in $AppPayLoad) { 197 | Foreach ($BadApp in $BadApps) { 198 | If ($App.AppName -like "*$BadApp*") { 199 | $tempbadapp = New-Object -TypeName PSObject 200 | $tempbadapp | Add-Member -MemberType NoteProperty -Name "AppName" -Value $App.AppName -Force 201 | $tempbadapp | Add-Member -MemberType NoteProperty -Name "AppVersion" -Value $App.AppVersion -Force 202 | $tempbadapp | Add-Member -MemberType NoteProperty -Name "AppPublisher" -Value $App.AppPublisher -Force 203 | $BadAppArray += $tempbadapp 204 | } 205 | } 206 | } 207 | $BadAppPayLoad = $BadAppArray 208 | 209 | #Update Event Text Message to include bad apps 210 | $EventText = $EventText + "`n" 211 | Foreach ($BadApp2 in $BadAppPayload) { 212 | $EventText = $EventText + "`n- $($BadApp2.AppName)" 213 | } 214 | Write-LogEntry -Value "################Toast Notification Details################" 215 | Write-LogEntry -Stamp -Value $EventText 216 | #endregion 217 | 218 | $BadAppPayLoadLog = $BadAppPayLoad | Out-String 219 | Write-LogEntry -Value "################Unsupported Apps Found################" 220 | Write-LogEntry -Stamp -Value $BadAppPayLoadLog 221 | 222 | #region CUSTOMHANDLER 223 | #https://docs.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/send-local-toast-other-apps 224 | $CustomToastNotifyRegKey = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings\$CustomHandlerAppID" 225 | $CustomHandlerClassRegKey = "HKCU:\Software\Classes\AppUserModelId" 226 | 227 | Try { 228 | If (!(Test-Path -Path $CustomToastNotifyRegKey)) { 229 | New-Item -Path $CustomToastNotifyRegKey -Force | Out-Null 230 | New-ItemProperty -Path $CustomToastNotifyRegKey -Name "ShowInActionCenter" -Value 1 -PropertyType DWORD -Force | Out-Null 231 | } 232 | } 233 | Catch { 234 | $_.Exception.Message 235 | } 236 | 237 | Try { 238 | If (!(Test-Path -Path $CustomHandlerClassRegKey\$CustomHandlerAppID)) { 239 | New-Item -Path $CustomHandlerClassRegKey -Name $CustomHandlerAppID -Force | Out-Null 240 | New-ItemProperty -Path $CustomHandlerClassRegKey\$CustomHandlerAppID -Name "DisplayName" -Value $CustomHandlerDisplayName -PropertyType String -Force | Out-Null 241 | New-ItemProperty -Path $CustomHandlerClassRegKey\$CustomHandlerAppID -Name "ShowInSettings" -Value 0 -PropertyType DWORD -Force | Out-Null 242 | } 243 | } 244 | Catch { 245 | $_.Exception.Message 246 | } 247 | 248 | Try { 249 | If ((Get-ItemProperty -Path $CustomHandlerClassRegKey\$CustomHandlerAppID -Name "DisplayName" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty DisplayName -ErrorAction SilentlyContinue) -ne $CustomHandlerDisplayName) { 250 | New-ItemProperty -Path $CustomHandlerClassRegKey\$CustomHandlerAppID -Name "DisplayName" -Value $CustomHandlerDisplayName -PropertyType String -Force | Out-Null 251 | } 252 | } 253 | Catch { 254 | $_.Exception.Message 255 | } 256 | #endregion 257 | 258 | #region TOAST 259 | #Get Hour of Day and set Custom Hello 260 | $Hour = (Get-Date).Hour 261 | If ($Hour -lt 12) { $CustomHello = $GoodMorning + ". " } 262 | ElseIf ($Hour -gt 16) { $CustomHello = $GoodEvening + ". " } 263 | Else { $CustomHello = $GoodAfternoon + ". " } 264 | 265 | $CustomHello = $CustomHello + $ToastText 266 | 267 | #Load Assemblies 268 | [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null 269 | [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null 270 | 271 | #Build XML ToastTemplate 272 | [xml]$ToastTemplate = @" 273 | 274 | 275 | 276 | $ToastTitle 277 | $CustomHello 278 | 279 | 280 | 281 | $EventText 282 | 283 | 284 | 285 | 286 | 299 | "@ 300 | 301 | #Prepare XML 302 | $ToastXml = [Windows.Data.Xml.Dom.XmlDocument]::New() 303 | $ToastXml.LoadXml($ToastTemplate.OuterXml) 304 | 305 | #Prepare and Create Toast 306 | $ToastMessage = [Windows.UI.Notifications.ToastNotification]::New($ToastXML) 307 | [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($CustomHandlerAppID).Show($ToastMessage) 308 | #endregion 309 | Exit 0 -------------------------------------------------------------------------------- /PRs/Windows Updates/Add-MSIGraphPermission.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Modules Microsoft.Graph 2 | # Install the module. (You need admin on the machine.) 3 | # Install-Module Microsoft.Graph 4 | 5 | # Set Static Variables 6 | $TenantID = "7408398c-d001-4f50-8d6a-cca33c6fd4e0" 7 | $ServicePrincipalAppDisplayName = "fn-mycontosoinventoryfunction" 8 | 9 | # Define dynamic variables 10 | $ServicePrincipalFilter = "displayName eq '$($ServicePrincipalAppDisplayName)'" 11 | $GraphAPIAppName = "Microsoft Graph" 12 | $ApiServicePrincipalFilter = "displayName eq '$($GraphAPIAppName)'" 13 | 14 | # Scopes needed for the managed identity (Add other scopes if needed) 15 | $Scopes = @( 16 | "Device.Read.All" 17 | ) 18 | 19 | # Connect to MG Graph - scopes must be consented the first time you run this. 20 | # Connect with Global Administrator 21 | Select-MgProfile -Name "beta" 22 | Connect-MgGraph -Scopes "Application.Read.All","AppRoleAssignment.ReadWrite.All" -TenantId $TenantID -UseDeviceAuthentication 23 | 24 | # Get the service principal for your managed identity. 25 | $ServicePrincipal = Get-MgServicePrincipal -Filter $ServicePrincipalFilter 26 | 27 | # Get the service principal for Microsoft Graph. 28 | # Result should be AppId 00000003-0000-0000-c000-000000000000 29 | $ApiServicePrincipal = Get-MgServicePrincipal -Filter "$ApiServicePrincipalFilter" 30 | 31 | # Apply permissions 32 | Foreach ($Scope in $Scopes) { 33 | Write-Host "`nGetting App Role '$Scope'" 34 | $AppRole = $ApiServicePrincipal.AppRoles | Where-Object {$_.Value -eq $Scope -and $_.AllowedMemberTypes -contains "Application"} 35 | if ($null -eq $AppRole) { Write-Error "Could not find the specified App Role on the Api Service Principal"; continue; } 36 | if ($AppRole -is [array]) { Write-Error "Multiple App Roles found that match the request"; continue; } 37 | Write-Host "Found App Role, Id '$($AppRole.Id)'" 38 | 39 | $ExistingRoleAssignment = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ServicePrincipal.Id | Where-Object { $_.AppRoleId -eq $AppRole.Id } 40 | if ($null -eq $existingRoleAssignment) { 41 | New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ServicePrincipal.Id -PrincipalId $ServicePrincipal.Id -ResourceId $ApiServicePrincipal.Id -AppRoleId $AppRole.Id 42 | } else { 43 | Write-Host "App Role has already been assigned, skipping" 44 | } 45 | } -------------------------------------------------------------------------------- /PRs/Windows Updates/Invoke-WUInventory.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Inventory Windows Update device settings 4 | .DESCRIPTION 5 | This script is designed to be run as a Proactive Remediation. 6 | Registry keys are inventoried on the device and uploaded to Log Analytics. 7 | Admins are able visualise the Windows Update settings in force across their devices and understand if legacy settings or GPO's are having an undesirable effect on the device Windows Update experience 8 | .EXAMPLE 9 | Invoke-WUInventory.ps1 (Required to run as System or Administrator) 10 | .NOTES 11 | FileName: Invoke-WUInventory.ps1 12 | Author: Ben Whitmore 13 | Contributor: Maurice Daly 14 | Contact: @byteben 15 | Created: 2022-10-April 16 | 17 | Version history: 18 | 1.0 - (2022-04-10) Original Release 19 | #> 20 | 21 | #region SCRIPTVARIABLES 22 | 23 | #Track Script Version in Log Analytics Table 24 | $ScriptVersion = "PR003" 25 | 26 | #Log Analytics Workspace ID 27 | $CustomerID = "" 28 | 29 | #Log Analytics Workspace Primary Key 30 | $SharedKey = "" 31 | 32 | #Custom Log Name 33 | $LogType = "WUDevice_Settings" 34 | 35 | # You can use an optional field to specify the timestamp from the data. If the time field is not specified, Azure Monitor assumes the time is the message ingestion time 36 | # DO NOT DELETE THIS VARIABLE. Recommened keep this blank. 37 | $TimeStampField = "" 38 | 39 | #Create Windows Update Settings Array 40 | #Software\Policies\Microsoft\Windows\WindowsUpdate 41 | $WUSettingsArray = @() 42 | $WUSettingsArray += "AutoRestartDeadlinePeriodInDays" 43 | $WUSettingsArray += "AutoRestartNotificationSchedule" 44 | $WUSettingsArray += "AutoRestartRequiredNotificationDismissal" 45 | $WUSettingsArray += "BranchReadinessLevel" 46 | $WUSettingsArray += "DeferFeatureUpdates" 47 | $WUSettingsArray += "DeferFeatureUpdatesPeriodInDays" 48 | $WUSettingsArray += "DeferQualityUpdates" 49 | $WUSettingsArray += "DeferQualityUpdatesPeriodInDays" 50 | $WUSettingsArray += "DisableDualScan" 51 | $WUSettingsArray += "DoNotConnectToWindowsUpdateInternetLocations" 52 | $WUSettingsArray += "ElevateNonAdmins" 53 | $WUSettingsArray += "EngagedRestartDeadline" 54 | $WUSettingsArray += "EngagedRestartSnoozeSchedule" 55 | $WUSettingsArray += "EngagedRestartTransitionSchedule" 56 | $WUSettingsArray += "PauseFeatureUpdatesStartTime" 57 | $WUSettingsArray += "PauseQualityUpdatesStartTime" 58 | $WUSettingsArray += "ScheduleImminentRestartWarning" 59 | $WUSettingsArray += "ScheduleRestartWarning" 60 | $WUSettingsArray += "SetAutoRestartDeadline" 61 | $WUSettingsArray += "SetAutoRestartNotificationConfig" 62 | $WUSettingsArray += "SetAutoRestartNotificationDisable" 63 | $WUSettingsArray += "SetAutoRestartRequiredNotificationDismissal" 64 | $WUSettingsArray += "SetEDURestart" 65 | $WUSettingsArray += "SetEngagedRestartTransitionSchedule" 66 | $WUSettingsArray += "SetRestartWarningSchd" 67 | $WUSettingsArray += "WUServer" 68 | $WUSettingsArray += "WUStatusServer" 69 | 70 | #Collect Scan Source Registry Keys 71 | $WUSettingsArray += "SetPolicyDrivenUpdateSourceForDriverUpdates" 72 | $WUSettingsArray += "SetPolicyDrivenUpdateSourceForFeatureUpdates" 73 | $WUSettingsArray += "SetPolicyDrivenUpdateSourceForOtherUpdates" 74 | $WUSettingsArray += "SetPolicyDrivenUpdateSourceForQualityUpdates" 75 | 76 | #Software\Policies\Microsoft\Windows\WindowsUpdate\AU 77 | $WUAUSettingsArray = @() 78 | $WUAUSettingsArray += "AutoInstallMinorUpdates" 79 | $WUAUSettingsArray += "EnableFeaturedSoftware" 80 | $WUAUSettingsArray += "IncludeRecommendedUpdates" 81 | $WUAUSettingsArray += "NoAUAsDefaultShutdownOption" 82 | $WUAUSettingsArray += "NoAUShutdownOption" 83 | $WUAUSettingsArray += "NoAutoRebootWithLoggedOnUsers" 84 | $WUAUSettingsArray += "NoAutoUpdate" 85 | $WUAUSettingsArray += "RebootRelaunchTimeout" 86 | $WUAUSettingsArray += "RebootRelaunchTimeoutEnabled" 87 | $WUAUSettingsArray += "RebootWarningTimeout" 88 | $WUAUSettingsArray += "RebootWarningTimeoutEnabled" 89 | $WUAUSettingsArray += "RescheduleWaitTime" 90 | $WUAUSettingsArray += "RescheduleWaitTimeEnabled" 91 | 92 | #CoManagement Capabilities for WIndows Update Workload 93 | $CoMgmtArray = @(17, 19, 21, 23, 25, 27, 29, 31, 49, 51, 53, 55, 57, 59, 61, 63, 81, 83, 85, 87, 89, 91, 93, 95, 113, 115, 117, 119, 121, 123, 125, 127, 145, 147, 149, 151, 153, 155, 157, 159, 177, 179, 181, 183, 185, 187, 189, 191, 209, 211, 213, 215, 217, 219, 221, 223, 241, 243, 245, 247, 249, 251, 253, 255) 94 | 95 | #endregion 96 | 97 | #region Initialize 98 | 99 | # Enable TLS 1.2 support 100 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 101 | 102 | #Get Intune DeviceID and ManagedDeviceName 103 | if (@(Get-ChildItem HKLM:SOFTWARE\Microsoft\Enrollments\ -Recurse | Where-Object { $_.PSChildName -eq 'MS DM Server' })) { 104 | $MSDMServerInfo = Get-ChildItem HKLM:SOFTWARE\Microsoft\Enrollments\ -Recurse | Where-Object { $_.PSChildName -eq 'MS DM Server' } 105 | $ManagedDeviceInfo = Get-ItemProperty -LiteralPath "Registry::$($MSDMServerInfo)" 106 | } 107 | $ManagedDeviceID = $ManagedDeviceInfo.EntDMID 108 | 109 | #Get AAD DeviceID 110 | # Define Cloud Domain Join information registry path 111 | $AzureADJoinInfoRegistryKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\JoinInfo" 112 | 113 | # Retrieve the child key name that is the thumbprint of the machine certificate containing the device identifier guid 114 | $AzureADJoinInfoThumbprint = Get-ChildItem -Path $AzureADJoinInfoRegistryKeyPath | Select-Object -ExpandProperty "PSChildName" 115 | if (-not ($AzureADJoinInfoThumbprint -eq $null)) { 116 | # Retrieve the machine certificate based on thumbprint from registry key 117 | $AzureADJoinCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Thumbprint -eq $AzureADJoinInfoThumbprint } 118 | if (-not ($AzureADJoinCertificate -eq $null)) { 119 | # Determine the device identifier from the subject name 120 | $AzureADDeviceID = ($AzureADJoinCertificate | Select-Object -ExpandProperty "Subject") -replace "CN=", "" 121 | } 122 | } 123 | 124 | #Get Computer Name 125 | $DeviceName = (Get-CIMInstance -ClassName Win32_OperatingSystem -NameSpace root\cimv2).CSName 126 | 127 | #Get OS Information 128 | $ComputerOSVersion = (Get-CIMInstance -ClassName Win32_OperatingSystem -NameSpace root\cimv2).Version 129 | $ComputerOSBuild = (Get-CIMInstance -ClassName Win32_OperatingSystem -NameSpace root\cimv2).BuildNumber 130 | 131 | #Get Default AU Service 132 | $DefaultAUService = (New-Object -ComObject "Microsoft.Update.ServiceManager").services | Where-Object { $_.IsDefaultAUService -eq 'True' } | Select-Object -ExpandProperty Name 133 | 134 | #endregion 135 | 136 | #region Functions 137 | 138 | # Function to create the authorization signature 139 | Function New-Signature ($customerID, $SharedKey, $Date, $ContentLength, $Method, $ContentType, $Resource) { 140 | $xHeaders = "x-ms-date:" + $Date 141 | $stringToHash = $Method + "`n" + $ContentLength + "`n" + $ContentType + "`n" + $xHeaders + "`n" + $Resource 142 | 143 | $bytesToHash = [Text.Encoding]::UTF8.GetBytes($stringToHash) 144 | $keyBytes = [Convert]::FromBase64String($sharedKey) 145 | 146 | $sha256 = New-Object System.Security.Cryptography.HMACSHA256 147 | $sha256.Key = $keyBytes 148 | $calculatedHash = $sha256.ComputeHash($bytesToHash) 149 | $encodedHash = [Convert]::ToBase64String($calculatedHash) 150 | $authorization = 'SharedKey {0}:{1}' -f $customerId, $encodedHash 151 | return $authorization 152 | } 153 | 154 | # Function to create and post the request 155 | Function Send-LogAnalyticsData($CustomerID, $SharedKey, $Body, $LogType) { 156 | $Method = "POST" 157 | $ContentType = "application/json" 158 | $Resource = "/api/logs" 159 | $rfc1123date = [DateTime]::UtcNow.ToString("r") 160 | $ContentLength = $Body.Length 161 | $signature = New-Signature ` 162 | -customerId $customerId ` 163 | -sharedKey $sharedKey ` 164 | -date $rfc1123date ` 165 | -ContentLength $ContentLength ` 166 | -Method $Method ` 167 | -ContentType $ContentType ` 168 | -Resource $Resource 169 | $uri = "https://" + $CustomerID + ".ods.opinsights.azure.com" + $Resource + "?api-version=2016-04-01" 170 | 171 | #validate that payload data does not exceed limits 172 | if ($Body.Length -gt (31.9 * 1024 * 1024)) { 173 | throw ("Upload payload is too big and exceed the 32Mb limit for a single upload. Please reduce the payload size. Current payload size is: " + ($Body.Length / 1024 / 1024).ToString("#.#") + "Mb") 174 | } 175 | 176 | $PayLoadSize = ("Upload payload size is " + ($Body.Length / 1024).ToString("#.#") + "Kb ") 177 | 178 | $Headers = @{ 179 | "Authorization" = $signature; 180 | "Log-Type" = $logType; 181 | "x-ms-date" = $rfc1123date; 182 | "time-generated-field" = $TimeStampField; 183 | } 184 | 185 | $Response = Invoke-WebRequest -Uri $uri -Method $Method -ContentType $ContentType -Headers $Headers -Body $Body -UseBasicParsing 186 | $StatusMessage = "$($Response.StatusCode) : $($PayLoadSize)" 187 | return "$StatusMessage" 188 | } 189 | 190 | Function Get-RegSetting { 191 | param ( 192 | [parameter(Mandatory = $True)] 193 | [ValidateNotNullOrEmpty()] 194 | [string]$RegKey, 195 | [string]$RegSetting 196 | ) 197 | Try { 198 | $RegSettingValue = Get-ItemPropertyValue -Path $RegKey -Name $RegSetting -ErrorAction Stop 199 | If (-not ($RegSettingValue -eq $Null)) { 200 | Return $RegSettingValue 201 | } 202 | else { 203 | If ($RegSetting -in ("WUServer", "WUStatusServer")) { 204 | Return "" 205 | } 206 | else { 207 | Return 0 208 | } 209 | } 210 | } 211 | Catch { 212 | #Not returning caught errors 213 | If ($RegSetting -in ("WUServer", "WUStatusServer")) { 214 | Return "" 215 | } 216 | else { 217 | Return 0 218 | } 219 | } 220 | } 221 | 222 | #endregion 223 | 224 | #region Workspace 225 | 226 | #Build WindowsUpdate Regsitry Key Inventory 227 | $WUSettingPayloadInventory = $Null 228 | $WUSettingPayloadInventory = New-Object -TypeName PSObject 229 | 230 | #Build Device Inventory 231 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name ScriptVersion -Value $ScriptVersion -Force 232 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name DeviceName -Value $DeviceName -Force 233 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name ManagedDeviceID -Value $ManagedDeviceID -Force 234 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name AzureADDeviceID -Value $AzureADDeviceID -Force 235 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name ComputerOSVersion -Value $ComputerOSVersion -Force 236 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name ComputerOSBuild -Value $ComputerOSBuild -Force 237 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name DefaultAUService -Value $DefaultAUService -Force 238 | 239 | #Get CoManagement State and Workload 240 | $CoMgmtRegKey = "HKLM:\Software\Microsoft\CCM" 241 | $CoMgmtSetting = "CoManagementFlags" 242 | $CoMgmtValue = Get-RegSetting -RegKey $CoMgmtRegKey -RegSetting $CoMgmtSetting 243 | If ($CoMgmtValue -in $CoMgmtArray) { 244 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name CoMgmtWorkload -Value $true -Force 245 | } 246 | else { 247 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name CoMgmtWorkload -Value $false -Force 248 | } 249 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name CoMgmtValue -Value $CoMgmtValue -Force 250 | 251 | #Get Registry Values for Software\Policies\Microsoft\Windows\WindowsUpdate 252 | $WURegKey = "HKLM:Software\Policies\Microsoft\Windows\WindowsUpdate" 253 | ForEach ($WUSetting in $WUSettingsArray) { 254 | $RegValue = Get-RegSetting -RegKey $WURegKey -RegSetting $WUSetting 255 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name $WUSetting -Value $RegValue -Force 256 | } 257 | 258 | #Get Registry Values for Software\Policies\Microsoft\Windows\WindowsUpdate 259 | $WUAURegKey = "HKLM:Software\Policies\Microsoft\Windows\WindowsUpdate\AU" 260 | ForEach ($WUAUSetting in $WUAUSettingsArray) { 261 | $RegValue = Get-RegSetting -RegKey $WUAURegKey -RegSetting $WUAUSetting 262 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name $WUAUSetting -Value $RegValue -Force 263 | } 264 | 265 | #Build Payload Array 266 | $WUSettingPayload = $Null 267 | $WUSettingPayload = @() 268 | $WUSettingPayload += $WUSettingPayloadInventory 269 | 270 | #Prepare Array for Upload 271 | $PayloadJson = $WUSettingPayload | ConvertTo-Json 272 | 273 | #Write upload intent to console 274 | Write-Output "Sending Payload:" 275 | Write-Output $PayloadJson 276 | 277 | #Upload Data 278 | $ResponseWUInventory = Send-LogAnalyticsData -CustomerID $CustomerID -SharedKey $SharedKey -Body ([System.Text.Encoding]::UTF8.GetBytes($PayloadJson)) -LogType $LogType 279 | $ResponseWUInventory 280 | 281 | #Status Report 282 | $Date = Get-Date -Format "dd-MM HH:mm" 283 | $OutputMessage = "InventoryDate: $Date " 284 | 285 | if ($ResponseWUInventory) { 286 | if ($ResponseWUInventory -like "200*") { 287 | $OutputMessage = $OutputMessage + " WUInventory:OK" 288 | } 289 | else { 290 | $OutputMessage = $OutputMessage + " WUInventory:Fail" 291 | } 292 | } 293 | Write-Output $OutputMessage 294 | 295 | #endregion 296 | #> -------------------------------------------------------------------------------- /PRs/Windows Updates/Invoke-WUInventory_Funky.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Inventory Windows Update device settings 4 | .DESCRIPTION 5 | This script is designed to be run as a Proactive Remediation. 6 | Registry keys are inventoried on the device and uploaded to Log Analytics. 7 | Admins are able visualise the Windows Update settings in force across their devices and understand if legacy settings or GPO's are having an undesirable effect on the device Windows Update experience 8 | .EXAMPLE 9 | Invoke-WUInventory.ps1 (Required to run as System or Administrator) 10 | .NOTES 11 | FileName: Invoke-WUInventory.ps1 12 | Author: Ben Whitmore 13 | Contributor: Maurice Daly 14 | Contact: @byteben 15 | Created: 2022-10-April 16 | 17 | Version history: 18 | 1.0 - (2022-04-10) Original Release 19 | #> 20 | 21 | #region SCRIPTVARIABLES 22 | 23 | #Track Script Version in Log Analytics Table 24 | $ScriptVersion = "PR004" 25 | 26 | #FunctionAppURI for Log Upload 27 | $AzureFunctionURL = "https://fn-mycontosoinventoryfunction.azurewebsites.net/api/LogCollectorAPI" 28 | 29 | #Custom Log Name 30 | $CustomLogName = "WUDevice_Settings" 31 | 32 | #Date 33 | $Date = (Get-Date) 34 | 35 | #Create Windows Update Settings Array 36 | #Software\Policies\Microsoft\Windows\WindowsUpdate 37 | $WUSettingsArray = @() 38 | $WUSettingsArray += "AutoRestartDeadlinePeriodInDays" 39 | $WUSettingsArray += "AutoRestartNotificationSchedule" 40 | $WUSettingsArray += "AutoRestartRequiredNotificationDismissal" 41 | $WUSettingsArray += "BranchReadinessLevel" 42 | $WUSettingsArray += "DeferFeatureUpdates" 43 | $WUSettingsArray += "DeferFeatureUpdatesPeriodInDays" 44 | $WUSettingsArray += "DeferQualityUpdates" 45 | $WUSettingsArray += "DeferQualityUpdatesPeriodInDays" 46 | $WUSettingsArray += "DisableDualScan" 47 | $WUSettingsArray += "DoNotConnectToWindowsUpdateInternetLocations" 48 | $WUSettingsArray += "ElevateNonAdmins" 49 | $WUSettingsArray += "EngagedRestartDeadline" 50 | $WUSettingsArray += "EngagedRestartSnoozeSchedule" 51 | $WUSettingsArray += "EngagedRestartTransitionSchedule" 52 | $WUSettingsArray += "PauseFeatureUpdatesStartTime" 53 | $WUSettingsArray += "PauseQualityUpdatesStartTime" 54 | $WUSettingsArray += "ScheduleImminentRestartWarning" 55 | $WUSettingsArray += "ScheduleRestartWarning" 56 | $WUSettingsArray += "SetAutoRestartDeadline" 57 | $WUSettingsArray += "SetAutoRestartNotificationConfig" 58 | $WUSettingsArray += "SetAutoRestartNotificationDisable" 59 | $WUSettingsArray += "SetAutoRestartRequiredNotificationDismissal" 60 | $WUSettingsArray += "SetEDURestart" 61 | $WUSettingsArray += "SetEngagedRestartTransitionSchedule" 62 | $WUSettingsArray += "SetRestartWarningSchd" 63 | $WUSettingsArray += "WUServer" 64 | $WUSettingsArray += "WUStatusServer" 65 | 66 | #Collect Scan Source Registry Keys 67 | $WUSettingsArray += "SetPolicyDrivenUpdateSourceForDriverUpdates" 68 | $WUSettingsArray += "SetPolicyDrivenUpdateSourceForFeatureUpdates" 69 | $WUSettingsArray += "SetPolicyDrivenUpdateSourceForOtherUpdates" 70 | $WUSettingsArray += "SetPolicyDrivenUpdateSourceForQualityUpdates" 71 | 72 | #Software\Policies\Microsoft\Windows\WindowsUpdate\AU 73 | $WUAUSettingsArray = @() 74 | $WUAUSettingsArray += "AutoInstallMinorUpdates" 75 | $WUAUSettingsArray += "EnableFeaturedSoftware" 76 | $WUAUSettingsArray += "IncludeRecommendedUpdates" 77 | $WUAUSettingsArray += "NoAUAsDefaultShutdownOption" 78 | $WUAUSettingsArray += "NoAUShutdownOption" 79 | $WUAUSettingsArray += "NoAutoRebootWithLoggedOnUsers" 80 | $WUAUSettingsArray += "NoAutoUpdate" 81 | $WUAUSettingsArray += "RebootRelaunchTimeout" 82 | $WUAUSettingsArray += "RebootRelaunchTimeoutEnabled" 83 | $WUAUSettingsArray += "RebootWarningTimeout" 84 | $WUAUSettingsArray += "RebootWarningTimeoutEnabled" 85 | $WUAUSettingsArray += "RescheduleWaitTime" 86 | $WUAUSettingsArray += "RescheduleWaitTimeEnabled" 87 | 88 | #CoManagement Capabilities for WIndows Update Workload 89 | $CoMgmtArray = @(17, 19, 21, 23, 25, 27, 29, 31, 49, 51, 53, 55, 57, 59, 61, 63, 81, 83, 85, 87, 89, 91, 93, 95, 113, 115, 117, 119, 121, 123, 125, 127, 145, 147, 149, 151, 153, 155, 157, 159, 177, 179, 181, 183, 185, 187, 189, 191, 209, 211, 213, 215, 217, 219, 221, 223, 241, 243, 245, 247, 249, 251, 253, 255) 90 | 91 | #endregion 92 | 93 | #region Initialize 94 | 95 | # Enable TLS 1.2 support 96 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 97 | 98 | #Get Intune DeviceID and ManagedDeviceName 99 | if (@(Get-ChildItem HKLM:SOFTWARE\Microsoft\Enrollments\ -Recurse | Where-Object { $_.PSChildName -eq 'MS DM Server' })) { 100 | $MSDMServerInfo = Get-ChildItem HKLM:SOFTWARE\Microsoft\Enrollments\ -Recurse | Where-Object { $_.PSChildName -eq 'MS DM Server' } 101 | $ManagedDeviceInfo = Get-ItemProperty -LiteralPath "Registry::$($MSDMServerInfo)" 102 | } 103 | $ManagedDeviceID = $ManagedDeviceInfo.EntDMID 104 | 105 | #Get AAD DeviceID 106 | # Define Cloud Domain Join information registry path 107 | $AzureADJoinInfoRegistryKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\JoinInfo" 108 | 109 | # Retrieve the child key name that is the thumbprint of the machine certificate containing the device identifier guid 110 | $AzureADJoinInfoThumbprint = Get-ChildItem -Path $AzureADJoinInfoRegistryKeyPath | Select-Object -ExpandProperty "PSChildName" 111 | if (-not ($AzureADJoinInfoThumbprint -eq $null)) { 112 | # Retrieve the machine certificate based on thumbprint from registry key 113 | $AzureADJoinCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Thumbprint -eq $AzureADJoinInfoThumbprint } 114 | if (-not ($AzureADJoinCertificate -eq $null)) { 115 | # Determine the device identifier from the subject name 116 | $AzureADDeviceID = ($AzureADJoinCertificate | Select-Object -ExpandProperty "Subject") -replace "CN=", "" 117 | } 118 | } 119 | 120 | #Get Computer Name 121 | $DeviceName = (Get-CIMInstance -ClassName Win32_OperatingSystem -NameSpace root\cimv2).CSName 122 | 123 | #Get OS Information 124 | $ComputerOSVersion = (Get-CIMInstance -ClassName Win32_OperatingSystem -NameSpace root\cimv2).Version 125 | $ComputerOSBuild = (Get-CIMInstance -ClassName Win32_OperatingSystem -NameSpace root\cimv2).BuildNumber 126 | 127 | #Get Default AU Service 128 | $DefaultAUService = (New-Object -ComObject "Microsoft.Update.ServiceManager").services | Where-Object { $_.IsDefaultAUService -eq 'True' } | Select-Object -ExpandProperty Name 129 | 130 | #endregion 131 | 132 | #region Functions 133 | 134 | 135 | Function Get-RegSetting { 136 | param ( 137 | [parameter(Mandatory = $True)] 138 | [ValidateNotNullOrEmpty()] 139 | [string]$RegKey, 140 | [string]$RegSetting 141 | ) 142 | Try { 143 | $RegSettingValue = Get-ItemPropertyValue -Path $RegKey -Name $RegSetting -ErrorAction Stop 144 | If (-not ($RegSettingValue -eq $Null)) { 145 | Return $RegSettingValue 146 | } 147 | else { 148 | If ($RegSetting -in ("WUServer", "WUStatusServer")) { 149 | Return "" 150 | } 151 | else { 152 | Return 0 153 | } 154 | } 155 | } 156 | Catch { 157 | #Not returning caught errors 158 | If ($RegSetting -in ("WUServer", "WUStatusServer")) { 159 | Return "" 160 | } 161 | else { 162 | Return 0 163 | } 164 | } 165 | } 166 | function Get-AzureADDeviceID { 167 | <# 168 | .SYNOPSIS 169 | Get the Azure AD device ID from the local device. 170 | 171 | .DESCRIPTION 172 | Get the Azure AD device ID from the local device. 173 | 174 | .NOTES 175 | Author: Nickolaj Andersen 176 | Contact: @NickolajA 177 | Created: 2021-05-26 178 | Updated: 2021-05-26 179 | 180 | Version history: 181 | 1.0.0 - (2021-05-26) Function created 182 | #> 183 | Process { 184 | # Define Cloud Domain Join information registry path 185 | $AzureADJoinInfoRegistryKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\JoinInfo" 186 | 187 | # Retrieve the child key name that is the thumbprint of the machine certificate containing the device identifier guid 188 | $AzureADJoinInfoThumbprint = Get-ChildItem -Path $AzureADJoinInfoRegistryKeyPath | Select-Object -ExpandProperty "PSChildName" 189 | if ($AzureADJoinInfoThumbprint -ne $null) { 190 | # Retrieve the machine certificate based on thumbprint from registry key 191 | $AzureADJoinCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Thumbprint -eq $AzureADJoinInfoThumbprint } 192 | if ($AzureADJoinCertificate -ne $null) { 193 | # Determine the device identifier from the subject name 194 | $AzureADDeviceID = ($AzureADJoinCertificate | Select-Object -ExpandProperty "Subject") -replace "CN=", "" 195 | # Handle return value 196 | return $AzureADDeviceID 197 | } 198 | } 199 | } 200 | } #endfunction 201 | function Get-AzureADJoinDate { 202 | <# 203 | .SYNOPSIS 204 | Get the Azure AD device ID from the local device. 205 | 206 | .DESCRIPTION 207 | Get the Azure AD device ID from the local device. 208 | 209 | .NOTES 210 | Author: Nickolaj Andersen 211 | Contact: @NickolajA 212 | Created: 2021-05-26 213 | Updated: 2021-05-26 214 | 215 | Version history: 216 | 1.0.0 - (2021-05-26) Function created 217 | #> 218 | Process { 219 | # Define Cloud Domain Join information registry path 220 | $AzureADJoinInfoRegistryKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\JoinInfo" 221 | 222 | # Retrieve the child key name that is the thumbprint of the machine certificate containing the device identifier guid 223 | $AzureADJoinInfoThumbprint = Get-ChildItem -Path $AzureADJoinInfoRegistryKeyPath | Select-Object -ExpandProperty "PSChildName" 224 | if ($AzureADJoinInfoThumbprint -ne $null) { 225 | # Retrieve the machine certificate based on thumbprint from registry key 226 | $AzureADJoinCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Thumbprint -eq $AzureADJoinInfoThumbprint } 227 | if ($AzureADJoinCertificate -ne $null) { 228 | # Determine the device identifier from the subject name 229 | $AzureADJoinDate = ($AzureADJoinCertificate | Select-Object -ExpandProperty "NotBefore") 230 | # Handle return value 231 | return $AzureADJoinDate 232 | } 233 | } 234 | } 235 | } #endfunction 236 | #Function to get AzureAD TenantID 237 | function Get-AzureADTenantID { 238 | # Cloud Join information registry path 239 | $AzureADTenantInfoRegistryKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\TenantInfo" 240 | # Retrieve the child key name that is the tenant id for AzureAD 241 | $AzureADTenantID = Get-ChildItem -Path $AzureADTenantInfoRegistryKeyPath | Select-Object -ExpandProperty "PSChildName" 242 | return $AzureADTenantID 243 | } 244 | #endregion functions 245 | 246 | 247 | #region Workspace 248 | #Get Common data for validation in Azure Function: 249 | $AzureADDeviceID = Get-AzureADDeviceID 250 | $AzureADTenantID = Get-AzureADTenantID 251 | 252 | #Build WindowsUpdate Regsitry Key Inventory 253 | $WUSettingPayloadInventory = $Null 254 | $WUSettingPayloadInventory = New-Object -TypeName PSObject 255 | 256 | #Build Device Inventory 257 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name ScriptVersion -Value $ScriptVersion -Force 258 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name DeviceName -Value $DeviceName -Force 259 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name ManagedDeviceID -Value $ManagedDeviceID -Force 260 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name AzureADDeviceID -Value $AzureADDeviceID -Force 261 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name ComputerOSVersion -Value $ComputerOSVersion -Force 262 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name ComputerOSBuild -Value $ComputerOSBuild -Force 263 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name DefaultAUService -Value $DefaultAUService -Force 264 | 265 | #Get CoManagement State and Workload 266 | $CoMgmtRegKey = "HKLM:\Software\Microsoft\CCM" 267 | $CoMgmtSetting = "CoManagementFlags" 268 | $CoMgmtValue = Get-RegSetting -RegKey $CoMgmtRegKey -RegSetting $CoMgmtSetting 269 | If ($CoMgmtValue -in $CoMgmtArray) { 270 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name CoMgmtWorkload -Value $true -Force 271 | } 272 | else { 273 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name CoMgmtWorkload -Value $false -Force 274 | } 275 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name CoMgmtValue -Value $CoMgmtValue -Force 276 | 277 | #Get Registry Values for Software\Policies\Microsoft\Windows\WindowsUpdate 278 | $WURegKey = "HKLM:Software\Policies\Microsoft\Windows\WindowsUpdate" 279 | ForEach ($WUSetting in $WUSettingsArray) { 280 | $RegValue = Get-RegSetting -RegKey $WURegKey -RegSetting $WUSetting 281 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name $WUSetting -Value $RegValue -Force 282 | } 283 | 284 | #Get Registry Values for Software\Policies\Microsoft\Windows\WindowsUpdate 285 | $WUAURegKey = "HKLM:Software\Policies\Microsoft\Windows\WindowsUpdate\AU" 286 | ForEach ($WUAUSetting in $WUAUSettingsArray) { 287 | $RegValue = Get-RegSetting -RegKey $WUAURegKey -RegSetting $WUAUSetting 288 | $WUSettingPayloadInventory | Add-Member -MemberType NoteProperty -Name $WUAUSetting -Value $RegValue -Force 289 | } 290 | 291 | #Build Payload Array 292 | $WUSettingPayload = $Null 293 | $WUSettingPayload = @() 294 | $WUSettingPayload += $WUSettingPayloadInventory 295 | 296 | #Randomize over 50 minutes to spread load on Azure Function - disabled on date of enrollment (Disabled in sample - Enable only in larger environments) 297 | $JoinDate = Get-AzureADJoinDate 298 | $DelayDate = $JoinDate.AddDays(1) 299 | $CompareDate = ($DelayDate - $JoinDate) 300 | if ($CompareDate.Days -ge 1) { 301 | #Write-Output "Randomzing execution time" 302 | #$ExecuteInSeconds = (Get-Random -Maximum 3000 -Minimum 1) 303 | #Start-Sleep -Seconds $ExecuteInSeconds 304 | } 305 | #Start sending logs 306 | $date = Get-Date -Format "dd-MM HH:mm" 307 | $OutputMessage = "InventoryDate:$date " 308 | 309 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 310 | $headers.Add("Content-Type", "application/json") 311 | 312 | # Add arrays of logs into payload array 313 | $LogPayLoad = New-Object -TypeName PSObject 314 | $LogPayLoad | Add-Member -NotePropertyMembers @{$CustomLogName = $WUSettingPayload } 315 | 316 | # Construct main payload to send to LogCollectorAPI // IMPORTANT // KEEP AND DO NOT CHANGE THIS 317 | $MainPayLoad = [PSCustomObject]@{ 318 | AzureADTenantID = $AzureADTenantID 319 | AzureADDeviceID = $AzureADDeviceID 320 | LogPayloads = $LogPayLoad 321 | } 322 | $MainPayLoadJson = $MainPayLoad | ConvertTo-Json -Depth 9 323 | 324 | #Write upload intent to console 325 | Write-Output "Sending Payload:" 326 | Write-Output $MainPayLoadJson 327 | 328 | # Sending data to API 329 | try { 330 | $ResponseInventory = Invoke-RestMethod $AzureFunctionURL -Method 'POST' -Headers $headers -Body $MainPayLoadJson 331 | $OutputMessage = $OutPutMessage + "Inventory:OK " + $ResponseInventory 332 | } 333 | catch { 334 | $ResponseInventory = "Error Code: $($_.Exception.Response.StatusCode.value__)" 335 | $ResponseMessage = $_.Exception.Message 336 | $OutputMessage = $OutPutMessage + "Inventory:Fail " + $ResponseInventory + $ResponseMessage 337 | } 338 | 339 | # Check status and report to Proactive Remediations 340 | if ($ResponseInventory -match "200") { 341 | Write-Output $OutputMessage 342 | Exit 0 343 | } 344 | else { 345 | Write-Output "Error: $($ResponseInventory), Message: $($ResponseMessage)" 346 | Exit 1 347 | } 348 | 349 | #endregion 350 | #> -------------------------------------------------------------------------------- /PRs/Windows Updates/Invoke-WUInventory_testdata.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Inventory Windows Update device settings 4 | .DESCRIPTION 5 | This script is designed to be run as a Proactive Remediation. 6 | Registry keys are inventoried on the device and uploaded to Log Analytics. 7 | Admins are able visualise the Windows Update settings in force across their devices and understand if legacy settings or GPO's are having an undesirable effect on the device Windows Update experience 8 | .EXAMPLE 9 | Invoke-WUInventory.ps1 (Required to run as System or Administrator) 10 | .NOTES 11 | FileName: Invoke-WUInventory.ps1 12 | Author: Ben Whitmore 13 | Contributor: Maurice Daly 14 | Contact: @byteben 15 | Created: 2022-10-April 16 | 17 | Version history: 18 | 1.0 - (2022-04-10) Original Release 19 | #> 20 | Param ( 21 | [Parameter(Mandatory = $true)] 22 | [string]$testdatapath 23 | ) 24 | 25 | #region SCRIPTVARIABLES 26 | 27 | #Log Analytics Workspace ID 28 | $CustomerID = "13ef5887-f37c-432d-be47-17c3aa40fc55" 29 | 30 | #Log Analytics Workspace Primary Key 31 | $SharedKey = "+f9wKsv3BLWX1nlU/ywFQIShFeF8CUyPTECHE+Wc0LW3DwcONLU5UYpcXqIFn3/cGK2obsCPw9jvCebJJlEZCg==" 32 | 33 | #Custom Log Name 34 | $LogType = "WUDevice_Settings" 35 | 36 | # You can use an optional field to specify the timestamp from the data. If the time field is not specified, Azure Monitor assumes the time is the message ingestion time 37 | # DO NOT DELETE THIS VARIABLE. Recommened keep this blank. 38 | $TimeStampField = "" 39 | 40 | #region Initialize 41 | 42 | # Enable TLS 1.2 support 43 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 44 | 45 | #region Functions 46 | 47 | # Function to create the authorization signature 48 | Function New-Signature ($customerID, $SharedKey, $Date, $ContentLength, $Method, $ContentType, $Resource) { 49 | $xHeaders = "x-ms-date:" + $Date 50 | $stringToHash = $Method + "`n" + $ContentLength + "`n" + $ContentType + "`n" + $xHeaders + "`n" + $Resource 51 | 52 | $bytesToHash = [Text.Encoding]::UTF8.GetBytes($stringToHash) 53 | $keyBytes = [Convert]::FromBase64String($sharedKey) 54 | 55 | $sha256 = New-Object System.Security.Cryptography.HMACSHA256 56 | $sha256.Key = $keyBytes 57 | $calculatedHash = $sha256.ComputeHash($bytesToHash) 58 | $encodedHash = [Convert]::ToBase64String($calculatedHash) 59 | $authorization = 'SharedKey {0}:{1}' -f $customerId, $encodedHash 60 | return $authorization 61 | } 62 | 63 | # Function to create and post the request 64 | Function Send-LogAnalyticsData($CustomerID, $SharedKey, $Body, $LogType) { 65 | $Method = "POST" 66 | $ContentType = "application/json" 67 | $Resource = "/api/logs" 68 | $rfc1123date = [DateTime]::UtcNow.ToString("r") 69 | $ContentLength = $Body.Length 70 | $signature = New-Signature ` 71 | -customerId $customerId ` 72 | -sharedKey $sharedKey ` 73 | -date $rfc1123date ` 74 | -ContentLength $ContentLength ` 75 | -Method $Method ` 76 | -ContentType $ContentType ` 77 | -Resource $Resource 78 | $uri = "https://" + $CustomerID + ".ods.opinsights.azure.com" + $Resource + "?api-version=2016-04-01" 79 | 80 | #validate that payload data does not exceed limits 81 | if ($Body.Length -gt (31.9 * 1024 * 1024)) { 82 | throw ("Upload payload is too big and exceed the 32Mb limit for a single upload. Please reduce the payload size. Current payload size is: " + ($Body.Length / 1024 / 1024).ToString("#.#") + "Mb") 83 | } 84 | 85 | $PayLoadSize = ("Upload payload size is " + ($Body.Length / 1024).ToString("#.#") + "Kb ") 86 | 87 | $Headers = @{ 88 | "Authorization" = $signature; 89 | "Log-Type" = $logType; 90 | "x-ms-date" = $rfc1123date; 91 | "time-generated-field" = $TimeStampField; 92 | } 93 | 94 | $Response = Invoke-WebRequest -Uri $uri -Method $Method -ContentType $ContentType -Headers $Headers -Body $Body -UseBasicParsing 95 | $StatusMessage = "$($Response.StatusCode) : $($PayLoadSize)" 96 | return "$StatusMessage" 97 | } 98 | 99 | #region Workspace 100 | 101 | #Import test data 102 | $csv = Import-Csv $testdatapath | Select-Object ` 103 | ScriptVersion, ` 104 | DeviceName, ` 105 | ManagedDeviceID, ` 106 | AzureADDeviceID, ` 107 | ComputerOSVersion, ` 108 | ComputerOSBuild, ` 109 | DefaultAUService, ` 110 | @{Name = "CoMgmtWorkload"; Expression = { [bool]$_.CoMgmtWorkload } }, ` 111 | @{Name = "CoMgmtValue"; Expression = { [int]$_.CoMgmtValue } }, ` 112 | @{Name = "AutoInstallMinorUpdates"; Expression = { [int]$_.AutoInstallMinorUpdates } }, ` 113 | @{Name = "AutoRestartDeadlinePeriodInDays"; Expression = { [int]$_.AutoRestartDeadlinePeriodInDays } }, ` 114 | @{Name = "AutoRestartNotificationSchedule"; Expression = { [int]$_.AutoRestartNotificationSchedule } }, ` 115 | @{Name = "AutoRestartRequiredNotificationDismissal"; Expression = { [int]$_.AutoRestartRequiredNotificationDismissal } }, ` 116 | @{Name = "BranchReadinessLevel"; Expression = { [int]$_.BranchReadinessLevel } }, ` 117 | @{Name = "DeferFeatureUpdates"; Expression = { [int]$_.DeferFeatureUpdates } }, ` 118 | @{Name = "DeferFeatureUpdatesPeriodInDays"; Expression = { [int]$_.DeferFeatureUpdatesPeriodInDays } }, ` 119 | @{Name = "DeferQualityUpdates"; Expression = { [int]$_.DeferQualityUpdates } }, ` 120 | @{Name = "DeferQualityUpdatesPeriodInDays"; Expression = { [int]$_.DeferQualityUpdatesPeriodInDays } }, ` 121 | @{Name = "DisableDualScan"; Expression = { [int]$_.DisableDualScan } }, ` 122 | @{Name = "DoNotConnectToWindowsUpdateInternetLocations"; Expression = { [int]$_.DoNotConnectToWindowsUpdateInternetLocations } }, ` 123 | @{Name = "ElevateNonAdmins"; Expression = { [int]$_.ElevateNonAdmins } }, ` 124 | @{Name = "EnableFeaturedSoftware"; Expression = { [int]$_.EnableFeaturedSoftware } }, ` 125 | @{Name = "EngagedRestartDeadline"; Expression = { [int]$_.EngagedRestartDeadline } }, ` 126 | @{Name = "EngagedRestartSnoozeSchedule"; Expression = { [int]$_.EngagedRestartSnoozeSchedule } }, ` 127 | @{Name = "EngagedRestartTransitionSchedule"; Expression = { [int]$_.EngagedRestartTransitionSchedule } }, ` 128 | @{Name = "IncludeRecommendedUpdates"; Expression = { [int]$_.IncludeRecommendedUpdates } }, ` 129 | @{Name = "NoAUAsDefaultShutdownOption"; Expression = { [int]$_.NoAUAsDefaultShutdownOption } }, ` 130 | @{Name = "NoAUShutdownOption"; Expression = { [int]$_.NoAUShutdownOption } }, ` 131 | @{Name = "NoAutoRebootWithLoggedOnUsers"; Expression = { [int]$_.NoAutoRebootWithLoggedOnUsers } }, ` 132 | @{Name = "NoAutoUpdate"; Expression = { [int]$_.NoAutoUpdate } }, ` 133 | @{Name = "PauseFeatureUpdatesStartTime"; Expression = { [int]$_.PauseFeatureUpdatesStartTime } }, ` 134 | @{Name = "PauseQualityUpdatesStartTime"; Expression = { [int]$_.PauseQualityUpdatesStartTime } }, ` 135 | @{Name = "RebootRelaunchTimeout"; Expression = { [int]$_.RebootRelaunchTimeout } }, ` 136 | @{Name = "RebootRelaunchTimeoutEnabled"; Expression = { [int]$_.RebootRelaunchTimeoutEnabled } }, ` 137 | @{Name = "RebootWarningTimeout"; Expression = { [int]$_.RebootWarningTimeout } }, ` 138 | @{Name = "RebootWarningTimeoutEnabled"; Expression = { [int]$_.RebootWarningTimeoutEnabled } }, ` 139 | @{Name = "RescheduleWaitTime"; Expression = { [int]$_.RescheduleWaitTime } }, ` 140 | @{Name = "RescheduleWaitTimeEnabled"; Expression = { [int]$_.RescheduleWaitTimeEnabled } }, ` 141 | @{Name = "ScheduleImminentRestartWarning"; Expression = { [int]$_.ScheduleImminentRestartWarning } }, ` 142 | @{Name = "ScheduleRestartWarning"; Expression = { [int]$_.ScheduleRestartWarning } }, ` 143 | @{Name = "SetAutoRestartDeadline"; Expression = { [int]$_.SetAutoRestartDeadline } }, ` 144 | @{Name = "SetAutoRestartNotificationConfig"; Expression = { [int]$_.SetAutoRestartNotificationConfig } }, ` 145 | @{Name = "SetAutoRestartNotificationDisable"; Expression = { [int]$_.SetAutoRestartNotificationDisable } }, ` 146 | @{Name = "SetAutoRestartRequiredNotificationDismissal"; Expression = { [int]$_.SetAutoRestartRequiredNotificationDismissal } }, ` 147 | @{Name = "SetEDURestart"; Expression = { [int]$_.SetEDURestart } }, ` 148 | @{Name = "SetEngagedRestartTransitionSchedule"; Expression = { [int]$_.SetEngagedRestartTransitionSchedule } }, ` 149 | @{Name = "SetPolicyDrivenUpdateSourceForDriverUpdates"; Expression = { [int]$_.SetPolicyDrivenUpdateSourceForDriverUpdates } }, ` 150 | @{Name = "SetPolicyDrivenUpdateSourceForFeatureUpdates"; Expression = { [int]$_.SetPolicyDrivenUpdateSourceForFeatureUpdates } }, ` 151 | @{Name = "SetPolicyDrivenUpdateSourceForOtherUpdates"; Expression = { [int]$_.SetPolicyDrivenUpdateSourceForOtherUpdates } }, ` 152 | @{Name = "SetPolicyDrivenUpdateSourceForQualityUpdates"; Expression = { [int]$_.SetPolicyDrivenUpdateSourceForQualityUpdates } }, ` 153 | @{Name = "SetRestartWarningSchd"; Expression = { [int]$_.SetRestartWarningSchd } }, ` 154 | WUServer, ` 155 | WUStatusServer 156 | 157 | 158 | #Prepare Array for Upload 159 | $PayloadJson = $csv | ConvertTo-Json 160 | 161 | #Write upload intent to console 162 | Write-Output "Sending Payload:" 163 | Write-Output $PayloadJson 164 | 165 | #Upload Data 166 | $ResponseWUInventory = Send-LogAnalyticsData -CustomerID $CustomerID -SharedKey $SharedKey -Body ([System.Text.Encoding]::UTF8.GetBytes($PayloadJson)) -LogType $LogType 167 | $ResponseWUInventory 168 | 169 | #Status Report 170 | $Date = Get-Date -Format "dd-MM HH:mm" 171 | $OutputMessage = "InventoryDate: $Date " 172 | 173 | if ($ResponseWUInventory) { 174 | if ($ResponseWUInventory -like "200*") { 175 | $OutputMessage = $OutputMessage + " WUInventory:OK" 176 | } 177 | else { 178 | $OutputMessage = $OutputMessage + " WUInventory:Fail" 179 | } 180 | } 181 | Write-Output $OutputMessage 182 | 183 | #endregion 184 | #> -------------------------------------------------------------------------------- /PRs/basic_detect.ps1: -------------------------------------------------------------------------------- 1 | #Simple Script to detect a Ninja Registry Value 2 | $RegPath = "HKLM:\Software\Ninja\Offers" 3 | $RegName = "WeAllGetFreeStuff" 4 | $RegValue = "Yes" 5 | 6 | Try { 7 | If (Test-Path $RegPath) { 8 | $RegResult = Get-ItemProperty $RegPath -Name $RegName -ErrorAction Stop | Select-Object -ExpandProperty $RegName 9 | 10 | If ($RegResult -eq $RegValue) { 11 | Write-Output "Match. Expected $($RegValue) and got $($RegResult)" 12 | Exit 0 13 | } 14 | else { 15 | If ($RegResult::IsNullOrEmpty) { 16 | $RegResult = "<>" 17 | } 18 | Write-Output "No Match. Expected $($RegValue) but got $($RegResult)" 19 | Exit 1 20 | } 21 | } 22 | else { 23 | Write-Output "No Match. Expected $($RegPath) not found" 24 | Exit 1 25 | } 26 | } 27 | Catch { 28 | $errMsg = $_.Exception.Message 29 | Write-Error $errMsg 30 | Exit 1 31 | } 32 | -------------------------------------------------------------------------------- /PRs/basic_detect_UTF8BOM.ps1: -------------------------------------------------------------------------------- 1 | #Simple Script to detect a Ninja Registry Value 2 | $RegPath = "HKLM:\Software\Ninja\Offers" 3 | $RegName = "WeAllGetFreeStuff" 4 | $RegValue = "Yes" 5 | 6 | Try { 7 | If (Test-Path $RegPath) { 8 | $RegResult = Get-ItemProperty $RegPath -Name $RegName -ErrorAction Stop | Select-Object -ExpandProperty $RegName 9 | 10 | If ($RegResult -eq $RegValue) { 11 | Write-Output "Match. Expected $($RegValue) and got $($RegResult)" 12 | Exit 0 13 | } 14 | else { 15 | If ($RegResult::IsNullOrEmpty) { 16 | $RegResult = "<>" 17 | } 18 | Write-Output "No Match. Expected $($RegValue) but got $($RegResult)" 19 | Exit 1 20 | } 21 | } 22 | else { 23 | Write-Output "No Match. Expected $($RegPath) not found" 24 | Exit 1 25 | } 26 | } 27 | Catch { 28 | $errMsg = $_.Exception.Message 29 | Write-Error $errMsg 30 | Exit 1 31 | } 32 | -------------------------------------------------------------------------------- /PRs/basic_remediate.ps1: -------------------------------------------------------------------------------- 1 | #Simple Script to remediate a Ninja Registry Value 2 | $RegPath = "HKLM:\Software\Ninja\Offers" 3 | $RegName = "WeAllGetFreeStuff" 4 | $RegValue = "Yes" 5 | $RegType = "String" 6 | 7 | Try { 8 | New-Item $RegPath -Force | New-ItemProperty -Name $RegName -Value $RegValue -PropertyType $RegType -Force | Out-Null 9 | Exit 0 10 | } 11 | Catch { 12 | $errMsg = $_.Exception.Message 13 | Write-Error $errMsg 14 | Exit 1 15 | } -------------------------------------------------------------------------------- /PRs/ninja-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byteben/MEM/1cd804b002274748fde9bfc342f6e0b46cde916e/PRs/ninja-banner.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MEM 2 | 3 | Various MEM / ConfigMgr / SCCM / Intune Scripts 4 | -------------------------------------------------------------------------------- /Set-IconSize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/byteben/MEM/1cd804b002274748fde9bfc342f6e0b46cde916e/Set-IconSize.png -------------------------------------------------------------------------------- /Set-IconSize.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script is used to resize icons for applications in ConfigMgr. 4 | Large icons result in a larger base64 string which can cause issues with the size of the CI download by BITS from a CMG. 5 | This script will resize the icon to a smaller size in ConfigMgr. 6 | Original and resized icons are saved in the $IconBackupDirectory 7 | 8 | .NOTES 9 | Author Ben Whitmore 10 | Date: 2023-11-25 11 | Version: 1.0 12 | 13 | ################# DISCLAIMER ################# 14 | The author provides scripts, macro, and other code examples for illustration only, without warranty 15 | either expressed or implied, including but not limited to the implied warranties of merchantability 16 | and/or fitness for a particular purpose. This script is provided 'AS IS' and the author does not 17 | guarantee that the following script, macro, or code can or should be used in any situation or that 18 | operation of the code will be error-free. 19 | 20 | .PARAMETER SiteCode 21 | The ConfigMgr site code 22 | 23 | .PARAMETER ProviderMachineName 24 | The ConfigMgr site server 25 | 26 | .PARAMETER ApplicationName 27 | The name of the application to resize the icon for. The default value is "*" 28 | 29 | .PARAMETER NewWidth 30 | The new width of the resized icon. The default value is 110. 31 | 32 | .PARAMETER IconBackupDir 33 | The directory path where the icon backup will be created and the resized icon will be saved. The default value is "C:\ConfigMgrIconBackup". 34 | 35 | .EXAMPLE 36 | Set-IconSize.ps1 -AppName "*Microsoft*" -NewWidth 110 -IconBackupDir "E:\IconBackup" -OnlyPMPApps 37 | #> 38 | [CmdletBinding()] 39 | param ( 40 | [Parameter(Mandatory = $true, Position = 0)] 41 | [string]$SiteCode, 42 | [Parameter(Mandatory = $true, Position = 1)] 43 | [string]$ProviderMachineName, 44 | [Parameter(Mandatory = $false, Position = 2)] 45 | [string]$ApplicationName = "*", 46 | [Parameter(Mandatory = $false, Position = 3)] 47 | [int]$NewWidth = 110, 48 | [Parameter(Mandatory = $false, Position = 4)] 49 | [string]$IconBackupDir = "C:\ConfigMgrIconBackup", 50 | [Parameter(Mandatory = $false)] 51 | [switch]$OnlyPMPApps 52 | ) 53 | 54 | function New-IconBackupDir { 55 | param ( 56 | [Parameter(Mandatory = $false)] 57 | [string]$IconBackupDir 58 | ) 59 | 60 | # Set icon backup directory name 61 | $iconDirectory = Join-Path -Path $IconBackupDir -ChildPath $dateToString 62 | try { 63 | 64 | # Create a new directory for icon backup 65 | If (-not (Test-Path -Path $iconDirectory) ) { 66 | Write-Verbose ("Creating icon backup directory '{0}'..." -f $iconDirectory) -Verbose 67 | New-Item -Path $iconDirectory -ItemType Directory -Force | Out-Null 68 | } 69 | else { 70 | Write-Verbose ("Directory '{0}' already exists" -f $iconDirectory) -Verbose 71 | } 72 | } 73 | catch { 74 | Write-Verbose ("Error creating directory '{0}'" -f $iconDirectory) -Verbose 75 | throw 76 | } 77 | } 78 | 79 | function Connect-SiteServer { 80 | param ( 81 | [Parameter(Mandatory = $true)] 82 | [string]$SiteCode, 83 | [Parameter(Mandatory = $true)] 84 | [string]$ProviderMachineName 85 | ) 86 | Write-Verbose -Message "Importing Module: ConfigurationManager.psd1 and connecting to Provider $($ProviderMachineName)..." 87 | 88 | # Import the ConfigurationManager.psd1 module 89 | try { 90 | 91 | # Check if the ConfigurationManager module is already imported 92 | if (-not (Get-Module ConfigurationManager)) { 93 | Import-Module "$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1" -Verbose:$False 94 | } 95 | } 96 | catch { 97 | Write-Verbose -Message 'Warning: Could not import the ConfigurationManager.psd1 Module' 98 | throw 99 | } 100 | 101 | # Check Provider is valid 102 | if (!($ProviderMachineName -eq (Get-PSDrive -ErrorAction SilentlyContinue | Where-Object { $_.Provider -like "*CMSite*" }).Root)) { 103 | Write-Verbose -Message ("Could not connect to the Provider '{0}'. Did you specify the correct Site Server?" -f $iconDirectory) -Verbose 104 | break 105 | } 106 | else { 107 | 108 | # Write which provider we are connecting to 109 | Write-Verbose -Message ("Connected to provider '{0}'" -f $ProviderMachineName) -Verbose 110 | } 111 | 112 | try { 113 | 114 | # Check if the site's drive is already present 115 | if (-not ( $SiteCode -eq (Get-PSDrive -ErrorAction SilentlyContinue | Where-Object { $_.Provider -like "*CMSite*" }).Name) ) { 116 | Write-Verbose -Message ("No PSDrive found for '{0}' in PSProvider CMSite for Root '{1}'. Did you specify the correct Site Code?" -f $SiteCode, $ProviderMachineName) -Verbose 117 | break 118 | } 119 | else { 120 | 121 | # Connect to the site's drive 122 | Write-Verbose -Message ("Connected to PSDrive " -f $SiteCode) -Verbose 123 | Set-Location "$($SiteCode):\" 124 | } 125 | } 126 | catch { 127 | Write-Verbose -Message ("Warning: Could not connect to the specified provider '{0}' at site '{1}'" -f $SiteCode, $ProviderMachineName) -Verbose 128 | break 129 | } 130 | } 131 | 132 | function Set-IconSizeLower { 133 | param ( 134 | [Parameter(Mandatory = $false)] 135 | [string[]]$ApplicationName, 136 | [Parameter(Mandatory = $false)] 137 | [string]$IconBackupDir = $IconBackupDir, 138 | [Parameter(Mandatory = $false)] 139 | [int]$NewWidth = $NewWidth 140 | ) 141 | 142 | # Characters that are not allowed in the icon path name when saving icons 143 | $invalidChars = '[<>:"/\\|\?\*]' 144 | 145 | # Grab the applications 146 | if ($OnlyPMPApps) { 147 | $apps = Get-CMApplication -Name "*$ApplicationName*" | Where-Object { $_.CIType_ID -eq 10 -and $_.IsLatest -eq $true -and $_.LocalizedDescription -like "Created by Patch My PC*" } 148 | } 149 | else { 150 | $apps = Get-CMApplication -Name "*$ApplicationName*" | Where-Object { $_.CIType_ID -eq 10 -and $_.IsLatest -eq $true } 151 | } 152 | 153 | # Grab properties to display in OGV including current icon data length 154 | $appResults = $apps | ForEach-Object { 155 | $xml = [xml]$_.SDMPackageXML 156 | $singleIcon = $xml.AppMgmtDigest.Resources.Icon.Data 157 | $singleIconLength = $xml.AppMgmtDigest.Resources.Icon.Data.Length 158 | if ($singleIconLength -ne 0) { 159 | $singleIconStream = [System.IO.MemoryStream][System.Convert]::FromBase64String($singleIcon) 160 | $singleIconBitmap = [System.Drawing.Bitmap][System.Drawing.Image]::FromStream($singleIconStream) 161 | } 162 | $_ | Where-Object { $singleIconLength -ne 0 } | Select-Object -Property CI_ID, @{Name = 'AppName'; Expression = { $_.LocalizedDisplayName } }, @{Name = 'IconLength'; Expression = { $singleIconLength } }, @{Name = 'IconWidth'; Expression = { $singleIconBitmap.Width } } 163 | } 164 | 165 | $ogvResults = $appResults | Sort-Object IconLength -Descending | Out-GridView -Title "Select the Application(s) to resize the icon for" -PassThru 166 | 167 | # If a selection is made, resize the icon(s) 168 | if ($ogvResults) { 169 | 170 | # Create the icon backup directory if it doesn't exist 171 | New-IconBackupDir -IconBackupDir $IconBackupDir 172 | 173 | foreach ($app in $ogvResults) { 174 | 175 | # Loop through the selected applications 176 | Write-Host "`n" 177 | Write-Verbose -Message ("Getting details for Application '{0}' with CI_ID '{1}'" -f $app.AppName, $app.CI_ID) -Verbose 178 | $appXml = Get-CMApplication -ID $app.CI_ID | Where-Object { $Null -ne $_.SDMPackageXML } | Select-Object -ExpandProperty SDMPackageXML 179 | 180 | # Get the icon for the application 181 | $package = [xml]($appXml) 182 | $icon = $package.AppMgmtDigest.Resources.Icon.Data 183 | 184 | # Sanitize the folder names 185 | $ApplicationNameSanitized = ($app.AppName -replace $invalidChars, '_').TrimEnd('.', ' ') 186 | 187 | if ($icon) { 188 | 189 | # Convert the icon to a bitmap 190 | $iconStream = [System.IO.MemoryStream][System.Convert]::FromBase64String($icon) 191 | $iconBitmap = [System.Drawing.Bitmap][System.Drawing.Image]::FromStream($iconStream) 192 | Write-Verbose -Message ("Original icon size is {0}" -f $iconBitmap.Size) -Verbose 193 | Write-Verbose -Message ("Original icon base64 length is {0}" -f $app.IconLength) -Verbose 194 | 195 | if ($iconBitmap.Width -gt $NewWidth) { 196 | Write-Verbose -Message ("Resizing icon for '{0}'" -f $app.AppName) -Verbose 197 | 198 | # Backup the existing icon 199 | $iconPath = Join-Path -Path $IconBackupDir -ChildPath "$($ApplicationNameSanitized).png" 200 | $iconBitmap.Save($iconPath, [System.Drawing.Imaging.ImageFormat]::Png) 201 | Write-Verbose -Message ("Original icon saved to '{0}'" -f $iconPath) -Verbose 202 | 203 | # Resize the icon 204 | $newHeight = ($iconBitmap.Height / $iconBitmap.Width) * $newWidth 205 | $newIconBitmap = New-Object -TypeName System.Drawing.Bitmap -ArgumentList $iconBitmap, $NewWidth, $newHeight 206 | $newIconBitmap.SetResolution($iconBitmap.HorizontalResolution, $iconBitmap.VerticalResolution) 207 | Write-Verbose -Message ("New icon size is {0}" -f $newIconBitmap.Size) -Verbose 208 | 209 | # Save the new icon 210 | $newIconPath = Join-Path -Path $IconBackupDir -ChildPath "$($ApplicationNameSanitized)_Resized.png" 211 | $newIconBitmap.Save($newIconPath, [System.Drawing.Imaging.ImageFormat]::Png) 212 | Write-Verbose -Message ("New icon saved to '{0}'" -f $newIconPath) -Verbose 213 | 214 | if (Test-Path -Path $newIconPath -PathType Leaf) { 215 | 216 | # Read the bitmap file as a byte array 217 | $imageBytes = [System.IO.File]::ReadAllBytes($newIconPath) 218 | 219 | # Convert the byte array to base64 220 | $base64String = [System.Convert]::ToBase64String($imageBytes) 221 | 222 | # Get the length of the base64 string 223 | $base64Length = $base64String.Length 224 | Write-Verbose -Message ("New icon base64 length is '{0}'" -f $base64Length) -Verbose 225 | } 226 | 227 | # Update the application with the resized icon 228 | try { 229 | Set-CMApplication -Id $app.CI_ID -IconLocationFile $newIconPath 230 | $newAppXml = Get-CMApplication -ID $app.CI_ID | Where-Object { $Null -ne $_.SDMPackageXML } | Select-Object -ExpandProperty SDMPackageXML 231 | 232 | # Get the icon for the application 233 | $newPackage = [xml]($newAppXml) 234 | $newIcon = $newPackage.AppMgmtDigest.Resources.Icon.Data 235 | if ($newIcon.Length -lt $app.IconLength) { 236 | Write-Verbose -Message ("Icon for '{0}' was resized and updated in ConfigMgr successfully" -f $app.AppName) -Verbose 237 | } 238 | else { 239 | Write-Verbose -Message ("Error updating icon for '{0}'. The icon was not updated in ConfigMgr." -f $app.AppName) -Verbose 240 | } 241 | } 242 | catch { 243 | Write-Verbose -Message ("Error updating icon for '{0}'" -f $app.AppName) -Verbose 244 | throw 245 | } 246 | } 247 | else { 248 | Write-Verbose -Message ("Icon for '{0}' is already '{1}' the new width of '{2}'" -f $app.AppName, $(if ($iconBitmap.Width -eq $NewWidth) { 'equal to' } else { 'less than' }), $NewWidth) -Verbose 249 | continue 250 | } 251 | } 252 | else { 253 | Write-Verbose -Message ("There is no custom icon assigned to '{0}'" -f $app.AppName) -Verbose 254 | } 255 | } 256 | } 257 | else { 258 | Write-Verbose -Message "No applications selected" -Verbose 259 | return 260 | } 261 | } 262 | 263 | # Connect to the site server and set the icon size(s) 264 | Connect-SiteServer -SiteCode $SiteCode -ProviderMachineName $ProviderMachineName 265 | if (-not (Test-Path -Path $IconBackupDir) ) { New-IconBackupDir -IconBackupDir $IconBackupDir } 266 | Set-IconSizeLower -ApplicationName $ApplicationName -IconBackupDir $IconBackupDir -NewWidth $NewWidth 267 | Set-Location $PSScriptRoot --------------------------------------------------------------------------------