├── WinRE ├── unmount.txt ├── recovery.txt └── winre.ps1 ├── .DS_Store ├── New Teams Scripts ├── New Teams │ ├── Uninstall.ps1 │ ├── Detect.ps1 │ ├── Install.ps1 │ └── uninstallClassicTeams.ps1 ├── Remove Classic Teams │ ├── appVersion │ │ ├── IntuneWinAppUtil.exe │ │ ├── requirement.ps1 │ │ └── install.ps1 │ └── Remediation │ │ ├── detect.ps1 │ │ └── remediate.ps1 ├── DisableAutoStart │ ├── detect.ps1 │ └── remediate.ps1 └── Remove Personal Teams │ ├── detect.ps1 │ └── remediate.ps1 ├── Sandbox Config ├── start.cmd └── setup.ps1 ├── Misc Intune ├── .DS_Store ├── Tasks │ ├── Primary User Check.xml │ ├── userCheck.ps1 │ ├── installTask.ps1 │ └── createTask-SINGLE-SCRIPT.ps1 ├── DeBloater │ ├── bloatware.xml │ └── deBloat.ps1 ├── win11Activate │ ├── detect.ps1 │ └── remediate.ps1 ├── IsOOBEComplete.ps1 ├── Dynamic Firefox Remediation │ ├── detect.ps1 │ └── remediate.ps1 ├── intuneGraph_EXAMPLE.ps1 ├── removeChat.ps1 ├── checkForOOBE.ps1 ├── installEntraModule.ps1 ├── lockScreenChange.ps1 ├── fixWinRE.ps1 ├── createHyperV.ps1 ├── getEnrollmentDateTime.ps1 ├── fontInstall.ps1 ├── taskbar_template.ps1 ├── win11SettingsFix.ps1 ├── InstallChromeIfOOBEComplete.ps1 ├── groupTagWithoutAP.ps1 ├── writeExtensionAttribute.ps1 ├── getScripts.ps1 ├── createHyperV-V2.ps1 ├── AssignmentsByGroup.ps1 ├── removePersonalDevices.ps1 ├── mapDrives.ps1 ├── updatePrimaryUser.ps1 ├── logAnalytics_EXAMPLE.ps1 ├── createHyperV-V3.ps1 └── intuneGraph_WinReporting_Script.ps1 ├── AppToast ├── LOGO-BADGE.jpeg ├── Interface.ps1 ├── ToastAppStatus.ps1 └── AppReport.ps1 ├── SearchBar Fix ├── searchRemediate.ps1 └── searchDetect.ps1 ├── bitlockerAutoRotate ├── V1 │ ├── clientScript.ps1 │ ├── webhookScript.ps1 │ └── MrTbone_Set-MgAzureAutomationAccountPermissions.ps1 └── V2 │ ├── webhookScript.ps1 │ └── clientScript.ps1 ├── Custom Compliance Template ├── customDetection.ps1 └── compliance.json ├── APV2 ├── APV2_CollectInfo.ps1 ├── APV2_Webhook_Script.ps1 ├── APV2_CorpID_Upload_Individual.ps1 └── migrateV1toV2 ├── 2025 Custom Compliance ├── 2025 Compliance.ps1 └── 2025 Compliance.json ├── LayoutModification.xml ├── Crowdstrike ├── detect.ps1 └── remediate.ps1 ├── Chocolatey scripts ├── chocoUpdateAllDetect.ps1 ├── chocoUpdateAllRemediate.ps1 ├── chocoUninstall.ps1 ├── chocoInstall.ps1 └── choco.ps1 ├── OneDrive_MFA_Fix └── oneDrive_signin.ps1 ├── addGroupAssignment.ps1 ├── mdmCertCheck.ps1 ├── Autopilot Helper Scripts ├── autoAutopilot.ps1 ├── bulkGroupTagUpdate.ps1 └── AutopilotBranding_STEVE.ps1 ├── W365 ├── PhysicalToCloudPC.ps1 └── W365_BACKUP_RESTORE │ ├── w365_BACKUP.ps1 │ └── w365_RESTORE.ps1 ├── Win11Readiness ├── remediate.ps1 └── detect.ps1 ├── HybridConnectorFixer.ps1 └── Hyper-V ├── autoCreateHyperV.ps1 └── createVM.ps1 /WinRE/unmount.txt: -------------------------------------------------------------------------------- 1 | select disk 0 2 | select partition 3 | remove letter=Z -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevecapacity/IntunePowershell/HEAD/.DS_Store -------------------------------------------------------------------------------- /New Teams Scripts/New Teams/Uninstall.ps1: -------------------------------------------------------------------------------- 1 | "$($PSScriptRoot)\teamsbootstrapper.exe -x" -------------------------------------------------------------------------------- /Sandbox Config/start.cmd: -------------------------------------------------------------------------------- 1 | powershell.exe -executionpolicy bypass -file C:\Work\setup.ps1 -------------------------------------------------------------------------------- /Misc Intune/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevecapacity/IntunePowershell/HEAD/Misc Intune/.DS_Store -------------------------------------------------------------------------------- /AppToast/LOGO-BADGE.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevecapacity/IntunePowershell/HEAD/AppToast/LOGO-BADGE.jpeg -------------------------------------------------------------------------------- /Misc Intune/Tasks/Primary User Check.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevecapacity/IntunePowershell/HEAD/Misc Intune/Tasks/Primary User Check.xml -------------------------------------------------------------------------------- /Misc Intune/Tasks/userCheck.ps1: -------------------------------------------------------------------------------- 1 | $userName = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object UserName).UserName 2 | 3 | $userName | Out-File "C:\ProgramData\Scripts\primaryUser.txt" -------------------------------------------------------------------------------- /New Teams Scripts/Remove Classic Teams/appVersion/IntuneWinAppUtil.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevecapacity/IntunePowershell/HEAD/New Teams Scripts/Remove Classic Teams/appVersion/IntuneWinAppUtil.exe -------------------------------------------------------------------------------- /SearchBar Fix/searchRemediate.ps1: -------------------------------------------------------------------------------- 1 | reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v "SearchOnTaskbarMode" /t REG_DWORD /d 1 /f | Out-Host 2 | 3 | Start-Sleep -seconds 1 4 | 5 | stop-process -name explorer -Force -------------------------------------------------------------------------------- /WinRE/recovery.txt: -------------------------------------------------------------------------------- 1 | select volume c 2 | shrink desired=665 minimum=650 3 | create partition primary size=665 id=de94bba4-06d1-4d40-a16a-bfd50179d6ac 4 | format fs=ntfs quick label=WinRE 5 | assign letter=z 6 | gpt attributes=0x8000000000000001 -------------------------------------------------------------------------------- /Misc Intune/DeBloater/bloatware.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Clipchamp.Clipchamp 4 | Microsoft.MicrosoftSolitaireCollection 5 | Microsoft.MSPaint 6 | Microosft.XboxApp 7 | 8 | -------------------------------------------------------------------------------- /New Teams Scripts/DisableAutoStart/detect.ps1: -------------------------------------------------------------------------------- 1 | $detectionFlag = (Get-ItemProperty -Path "HKLM:\SOFTWARE\RubixDev" -Name "teamsAutoStartDisabled").teamsAutoStartDisabled 2 | 3 | if ($detectionFlag -eq 1) { 4 | Write-Output "Setting already applied." 5 | Exit 0 6 | } else { 7 | Write-Output "Setting not applied." 8 | Exit 1 9 | } -------------------------------------------------------------------------------- /New Teams Scripts/Remove Personal Teams/detect.ps1: -------------------------------------------------------------------------------- 1 | $TeamsApp = Get-AppxPackage "*Teams*" -AllUsers -ErrorAction SilentlyContinue 2 | if($TeamsApp.Name -eq "MicrosoftTeams") 3 | { 4 | Write-Host "Built-in Teams App found" 5 | Exit 1 6 | } 7 | else 8 | { 9 | Write-Host "Built-in Teams App found" 10 | Exit 0 11 | } -------------------------------------------------------------------------------- /New Teams Scripts/New Teams/Detect.ps1: -------------------------------------------------------------------------------- 1 | $path = "C:\Program Files\WindowsApps" 2 | 3 | $newTeams = Get-ChildItem -Path $path -Filter "MSTeams_*" 4 | 5 | if($newTeams) 6 | { 7 | Write-Host "New Teams is installed" 8 | exit 0 9 | } 10 | else 11 | { 12 | Write-Host "New Teams not found" 13 | exit 1 14 | } 15 | 16 | -------------------------------------------------------------------------------- /Misc Intune/Tasks/installTask.ps1: -------------------------------------------------------------------------------- 1 | $destination = "C:\ProgramData\Scripts" 2 | if(!(Test-Path $Destination) 3 | { 4 | mkdir $Destination 5 | } 6 | 7 | Copy-Item -Path "$($psscriptroot)\userCheck.ps1" -Destination $Destination -Recurse -Force 8 | 9 | schtasks.exe /create /xml "$($psscriptroot)\Primary User Check.xml" /tn "Primary User Check" /f | Out-Host -------------------------------------------------------------------------------- /SearchBar Fix/searchDetect.ps1: -------------------------------------------------------------------------------- 1 | $path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search" 2 | $name = "SearchOnTaskbarMode" 3 | 4 | $currentSetting = Get-ItemProperty -Path $path -Name $name -ErrorAction SilentlyContinue 5 | 6 | if($currentSetting -ne 1) 7 | { 8 | Write-Host "Search box not set to 1" 9 | Exit 1 10 | } 11 | else 12 | { 13 | Write-Host "Search box is already set to 1" 14 | Exit 0 15 | } -------------------------------------------------------------------------------- /Misc Intune/win11Activate/detect.ps1: -------------------------------------------------------------------------------- 1 | # Get the license status 2 | 3 | $licenseStatus = Get-CimInstance -ClassName SoftwareLicensingProduct | Where-Object { $_.PartialProductKey } | Select-Object -ExpandProperty LicenseStatus 4 | 5 | # If the license status is 1, the OS is activated 6 | if ($licenseStatus -eq 1) 7 | { 8 | Write-Output "Activated" 9 | Exit 0 10 | } 11 | else 12 | { 13 | Write-Output "Not Activated" 14 | Exit 1 15 | } -------------------------------------------------------------------------------- /New Teams Scripts/New Teams/Install.ps1: -------------------------------------------------------------------------------- 1 | $msix = "$($PSScriptRoot)\MSTeams-x64.msix" 2 | $destination = "C:\ProgramData\Microsoft\NEW-TEAMS-TEMP" 3 | $exePath = "$($PSScriptRoot)\teamsbootstrapper.exe" 4 | 5 | if(!(Test-Path $destination)) 6 | { 7 | mkdir $destination 8 | } 9 | 10 | Copy-Item -Path $msix -Destination $destination -Force 11 | 12 | Start-Process -FilePath $exePath -ArgumentList "-p", "-o", "$($destination)\MSTeams-x64.msix" -Wait -WindowStyle Hidden -------------------------------------------------------------------------------- /New Teams Scripts/Remove Personal Teams/remediate.ps1: -------------------------------------------------------------------------------- 1 | try 2 | { 3 | Get-AppxPackage -Name "MicrosoftTeams" -AllUsers | Remove-AppxPackage 4 | Get-AppxProvisionedPackage -Online | Where-Object {$_.DisplayName -eq "MicrosoftTeams"} | Remove-AppxProvisionedPackage -Online 5 | Write-Host "Removed personal Teams" 6 | Exit 0 7 | } 8 | catch 9 | { 10 | $message = $_.Exception.Message 11 | Write-Host "Failed to remove personal Teams: $message" 12 | Exit 1 13 | } -------------------------------------------------------------------------------- /Misc Intune/win11Activate/remediate.ps1: -------------------------------------------------------------------------------- 1 | # Define your MAK key 2 | $MAKKey = "ABCD-1234-EFGH-5678-IJKL" # Replace with your actual MAK key 3 | 4 | # Set the MAK key 5 | Write-Output "Installing MAK key... $MAKKey" 6 | slmgr.vbs /ipk $MAKKey 7 | 8 | # Activate Windows 9 | Write-Output "Activating Windows..." 10 | slmgr.vbs /ato 11 | 12 | # Display the activation status 13 | Write-Output "Checking activation status..." 14 | slmgr.vbs /dlv 15 | 16 | Write-Output "Windows activation process completed." -------------------------------------------------------------------------------- /bitlockerAutoRotate/V1/clientScript.ps1: -------------------------------------------------------------------------------- 1 | # get device ID and look up bitlocker recovery key in Entra 2 | $deviceId = ((Get-ChildItem -Path "Cert:\LocalMachine\My" | Where-Object {$_.Issuer -match "MS-Organization-Access"} | Select-Object Subject).Subject).TrimStart("CN=") 3 | 4 | # assemble payload 5 | $payload = @{ 6 | deviceId = $deviceId 7 | } | ConvertTo-Json 8 | 9 | # send request to webhook 10 | $webhook = "" 11 | Invoke-WebRequest -Uri $webhook -Method POST -Body $payload -UseBasicParsing 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /New Teams Scripts/Remove Classic Teams/appVersion/requirement.ps1: -------------------------------------------------------------------------------- 1 | # Registry path for teams machine wide installer 2 | $registryPath = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" 3 | 4 | # Check if uninstall string is in registry 5 | $classicTeams = Get-ItemProperty -Path $registryPath | Where-Object {$_.DisplayName -eq "Teams Machine-Wide Installer"} 6 | 7 | # Check if exists 8 | if($classicTeams) 9 | { 10 | Write-Output "Teams installed" 11 | } 12 | else 13 | { 14 | Write-Output "Teams not installed" 15 | } -------------------------------------------------------------------------------- /Custom Compliance Template/customDetection.ps1: -------------------------------------------------------------------------------- 1 | # Set variables 2 | $autopilotRegistry = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Provisioning\Diagnostics\AutoPilot' 3 | $autopilotTenant = $autopilotRegistry.CloudAssignedTenantDomain 4 | 5 | $firefox = $false 6 | if("C:\Program Files\Mozilla Firefox\firefox.exe") 7 | { 8 | $firefox = $true 9 | } 10 | 11 | # Create json hash array 12 | $hash = @{ 13 | AutopilotTenant = $autopilotTenant; 14 | FirefoxInstalled = $firefox 15 | } 16 | 17 | # return and convert to JSON 18 | return $hash | ConvertTo-Json -Compress -------------------------------------------------------------------------------- /APV2/APV2_CollectInfo.ps1: -------------------------------------------------------------------------------- 1 | # Webhook URL 2 | $webhook = "WEBHOOK URL GOES HERE" 3 | 4 | # Get the computer system and BIOS information 5 | $computerSystem = Get-CimInstance -ClassName Win32_ComputerSystem 6 | $bios = Get-CimInstance -ClassName Win32_BIOS 7 | 8 | # Create the JSON payload 9 | $webhookData = @{ 10 | manufacturer = $computerSystem.Manufacturer 11 | model = $computerSystem.Model 12 | serialNumber = $bios.SerialNumber 13 | } | ConvertTo-Json 14 | 15 | # Upload the device identity 16 | Invoke-WebRequest -Method POST -Uri $webhook -Body $webhookData -UseBasicParsing 17 | -------------------------------------------------------------------------------- /Misc Intune/DeBloater/deBloat.ps1: -------------------------------------------------------------------------------- 1 | $bloatApps = @( 2 | "Microsoft.XboxApp", 3 | "Clipchamp.Clipchamp", 4 | "Microsoft.MSPaint", 5 | "Microsoft.MicrosoftSolitaireCollection" 6 | ) 7 | 8 | foreach($app in $bloatApps) 9 | { 10 | $installed = (Get-AppxProvisionedPackage -Online | Where-Object {$_.DisplayName -eq "$($app)"}) 11 | if($installed) 12 | { 13 | try { 14 | $installed | Remove-AppxProvisionedPackage -Online 15 | } 16 | catch { 17 | $message = $_ 18 | Write-Host "Error removing $($app): $message" 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /New Teams Scripts/Remove Classic Teams/Remediation/detect.ps1: -------------------------------------------------------------------------------- 1 | # Registry path for teams machine wide installer 2 | $registryPath = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" 3 | 4 | # Check if uninstall string is in registry 5 | $classicTeams = Get-ItemProperty -Path $registryPath | Where-Object {$_.DisplayName -eq "Teams Machine-Wide Installer"} 6 | 7 | # Check if exists 8 | if($classicTeams) 9 | { 10 | # trigger remediation 11 | Write-Output "Classic Teams found; attempting uninstall" 12 | Exit 1 13 | } 14 | else 15 | { 16 | Write-Output "Classic Teams not found." 17 | Exit 0 18 | } 19 | -------------------------------------------------------------------------------- /Misc Intune/IsOOBEComplete.ps1: -------------------------------------------------------------------------------- 1 | $Definition = @" 2 | 3 | using System; 4 | using System.Text; 5 | using System.Collections.Generic; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace Api 9 | { 10 | public class Kernel32 11 | { 12 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 13 | public static extern int OOBEComplete(ref int bIsOOBEComplete); 14 | } 15 | } 16 | "@ 17 | 18 | Add-Type -TypeDefinition $Definition -Language CSharp 19 | 20 | $IsOOBEComplete = $false 21 | $appRequirement = [Api.Kernel32]::OOBEComplete([ref] $IsOOBEComplete) 22 | 23 | $IsOOBEComplete -------------------------------------------------------------------------------- /2025 Custom Compliance/2025 Compliance.ps1: -------------------------------------------------------------------------------- 1 | # Check if Google Chrome is installed 2 | $googleChrome = $false 3 | if(Test-Path "C:\Program Files\Google\Chrome\Application\chrome.exe") 4 | { 5 | $googleChrome = $true 6 | } 7 | 8 | # Check if DisableConsumerFeatures is enabled 9 | $regKey = Get-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent" 10 | $disableConsumerFeatureValue = $regKey.DisableWindowsConsumerFeatures 11 | 12 | # Build the JSON table 13 | $hash = @{ 14 | ChromeInstalled = $googleChrome 15 | DisableConsumerFeatures = $disableConsumerFeatureValue 16 | } 17 | 18 | # return 19 | return $hash | ConvertTo-Json -Compress -------------------------------------------------------------------------------- /New Teams Scripts/DisableAutoStart/remediate.ps1: -------------------------------------------------------------------------------- 1 | New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS 2 | 3 | $user = (Get-WmiObject -Class Win32_ComputerSystem | Select-Object Username).Username 4 | $sid = (New-Object System.Security.Principal.NTAccount($user)).Translate([System.Security.Principal.SecurityIdentifier]).Value 5 | 6 | $userRegPath = "HKU\$($sid)\SOFTWARE" 7 | 8 | # Prevent Teams from starting on login 9 | reg.exe add "$($userRegPath)\Classes\LocalSettings\Software\Microsoft\Windows\CurrentVersion\AppModel\SystemAppData\MSTeams_8wekyb3d8bbwe\TeamsTfwStartupTask" /v "State" /t DWORD /d 1 /f | Out-Host 10 | 11 | # Set the detection flag 12 | reg.exe add "HKLM\SOFTWARE\RubixDev" /v "teamsAutoStartDisabled" /t DWORD /d 1 /f | Out-Host 13 | -------------------------------------------------------------------------------- /Misc Intune/Dynamic Firefox Remediation/detect.ps1: -------------------------------------------------------------------------------- 1 | # Check for Firefox in 32 and 64 bit registry 2 | $registryPaths = @( 3 | "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", 4 | "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" 5 | ) 6 | 7 | $firefoxInstalled = $false 8 | 9 | foreach($path in $registryPaths) 10 | { 11 | $apps = Get-ItemProperty $path -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like "*Firefox*" } 12 | if($apps) 13 | { 14 | $firefoxInstalled = $true 15 | break 16 | } 17 | } 18 | 19 | # Remediate if found 20 | if($firefoxInstalled) 21 | { 22 | Write-Output "Firefox is installed" 23 | exit 1 24 | } 25 | else 26 | { 27 | Write-Output "Firefox is not installed" 28 | exit 0 29 | } -------------------------------------------------------------------------------- /LayoutModification.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /bitlockerAutoRotate/V2/webhookScript.ps1: -------------------------------------------------------------------------------- 1 | # get the payload body 2 | [cmdletbinding()] 3 | param( 4 | [object]$WebhookData 5 | ) 6 | 7 | if($WebhookData) 8 | { 9 | $bodyData = ConvertFrom-Json -InputObject $WebhookData.RequestBody 10 | $deviceId = ((($bodyData.deviceId) | Out-String).Trim()) 11 | } 12 | 13 | # connect to the graph 14 | Connect-MgGraph -Identity 15 | 16 | Write-Output "Recovery key for deviceId $($deviceId) was not updated within the last 30 days, attempting to rotate..." 17 | try 18 | { 19 | Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$($deviceId)/rotateBitlockerKeys" 20 | Write-Output "Successfully rotated BitLocker key." 21 | } 22 | catch 23 | { 24 | $message = $_.Exception.Message 25 | Write-Output "Failed to rotate BitLocker key: $message" 26 | } 27 | -------------------------------------------------------------------------------- /Crowdstrike/detect.ps1: -------------------------------------------------------------------------------- 1 | # Detecting the presence of the CrowdStrike driver on a system 2 | # Thanks to Daniel Bleyer (https://www.linkedin.com/in/danielbleyer-uk/) for contributing the original version of this script 3 | $driverFolder = "C:\Windows\System32\drivers\CrowdStrike" 4 | 5 | if(Test-Path $driveFolder) 6 | { 7 | $files = Get-ChildItem -Path $driverFolder -Recurse -Filter "*CD-00000291*.sys" 8 | foreach($file in $files) 9 | { 10 | $UTCwriteTime = $file.LastWriteTimeUtc 11 | if($UTCwriteTime.Hour -eq 4 -and $UTCwriteTime.minute -eq 9) 12 | { 13 | Write-Host "CrowdStrike driver found, removing..." 14 | exit 1 15 | } 16 | else 17 | { 18 | Write-Host "CrowdStrike driver found, but not the problem version, nothing to do." 19 | exit 0 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Misc Intune/intuneGraph_EXAMPLE.ps1: -------------------------------------------------------------------------------- 1 | function Filter-Intune($emailAddress,$OSVersion) 2 | { 3 | if($emailAddress -ne $null) 4 | { 5 | $intuneDevices = Get-MgDeviceManagementManagedDevices | Where-Object {$_.EmailAddress -eq "$($emailAddress)"} 6 | if($intuneDevices.Count -gt 0) 7 | { 8 | $userDevices = $intuneDevices | Select-Object DisplayName,OSVersion 9 | if($osVersion -ne $null) 10 | { 11 | $selectedDevices = $userDevices | Where-Object {$_.OSVersion -match "$($OSVersion)"} 12 | return $selectedDevices 13 | } 14 | else 15 | { 16 | $selectedDevices = $userDevices 17 | return $userDevices 18 | } 19 | } 20 | else 21 | { 22 | Write-Host "No device found associated with $($emailAddress)" 23 | } 24 | } 25 | else 26 | { 27 | Write-Host "No email address provided to function 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Misc Intune/removeChat.ps1: -------------------------------------------------------------------------------- 1 | $Action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-executionpolicy bypass -command "reg.exe add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Communications /v ConfigureChatAutoInstall /t REG_DWORD /d 0 /f | Out-Host"' 2 | 3 | $Principal = New-ScheduledTaskPrincipal -GroupId "BUILTIN\Administrators" #Warning: the admin Group name is localised 4 | 5 | Register-ScheduledTask -TaskName 'uninstallChat' -Action $action -Principal $Principal 6 | 7 | $svc = New-Object -ComObject 'Schedule.Service' 8 | $svc.Connect() 9 | 10 | $user = 'NT SERVICE\TrustedInstaller' 11 | $folder = $svc.GetFolder('\') 12 | $task = $folder.GetTask('uninstallChat') 13 | 14 | #Start Task 15 | $task.RunEx($null, 0, 0, $user) 16 | 17 | Start-Sleep -Seconds 5 18 | 19 | #Kill Task 20 | $task.Stop(0) 21 | 22 | #remove task From Task Scheduler 23 | Unregister-ScheduledTask -TaskName 'uninstallChat' -Confirm:$false 24 | -------------------------------------------------------------------------------- /APV2/APV2_Webhook_Script.ps1: -------------------------------------------------------------------------------- 1 | [cmdletbinding()] 2 | 3 | param( 4 | [object]$WebhookData 5 | ) 6 | 7 | ## WebhookData 8 | 9 | if($WebHookData) 10 | { 11 | $bodyData = ConvertFrom-Json -InputObject $WebhookData.RequestBody 12 | $serialNumber = ((($bodyData.serialNumber) | Out-String).Trim()) 13 | $manufacturer = ((($bodyData.manufacturer) | Out-String).Trim()) 14 | $model = ((($bodyData.model) | Out-String).Trim()) 15 | } 16 | 17 | Connect-MgGraph -Identity 18 | 19 | $params = @{ 20 | overwriteImportedDeviceIdentities = $false 21 | importedDeviceIdentities = @( 22 | @{ 23 | importedDeviceIdentityType = "manufacturerModelSerial" 24 | importedDeviceIdentifier = "$($manufacturer),$($model),$($serialNumber)" 25 | } 26 | ) 27 | } | ConvertTo-Json 28 | 29 | Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/beta/deviceManagement/importedDeviceIdentities/importDeviceIdentityList" -Body $params -ContentType "application/json" 30 | -------------------------------------------------------------------------------- /WinRE/winre.ps1: -------------------------------------------------------------------------------- 1 | Start-Process "reagentc.exe" -ArgumentList "/disable" 2 | 3 | Start-Sleep -Seconds 5 4 | 5 | Get-Partition | Where-Object -FilterScript {$_.Type -eq "Recovery"} | Remove-Partition -Confirm:$false 6 | 7 | 8 | Start-Sleep -Seconds 5 9 | 10 | diskpart /s ".\recovery.txt" 11 | 12 | Start-Sleep -Seconds 5 13 | 14 | mkdir "Z:\Recovery\WindowsRE" 15 | 16 | Start-Sleep -Seconds 2 17 | 18 | Copy-Item "$($psscriptroot)\Winre.wim" "Z:\Recovery\WindowsRE" 19 | 20 | Start-Process "reagentc.exe" -ArgumentList '/SetREimage /Path "Z:\Recovery\WindowsRE"' 21 | 22 | Start-Sleep -Seconds 5 23 | 24 | Start-Process "reagentc.exe" -ArgumentList '/enable' 25 | 26 | $partitionNumber = Get-Partition | Where-Object -FilterScript {$_.Type -eq "Recovery"} | Select-Object -ExpandProperty PartitionNumber 27 | 28 | (Get-Content "$($psscriptroot)\unmount.txt") | ForEach-Object {$_ -replace "", "$partitionNumber"} | Set-Content "$($psscriptroot)\unmount.txt" 29 | 30 | Start-Sleep -Seconds 2 31 | 32 | diskpart /s ".\unmount.txt" -------------------------------------------------------------------------------- /Misc Intune/checkForOOBE.ps1: -------------------------------------------------------------------------------- 1 | [string]$AutoPilotSettingsKey = 'HKLM:\SOFTWARE\Microsoft\Provisioning\AutopilotSettings' 2 | [string]$DevicePrepName = 'DevicePreparationCategory.Status' 3 | [string]$DeviceSetupName = 'DeviceSetupCategory.Status' 4 | [bool]$DevicePrepNotRunning = $false 5 | [bool]$DeviceSetupNotRunning = $false 6 | 7 | $DevicePrepDetails = (Get-ItemProperty -Path $AutoPilotSettingsKey -Name $DevicePrepName -ErrorAction 'Ignore').$DevicePrepName 8 | $DeviceSetupDetails = (Get-ItemProperty -Path $AutoPilotSettingsKey -Name $DeviceSetupName -ErrorAction 'Ignore').$DeviceSetupName 9 | 10 | if (-not [string]::IsNullOrEmpty($DevicePrepDetails)) { 11 | $DeviceSetupDetails = $DeviceSetupDetails | ConvertFrom-Json 12 | } 13 | else { 14 | Write-Output "No_Autopilot_Config" 15 | Exit 16 | } 17 | 18 | 19 | if ($DeviceSetupDetails.categoryState -eq "inProgress") { 20 | Write-Output "ESP_Running" 21 | Exit 22 | } 23 | else { 24 | Write-Output "ESP_NotRunning" 25 | Exit 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Crowdstrike/remediate.ps1: -------------------------------------------------------------------------------- 1 | # Remediate CrowdStrike driver file on a system 2 | # Thanks to Daniel Bleyer (https://www.linkedin.com/in/danielbleyer-uk/) for contributing the original version of this script 3 | 4 | #Define the CrowdStrike Driver Folder 5 | 6 | $driverFolder = "C:\Windows\System32\drivers\CrowdStrike" 7 | 8 | if(Test-Path $driverFolder) 9 | { 10 | $files = Get-ChildItem -Path $driverFolder -Recurse -Filter "*CD-00000291*.sys" 11 | foreach($file in $files) 12 | { 13 | # get last write time 14 | $UTCwriteTime = $file.LastWriteTimeUtc 15 | 16 | # Check last LastWriteTimeUTC matches issue 17 | if($UTCwriteTime.Hour -eq 4 -and $UTCwriteTime.minute -eq 9) 18 | { 19 | Write-Output "CrowdStrike file found, removing..." 20 | Remove-Item -Path $file.FullName -Force 21 | } 22 | else 23 | { 24 | Write-Output "CrowdStrike file found, but not the problem version, nothing to do." 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Misc Intune/installEntraModule.ps1: -------------------------------------------------------------------------------- 1 | Install-PackageProvider -Name NuGet -Force 2 | Install-Module -Name PowerShellGet -Force -AllowClobber 3 | 4 | Remove-Module PowerShellGet,PackageManagement -Force 5 | 6 | Import-Module PowerShellGet -MinimumVersion 2.0 -Force 7 | Import-PackageProvider PowerShellGet -MinimumVersion 2.0 -Force 8 | 9 | $requiredModules = @( 10 | "Microsoft.Graph.DirectoryObjects", 11 | "Microsoft.Graph.Users", 12 | "Microsoft.Graph.Groups", 13 | "Microsoft.Graph.Users.Actions", 14 | "Microsoft.Graph.Users.Functions", 15 | "Microsoft.Graph.Identity.DirectoryManagement", 16 | "Microsoft.Graph.Identity.SignIns", 17 | "Microsoft.Graph.Governance", 18 | "Microsoft.Graph.Applications" 19 | ) 20 | 21 | foreach($module in $requiredModules) 22 | { 23 | if(!(Get-Module -Name $module -ListAvailable)) 24 | { 25 | Install-Module -Name $module -Force -AllowClobber 26 | } 27 | } 28 | 29 | Install-Module -Name Microsoft.Graph.Entra -Repository PSGallery -Force -AllowPreRelease -------------------------------------------------------------------------------- /APV2/APV2_CorpID_Upload_Individual.ps1: -------------------------------------------------------------------------------- 1 | # Install and import the Microsoft.Graph.Beta.DeviceManagement module 2 | Install-Module Microsoft.Graph.Beta.DeviceManagement -confirm:$false -Force -AllowClobber 3 | Import-Module Microsoft.Graph.Beta.DeviceManagement 4 | 5 | # Connect to Microsoft Graph 6 | Connect-MgGraph 7 | 8 | # Get the computer system and BIOS information 9 | $computerSystem = Get-CimInstance -ClassName Win32_ComputerSystem 10 | $bios = Get-CimInstance -ClassName Win32_BIOS 11 | 12 | # Create the JSON payload 13 | $params = @{ 14 | overwriteImportedDeviceIdentities = $false 15 | importedDeviceIdentities = @( 16 | @{ 17 | importedDeviceIdentityType = "manufacturerModelSerial" 18 | importedDeviceIdentifier = "$($computerSystem.Manufacturer),$($computerSystem.Model),$($bios.SerialNumber)" 19 | } 20 | ) 21 | } | ConvertTo-Json 22 | 23 | # Upload the device identity 24 | Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/beta/deviceManagement/importedDeviceIdentities/importDeviceIdentityList" -Body $params -------------------------------------------------------------------------------- /Misc Intune/lockScreenChange.ps1: -------------------------------------------------------------------------------- 1 | # Script components: do not run as full script 2 | # These are meant to be added to the intune migration solution 3 | 4 | # Change wallpaper after first reboot during middleBoot sequence. 5 | # Add the below section to 'startMigrate.ps1' 6 | 7 | $regPath = "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP" 8 | $imgPath = "path\ToYour\Migration\Image.jpg" 9 | 10 | reg.exe add $regPath /v LockScreenImagePath /t REG_SZ /d $imgPath /f 11 | reg.exe add $regPath /v LockScreenImageUrl /t REG_SZ /d $imgPath /f 12 | reg.exe add $regPath /v LockScreenImageStatus /t REG_DWORD /d 1 /f 13 | 14 | # The next section will set the Lock screen wallpaper to the corporate image 15 | # The below should be added to the 'middleBoot.ps1' script 16 | 17 | $regPath = "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP" 18 | $imgPath = "path\ToYour\Corporate\LockScreenImage.jpg" 19 | 20 | reg.exe add $regPath /v LockScreenImagePath /t REG_SZ /d $imgPath /f 21 | reg.exe add $regPath /v LockScreenImageUrl /t REG_SZ /d $imgPath /f 22 | -------------------------------------------------------------------------------- /Misc Intune/fixWinRE.ps1: -------------------------------------------------------------------------------- 1 | # Get the disk that contains the Win RE partition 2 | $disk = Get-Disk | Where-Object { $_.PartitionStyle -eq 'GPT' } | Sort-Object -Property Number | Select-Object -Last 1 3 | 4 | # Get the Win RE partition 5 | $partition = Get-Partition -DiskNumber $disk.Number | Where-Object { $_.Type -eq 'Recovery' } 6 | 7 | # Get all partitions on the disk, sorted by partition number 8 | $allPartitions = Get-Partition -DiskNumber $disk.Number | Sort-Object -Property PartitionNumber 9 | 10 | # Check if the Win RE partition is the last partition on the disk 11 | if ($partition.PartitionNumber -eq $allPartitions[-1].PartitionNumber) { 12 | # Calculate the new size in bytes 13 | $newSize = 1GB 14 | 15 | # Calculate the size to add in bytes 16 | $sizeToAdd = $newSize - $partition.Size 17 | 18 | # Resize the partition 19 | Resize-Partition -DiskNumber $disk.Number -PartitionNumber $partition.PartitionNumber -Size ($partition.Size + $sizeToAdd) 20 | } else { 21 | Write-Output "The Win RE partition is not the last partition on the disk." 22 | } 23 | -------------------------------------------------------------------------------- /Misc Intune/Tasks/createTask-SINGLE-SCRIPT.ps1: -------------------------------------------------------------------------------- 1 | $destination = "C:\ProgramData\Scripts" 2 | if(!(Test-Path $destination)) 3 | { 4 | mkdir $destination 5 | } 6 | 7 | $scriptText = @" 8 | $userName = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object UserName).UserName 9 | 10 | $userName | Out-File "C:\ProgramData\Scripts\primaryUser.txt" 11 | "@ 12 | 13 | New-Item -ItemType File -Path $destination -Name "userCheck.ps1" 14 | $scriptText | Set-Content -Path "$destination\userCheck.ps1" -Force 15 | 16 | 17 | $taskName = "Primary User Check" 18 | $scriptPath = "C:\ProgramData\Scripts\userCheck.ps1" 19 | 20 | $taskAction = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-Executionpolicy bypass -File `"$scriptPath`"" 21 | $taskTrigger = New-ScheduledTaskTrigger -AtLogOn 22 | $taskPrincipal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevl Highest 23 | 24 | Register-ScheduledTask -TaskName $taskName -Action $taskAction -Trigger $taskTrigger -Principal $taskPrincipal -Description "Run the Primary User Check Script at everylogon" 25 | 26 | -------------------------------------------------------------------------------- /bitlockerAutoRotate/V2/clientScript.ps1: -------------------------------------------------------------------------------- 1 | # get device ID 2 | $deviceId = ((Get-ChildItem -Path "Cert:\LocalMachine\My" | Where-Object {$_.Issuer -match "Microsoft Intune MDM Device CA"} | Select-Object Subject).Subject).TrimStart("CN=") 3 | 4 | # assemble JSON payload 5 | $payload = @{ 6 | deviceId = $deviceId 7 | } | ConvertTo-Json 8 | 9 | 10 | $cutoffDate = ((Get-Date).AddDays(-30).ToString()) 11 | $currentRotationValue = Get-ItemPropertyValue -Path "HKLM:\SOFTWARE\BitLockerRotation" -Name "RotationDate" 12 | 13 | if($null -eq $currentRotationValue -or $currentRotationValue -lt $cutoffDate) 14 | { 15 | Write-Output "BitLocker recovery key was rotated more than 30 days ago. Attempting to rotate" 16 | reg.exe add "HKLM\SOFTWARE\BitLockerRotation" /v "RotationDate" /t REG_SZ /d (Get-Date).ToString() /f | Out-Host 17 | # send payload to webhook 18 | $webhook = "" 19 | Invoke-WebRequest -Uri $webhook -Method POST -Body $payload -UseBasicParsing 20 | Exit 1 21 | } 22 | else 23 | { 24 | Write-Output "Bitlocker recovery key was rotated within 30 days. No action needed." 25 | Exit 0 26 | } -------------------------------------------------------------------------------- /Chocolatey scripts/chocoUpdateAllDetect.ps1: -------------------------------------------------------------------------------- 1 | #Query outdated apps into array 2 | # Check for Chocolatey and install 3 | $choco = "C:\ProgramData\chocolatey" 4 | Write-Host "Checking if Chocolatey is installed on $($env:COMPUTERNAME)..." 5 | if(!(Test-Path $choco)) 6 | { 7 | Write-Host "Chocolatey was not found; installing..." 8 | Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) 9 | Write-Host "Chocolatey was successfully installed." 10 | } 11 | else 12 | { 13 | Write-Host "Chocolatey already installed." 14 | } 15 | 16 | 17 | $outdated = choco outdated 18 | $counter = 0 19 | $apps = @() 20 | 21 | foreach($x in $outdated) 22 | { 23 | if($counter -lt 4) 24 | { 25 | $counter += 1 26 | continue 27 | } 28 | if($x.Trim() -eq "") 29 | { 30 | break 31 | } 32 | $apps += $x.Split('|')[0] 33 | } 34 | 35 | if($apps -gt 0) 36 | { 37 | Write-Host "Out of date choco packages found" 38 | Exit 1 39 | 40 | } 41 | else 42 | { 43 | Write-Host "All choco packages are up to date." 44 | Exit 0 45 | } -------------------------------------------------------------------------------- /APV2/migrateV1toV2: -------------------------------------------------------------------------------- 1 | # Authenticate 2 | Connect-MgGraph 3 | 4 | # Get all Autopilot devices 5 | $url = "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities" 6 | $autopilotDevices = @() 7 | 8 | do { 9 | $response = Invoke-MgGraphRequest -Method GET -Uri $url 10 | $autopilotDevices += $response.value 11 | $url = $response."@odata.nextLink" 12 | } while ($url) 13 | 14 | foreach ($device in $autopilotDevices) { 15 | $serial = $device.serialNumber 16 | $manufacturer = $device.manufacturer 17 | $model = $device.model 18 | 19 | $params = @{ 20 | overwriteImportedDeviceIdentities = $false 21 | importedDeviceIdentities = @( 22 | @{ 23 | importedDeviceIdentityType = "manufacturerModelSerial"; 24 | importedDeviceIdentifier = "$($manufacturer),$($model),$($serial)" 25 | } 26 | ) 27 | } | ConvertTo-Json 28 | 29 | Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/beta/deviceManagement/importedDeviceIdentities/importDeviceIdentityList" -Body $params 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /Sandbox Config/setup.ps1: -------------------------------------------------------------------------------- 1 | # comfort settings 2 | 3 | # taskbar alignment 4 | reg.exe add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v TaskbarAl /t REG_DWORD /d 0 /f | Out-Host 5 | # windows search 6 | reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v SearchOnTaskbarMode /t REG_DWORD /d 1 /f | Out-Host 7 | # right click 8 | reg.exe add "HKCU\SOFTWARE\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve /reg:64 | Out-Host 9 | # wallpaper 10 | reg.exe add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v Wallpaper /t REG_SZ /d "C:\Work\ninjaCat.jpg" /f | Out-Host 11 | reg.exe add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v WallpaperStyle /t REG_DWORD /d 4 /f | Out-Host 12 | 13 | Stop-Process -name explorer -force 14 | # my apps 15 | 16 | Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) 17 | 18 | $apps = @( 19 | "notepadplusplus.install", 20 | "vscode", 21 | "7zip.install", 22 | "firefox", 23 | "vlc" 24 | ) 25 | 26 | foreach($app in $apps) 27 | { 28 | choco install $app -y 29 | } 30 | # notepad++ 31 | # vscode 32 | # vlc 33 | # firefox -------------------------------------------------------------------------------- /2025 Custom Compliance/2025 Compliance.json: -------------------------------------------------------------------------------- 1 | { 2 | "Rules":[ 3 | { 4 | "SettingName": "ChromeInstalled", 5 | "Operator" : "IsEquals", 6 | "DataType" : "Boolean", 7 | "Operand" : true, 8 | "MoreInfoURL": "https://getrubix.com", 9 | "RemediationStrings":[ 10 | { 11 | "Language":"en_US", 12 | "Title": "Google Chrome Required", 13 | "Description" : "This PC must have Google Chrome installed to be compliant." 14 | } 15 | ] 16 | }, 17 | { 18 | "SettingName": "DisableConsumerFeatures", 19 | "Operator" : "IsEquals", 20 | "DataType" : "String", 21 | "Operand" : "1", 22 | "MoreInfoURL": "https://getrubix.com", 23 | "RemediationStrings":[ 24 | { 25 | "Language":"en_US", 26 | "Title": "Disable Windodws Consumer Features", 27 | "Description" : "Your organization requires Windows consumer features to be disabled." 28 | } 29 | ] 30 | 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /Custom Compliance Template/compliance.json: -------------------------------------------------------------------------------- 1 | { 2 | "Rules":[ 3 | { 4 | "SettingName":"AutopilotTenant", 5 | "Operator":"IsEquals", 6 | "DataType":"String", 7 | "Operand":"stevecapacity.com", 8 | "MoreInfoUrl":"https://stevecapacity.com", 9 | "RemediationStrings":[ 10 | { 11 | "Language":"en_US", 12 | "Title":"Autopilot Tenant Registration", 13 | "Description":"This PC must be registered to Autopilot in the stevecapacity.com tenant." 14 | } 15 | ] 16 | }, 17 | { 18 | "SettingName":"FirefoxInstalled", 19 | "Operator":"IsEquals", 20 | "DataType":"Boolean", 21 | "Operand":true, 22 | "MoreInfoUrl":"https://stevecapacity.com", 23 | "RemediationStrings":[ 24 | { 25 | "Language":"en_US", 26 | "Title":"Mozilla Firefox required", 27 | "Description":"Your organization requires Mozilla Firefox web browser to be installed on this PC." 28 | } 29 | ] 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /OneDrive_MFA_Fix/oneDrive_signin.ps1: -------------------------------------------------------------------------------- 1 | reg.exe load HKLM\TempUser "C:\Users\Default\NTUSER.DAT" | Out-Host 2 | reg.exe add "HKLM\TempUser\Software\Microsoft\Windows\CurrentVersion\Run" /v "LaunchOneDriveWithUser" /d "C:\Windows\LaunchOneDriveWithCurrentUser.cmd" 3 | reg.exe unload HKLM\TempUser | Out-Host 4 | 5 | $scriptText = "@echo off > nul 6 | for /f `"delims=`" %%n in ('whoami/upn') do set upn=%%n 7 | 8 | REM determine if Business1 instance is configured on this machine 9 | reg.exe query HKCU\Software\Microsoft\OneDrive\Accounts\Business1 /v UserEmail > nul 2> nul 10 | if errorlevel 1 goto :NotProvisioned 11 | REM since OneDrive is provisioned already, dont run this script anymore 12 | reg.exe delete HKCU\Software\Microsoft\Windows\CurrentVersion\Run /v LaunchOneDriveWithUser /f 13 | goto :EOF 14 | :NotProvisioned 15 | REM there was no Business1 UserEmail registry entry so launch OneDrive first run wizard with the logged in user ID 16 | start odopen://sync?useremail=%upn% 17 | " 18 | 19 | New-Item -ItemType File -Path "C:\Windows" -Name "LaunchOneDriveWithCurrentUser.cmd" -Force 20 | Add-Content -Path "C:\Windows\LaunchOneDriveWithCurrentUser.cmd" $scriptText | Set-Content "C:\Windows\LaunchOneDriveWithCurrentUser.cmd" -Force 21 | Start-Sleep -Seconds 2 -------------------------------------------------------------------------------- /Misc Intune/createHyperV.ps1: -------------------------------------------------------------------------------- 1 | # Parameters for VMNAME, WINDOWS VERSION, and CPU COUNT 2 | Param( 3 | [Parameter(Mandatory=$True,Position=1)] 4 | [string]$VMName, 5 | [Parameter(Mandatory=$True,Position=2)] 6 | [string]$version, 7 | [Parameter(Mandatory=$True,Position=3)] 8 | [string]$CPUCount 9 | ) 10 | 11 | # Copy disk from TEMPLATES FOLDER and place in Hyper-V directory with VM name 12 | Copy-Item -Path "D:\Templates\GM-$($version).vhdx" -Destination "D:\Hyper-V\Virtual hard disks\$($VMName).vhdx" -Force | Out-Null 13 | 14 | # Set some VM definitions 15 | $VMSwitchName = "Default Switch" 16 | $VhdxPath = "D:\Hyper-V\Virtual hard disks\$($VMName).vhdx" 17 | $VMPath = "D:\Hyper-V\Virtual machines" 18 | 19 | # VM settings and create the VM 20 | New-VM -Name $VMName -BootDevice VHD -VHDPath $VhdxPath -Path $VMPath -Generation 2 -Switch $VMSwitchName 21 | Set-VM -VMName $VMName -ProcessorCount $CPUCount 22 | Set-VMMemory -VMName $VMName -StartupBytes 8GB -DynamicMemoryEnabled $false 23 | Set-VMSecurity -VMName $VMName -VirtualizationBasedSecurityOptOut $false 24 | Set-VMKeyProtector -VMName $VMName -NewLocalKeyProtector 25 | Enable-VMTPM -VMName $VMName 26 | Enable-VMIntegrationService -VMName $VMName -Name "Guest Service Interface" 27 | Set-VM -VMName $VMName -AutomaticCheckpointsEnabled $false 28 | -------------------------------------------------------------------------------- /Misc Intune/getEnrollmentDateTime.ps1: -------------------------------------------------------------------------------- 1 | # Find the Intune enrollment registry path 2 | $ErrorActionPreference = "SilentlyContinue" 3 | 4 | $enrollmentPath = "HKLM:\SOFTWARE\Microsoft\Enrollments\" 5 | $enrollments = Get-ChildItem -Path $enrollmentPath 6 | foreach($enrollment in $enrollments) 7 | { 8 | $object = Get-ItemProperty Registry::$enrollment 9 | $enrollPath = Join-Path -Path $enrollmentPath -ChildPath $object.PSChildName 10 | $key = Get-ItemProperty -Path $enrollPath -Name "DiscoveryServiceFullURL" 11 | if($key) 12 | { 13 | $regPath = "$($enrollPath)\DeviceEnroller" 14 | break 15 | } 16 | else 17 | { 18 | Write-Host "Not enrolled" 19 | } 20 | } 21 | 22 | # get enrolled date and time 23 | $firstSessionBinary = Get-ItemProperty -Path $regPath -Name "FirstSessionTimestamp" -ErrorAction SilentlyContinue 24 | 25 | function convertFromBinary($binary) 26 | { 27 | if($binary) 28 | { 29 | $fileTime = [System.BitConverter]::ToInt64($binary, 0) 30 | return [datetime]::FromFileTimeUtc($fileTime) 31 | } 32 | return "Not Found" 33 | } 34 | 35 | $firstSessionTime = convertFromBinary($firstSessionBinary.FirstSessionTimestamp) 36 | 37 | $currentTime = Get-Date 38 | 39 | $timeDifference = $currentTime - $firstSessionTime 40 | 41 | if($timeDifference.TotalHours -lt 3) 42 | { 43 | Write-Output "Install" 44 | } 45 | else 46 | { 47 | Write-Output "Dont Install" 48 | } -------------------------------------------------------------------------------- /Chocolatey scripts/chocoUpdateAllRemediate.ps1: -------------------------------------------------------------------------------- 1 | #Query outdated apps into array 2 | # Check for Chocolatey and install 3 | $choco = "C:\ProgramData\chocolatey" 4 | Write-Host "Checking if Chocolatey is installed on $($env:COMPUTERNAME)..." 5 | if(!(Test-Path $choco)) 6 | { 7 | Write-Host "Chocolatey was not found; installing..." 8 | Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) 9 | Write-Host "Chocolatey was successfully installed." 10 | } 11 | else 12 | { 13 | Write-Host "Chocolatey already installed." 14 | } 15 | 16 | $outdated = choco outdated 17 | $counter = 0 18 | $apps = @() 19 | 20 | foreach($x in $outdated) 21 | { 22 | if($counter -lt 4) 23 | { 24 | $counter += 1 25 | continue 26 | } 27 | if($x.Trim() -eq "") 28 | { 29 | break 30 | } 31 | $apps += $x.Split('|')[0] 32 | } 33 | 34 | #Attempt to update each app 35 | if($apps -gt 0) 36 | { 37 | foreach($app in $apps) 38 | { 39 | Write-Host "$($app) installed and out of date. Attempting to update..." 40 | try 41 | { 42 | choco upgrade $app -y 43 | Write-Host "$($app) successfully updated to latest version." 44 | } 45 | catch 46 | { 47 | $message = $_ 48 | Write-Host "Error updating $($app): $message" 49 | } 50 | } 51 | } 52 | else 53 | { 54 | Write-Host "All apps are up to date" 55 | } 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /New Teams Scripts/Remove Classic Teams/Remediation/remediate.ps1: -------------------------------------------------------------------------------- 1 | # get all users 2 | $allUsers = Get-ChildItem -Path "$($ENV:SystemDrive)\Users" 3 | 4 | # uninstall Teams function 5 | function Uninstall-ClassicTeams($teamsPath) 6 | { 7 | Start-Process -FilePath "$($teamsPath)\Update.exe" -ArgumentList "--uninstall /s" -PassThru -Wait -ErrorAction Stop 8 | } 9 | 10 | # loop through all users 11 | foreach($user in $allUsers) 12 | { 13 | # Teams install paths 14 | $localAppData = "$($ENV:SystemDrive)\Users\$($user.Name)\AppData\Local\Microsoft\Teams" 15 | $programData = "$($ENV:SystemDrive)\$($user.Name)\Microsoft\Teams" 16 | 17 | # Check each install location and remove if found 18 | if (Test-Path $localAppData) 19 | { 20 | Write-Output "Uninstall Teams classic for user $($user.Name)" 21 | Uninstall-ClassicTeams -teamsPath $localAppData 22 | } 23 | elseif (Test-Path $programData) 24 | { 25 | Write-Output "Uninstall Teams classic for user $($user.Name)" 26 | Uninstall-ClassicTeams -teamsPath $programData 27 | } 28 | else 29 | { 30 | Write-Output "Classic Teams not installed for user $($user.Name)" 31 | } 32 | } 33 | 34 | $oldFolder = "$($env:SystemDrive)\Users\*\AppData\Local\Microsoft\Teams" 35 | $oldIcon = "$($env:SystemDrive)\Users\*\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Microsoft Teams*.lnk" 36 | 37 | Get-Item $oldFolder | Remove-Item -Recurse -Force 38 | Get-Item $oldIcon | Remove-Item -Recurse -Force -------------------------------------------------------------------------------- /addGroupAssignment.ps1: -------------------------------------------------------------------------------- 1 | $policyId = "" 2 | $targetGroup = "" 3 | 4 | $getUri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$($policyId)/assignments" 5 | $postUri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$($policyId)/microsoft.graph.assign" 6 | 7 | Connect-MgGraph 8 | 9 | try { 10 | $assignments = Invoke-MgGraphRequest -Method GET -Uri $getUri 11 | } 12 | catch { 13 | Write-Warning $_.Exception.Message 14 | } 15 | 16 | $groupAssignments = @($assignments.value | Where-Object { $_.target.'@odata.type' -eq "#microsoft.graph.groupAssignmentTarget" }) 17 | 18 | $alreadyAssigned = $groupAssignments | Where-Object { $_.target.groupId -eq $targetGroup } 19 | 20 | if(-not $alreadyAssigned){ 21 | $groupAssignments += [PSCustomObject]@{ 22 | target = @{ 23 | "@odata.type" = "#microsoft.graph.groupAssignmentTarget" 24 | groupId = $targetGroup 25 | } 26 | } 27 | } 28 | 29 | $body = @{ 30 | assignments = $groupAssignments | ForEach-Object { 31 | @{ 32 | target = @{ 33 | "@odata.type" = $_.target.'@odata.type' 34 | groupId = $_.target.groupId 35 | } 36 | } 37 | } 38 | } | ConvertTo-Json -Depth 10 39 | 40 | try { 41 | Invoke-MgGraphRequest -Method POST -Uri $postUri -Body $body -ContentType "application/json" 42 | } 43 | catch { 44 | Write-Warning $_.Exception.Message 45 | } 46 | -------------------------------------------------------------------------------- /Misc Intune/fontInstall.ps1: -------------------------------------------------------------------------------- 1 | function log() 2 | { 3 | Param( 4 | [string]$message 5 | ) 6 | $date = Get-Date -Format "yyyy-MM-dd HH:mm:ss tt" 7 | Write-Output "$date - $message" 8 | } 9 | 10 | Start-Transcript -Path "C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\fontInstall.log" -Force -Verbose 11 | 12 | # Get the fonts in the 'FONT' folder 13 | $fonts = Get-ChildItem -Path ".\Fonts" 14 | 15 | # Set the font REGPATH 16 | $regpath = "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" 17 | 18 | # Set font values for each font 19 | foreach($font in $fonts) 20 | { 21 | $basename = $font.basename 22 | $extension = $font.extension 23 | $fullname = $font.fullname 24 | $fontname = $font.name 25 | if($extension -eq ".ttf") 26 | { 27 | $fontValue = $basename + " (TrueType)" 28 | log "Font value is $fontvalue" 29 | } 30 | if([string]::IsNullOrEmpty($fontValue)) 31 | { 32 | log "Font not found" 33 | } 34 | else 35 | { 36 | if(Test-Path "C:\Windows\fonts\$fontname") 37 | { 38 | log "Font $fontname already exists" 39 | } 40 | else 41 | { 42 | Copy-Item -Path $fullname -Destination "C:\Windows\Fonts" -Force 43 | log "Copied $fullname to C:\Windows\Fonts..." 44 | reg.exe add $regpath /v $fontValue /t REG_SZ /d $fontname /f | Out-Host 45 | log "Added $fontValue to registry" 46 | } 47 | } 48 | } 49 | 50 | Stop-Transcript -------------------------------------------------------------------------------- /Chocolatey scripts/chocoUninstall.ps1: -------------------------------------------------------------------------------- 1 | # Parameters for app name and package name 2 | Param( 3 | [Parameter(Mandatory=$True,Position=1)] 4 | [string]$app 5 | ) 6 | 7 | <# Check for 64 bit powershell 8 | if("$env:PROCESSOR_ARCHITEW6432" -ne "ARM64") 9 | { 10 | if(Test-Path "$($env:windir)\SysNative\WindowsPowerShell\v1.0\powershell.exe") 11 | { 12 | & "$($env:windir)\SysNative\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy bypass -NoProfile -File "$PSCommandPath" 13 | exit $LASTEXITCODE 14 | } 15 | }#> 16 | 17 | # make log path and start logging 18 | $logPath = "C:\ProgramData\Microsoft\IntuneApps\$($app)" 19 | if(!(Test-Path $logPath)) 20 | { 21 | mkdir $logPath 22 | } 23 | Start-Transcript -Path "$($logPath)\$($app)_Uninstall.log" 24 | 25 | $choco = "C:\ProgramData\chocolatey" 26 | 27 | # check for app and uninstall 28 | Write-Host "Checking if $($app) is installed on $($env:COMPUTERNAME)..." 29 | $installed = choco list | Select-String $app 30 | 31 | if($installed -ne $null) 32 | { 33 | Write-Host "$($app) is installed; uninstalling now..." 34 | try 35 | { 36 | Start-Process -Wait -FilePath "$($choco)\choco.exe" -ArgumentList "uninstall $($app) -y" 37 | Write-Host "$($app) was successfully uninstalled." 38 | } 39 | catch 40 | { 41 | $message = $_ 42 | Write-Host "Error uninstalling $($app): $message" 43 | } 44 | } 45 | else 46 | { 47 | Write-Host "$($app) is no longer detected." 48 | } 49 | 50 | Stop-Transcript -------------------------------------------------------------------------------- /Misc Intune/taskbar_template.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Misc Intune/win11SettingsFix.ps1: -------------------------------------------------------------------------------- 1 | Copy-Item -Path "$($PSScriptRoot)\YOURPIC.jpeg" -Destination "C:\Windows\Web\Wallpaper" -Force 2 | 3 | New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS 4 | 5 | $user = (Get-WmiObject -Class Win32_ComputerSystem | Select-Object Username).Username 6 | $sid = (New-Object System.Security.Principal.NTAccount($user)).Translate([System.Security.Principal.SecurityIdentifier]).Value 7 | 8 | $userRegPath = "HKU\$($sid)\SOFTWARE" 9 | 10 | reg.exe add "$($userRegPath)\Microsoft\Windows\CurrentVersion\Policies\System" /v "Wallpaper" /t REG_SZ /d "C:\Windows\Web\Wallpaper\rubix.jpeg" /f | Out-Host 11 | reg.exe add "$($userRegPath)\Microsoft\Windows\CurrentVersion\Policies\System" /v "WallpaperStyle" /t REG_DWORD /d 4 /f | Out-Host 12 | reg.exe add "$($userRegPath)\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve /reg:64 | Out-Host 13 | reg.exe add "$($userRegPath)\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "TaskbarDa" /t REG_DWORD /d 0 /f | Out-Host 14 | reg.exe add "$($userRegPath)\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "TaskbarAl" /t REG_DWORD /d 0 /f | Out-Host 15 | reg.exe add "$($userRegPath)\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "ShowTaskViewButton" /t REG_DWORD /d 0 /f | Out-Host 16 | 17 | reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v "SearchOnTaskbarMode" /t REG_DWORD /d 1 /f | Out-Host 18 | 19 | New-Item -Path "C:\ProgramData\Microsoft\win11Settings.tag" -ItemType File -Force 20 | 21 | Stop-Process -Name explorer -Force -------------------------------------------------------------------------------- /Misc Intune/Dynamic Firefox Remediation/remediate.ps1: -------------------------------------------------------------------------------- 1 | # Authenticate with Graph 2 | 3 | $clientID = "" 4 | $clientSecret = "" 5 | $tenantID = "" 6 | 7 | # Assemble the token 8 | $tokenUrl = "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/token" 9 | 10 | $tokenBody = @{ 11 | client_id = $clientID 12 | scope = "https://graph.microsoft.com/.default" 13 | client_secret = $clientSecret 14 | grant_type = "client_credentials" 15 | } 16 | 17 | $tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenUrl -Body $tokenBody -ContentType "application/x-www-form-urlencoded" 18 | $accessToken = $tokenResponse.access_token 19 | 20 | $headers = @{ 21 | "Authorization" = "Bearer $accessToken" 22 | "Content-Type" = "application/json" 23 | } 24 | 25 | # Get entra device ID 26 | $entraDeviceId = ((Get-ChildItem "Cert:\LocalMachine\My" | Where-Object {$_.Issuer -match "MS-Organization-Access"} | Select-Object Subject).Subject).TrimStart("CN=") 27 | $entraId = (Invoke-RestMethod -Method Get -Uri "https://graph.microsoft.com/beta/devices?`$filter=deviceid eq '$entraDeviceId'" -Headers $headers).value.id 28 | 29 | $body = @" 30 | { 31 | "extensionAttributes":{ 32 | "extensionAttribute7":"Firefox" 33 | } 34 | } 35 | "@ 36 | 37 | # Make patch request 38 | Invoke-RestMethod -Method Patch -Uri "https://graph.microsoft.com/beta/devices/$($entraId)" -Headers $headers -Body $body 39 | 40 | Write-Output "extensionAttribute7 set to Firefox for $($entraId)" 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /New Teams Scripts/Remove Classic Teams/appVersion/install.ps1: -------------------------------------------------------------------------------- 1 | # get all users 2 | $allUsers = Get-ChildItem -Path "$($ENV:SystemDrive)\Users" 3 | 4 | # uninstall Teams function 5 | function Uninstall-ClassicTeams($teamsPath) 6 | { 7 | Start-Process -FilePath "$($teamsPath)\Update.exe" -ArgumentList "--uninstall /s" -PassThru -Wait -ErrorAction Stop 8 | } 9 | 10 | # loop through all users 11 | foreach($user in $allUsers) 12 | { 13 | # Teams install paths 14 | $localAppData = "$($ENV:SystemDrive)\Users\$($user.Name)\AppData\Local\Microsoft\Teams" 15 | $programData = "$($ENV:SystemDrive)\$($user.Name)\Microsoft\Teams" 16 | 17 | # Check each install location and remove if found 18 | if (Test-Path $localAppData) 19 | { 20 | Write-Output "Uninstall Teams classic for user $($user.Name)" 21 | Uninstall-ClassicTeams -teamsPath $localAppData 22 | } 23 | elseif (Test-Path $programData) 24 | { 25 | Write-Output "Uninstall Teams classic for user $($user.Name)" 26 | Uninstall-ClassicTeams -teamsPath $programData 27 | } 28 | else 29 | { 30 | Write-Output "Classic Teams not installed for user $($user.Name)" 31 | } 32 | } 33 | 34 | $oldFolder = "$($env:SystemDrive)\Users\*\AppData\Local\Microsoft\Teams" 35 | $oldIcon = "$($env:SystemDrive)\Users\*\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Microsoft Teams*.lnk" 36 | 37 | Get-Item $oldFolder | Remove-Item -Recurse -Force 38 | Get-Item $oldIcon | Remove-Item -Recurse -Force 39 | 40 | New-Item -Path "C:\ProgramData\Microsoft" -ItemType File -Name "classicTeamsUninstall.txt" -------------------------------------------------------------------------------- /Misc Intune/InstallChromeIfOOBEComplete.ps1: -------------------------------------------------------------------------------- 1 | $Definition = @" 2 | 3 | using System; 4 | using System.Text; 5 | using System.Collections.Generic; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace Api 9 | { 10 | public class Kernel32 11 | { 12 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 13 | public static extern int OOBEComplete(ref int bIsOOBEComplete); 14 | } 15 | } 16 | "@ 17 | 18 | function log 19 | { 20 | Param( 21 | [string]$message 22 | ) 23 | $time = Get-Date -Format "yyyy-MM-dd HH:mm:ss tt" 24 | $output = "$time - $message" 25 | Write-Output $output 26 | } 27 | 28 | Add-Type -TypeDefinition $Definition -Language CSharp 29 | 30 | $IsOOBEComplete = $false 31 | [void][Api.Kernel32]::OOBEComplete([ref] $IsOOBEComplete) 32 | 33 | $logFile = "C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\ChromeInstaller.log" 34 | 35 | if(-not(Test-Path $logFile)) 36 | { 37 | New-Item -Path $logFile -ItemType File -Force | Out-Null 38 | } 39 | 40 | Start-Transcript -Path $logFile -Verbose -Append 41 | 42 | log "IsOOBEComplete variable is equal to $($IsOOBEComplete)" 43 | 44 | if(-not $IsOOBEComplete) 45 | { 46 | log "OOBE is not complete. Skipping Chrome install." 47 | Exit 0 # Intune will retry later 48 | } 49 | 50 | log "OOBE complete. Installing Chrome..." 51 | 52 | Start-Process -FilePath "$($PSScriptRoot)\GoogleChromeStandaloneEnterprise64.msi" -ArgumentList "/qn" -Wait -NoNewWindow 53 | 54 | log "Chrome installation completed." 55 | 56 | Stop-Transcript 57 | Exit 0 58 | -------------------------------------------------------------------------------- /Misc Intune/groupTagWithoutAP.ps1: -------------------------------------------------------------------------------- 1 | # This powershell script can apply a group tag to a device without Autopilot registration. 2 | 3 | $clientID = "YOUR_CLIENT_ID" 4 | $groupTag = "YOUR_GROUP_TAG" 5 | $clientSecret = "YOUR_CLIENT SECRET" 6 | $tenant = "YOUR_TENANT_NAME.COM" 7 | 8 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 9 | $headers.Add("Content-Type", "application/x-www-form-urlencoded") 10 | 11 | $body = "grant_type=client_credentials&scope=https://graph.microsoft.com/.default" 12 | $body += -join ("&client_id=" , $clientId, "&client_secret=", $clientSecret) 13 | 14 | $response = Invoke-RestMethod "https://login.microsoftonline.com/$tenant/oauth2/v2.0/token" -Method 'POST' -Headers $headers -Body $body 15 | 16 | #Get Token form OAuth. 17 | $token = -join ("Bearer ", $response.access_token) 18 | 19 | #Reinstantiate headers. 20 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 21 | $headers.Add("Authorization", $token) 22 | $headers.Add("Content-Type", "application/json") 23 | 24 | $entraDeviceId = ((Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.Issuer -match "MS-Organization-Access"} | Select-Object Subject).Subject).TrimStart("CN=") 25 | $physicalIds = (Invoke-RestMethod GET -Uri "https://graph.microsoft.com/beta/devices/$($entraDeviceId)" -Headers $headers).physicalIds 26 | $groupTag = "[OrderID]:$($groupTag)" 27 | $physicalIds += $groupTag 28 | 29 | $body = @{ 30 | physicalIds = $physicalIds 31 | } | ConvertTo-Json 32 | 33 | Invoke-RestMethod -Method PATCH -Uri "https://graph.microsoft.com/beta/devices/$($entraDeviceId)" -Headers $headers -Body $body 34 | -------------------------------------------------------------------------------- /mdmCertCheck.ps1: -------------------------------------------------------------------------------- 1 | # PowerShell Script to Check for Intune Device Certificate 2 | 3 | # Define the certificate store location 4 | $certStore = "Cert:\LocalMachine\My" 5 | 6 | # Search criteria: Common name or issuer name for Intune certificate 7 | $searchCriteria = "Microsoft Intune MDM Device CA" 8 | 9 | # Get certificates from the specified store 10 | Write-Host "Searching for Intune Device Certificates in $certStore..." 11 | $certificates = Get-ChildItem -Path $certStore | Where-Object { 12 | $_.Issuer -like "*$searchCriteria*" -or $_.Subject -like "*$searchCriteria*" 13 | } 14 | 15 | if ($certificates) { 16 | Write-Host "Found Intune Device Certificate(s):" -ForegroundColor Green 17 | foreach ($cert in $certificates) { 18 | Write-Host "Subject: $($cert.Subject)" 19 | Write-Host "Issuer: $($cert.Issuer)" 20 | Write-Host "Thumbprint: $($cert.Thumbprint)" 21 | Write-Host "Not Before: $($cert.NotBefore)" 22 | Write-Host "Not After: $($cert.NotAfter)" 23 | Write-Host "-----------------------------------------" 24 | } 25 | } else { 26 | Write-Host "No Intune Device Certificate found." -ForegroundColor Red 27 | } 28 | 29 | # Check if any certificate is valid 30 | $validCertificates = $certificates | Where-Object { 31 | $_.NotAfter -gt (Get-Date) -and $_.NotBefore -lt (Get-Date) 32 | } 33 | 34 | if ($validCertificates) { 35 | Write-Host "Valid Intune Device Certificate(s) Found:" -ForegroundColor Green 36 | foreach ($cert in $validCertificates) { 37 | Write-Host "Subject: $($cert.Subject)" 38 | Write-Host "Expires On: $($cert.NotAfter)" 39 | } 40 | } else { 41 | Write 42 | -------------------------------------------------------------------------------- /bitlockerAutoRotate/V1/webhookScript.ps1: -------------------------------------------------------------------------------- 1 | [cmdletbinding()] 2 | 3 | param( 4 | [object]$WebhookData 5 | ) 6 | 7 | ## WebhookData 8 | 9 | if($WebhookData) 10 | { 11 | $bodyData = ConvertFrom-Json -InputObject $WebhookData.RequestBody 12 | $deviceId = ((($bodyData.deviceId) | Out-String).Trim()) 13 | } 14 | 15 | Connect-MgGraph -Identity -Scopes BitlockerKey.Read.All 16 | 17 | $recoveryKeys = (Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/informationProtection/bitlocker/recoveryKeys?`$filter=deviceId eq '$($deviceId)'") 18 | 19 | $key = $recoveryKeys.value | Sort-Object -Property createdDateTime -Descending | Select-Object -First 1 20 | 21 | $createdDateTime = $key.createdDateTime 22 | $intuneDeviceObject = (Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=azureADDeviceId eq '$($deviceId)'").value 23 | 24 | $intuneId = $intuneDeviceObject.id 25 | $intuneName = $intuneDeviceObject.deviceName 26 | 27 | $cutOffDate = (Get-Date).AddDays(-30) 28 | Write-Output "Checking $($intuneName) for key rotation..." 29 | if($createdDateTime -lt $cutOffDate) 30 | { 31 | Write-Output "Recovery key for $($intuneName) was updated within the last 30 days, no need to rotate" 32 | } 33 | else 34 | { 35 | Write-Output "Recovery key for $($intuneName) was not updated within the last 30 days, attempting to rotate..." 36 | try 37 | { 38 | Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$($intuneId)/rotateBitlockerKeys" 39 | Write-Output "Successfully rotated key for $($intuneName)" 40 | } 41 | catch 42 | { 43 | $message = $_.Exception.Message 44 | Write-Output "Failed to rotate key for $($intuneName): $message" 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Autopilot Helper Scripts/autoAutopilot.ps1: -------------------------------------------------------------------------------- 1 | # AUTHENTICATE TO MS GRAPH 2 | $clientId = "" 3 | $clientSecret = "" 4 | $tenantId = "" 5 | 6 | $tokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" 7 | $tokenRequestBody = @{ 8 | client_id = $clientId 9 | client_secret = $clientSecret 10 | scope = "https://graph.microsoft.com/.default" 11 | grant_type = "client_credentials" 12 | } 13 | 14 | $tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenEndpoint -Body $tokenRequestBody 15 | $accessToken = $tokenResponse.access_token 16 | 17 | $headers = @{ 18 | "Authorization" = "Bearer $accessToken" 19 | } 20 | 21 | 22 | 23 | # GET HARDWARE INFO 24 | $serialNumber = (Get-WmiObject -Class Win32_BIOS).SerialNumber 25 | $hardwareId = ((Get-WmiObject -Namespace root/cimv2/mdm/dmmap -Class MDM_DevDetail_Ext01 -Filter "InstanceID='Ext' AND ParentID='./DevDetail'").DeviceHardwareData) 26 | $groupTag = "M365" 27 | 28 | # CONSTRUCT JSON 29 | $json = @" 30 | { 31 | "@odata.type": "#microsoft.graph.importedWindowsAutopilotDeviceIdentity", 32 | "groupTag":"$groupTag", 33 | "serialNumber":"$serialNumber", 34 | "productKey":"", 35 | "hardwareIdentifier":"$hardwareId", 36 | "assignedUserPrincipalName":"", 37 | "state":{ 38 | "@odata.type":"microsoft.graph.importedWindowsAutopilotDeviceIdentityState", 39 | "deviceImportStatus":"pending", 40 | "deviceRegistrationId":"", 41 | "deviceErrorCode":0, 42 | "deviceErrorName":"" 43 | } 44 | } 45 | "@ 46 | 47 | # POST DEVICE 48 | Invoke-RestMethod -Method Post -Body $json -ContentType "application/json" -Uri "https://graph.microsoft.com/beta/deviceManagement/importedWindowsAutopilotDeviceIdentities" -Headers $headers 49 | -------------------------------------------------------------------------------- /Misc Intune/writeExtensionAttribute.ps1: -------------------------------------------------------------------------------- 1 | 2 | # TENANT A CREDENTIALS 3 | $clientId = "" 4 | $clientSecret = "" 5 | $tenant = "" 6 | 7 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 8 | $headers.Add("Content-Type", "application/x-www-form-urlencoded") 9 | 10 | $body = "grant_type=client_credentials&scope=https://graph.microsoft.com/.default" 11 | $body += -join ("&client_id=" , $clientId, "&client_secret=", $clientSecret) 12 | 13 | $response = Invoke-RestMethod "https://login.microsoftonline.com/$tenant/oauth2/v2.0/token" -Method 'POST' -Headers $headers -Body $body 14 | 15 | #Get Token form OAuth. 16 | $token = -join ("Bearer ", $response.access_token) 17 | 18 | #Reinstantiate headers. 19 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 20 | $headers.Add("Authorization", $token) 21 | $headers.Add("Content-Type", "application/json") 22 | Write-Host "MS Graph Authenticated" 23 | 24 | #============================================================# 25 | 26 | # Get UserSID 27 | $activeUsername = (Get-WmiObject Win32_ComputerSystem | Select-Object | username).username 28 | $objUser = New-Object System.Security.Principal.NTAccount("$activeUsername") 29 | $strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier]) 30 | $userSID = $strSID.Value 31 | 32 | # Get full UPN 33 | $regPath = "HKLM:\SOFTWARE\Microsoft\IdentityStore\Cache\$($userSID)\IdentityCache\$($userSID)" 34 | $upn = Get-ItemPropertyValue -Path $regPath -Name UserName 35 | 36 | $userObject = Invoke-RestMethod -Method GET -Uri "https://graph.microsoft.com/beta/users/$($upn)" -Headers $headers 37 | 38 | $attribute = $userObject.Value.extensionAttribute7 39 | 40 | reg.exe add 'HKLM\SOFTWARE\IntuneMigration' /v ExtensionAttribute /t REG_SZ /d $attribute /f /reg:64 | Out-Host 41 | -------------------------------------------------------------------------------- /AppToast/Interface.ps1: -------------------------------------------------------------------------------- 1 | Add-Type -AssemblyName PresentationFramework 2 | 3 | [xml]$xaml = @" 4 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | "@ 35 | 36 | $reader = (New-Object System.Xml.XmlNodeReader $xaml) 37 | $window = [Windows.Markup.XamlReader]::Load($reader) 38 | 39 | $window.ShowDialog() -------------------------------------------------------------------------------- /Misc Intune/getScripts.ps1: -------------------------------------------------------------------------------- 1 | # Install Microsoft Graph module 2 | if (-not (Get-PackageProvider -ListAvailable -Name nuget -ErrorAction SilentlyContinue)) { 3 | Install-PackageProvider -Name NuGet -Confirm:$false -Force 4 | } 5 | 6 | if (-not (Get-InstalledModule -Name Microsoft.Graph -ListAvailable -ErrorAction SilentlyContinue)) { 7 | Install-Module -Name Microsoft.Graph -Confirm:$false -Force 8 | } 9 | 10 | Import-Module Microsoft.Graph -Scope CurrentUser 11 | 12 | # Connect to Microsoft Graph 13 | Connect-MgGraph -Scopes "DeviceManagementScripts.ReadWrite.All" 14 | 15 | # Create a directory to save scripts to 16 | $scriptDirectory = "C:\IntuneScripts" 17 | if(-not (Test-Path $scriptDirectory)) { 18 | New-Item -ItemType Directory -Path $scriptDirectory -Force 19 | Write-Output "Created directory: $scriptDirectory" 20 | } 21 | 22 | # Construct the graph call to get all scripts 23 | $scriptsUri = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts?`$select=id,displayName,fileName" 24 | $platformScripts = @() 25 | 26 | do { 27 | $reponse = Invoke-MgGraphRequest -Method GET -Uri $scriptsUri 28 | $platformScripts += $reponse.value 29 | $scriptsUri = $response.'@odata.nextLink' 30 | } while ($scriptsUri) 31 | 32 | 33 | # Loop through each script to get and decode contents 34 | foreach ($script in $platformScripts) { 35 | $scriptUri = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts/$($script.id)?`$select=scriptContent" 36 | $scriptResponse = Invoke-MgGraphRequest -Method GET -Uri $scriptUri 37 | $decodedContent = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($scriptResponse.scriptContent)) 38 | 39 | # Save to .ps1 file 40 | $filePath = Join-Path $scriptDirectory $($script.fileName) 41 | $decodedContent | Out-File -FilePath $filePath -Encoding UTF8 42 | 43 | Write-Output "Exported: $($script.displayName) to $filePath" 44 | } 45 | 46 | 47 | -------------------------------------------------------------------------------- /Chocolatey scripts/chocoInstall.ps1: -------------------------------------------------------------------------------- 1 | # Parameters for app name and package name 2 | Param( 3 | [Parameter(Mandatory=$True,Position=1)] 4 | [string]$app 5 | ) 6 | 7 | <# Check for 64 bit powershell 8 | if("$env:PROCESSOR_ARCHITEW6432" -ne "ARM64") 9 | { 10 | if(Test-Path "$($env:windir)\SysNative\WindowsPowerShell\v1.0\powershell.exe") 11 | { 12 | & "$($env:windir)\SysNative\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy bypass -NoProfile -File "$PSCommandPath" 13 | exit $LASTEXITCODE 14 | } 15 | }#> 16 | 17 | # make log path and start logging 18 | $logPath = "C:\ProgramData\Microsoft\IntuneApps\$($app)" 19 | if(!(Test-Path $logPath)) 20 | { 21 | mkdir $logPath 22 | } 23 | Start-Transcript -Path "$($logPath)\$($app)_Install.log" 24 | 25 | # Check for chocolatey and install 26 | $choco = "C:\ProgramData\chocolatey" 27 | Write-Host "Checking if Chocolatey is installed on $($env:COMPUTERNAME)..." 28 | 29 | if(!(Test-Path $choco)) 30 | { 31 | Write-Host "Chocolatey was not found; installing now..." 32 | try 33 | { 34 | Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) 35 | Write-Host "Chocolatey was successfully installed." 36 | } 37 | catch 38 | { 39 | $message = $_ 40 | Write-Host "Error installing Chocolatey: $message" 41 | } 42 | } 43 | else 44 | { 45 | Write-Host "Chocolatey already installed." 46 | } 47 | 48 | # Check for app and install 49 | Write-Host "Checking if $($app) is installed on $($env:COMPUTERNAME)..." 50 | $installed = choco list | Select-String $app 51 | 52 | if($installed -eq $null) 53 | { 54 | Write-Host "$($app) was not found; installing now..." 55 | try 56 | { 57 | Start-Process -Wait -FilePath "$($choco)\choco.exe" -ArgumentList "install $($app) -y" 58 | Write-Host "$($app) was successfully installed." 59 | } 60 | catch 61 | { 62 | $message = $_ 63 | Write-Host "Error installing $($app): $message" 64 | } 65 | } 66 | else 67 | { 68 | Write-Host "$($app) is already installed." 69 | } 70 | 71 | Stop-Transcript -------------------------------------------------------------------------------- /New Teams Scripts/New Teams/uninstallClassicTeams.ps1: -------------------------------------------------------------------------------- 1 | function Uninstall-TeamsClassic($teamsPath) 2 | { 3 | try 4 | { 5 | $process = Start-Process -FilePath "$($teamsPath)\Update.exe" -ArgumentList "--uninstall /s" -PassThru -Wait -ErrorAction Stop 6 | if($process.ExitCode -ne 0) 7 | { 8 | $message = $_.Exception.Message 9 | Write-Host "Error uninstalling Classic Teams Client: $message" 10 | } 11 | } 12 | catch 13 | { 14 | $message = $_.Exception.Message 15 | } 16 | } 17 | 18 | 19 | $registryPath = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" 20 | 21 | $MachineWide = Get-ItemProperty -Path $registryPath | Where-Object {$_.DisplayName -eq "Teams Machine-Wide Installer"} 22 | 23 | if($MachineWide) 24 | { 25 | Start-Process -FilePath "msiexec.exe" -ArgumentList "/x ""$($MachineWide.PSChildName)"" /qn" -NoNewWindow -Wait 26 | } 27 | else 28 | { 29 | Write-Host "Teams Classic (Machine-Wide installer) not found" 30 | } 31 | 32 | $AllUsers = Get-ChildItem -Path "$($ENV:SystemDrive)\Users" 33 | 34 | foreach($user in $AllUsers) 35 | { 36 | Write-Host "Processing user: $($user.Name)" 37 | $localAppData = "$($ENV:SystemDrive)\Users\$($user.name)\AppData\Local\Microsoft\Teams" 38 | $programData = "$($ENV:ProgramData)\$($User.Name)\Microsoft\Teams" 39 | 40 | if(Test-Path "$localAppdata\Current\Teams.exe") 41 | { 42 | Write-Host "Uninstall Teams for user $($user.Name)" 43 | Uninstall-TeamsClassic -teamsPath $localAppData 44 | } 45 | elseif(Test-Path "$programData\Current\Teams.exe") 46 | { 47 | Write-Host "Uninstall Teams for user $($user.Name)" 48 | Uninstall-TeamsClassic -teamsPath $programData 49 | } 50 | else 51 | { 52 | Write-Host "Teams classic not found for user $($user.Name)" 53 | } 54 | } 55 | 56 | $TeamsFolder_old = "$($ENV:SystemDrive)\Users\*\AppData\Local\Microsoft\Teams" 57 | $TeamsIcon_old = "$($ENV:SystemDrive)\Users\*\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Microsoft Teams*.lnk" 58 | Get-Item $TeamsFolder_old | Remove-Item -Force -Recurse 59 | Get-Item $TeamsIcon_old | Remove-Item -Force Recurse 60 | 61 | -------------------------------------------------------------------------------- /Misc Intune/createHyperV-V2.ps1: -------------------------------------------------------------------------------- 1 | # Parameters for VMNAME, WINDOWS VERSION, and CPU COUNT 2 | Param( 3 | [Parameter(Mandatory=$True,Position=1)] 4 | [string]$Name, 5 | 6 | [Parameter(Mandatory=$True,Position=2)] 7 | [string]$version, 8 | 9 | [Parameter(Mandatory=$True,Position=3)] 10 | [string]$CPUCount, 11 | 12 | [Parameter(Mandatory=$False,Position=4)] 13 | [switch]$Autopilot = $False 14 | ) 15 | 16 | $number = Get-Random -Minimum 1000 -Maximum 10000 17 | $numberString = $number.ToString() 18 | $VMName = $Name + "-" + $version + "-" + $numberString 19 | 20 | # Copy disk from TEMPLATES FOLDER and place in Hyper-V directory with VM name 21 | Copy-Item -Path "D:\Templates\GM-$($version).vhdx" -Destination "C:\Hyper-V\Virtual hard disks\$($VMName).vhdx" -Force | Out-Null 22 | 23 | # Set some VM definitions 24 | $VMSwitchName = "Default Switch" 25 | $VhdxPath = "C:\Hyper-V\Virtual hard disks\$($VMName).vhdx" 26 | $VMPath = "C:\Hyper-V\Virtual machines" 27 | 28 | # VM settings and create the VM 29 | New-VM -Name $VMName -BootDevice VHD -VHDPath $VhdxPath -Path $VMPath -Generation 2 -Switch $VMSwitchName 30 | Set-VM -VMName $VMName -ProcessorCount $CPUCount 31 | Set-VMMemory -VMName $VMName -StartupBytes 8GB -DynamicMemoryEnabled $false 32 | Set-VMSecurity -VMName $VMName -VirtualizationBasedSecurityOptOut $false 33 | Set-VMKeyProtector -VMName $VMName -NewLocalKeyProtector 34 | Enable-VMTPM -VMName $VMName 35 | Enable-VMIntegrationService -VMName $VMName -Name "Guest Service Interface" 36 | Set-VM -VMName $VMName -AutomaticCheckpointsEnabled $false | Out-Host 37 | 38 | if($Autopilot -eq $True) 39 | { 40 | # make a path to export the csv to 41 | $exportPath = "C:\Autopilot" 42 | if(!(Test-Path $exportPath)) 43 | { 44 | mkdir $exportPath 45 | } 46 | # get the hardware info: manufacturer, model, serial 47 | $serial = Get-WmiObject -ComputerName localhost -Namespace root\virtualization\v2 -class Msvm_VirtualSystemSettingData | Where-Object {$_.elementName -eq $VMName} | Select-Object -ExpandProperty BIOSSerialNumber 48 | $data = "Microsoft Corporation,Virtual Machine,$($serial)" 49 | # add to CSV file in path 50 | Set-Content -Path "$($exportPath)\$($VMName).csv" -Value $data 51 | } -------------------------------------------------------------------------------- /Misc Intune/AssignmentsByGroup.ps1: -------------------------------------------------------------------------------- 1 | # Import Graph module 2 | #Import-Module Microsoft.Graph 3 | 4 | # Connect with required scopes 5 | #Connect-MgGraph -Scopes "DeviceManagementApps.Read.All","DeviceManagementConfiguration.Read.All","Group.Read.All" 6 | 7 | $graph = "https://graph.microsoft.com/beta" 8 | 9 | # Select Group 10 | $groups = (Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/groups?`$select=id,displayName").value 11 | 12 | $groupSelection = $groups | ForEach-Object { 13 | [PSCustomObject]@{ 14 | Name = $_.displayName 15 | Id = $_.id 16 | Raw = $_ 17 | } 18 | } 19 | 20 | $selectedGroup = $groupSelection | ogv -Title "Select a group" -PassThru 21 | 22 | if(-not $selectedGroup) 23 | { 24 | Write-Host "No group selected." 25 | return 26 | } 27 | 28 | $groupId = $selectedGroup.id 29 | Write-Host "`nSelected Group:`n$($selectedGroup.Name) ($groupId)`n" -ForegroundColor Cyan 30 | 31 | # Helper function 32 | function Get-AssignedItems 33 | { 34 | param( 35 | [string]$url, 36 | [string]$type, 37 | [string]$nameField = "displayName" 38 | ) 39 | 40 | $results = @() 41 | $items = Invoke-MgGraphRequest -Uri $url -Method GET 42 | 43 | foreach ($item in $items.value) 44 | { 45 | foreach ($assignment in $item.assignments) 46 | { 47 | if($assignment.target.groupId -eq $groupId) 48 | { 49 | $results += [PSCustomObject]@{ 50 | Type = $type 51 | Name = $item.$nameField 52 | } 53 | } 54 | } 55 | } 56 | 57 | return $results 58 | } 59 | 60 | # Get assignments 61 | $results = @() 62 | $results += Get-AssignedItems -url "$graph/deviceAppManagement/mobileApps?`$expand=assignments" -type "app" 63 | $results += Get-AssignedItems -url "$graph/deviceManagement/deviceConfigurations?`$expand=assignments" -type "Configuration Profiles" 64 | $results += Get-AssignedItems -url "$graph/deviceManagement/configurationPolicies?`$expand=assignments" -type "Settings Catalog Policy" -nameField "name" 65 | 66 | # Show results 67 | if($results.Count -eq 0) 68 | { 69 | Write-Host "No assignments found for this group." 70 | } 71 | else 72 | { 73 | $results | Sort-Object Type, Name | Format-Table Type, Name -AutoSize 74 | } 75 | -------------------------------------------------------------------------------- /Autopilot Helper Scripts/bulkGroupTagUpdate.ps1: -------------------------------------------------------------------------------- 1 | # install autopilot module 2 | $nuget = Get-PackageProvider -Name NuGet -ListAvailable -ErrorAction Ignore 3 | if(-not($nuget)) 4 | { 5 | Install-PackageProvider -Name NuGet -confirm:$false -Force 6 | Write-Host "Installed NuGet" 7 | } 8 | else 9 | { 10 | Write-Host "NuGet already installed" 11 | } 12 | 13 | $module = Get-Module -ListAvailable -Name WindowsAutopilotIntune -ErrorAction Ignore 14 | if(-not($module)) 15 | { 16 | Install-Module -Name WindowsAutopilotIntune -confirm:$false -Force 17 | Write-Host "Installed WindowsAutopilotIntune" 18 | } 19 | else 20 | { 21 | Write-Host "WindowsAutopilotIntune already installed" 22 | } 23 | 24 | 25 | # connect to microsoft graph 26 | Connect-MgGraph 27 | 28 | # You can change the group tag of autopilot devices using either a list of serial numbers or using an old group tag as a target 29 | 30 | # OPTION 1: Change group tag using a list of serial numbers 31 | 32 | # get list of serial numbers from CSV file 33 | $serialNumbers = Import-Csv -Path "C:\path\to\serialNumbers.csv" | Select-Object -ExpandProperty SerialNumber 34 | 35 | # for each serial number, get entra device object id 36 | foreach ($serialNumber in $serialNumbers) { 37 | try 38 | { 39 | $id = (Get-AutopilotDevice -serial $serialNumber).id 40 | Set-AutopilotDevice -id $id -GroupTag "NewGroupTag" 41 | Write-Host "Changed group tag for device with serial number $serialNumber" 42 | } 43 | catch 44 | { 45 | $message = $_.Exception.Message 46 | Write-Host "Failed to change group tag for device with serial number $($serialNumber): $message" 47 | } 48 | 49 | } 50 | 51 | # OPTION 2: Change group tag using an old group tag as a target 52 | $oldGroupTag = "OldGroupTag" 53 | 54 | # get list of devices with the old group tag 55 | $devices = Get-AutopilotDevice -GroupTag $oldGroupTag 56 | foreach($device in $devices) 57 | { 58 | try 59 | { 60 | Set-AutopilotDevice -id $device.id -GroupTag "NewGroupTag" 61 | Write-Host "Changed group tag for device with serial number $($device.serialNumber)" 62 | } 63 | catch 64 | { 65 | $message = $_.Exception.Message 66 | Write-Host "Failed to change group tag for device with serial number $($device.serialNumber): $message" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Misc Intune/removePersonalDevices.ps1: -------------------------------------------------------------------------------- 1 | function log() 2 | { 3 | Param( 4 | [string]$message 5 | ) 6 | $date = Get-Date -Format "yyyy-MM-dd hh:mm:ss tt" 7 | Write-Output "$date - $message" 8 | } 9 | 10 | 11 | # verify NuGet installed 12 | $nuget = Get-PackageProvider -ListAvailable -Name NuGet -ErrorAction Ignore 13 | if(-not($nuget)) 14 | { 15 | log "NuGet not found, installing..." 16 | Install-PackageProvider -Name NuGet -Confirm:$false -Force 17 | } 18 | else 19 | { 20 | log "NuGet already installed." 21 | } 22 | 23 | # verify Microsoft.Graph module 24 | $graphModule = Get-InstalledModule -Name Microsoft.Graph -ErrorAction Ignore 25 | if(-not($graphModule)) 26 | { 27 | log "Microsoft Graph module not found, installing now..." 28 | Install-Module -Name Microsoft.Graph -Confirm:$false -Force 29 | } 30 | else 31 | { 32 | log "Microsoft Graph module already installed." 33 | } 34 | 35 | <# Authentication method 1 - app reg and client secret 36 | # appreg goes here 37 | $clientID = "" 38 | # client secret goes here 39 | $clientSecret = "" 40 | $tenantID = "" 41 | 42 | $SecureClientSecret = ConvertTo-SecureString -String $clientSecret -AsPlainText -Force 43 | $ClientSecureCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $clientID, $SecureClientSecret 44 | Connect-MgGraph -ClientSecretCredential $ClientSecureCredential -TenantId $tenantID 45 | #> 46 | 47 | # Authentication method 2 - User sign in 48 | Connect-MgGraph -Scopes "DeviceManagementManagedDevices.ReadWrite.All" 49 | 50 | # Get personal Intune devices call to graph 51 | $personalDevices = (Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=ownerType eq 'personal'").value 52 | 53 | # Remove personal from Intune devic 54 | foreach($device in $personalDevices) 55 | { 56 | log "Found personal device $($device.deviceName)." 57 | $id = $device.id 58 | try 59 | { 60 | log "Attempting to delete $($device.deviceName) from tenant..." 61 | Invoke-MgGraphRequest -Method DELETE -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$($id)" 62 | log "Device $($device.deviceName) was deleted." 63 | } 64 | catch 65 | { 66 | log "Error trying to remove device $($device.deviceName): $_" 67 | } 68 | } 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Misc Intune/mapDrives.ps1: -------------------------------------------------------------------------------- 1 | # add an Intune detection rule 2 | $path = "C:\ProgramData\Microsoft\DriveMapping" 3 | if(!(Test-Path $path)) 4 | { 5 | mkdir $path 6 | } 7 | Set-Content -Path "$($path)\drivemapping.txt.tag" -Value "Installed" 8 | 9 | # authenticate to graph 10 | # user.read.all 11 | # group.read.all 12 | # groupmember.read.all 13 | $clientId = "" 14 | $clientSecret = "" 15 | $domain = "" 16 | 17 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 18 | $headers.Add("Content-Type", "application/x-www-form-urlencoded") 19 | 20 | $body = "grant_type=client_credentials&scope=https://graph.microsoft.com/.default" 21 | $body += -join("&client_id=" , $clientId, "&client_secret=", $clientSecret) 22 | 23 | $response = Invoke-RestMethod "https://login.microsoftonline.com/$($domain)/oauth2/v2.0/token" -Method 'POST' -Headers $header -Body $body 24 | $token = -join("Bearer ", $response.access_token) 25 | 26 | #Reinstantiate headers. 27 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 28 | $headers.Add("Authorization", $token) 29 | 30 | # get the user sid 31 | $user = (Get-WmiObject -Class Win32_ComputerSystem | Select-Object Username).Username 32 | $sid = (New-Object System.Security.Principal.NTAccount($user)).Translate([System.Security.Principal.SecurityIdentifier]).value 33 | 34 | # get the group memberships 35 | $upn = Get-ItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\IdentityStore\Cache\$($sid)\IdentityCache\$($sid)" -Name "UserName" 36 | $groupMemberships = Invoke-RestMethod -Method Get -uri "https://graph.microsoft.com/beta/users/$($upn)/memberOf" -Headers $headers | Select-Object -ExpandProperty onPremisesSamAccountName 37 | 38 | # loop through groups 39 | $drives = @("@echo off") 40 | 41 | foreach($group in $groupMemberships) 42 | { 43 | if($group -eq "Group A") 44 | { 45 | $drives += "net use g: \\rbxdv-dc-01\share_a" 46 | break 47 | } 48 | elseif($group -eq "Group B") 49 | { 50 | $drives += "net use g: \\rbxdv-dc-01\share_b" 51 | break 52 | } 53 | } 54 | 55 | $drives2string = $drives | Out-String 56 | 57 | # create desktop batch file to map 58 | New-Item -ItemType File -Path "C:\Users\Public\Desktop" -Name "Map Drives.bat" -Force 59 | Add-Content "C:\Users\Public\Desktop\Map Drives.bat" $drives2string | Set-Content "C:\Users\Public\Desktop\Map Drives.bat" -Force -------------------------------------------------------------------------------- /W365/PhysicalToCloudPC.ps1: -------------------------------------------------------------------------------- 1 | # Install Microsoft Graph module if not installed 2 | if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Intune)) { 3 | Install-Module Microsoft.Graph.Intune -Force -Scope CurrentUser 4 | } 5 | 6 | 7 | $deviceEndpoint = "https://graph.microsoft.com/beta/deviceManagement/managedDevices" 8 | 9 | # Connect to Microsoft Graph 10 | Connect-MgGraph -Scopes "Device.Read.All", "User.Read.All" 11 | 12 | $devices = Invoke-MgGraphRequest -Method Get -Uri "$($deviceEndpoint)?`$filter=operatingSystem eq 'Windows'" 13 | 14 | # Get all Windows devices managed by Intune 15 | $skuMapping = @{ 16 | "2vCPU-4GB-64GB" = @{ RAM = 4; Storage = 64 } 17 | "2vCPU-8GB-128GB" = @{ RAM = 8; Storage = 128 } 18 | "4vCPU-16GB-256GB" = @{ RAM = 16; Storage = 256 } 19 | "8vCPU-32GB-512GB" = @{ RAM = 32; Storage = 512 } 20 | } 21 | 22 | # Function to determine the best Windows 365 SKU 23 | function Get-Windows365SKU { 24 | param ($ram, $storage) 25 | 26 | # Sort SKUs by RAM and Storage (ensuring we always pick the smallest SKU that meets/exceeds both) 27 | $sortedSkus = $skuMapping.GetEnumerator() | 28 | Sort-Object { $_.Value.RAM }, { $_.Value.Storage } 29 | 30 | foreach ($sku in $sortedSkus) { 31 | $specs = $sku.Value 32 | if ($ram -le $specs.RAM -and $storage -le $specs.Storage) { 33 | return $sku.Key 34 | } 35 | } 36 | 37 | return "Custom SKU Required" 38 | } 39 | # Process device data 40 | $deviceReport = @() 41 | 42 | foreach ($device in $devices.value) { 43 | $deviceID = $device.id 44 | $deviceName = $device.deviceName 45 | $osVersion = $device.operatingSystemVersion 46 | $primaryUser = $device.userPrincipalName 47 | 48 | # Fetch hardware details for each device 49 | $hardwareUri = "$deviceEndpoint/$deviceID" 50 | $hardwareDetails = Invoke-MgGraphRequest -Method Get -Uri "$($hardwareUri)?`$select=hardwareInformation" 51 | $memoryDetails = Invoke-MgGraphRequest -Method Get -Uri "$($hardwareUri)?`$select=hardwareInformation,physicalMemoryInBytes" 52 | 53 | $storage = $hardwareDetails.hardwareInformation.totalStorageSpace / 1GB 54 | $ram = $memoryDetails.physicalMemoryInBytes / 1GB 55 | 56 | # Determine recommended Windows 365 SKU 57 | $recommendedSKU = Get-Windows365SKU -ram $ram -storage $storage 58 | 59 | # Add device info to the report 60 | $deviceReport += [PSCustomObject]@{ 61 | DeviceName = $deviceName 62 | OSVersion = $osVersion 63 | PrimaryUser = $primaryUser 64 | RAM = [math]::Round($ram, 2) 65 | Storage = [math]::Round($storage, 2) 66 | RecommendedSKU = $recommendedSKU 67 | } 68 | } 69 | 70 | # Display the report as a table 71 | $deviceReport | Format-Table -AutoSize 72 | 73 | -------------------------------------------------------------------------------- /Win11Readiness/remediate.ps1: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | $WU = @{ 4 | AU = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" 5 | WU = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" 6 | } 7 | 8 | $ToReset = @( 9 | @{ P='WU'; N='DoNotConnectToWindowsUpdateInternetLocations'; V=0 } 10 | @{ P='WU'; N='DisableDualScan'; V=0 }, 11 | @{ P='WU'; N='SetPolicyDrivenUpdateSourceForDriverUpdates'; V=0 }, 12 | @{ P='WU'; N='SetPolicyDrivenUpdateSourceForOtherUpdates'; V=0 }, 13 | @{ P='WU'; N='SetPolicyDrivenUpdateSourceForQualityUpdates'; V=0 }, 14 | @{ P='WU'; N='SetPolicyDrivenUpdateSourceForFeatureUpdates'; V=0 }, 15 | @{ P='WU'; N='DisableWindowsUpdateAccess'; V=0 }, 16 | @{ P='AU'; N='NoAutoUpdate'; V=0 }, 17 | @{ P='AU'; N='UseWUServer'; V=0 }, 18 | @{ P='AU'; N='UseUpdateClassPolicySource'; V=1 } 19 | ) 20 | 21 | $ToRemove = @('WUServer', 'WUStatusServer', 'TargetGroup', 'TargetGroupEnabled') 22 | 23 | # Function to set value 24 | function Set-Value { 25 | param( 26 | [string]$Path, 27 | [string]$Name, 28 | [int]$Value 29 | ) 30 | try { 31 | if (-not (Test-Path $Path)) { 32 | New-Item -Path $Path -Force | Out-Null 33 | } 34 | $curr = (Get-ItemProperty -Path $Path -ErrorAction SilentlyContinue).$Name 35 | if ($curr -ne $Value) { 36 | Set-ItemProperty -Path $Path -Name $Name -Value $Value -Type DWord -Force 37 | } 38 | } 39 | catch { 40 | Write-Host "$($_.Exception.Message)" 41 | } 42 | } 43 | 44 | # Function to remove reg key 45 | function Remove-Value { 46 | param( 47 | [string]$Path, 48 | [string]$Name 49 | ) 50 | try { 51 | if ((Get-ItemProperty -Path $Path -ErrorAction SilentlyContinue).PSObject.Properties.Name -contains $Name) { 52 | Remove-ItemProperty -Path $Path -Name $Name -Force 53 | } 54 | } 55 | catch { 56 | Write-Host "$($_.Exception.Message)" 57 | } 58 | } 59 | 60 | # Function to restart services 61 | function Restart-WU { 62 | foreach ($svc in 'wuauserv', 'bits', 'dosvc') { 63 | try { 64 | Restart-Service $svc -Force -ErrorAction Stop 65 | } catch { 66 | Write-Host "$($_.Exception.Message)" 67 | } 68 | } 69 | } 70 | 71 | # Function to trigger Update scan 72 | function Trigger-Scan { 73 | $uso = "$env:SystemRoot\System32\usoclient.exe" 74 | if (Test-Path $uso) { 75 | try { 76 | Start-Process $uso -ArgumentList 'StartInteractiveScan' -Wait -NoNewWindow 77 | } 78 | catch { 79 | Write-Host "$($_.Exception.Message)" 80 | } 81 | } 82 | } 83 | 84 | # Reset registry settings 85 | foreach ($s in $ToReset) { 86 | Set-Value -Path $WU[$s.P] -Name $s.N -Value $s.V 87 | } 88 | 89 | # Remove WSUS keys 90 | foreach ($r in $ToRemove) { 91 | Remove-Value -Path $WU.WU -Name $r 92 | } 93 | 94 | # Restart services and trigger scan 95 | Restart-WU 96 | Trigger-Scan 97 | 98 | exit 0 -------------------------------------------------------------------------------- /Win11Readiness/detect.ps1: -------------------------------------------------------------------------------- 1 | # Basic Configuration 2 | $WUKeys = @{ 3 | AU = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" 4 | WU = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" 5 | } 6 | 7 | $Critical = @('NoAutoUpdate', 'UseWUServer', 'DisableDualScan') 8 | $Audit = @('DoNotConnectToWindowsUpdateInternetLocations', 'SetPolicyDrivenUpdateSourceForDriverUpdates', 'SetPolicyDrivenUpdateSourceForOtherUpdates', 'SetPolicyDrivenUpdateSourceForQualityUpdates', 'SetPolicyDrivenUpdateSourceForFeatureUpdates', 'DisableWindowsUpdateAccess', 'WUServer', 'TargetGroup', 'WUStatusServer', 'TargetGroupEnable') 9 | 10 | $Result = @{ 11 | Issues = @() 12 | Status = 'Healthy' 13 | } 14 | 15 | # Registry Properties 16 | function Get-RegValue { 17 | param( 18 | [string]$Path, 19 | [string]$Property, 20 | [object]$ExpectedValue 21 | ) 22 | try { 23 | $val = (Get-ItemProperty -Path $Path -ErrorAction SilentlyContinue).$Property 24 | if ($null -ne $val) { 25 | return @{ 26 | Exists = $true 27 | Value = $val 28 | MatchesExpected = ($PSBoundParameters.ContainsKey('ExpectedValue') -and $val -eq $ExpectedValue) 29 | } 30 | } else { 31 | return @{ Exists = $false } 32 | } 33 | } catch { 34 | return @{ Exists = $false } 35 | } 36 | } 37 | 38 | # Test Windows Update Service 39 | function Test-WUService { 40 | try { 41 | $mgr = New-Object -ComObject 'Microsoft.Update.ServiceManager' 42 | $svc = $mgr.Services | Where-Object { $_.Name -eq 'Microsoft Update' } 43 | if ($svc) { 44 | return @{ 45 | IsDefault = $svc.IsDefaultAUService 46 | } 47 | } else { 48 | return @{ IsDefault = $false } 49 | } 50 | } 51 | catch { 52 | return @{ IsDefault = $false } 53 | } 54 | } 55 | 56 | # Detection logic 57 | foreach($s in $Critical) { 58 | $path = if ($s -eq 'DisableDualScan') { 59 | $WUKeys.WU 60 | } else { 61 | $WUKeys.AU 62 | } 63 | $r = Get-RegValue -Path $path -Property $s -ExpectedValue 0 64 | if ($r.Exists -and $r.Value -eq 1) { 65 | $Result.Issues += "$s = 1" 66 | $Result.Status = 'RequiresRemediation' 67 | } 68 | } 69 | 70 | $UseUpdateClass = Get-RegValue -Path $WUKeys.AU -Property 'UseUpdateClassPolicySource' -ExpectedValue 1 71 | if (-not $UseUpdateClass.Exists -or $UseUpdateClass.Value -ne 1) { 72 | $Result.Issues += 'UseUpdateClassPolicySource misconfigured or missing' 73 | $Result.Status = 'RequiresRemediation' 74 | } 75 | 76 | $WUService = Test-WUService 77 | if (-not $WUService.IsDefault) { 78 | $Result.Status = 'RequiresRemediation' 79 | } 80 | 81 | foreach ($a in $Audit) { 82 | $chk = Get-RegValue -Path $WUKeys.WU -Property $a 83 | if ($chk.Exists) { 84 | Write-Host " $a = $($chk.Value)" 85 | } 86 | } 87 | 88 | # Exit summary 89 | if ($Result.Status -eq 'RequiresRemediation') { 90 | exit 1 91 | } else { 92 | exit 0 93 | } -------------------------------------------------------------------------------- /Misc Intune/updatePrimaryUser.ps1: -------------------------------------------------------------------------------- 1 | # Define your Azure AD app details 2 | $clientId = "your-client-id" 3 | $tenantId = "your-tenant-id" 4 | $clientSecret = "your-client-secret" 5 | $scope = "https://graph.microsoft.com/.default" 6 | 7 | # Get the OAuth 2.0 token 8 | $body = @{ 9 | grant_type = "client_credentials" 10 | scope = $scope 11 | client_id = $clientId 12 | client_secret = $clientSecret 13 | } 14 | $tokenResponse = Invoke-WebRequest -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -Method Post -ContentType "application/x-www-form-urlencoded" -Body $body 15 | $token = ($tokenResponse.Content | ConvertFrom-Json).access_token 16 | 17 | # Function to get the last logged on user 18 | function Get-LastLoggedOnUser($deviceId, $token) { 19 | $uri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/$deviceId" 20 | $headers = @{ 21 | Authorization = "Bearer $token" 22 | } 23 | $response = Invoke-WebRequest -Uri $uri -Headers $headers -Method Get 24 | $deviceDetails = $response.Content | ConvertFrom-Json 25 | return $deviceDetails.userPrincipalName 26 | } 27 | 28 | # Function to get the primary user 29 | function Get-PrimaryUser($deviceId, $token) { 30 | $uri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/$deviceId/users" 31 | $headers = @{ 32 | Authorization = "Bearer $token" 33 | } 34 | $response = Invoke-WebRequest -Uri $uri -Headers $headers -Method Get 35 | $primaryUser = ($response.Content | ConvertFrom-Json).value | Where-Object { $_.isPrimaryUser -eq $true } 36 | return $primaryUser.userPrincipalName 37 | } 38 | 39 | # Function to update the primary user 40 | function Update-PrimaryUser($deviceId, $newPrimaryUser, $token) { 41 | $uri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/$deviceId/assignUser" 42 | $headers = @{ 43 | Authorization = "Bearer $token" 44 | "Content-Type" = "application/json" 45 | } 46 | $body = @{ 47 | userPrincipalName = $newPrimaryUser 48 | } | ConvertTo-Json 49 | Invoke-WebRequest -Uri $uri -Headers $headers -Method Post -Body $body 50 | Write-Output "Updated primary user to $newPrimaryUser for device $deviceId" 51 | } 52 | 53 | # Get list of all managed devices 54 | $uri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices" 55 | $headers = @{ 56 | Authorization = "Bearer $token" 57 | } 58 | $response = Invoke-WebRequest -Uri $uri -Headers $headers -Method Get 59 | $devices = ($response.Content | ConvertFrom-Json).value 60 | 61 | foreach ($device in $devices) { 62 | $deviceId = $device.id 63 | $lastLoggedOnUser = Get-LastLoggedOnUser -deviceId $deviceId -token $token 64 | $primaryUser = Get-PrimaryUser -deviceId $deviceId -token $token 65 | 66 | if ($lastLoggedOnUser -ne $primaryUser) { 67 | Write-Output "Mismatch found for device $deviceId. Updating primary user from $primaryUser to $lastLoggedOnUser" 68 | Update-PrimaryUser -deviceId $deviceId -newPrimaryUser $lastLoggedOnUser -token $token 69 | } else { 70 | Write-Output "No mismatch for device $deviceId. Primary user: $primaryUser, Last logged on user: $lastLoggedOnUser" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /HybridConnectorFixer.ps1: -------------------------------------------------------------------------------- 1 | # Setup 2 | $WorkingDir = "$env:ProgramData\IntuneConnectorInstall" 3 | $WebView2Installer = "$WorkingDir\MicrosoftEdgeWebView2Setup.exe" 4 | $IntuneConnectorInstaller = "$WorkingDir\ODJConnectorBootstrapper.exe" 5 | $IntuneConnectorProductName = "Intune Connector for Active Directory" 6 | $RequiredConnectorVersion = "6.2505.0" 7 | $RequiredDotNetRelease = 461808 # 4.7.2 8 | 9 | # Make a working directory 10 | if(-not(Test-Path $WorkingDir)) 11 | { 12 | mkdir $WorkingDir 13 | } 14 | 15 | # Check for WebView2 16 | $WebView2Installed = Get-ItemProperty HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* -ErrorAction SilentlyContinue | Where-Object {$_.DisplayName -like "*Microsoft Edge WebView2"} 17 | 18 | if($WebView2Installed) 19 | { 20 | Write-Host "Edge WebView2 already installed" 21 | } 22 | else 23 | { 24 | Write-Host "Downloading and installing WebView2..." 25 | Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -OutFile $WebView2Installer -UseBasicParsing 26 | Start-Process -FilePath $WebView2Installer -ArgumentList "/silent /install" -Wait 27 | } 28 | 29 | # Check for Net 4.7.2 30 | try 31 | { 32 | $dotNetRelease = Get-ItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" -Name Release -ErrorAction Stop 33 | if($dotNetRelease -lt $RequiredDotNetRelease) 34 | { 35 | Write-Warning ".NET Framework 4.7.2 or later is required. Detected Release: $dotNetRelase" 36 | Write-Warning "Please install .NET Framework 4.7.2 before continuing." 37 | exit 1 38 | } 39 | else 40 | { 41 | Write-Host ".NET Framework 4.7.2 or later is present." 42 | } 43 | } 44 | catch 45 | { 46 | Write-Warning "Unable to detect .NET Framework. Please ensure 4.7.2 or later is installed." 47 | exit 1 48 | } 49 | 50 | 51 | # Check and uninstall previous Intune Connector 52 | 53 | $IntuneConnector = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-Object {$_.DisplayName -like "*$($IntuneConnectorProductName)*"} | Sort-Object DisplayVersion -Descending | Select-Object -First 1 54 | 55 | if($IntuneConnector) 56 | { 57 | Write-Host "Found connector: $($IntuneConnector.DisplayName) v$($IntuneConnector.DisplayVersion)" 58 | Write-Host "Uninstalling existing version..." 59 | 60 | if($IntuneConnector.UninstallString) 61 | { 62 | $uninstallCmd = $IntuneConnector.UninstallString -replace '"','' 63 | if($uninstallCmd -like "msiexec*") 64 | { 65 | Start-Process "msiexec.exe" -ArgumentList "/x", "$($IntuneConnector.PSChildName)", "/quiet", "/norestart" -Wait 66 | } 67 | else 68 | { 69 | Start-Process -FilePath "cmd.exe" -ArgumentList "/c", "$uninstallCmd /quiet /norestart" -Wait 70 | } 71 | } 72 | } 73 | else 74 | { 75 | Write-Host "Connector not installed." 76 | } 77 | 78 | # Install new connector 79 | if(-not(Test-Path $IntuneConnectorInstaller)) 80 | { 81 | Write-Host "Downloading updated connector..." 82 | Invoke-WebRequest -Uri "https://download.microsoft.com/download/45476bf5-d8be-43a7-8e44-e76a4d1ab28f/ODJConnectorBootstrapper.exe" -OutFIle $IntuneConnectorInstaller -UseBasicParsing 83 | } 84 | 85 | Write-Host "Installing Intune Connector..." 86 | Start-Process -FilePath $IntuneConnectorInstaller -Wait 87 | -------------------------------------------------------------------------------- /bitlockerAutoRotate/V1/MrTbone_Set-MgAzureAutomationAccountPermissions.ps1: -------------------------------------------------------------------------------- 1 | <#PSScriptInfo 2 | .SYNOPSIS 3 | Script for setting permissions on Azure Automation Accounts 4 | 5 | .DESCRIPTION 6 | This script will assign specified permissions on an Azure Automation Account 7 | 8 | .EXAMPLE 9 | .\Set-MgAzureAutomationAccountPermissions.ps1 10 | will assign specified permissions on an Azure Automation Account with settings in the modifyable region. 11 | 12 | .NOTES 13 | Written by Mr-Tbone (Tbone Granheden) Coligo AB 14 | torbjorn.granheden@coligo.se 15 | 16 | .VERSION 17 | 1.0 18 | 19 | .RELEASENOTES 20 | 1.0 2024-01-04 Initial Build 21 | 22 | .AUTHOR 23 | Tbone Granheden 24 | @MrTbone_se 25 | 26 | .COMPANYNAME 27 | Coligo AB 28 | 29 | .GUID 30 | 00000000-0000-0000-0000-000000000000 31 | 32 | .COPYRIGHT 33 | Feel free to use this, But would be grateful if My name is mentioned in Notes 34 | 35 | .CHANGELOG 36 | 1.0.2401.1 - Initial Version 37 | #> 38 | 39 | #region ---------------------------------------------------[Set script requirements]----------------------------------------------- 40 | #endregion 41 | 42 | #region ---------------------------------------------------[Script Parameters]----------------------------------------------- 43 | #endregion 44 | 45 | #region ---------------------------------------------------[Modifiable Parameters and defaults]------------------------------------ 46 | $TenantID = "d6f19297-08a2-4ade-9a68-0db7586d80ad" 47 | $ManagedIdentity = "Tbone-IntuneAutomation" 48 | $Permissions = @( 49 | "DeviceManagementManagedDevices.ReadWrite.All" 50 | "AuditLog.Read.All" 51 | "User.Read.All" 52 | ) 53 | #endregion 54 | 55 | #region ---------------------------------------------------[Static Variables]------------------------------------------------------ 56 | $GraphAppId = "00000003-0000-0000-c000-000000000000" # Don't change this. 57 | $AdminPermissions = @("Application.Read.All","AppRoleAssignment.ReadWrite.All") # To be able to set persmissions on the Managed Identity 58 | #endregion 59 | 60 | #region ---------------------------------------------------[Import Modules and Extensions]----------------------------------------- 61 | import-module Microsoft.Graph.Authentication 62 | import-module Microsoft.Graph.Applications 63 | #endregion 64 | 65 | #region ---------------------------------------------------[Functions]------------------------------------------------------------ 66 | #endregion 67 | 68 | #region ---------------------------------------------------[[Script Execution]------------------------------------------------------ 69 | Connect-MgGraph -TenantId $TenantId -Scopes $AdminPermissions 70 | $IdentityServicePrincipal = Get-MgServicePrincipal -Filter "DisplayName eq '$managedidentity'" 71 | $GraphServicePrincipal = Get-MgServicePrincipal -Filter "appId eq '$GraphAppId'" 72 | $AppRoles = $GraphServicePrincipal.AppRoles | Where-Object {$_.Value -in $Permissions -and $_.AllowedMemberTypes -contains "Application"} 73 | 74 | foreach($AppRole in $AppRoles) 75 | { 76 | $AppRoleAssignment = @{ 77 | "PrincipalId" = $IdentityServicePrincipal.Id 78 | "ResourceId" = $GraphServicePrincipal.Id 79 | "AppRoleId" = $AppRole.Id 80 | } 81 | New-MgServicePrincipalAppRoleAssignment ` 82 | -ServicePrincipalId $AppRoleAssignment.PrincipalId ` 83 | -BodyParameter $AppRoleAssignment ` 84 | -Verbose 85 | } 86 | disconnect-mggraph -------------------------------------------------------------------------------- /Chocolatey scripts/choco.ps1: -------------------------------------------------------------------------------- 1 | #Parameters for app name 2 | Param( 3 | [Parameter(Mandatory=$True,Position=1)] 4 | [string]$app, 5 | [Parameter(Mandatory=$False,Position=2)] 6 | [switch]$uninstall = $False 7 | ) 8 | 9 | #Start logging 10 | $logPath = "C:\ProgramData\Microsoft\IntuneApps\$($app)" 11 | if(!(Test-Path $logPath)) 12 | { 13 | mkdir $logPath 14 | } 15 | Start-Transcript -Path "$($logPath)\$($app)_Install.log" -Verbose 16 | 17 | #Check if chocolatey is installed 18 | $choco = "C:\ProgramData\chocolatey" 19 | Write-Host "Checking if Chocolatey is installed on $($env:COMPUTERNAME)..." 20 | if(!(Test-Path $choco)) 21 | { 22 | Write-Host "Chocolatey not found; installing now..." 23 | try 24 | { 25 | Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) 26 | Write-Host "Chocolatey was successfully installed." 27 | } 28 | catch 29 | { 30 | $message = $_ 31 | Write-Host "Error installing Chocolatey: $message" 32 | } 33 | } 34 | else 35 | { 36 | Write-Host "Chocolatey is installed." 37 | } 38 | 39 | $installed = Start-Process -Wait -FilePath "$($choco)\choco.exe" -ArgumentList "list" | Select-String $app 40 | $installFlag = "$($logPath)\$($app)_installed.txt" 41 | 42 | #Check for app and install 43 | if($uninstall -eq $False) 44 | { 45 | Write-Host "Running choco-install" 46 | 47 | Write-Host "Checking if $($app) is installed on $($env:COMPUTERNAME)..." 48 | if($installed -eq $null) 49 | { 50 | Write-Host "$($app) not detected; installing now..." 51 | Start-Process -Wait -FilePath "$($choco)\choco.exe" -ArgumentList "install $($app) -y" 52 | if($LASTEXITCODE -ne 0) 53 | { 54 | $message = $_ 55 | Write-Host "Error installing $($app): $message" 56 | exit 1 57 | } 58 | else 59 | { 60 | Write-Host "$($app) installed successfully" 61 | $installFlag = "$($logPath)\$($app)_installed.txt" 62 | New-Item $installFlag -Force 63 | } 64 | } 65 | else 66 | { 67 | Write-Host "$($app) already installed. Updating to latest version..." 68 | Start-Process -Wait -FilePath "$($choco)\choco.exe" -ArgumentList "upgrade $($app) -y" 69 | if($LASTEXITCODE -ne 0) 70 | { 71 | $message = $_ 72 | Write-Host "Error installing $($app): $message" 73 | exit 1 74 | } 75 | else 76 | { 77 | Write-Host "$($app) updated successfully" 78 | New-Item $installFlag -Force 79 | } 80 | } 81 | } 82 | else 83 | { 84 | Write-Host "Running choco-uninstall" 85 | if($installed -ne $null) 86 | { 87 | Write-Host "$($app) detected; uninstalling now..." 88 | Start-Process -Wait -FilePath "$($choco)\choco.exe" -ArgumentList "uninstall $($app) -y" 89 | if($LASTEXITCODE -ne 0) 90 | { 91 | $message = $_ 92 | Write-Host "Error uninstalling $($app): $message" 93 | exit 1 94 | } 95 | else 96 | { 97 | Write-Host "$($app) successfully uninstalled" 98 | Remove-Item $installFlag -Force 99 | } 100 | } 101 | else 102 | { 103 | Write-Host "$($app) not detected" 104 | } 105 | } 106 | 107 | Stop-Transcript 108 | -------------------------------------------------------------------------------- /Misc Intune/logAnalytics_EXAMPLE.ps1: -------------------------------------------------------------------------------- 1 | # Log analytics connection info 2 | 3 | # workspace ID 4 | $workspaceID = "" 5 | # primary key 6 | $primaryKey = "" 7 | 8 | # create the object and array 9 | $logObject = New-Object System.Object 10 | 11 | $logInfo = @() 12 | 13 | # add value to the object 14 | $serialNumber = Get-WmiObject -Class Win32_Bios | Select-Object -ExpandProperty serialNumber 15 | Write-Host "The serial number is $($serialNumber)" 16 | $logInfo += @{Name="Serial Number";Value=$serialNumber} 17 | 18 | $hostname = $env:COMPUTERNAME 19 | Write-Host "The hostname is $($hostname)" 20 | $logInfo += @{Name="Hostname";Value=$hostname} 21 | 22 | $nuget = Get-PackageProvider -Name NuGet -ErrorAction Ignore 23 | if(-not($nuget)) 24 | { 25 | Write-Host "NuGet not found installing now..." 26 | try { 27 | Install-PackageProvider -Name Nuget -Confirm:$false -Force 28 | Write-Host "NuGet installed successfully" 29 | $logInfo += @{Name="NuGet install status:";Value="SUCCESS"} 30 | } 31 | catch { 32 | $message = $_ 33 | Write-Host "Error installing Nuget: $message" 34 | $logInfo += @{Name="NuGet install status:";Value="ERROR: $message"} 35 | } 36 | }else { 37 | Write-Host "NuGet already installed" 38 | $logInfo += @{Name="NuGet install status:";Value="Already installed"} 39 | } 40 | 41 | 42 | # construct the JSON table 43 | foreach($x in $logInfo) 44 | { 45 | $logObject | Add-Member -MemberType NoteProperty -Name $x.Name -Value $x.Value 46 | } 47 | 48 | $json = $logObject | ConvertTo-Json 49 | 50 | # post JSON to logs 51 | $logType = "deviceInfoLogs" 52 | $timeStampField = "" 53 | Function Build-Signature ($workspaceID, $primaryKey, $date, $contentLength, $method, $contentType, $resource) 54 | { 55 | $xHeaders = "x-ms-date:" + $date 56 | $stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource 57 | $bytesToHash = [Text.Encoding]::UTF8.GetBytes($stringToHash) 58 | $keyBytes = [Convert]::FromBase64String($primaryKey) 59 | 60 | $sha256 = New-Object System.Security.Cryptography.HMACSHA256 61 | $sha256.Key = $keyBytes 62 | $calculatedHash = $sha256.ComputeHash($bytesToHash) 63 | $encodedHash = [Convert]::ToBase64String($calculatedHash) 64 | $authorization = 'SharedKey {0}:{1}' -f $workspaceID,$encodedHash 65 | return $authorization 66 | } 67 | 68 | Function Post-LogAnalyticsData($workspaceID, $primaryKey, $body, $logType) 69 | { 70 | $method = 'POST' 71 | $contentType = "application/json" 72 | $resource = "/api/logs" 73 | $rfc1123date = [datetime]::UtcNow.ToString("r") 74 | $contentLength = $body.Length 75 | $signature = Build-Signature ` 76 | -workspaceID $workspaceID ` 77 | -primaryKey $primaryKey ` 78 | -date $rfc1123date ` 79 | -contentLength $contentLength ` 80 | -method $method ` 81 | -contentType $contentType ` 82 | -resource $resource 83 | $uri = "https://" + $workspaceID + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01" 84 | 85 | $headers = @{ 86 | "Authorization" = $signature; 87 | "Log-Type" = $logType; 88 | "x-ms-date" = $rfc1123date; 89 | "time-generated-field" = $timeStampField; 90 | } 91 | 92 | $response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $contentType -Headers $headers -Body $body -UseBasicParsing 93 | return $response.StatusCode 94 | } 95 | Post-LogAnalyticsData -workspaceID $workspaceID -primaryKey $primaryKey -body ([System.Text.Encoding]::UTF8.GetBytes($json)) -logType $logType 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /Misc Intune/createHyperV-V3.ps1: -------------------------------------------------------------------------------- 1 | # Prompt for Hyper-V VM name, Windows version, and CPU count 2 | Param( 3 | [Parameter(Mandatory=$True,Position=1)] 4 | [string]$VMname, 5 | 6 | [Parameter(Mandatory=$True,Position=2)] 7 | [string]$version, 8 | 9 | [Parameter(Mandatory=$True,Position=3)] 10 | [string]$CPUCount 11 | ) 12 | 13 | # log function 14 | function log() 15 | { 16 | Param( 17 | [Parameter(Mandatory=$True)] 18 | [string]$message 19 | ) 20 | $date = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 21 | Write-Output "$date - $message" 22 | } 23 | 24 | # set drive volumes and paths 25 | 26 | # set volume below 27 | $volume = "" # for example, $volume = "D:" 28 | 29 | # Check for paths 30 | $templates = "$($volume)\Templates" 31 | $virtualMachines = "$($volume)\Hyper-V\Virtual machines" 32 | $virtualHardDisks = "$($volume)\Hyper-V\Virtual hard disks" 33 | 34 | log "Checking for $virtualMachines..." 35 | if(!(Test-Path $virtualMachines)) 36 | { 37 | log "Creating directory $virtualMachines" 38 | try 39 | { 40 | mkdir $virtualMachines 41 | log "Directory $virtualMachines created" 42 | } 43 | catch 44 | { 45 | log "Failed to create directory $virtualMachines" 46 | log "Error: $_" 47 | exit 1 48 | } 49 | } 50 | else 51 | { 52 | log "Directory $virtualMachines already exists" 53 | } 54 | 55 | log "Checking for $virtualHardDisks..." 56 | if(!(Test-Path $virtualHardDisks)) 57 | { 58 | log "Creating directory $virtualHardDisks" 59 | try 60 | { 61 | mkdir $virtualHardDisks 62 | log "Directory $virtualHardDisks created" 63 | } 64 | catch 65 | { 66 | log "Failed to create directory $virtualHardDisks" 67 | log "Error: $_" 68 | exit 1 69 | } 70 | } 71 | else 72 | { 73 | log "Directory $virtualHardDisks already exists" 74 | } 75 | 76 | # generate random TEMP number 77 | log "Generating random number..." 78 | $number = Get-Random -Minimum 1000 -Maximum 10000 79 | $numberString = $number.ToString() 80 | log "Random number: $numberString" 81 | 82 | # get date 83 | log "Getting date..." 84 | $date = Get-Date -Format "MM-dd" 85 | log "Date: $date" 86 | 87 | # Set TEMP VM name 88 | log "Setting VM name..." 89 | $VMName = $VMname + "-" + $date + "-" + $numberString 90 | log "Temporarily setting VM name to $VMName" 91 | 92 | # Copy the gold master image to the Hyper-V directory with the VM name 93 | log "Copying $version disk from $templates..." 94 | try 95 | { 96 | Copy-Item -Path "$($templates)\GM-$($version).vhdx" -Destination "$($virtualHardDisks)\$($VMName).vhdx" -Force | Out-Null 97 | log "Disk copied to $virtualHardDisks" 98 | } 99 | catch 100 | { 101 | log "Failed to copy disk to $virtualHardDisks" 102 | log "Error: $_" 103 | exit 1 104 | } 105 | 106 | # define network and VM storage path 107 | $VMSwitchName = "" # for example, $VMSwitchName = "Default Switch" 108 | log "Setting VM switch name to $VMSwitchName" 109 | 110 | $VhdxPath = "$($virtualHardDisks)\$($VMName).vhdx" 111 | log "Setting VHD path to $VhdxPath" 112 | 113 | # Set VM paramters 114 | New-VM -Name $VMname -BootDevice VHD -VHDPath $VhdxPath -Path $virtualMachines -Generation 2 -Switch $VMSwitchName 115 | Set-VM -VMName $VMname -ProcessorCount $CPUCount 116 | Set-VMMemory -VMName $VMname -StartupBytes 8GB -DynamicMemoryEnabled $false 117 | Set-VMSecurity -VMName $VMname -VirtualizationBasedSecurityOptOut $false 118 | Set-VMKeyProtector -VMName $VMname -NewLocalKeyProtector 119 | Enable-VMTPM -VMName $VMname 120 | Enable-VMIntegrationService -VMName $VMname -Name "Guest Service Interface" 121 | Set-VM -VMName $VMname -AutomaticCheckpointsEnabled $false | Out-Host 122 | 123 | # Get serial number and rename VM 124 | $serial = Get-CimInstance -ClassName Win32_BIOS | Select-Object -ExpandProperty SerialNumber 125 | log "Serial number: $serial" 126 | 127 | # trim serial 128 | $subSerial = $serial.Substring(0, 4) 129 | log "Trimmed serial number: $subSerial" 130 | 131 | 132 | $newName = $name + "-" + $date + "-" + $version + "-" + $subSerial 133 | log "New VM name: $newName" 134 | 135 | try 136 | { 137 | Rename-VM -Name $VMname -NewName $newName 138 | log "VM renamed to $newName" 139 | } 140 | catch 141 | { 142 | log "Failed to rename VM to $newName" 143 | log "Error: $_" 144 | exit 1 145 | } 146 | 147 | -------------------------------------------------------------------------------- /W365/W365_BACKUP_RESTORE/w365_BACKUP.ps1: -------------------------------------------------------------------------------- 1 | # LOG FUNCTION 2 | function log 3 | { 4 | param( 5 | [string]$Message 6 | ) 7 | $TimeStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 8 | Write-Output = "$TimeStamp - $Message" 9 | } 10 | 11 | # START LOGGING 12 | $LogFile = "$($env:PROGRAMDATA)\w365Backup.log" 13 | 14 | if(!(Test-Path $LogFile)) 15 | { 16 | New-Item -Path $LogFile -ItemType File 17 | } 18 | 19 | Start-Transcript -Path $LogFile -Append -Verbose 20 | log "Starting w365BACKUP script..." 21 | 22 | # DEFINE AZURE SHARE DETAILS 23 | $StorageAccountName = "" 24 | $StorageAccountKey = "" 25 | $SASToken = "" 26 | $FileShare = "" 27 | 28 | # GET CURRENT USER SID (AS SYSTEM) 29 | $CurrentUser = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object UserName).UserName 30 | $CurrentUserSID = (New-Object System.Security.Principal.NTAccount($CurrentUser)).Translate([System.Security.Principal.SecurityIdentifier]).Value 31 | log "Current user: $($CurrentUser)" 32 | log "Current user SID: $($CurrentUserSID)" 33 | 34 | # INSTALL AZ.STORAGE POWERSHELL MODULE 35 | log "Checking for Az.Storage module..." 36 | if(-not(Get-Module -ListAvailable -Name Az.Storage -ErrorAction SilentlyContinue)) 37 | { 38 | log "Az.Storage module not found. Installing..." 39 | Install-Module -Name Az.Storage -Force -AllowClobber -Verbose 40 | } 41 | else 42 | { 43 | log "Az.Storage module found." 44 | } 45 | 46 | Import-Module Az.Storage 47 | 48 | 49 | # SET THE STORAGE CONTEXT 50 | log "Setting storage context..." 51 | $StorageContext = New-AzStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $StorageAccountKey 52 | if(-not $StorageContext) 53 | { 54 | log "Failed to set storage context" 55 | Exit 1 56 | } 57 | else 58 | { 59 | log "Storage context is set" 60 | } 61 | 62 | # CHECK FOR AZURE DIRECTORY (SID) 63 | log "Checking for existing SID folder in Azure Storage..." 64 | $AzureShare = Get-AzStorageFile -ShareName $FileShare -Path $CurrentUserSID -Context $StorageContext -ErrorAction SilentlyContinue 65 | if($null -eq $AzureShare) 66 | { 67 | log "Folder not found. Creating..." 68 | New-AzStorageDirectory -ShareName $FileShare -Path $CurrentUserSID -Context $StorageContext 69 | log "Folder created" 70 | } 71 | else 72 | { 73 | log "Folder exists" 74 | } 75 | 76 | # GET LOCAL LOCATIONS 77 | $UserLocations = @() 78 | $UserProfilePath = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$($CurrentUserSID)").ProfileImagePath 79 | $SourceFolders = @("Documents", "Downloads", "Desktop", "Pictures", "AppData") 80 | foreach($Folder in $SourceFolders) 81 | { 82 | $UserLocations += "$($UserProfilePath)\$($Folder)" 83 | log "Added $($UserProfilePath)\$($Folder) to backup list" 84 | } 85 | 86 | # DOWNLOAD AZCOPY 87 | log "Checking for AzCopy..." 88 | $AzCopyPath = "C:\Program Files (x86)\Microsoft SDKs\Azure\AzCopy\AzCopy.exe" 89 | if(!(Test-Path $AzCopyPath)) 90 | { 91 | log "AzCopy not found. Downloading..." 92 | 93 | $AzCopyUrl = "https://aka.ms/downloadazcopy-v10-windows" 94 | $TempZipPath = "$($env:TEMP)\azcopy.zip" 95 | $ExtractPath = "$($env:TEMP)\azcopy" 96 | 97 | try 98 | { 99 | Invoke-WebRequest -Uri $AzCopyUrl -OutFile $TempZipPath 100 | Expand-Archive -Path $TempZipPath -DestinationPath $ExtractPath -Force 101 | log "AzCopy downloaded and extracted to $($ExtractPath)" 102 | } 103 | catch 104 | { 105 | log "Failed to download AzCopy: $_" 106 | Exit 1 107 | } 108 | 109 | $AzCopyExe = Get-ChildItem -Path $ExtractPath -Recurse -Filter "azcopy.exe" | Select-Object -First 1 -ExpandProperty FullName 110 | if($AzCopyExe) 111 | { 112 | $AzCopyPath = $AzCopyExe 113 | log "AzCopy is installed at: $($AzCopyPath)" 114 | } 115 | else 116 | { 117 | log "Error: AzCopy executable not found after extraction." 118 | Exit 1 119 | } 120 | } 121 | else 122 | { 123 | log "AzCopy found at: $AzCopyPath" 124 | } 125 | 126 | # BACKUP DATA TO AZURE 127 | log "Backing up user data to Azure..." 128 | $DestinationPath = "https://$($StorageAccountName).file.core.windows.net/$($FileShare)/$($CurrentUserSID)?$($SASToken)" 129 | foreach($Location in $UserLocations) 130 | { 131 | log "Copying $Location to Azure" 132 | try 133 | { 134 | Start-Process -Wait $AzCopyPath -ArgumentList "copy $($Location) $($DestinationPath) --recursive=true" 135 | log "Copied $Location to Azure file share successfully" 136 | } 137 | catch 138 | { 139 | log "Filed to copy $Location to Azure File Share: $_" 140 | } 141 | } 142 | 143 | log "User data backup complete." 144 | # STOP LOGGING 145 | Stop-Transcript 146 | -------------------------------------------------------------------------------- /W365/W365_BACKUP_RESTORE/w365_RESTORE.ps1: -------------------------------------------------------------------------------- 1 | # LOG FUNCTION 2 | function log 3 | { 4 | param( 5 | [string]$Message 6 | ) 7 | $TimeStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 8 | Write-Output = "$TimeStamp - $Message" 9 | } 10 | 11 | # START LOGGING 12 | $LogFile = "$($env:PROGRAMDATA)\w365Backup.log" 13 | 14 | if(!(Test-Path $LogFile)) 15 | { 16 | New-Item -Path $LogFile -ItemType File 17 | } 18 | 19 | Start-Transcript -Path $LogFile -Append -Verbose 20 | log "Starting w365BACKUP script..." 21 | 22 | # DEFINE AZURE SHARE DETAILS 23 | $StorageAccountName = "" 24 | $StorageAccountKey = "" 25 | $SASToken = "" 26 | $FileShare = "" 27 | 28 | # GET CURRENT USER SID (AS SYSTEM) 29 | $CurrentUser = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object UserName).UserName 30 | $CurrentUserSID = (New-Object System.Security.Principal.NTAccount($CurrentUser)).Translate([System.Security.Principal.SecurityIdentifier]).Value 31 | log "Current user: $($CurrentUser)" 32 | log "Current user SID: $($CurrentUserSID)" 33 | 34 | # INSTALL AZ.STORAGE POWERSHELL MODULE 35 | log "Checking for Az.Storage module..." 36 | if(-not(Get-Module -ListAvailable -Name Az.Storage -ErrorAction SilentlyContinue)) 37 | { 38 | log "Az.Storage module not found. Installing..." 39 | Install-Module -Name Az.Storage -Force -AllowClobber -Verbose 40 | } 41 | else 42 | { 43 | log "Az.Storage module found." 44 | } 45 | 46 | Import-Module Az.Storage 47 | 48 | 49 | # SET THE STORAGE CONTEXT 50 | log "Setting storage context..." 51 | $StorageContext = New-AzStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $StorageAccountKey 52 | if(-not $StorageContext) 53 | { 54 | log "Failed to set storage context" 55 | Exit 1 56 | } 57 | else 58 | { 59 | log "Storage context is set" 60 | } 61 | 62 | # CHECK IF THE SHARE EXISTS 63 | log "Checking for $($CurrentUserSID) directory in Azure File Share..." 64 | $AzureShare = Get-AzStorageFile -ShareName $FileShare -Path $CurrentUserSID -Context $StorageContext -ErrorAction SilentlyContinue 65 | if($null -eq $AzureShare) 66 | { 67 | log "No backup found. Exiting..." 68 | Exit 1 69 | } 70 | else 71 | { 72 | log "$($CurrentUserSID) directory found. Proceeding with migration." 73 | } 74 | 75 | # DOWNLOAD AZCOPY 76 | # Check for AzCopy and download if missing 77 | log "Checking for AzCopy..." 78 | $AzCopyPath = "C:\Program Files (x86)\Microsoft SDKs\Azure\AzCopy\AzCopy.exe" 79 | if (!(Test-Path $AzCopyPath)) { 80 | log "AzCopy not found. Downloading..." 81 | 82 | $AzCopyUrl = "https://aka.ms/downloadazcopy-v10-windows" 83 | $TempZipPath = "$env:TEMP\azcopy.zip" 84 | $ExtractPath = "$env:TEMP\azcopy" 85 | 86 | # Download AzCopy 87 | log "Downloading AzCopy from: $AzCopyUrl" 88 | try { 89 | Invoke-WebRequest -Uri $AzCopyUrl -OutFile $TempZipPath 90 | Expand-Archive -Path $TempZipPath -DestinationPath $ExtractPath -Force 91 | log "AzCopy downloaded and extracted to: $ExtractPath" 92 | } catch { 93 | log "Failed to download AzCopy: $_" 94 | Exit 1 95 | } 96 | 97 | # Find AzCopy.exe 98 | $AzCopyExe = Get-ChildItem -Path $ExtractPath -Recurse -Filter "azcopy.exe" | Select-Object -First 1 -ExpandProperty FullName 99 | if ($AzCopyExe) { 100 | $AzCopyPath = $AzCopyExe 101 | log "AzCopy installed at: $AzCopyPath" 102 | } else { 103 | log "Error: AzCopy executable not found after extraction." 104 | Exit 1 105 | } 106 | } else { 107 | log "AzCopy found at: $AzCopyPath" 108 | } 109 | 110 | # GET LOCAL LOCATIONS 111 | $UserLocations = @() 112 | $UserProfilePath = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$($CurrentUserSID)").ProfileImagePath 113 | $SourceFolders = @("Documents", "Downloads", "Desktop", "Pictures", "AppData") 114 | foreach($Folder in $SourceFolders) 115 | { 116 | $UserLocations += "$($UserProfilePath)\$($Folder)" 117 | log "Added $($UserProfilePath)\$($Folder) to backup list" 118 | } 119 | 120 | # RESTORE THE DATA FROM AZURE 121 | log "Restoring user data..." 122 | foreach($Location in $UserLocations) 123 | { 124 | $FolderName = Split-Path $Location -Leaf 125 | $AzureSource = "https://$($StorageAccountName).file.core.windows.net/$($FileShareName)/$($CurrentUserSID)/$($FolderName)?$($SASToken)" 126 | 127 | log "Downloading $FolderName from Azure..." 128 | try 129 | { 130 | Start-Process -Wait $AzCopyPath -ArgumentList "copy $($AzureSource) $($Location) --recursive=true" 131 | log "Restored $FolderName from Azure" 132 | } 133 | catch 134 | { 135 | log "Failed to restore $FolderName from Azure: $_" 136 | } 137 | } 138 | 139 | log "User data restore complete" 140 | Stop-Transcript 141 | -------------------------------------------------------------------------------- /Hyper-V/autoCreateHyperV.ps1: -------------------------------------------------------------------------------- 1 | # Set parameters 2 | Param( 3 | [Parameter(Mandatory=$True,Position=1)] 4 | [string]$name, 5 | 6 | [Parameter(Mandatory=$True,Position=2)] 7 | [string]$version, 8 | 9 | [Parameter(Mandatory=$True,Position=3)] 10 | [string]$CPUcount, 11 | 12 | [Parameter(Mandatory=$False,Position=4)] 13 | [switch]$Autopilot=$False 14 | ) 15 | 16 | # log function 17 | function log() 18 | { 19 | Param( 20 | [Parameter(Mandatory=$True)] 21 | [string]$message 22 | ) 23 | $date = Get-Date -Format "yyyy-MM-dd HH:mm:ss tt" 24 | Write-Output "$date - $message" 25 | } 26 | 27 | # set drive volume and VM paths 28 | $volume = "" # for example, "D:" 29 | log "Hyper-V volume is $($volume)" 30 | 31 | $templates = "$($volume)\Templates" 32 | $virtualMachines = "$($volume)\Hyper-V\Virtual machines" 33 | $virtualHardDisks = "$($volume)\Hyper-V\Virtual hard disks" 34 | 35 | log "Checking for $virtualMachines..." 36 | if(!(Test-Path $virtualMachines)) 37 | { 38 | log "$virtualMachines not found; creating directory..." 39 | try 40 | { 41 | mkdir $virtualMachines 42 | log "Directory $virtualMachines created" 43 | } 44 | catch 45 | { 46 | log "Failed to create $($virtualMachines) - Error: $_" 47 | Exit 1 48 | } 49 | } 50 | else 51 | { 52 | log "Directory $virtualMachiens already exists." 53 | } 54 | 55 | log "Checking for $virtualHardDisks..." 56 | if(!(Test-Path $virtualHardDisks)) 57 | { 58 | log "$virtualHardDisks not found; creating directory..." 59 | try 60 | { 61 | mkdir $virtualHardDisks 62 | log "Directory $virtualHardDisks created" 63 | } 64 | catch 65 | { 66 | log "Failed to create $($virtualHardDisks) - Error: $_" 67 | Exit 1 68 | } 69 | } 70 | else 71 | { 72 | log "Directory $virtualHardDisks already exists." 73 | } 74 | 75 | # build the temp VM name 76 | log "Generating temporary VM name..." 77 | $number = Get-Random -Minimum 1000 -Maximum 10000 78 | $numberString = $number.ToString() 79 | $date = Get-Date -Format "MM-dd" 80 | $VMName = $name + "-" + $date + "-" + $numberString 81 | log "Temporarily setting VM name to $VMName" 82 | 83 | # copy the GM disk 84 | log "Copying the $version disk from $templates..." 85 | try 86 | { 87 | Copy-Item -Path "$($tempaltes)\GM-$($version).vhdx" -Destination "$($virtualHardDisks)\$($VMName).vhdx" -Force | Out-Null 88 | log "Disk coppied to $virtualHardDisks" 89 | } 90 | catch 91 | { 92 | log "Failed to copy disk to $($virtualHardDisks) - Error: $_" 93 | Exit 1 94 | } 95 | 96 | # Virtual switch name 97 | $VMSwitchName = "" # for example, "External", "Default Switch" 98 | log "Using virtual switch $($VMSwitchName)" 99 | 100 | # build the VM 101 | $vhdxPath = "$($virtualHardDisks)\$($VMName).vhdx" 102 | log "Setting VHDX path to $($vhdxPath)" 103 | 104 | New-VM -Name $VMName -BootDevice VHD -VHDPath $vhdxPath -Path $virtualMachines -Generation 2 -SwitchName $VMSwitchName 105 | Set-VM -VMName $VMName -ProcessorCount $CPUcount 106 | Set-VMMemory -VMName $VMName -StartupBytes 8GB -DynamicMemoryEnabled $false 107 | Set-VMSecurity -VMName $VMName -VirtualizationBasedSecurityOptOut $False 108 | Set-VMKeyProtector -VMName $VMName -NewLocalKeyProtector 109 | Enable-VMTPM -VMName $VMName 110 | Enable-VMIntegrationService -VMName $VMName -Name "Guest Service Interface" 111 | Set-VM -VMName $VMName -AutomaticCheckpointsEnabled $false | Out-Host 112 | 113 | $serial = Get-WmiObject -ComputerName localhost -Namespace root\virtualization\v2 -class Msvm_VirtualSystemSettingData | Where-Object {$_.elementName -eq $VMName} | Select-Object -ExpandProperty BIOSSerialNumber 114 | log "Serial number for $VMName is $($serial)" 115 | 116 | # set the new VM name 117 | log "Renaming VM..." 118 | $subSerial = $serial.Substring(0,4) 119 | log "Trimmed serial number to $subSerial" 120 | 121 | $newVMName = $name + "-" + $date + "-" + $subSerial 122 | log "New VM Name will be $newName" 123 | 124 | try 125 | { 126 | Renamve-VM -Name $VMName -NewName $newVMName 127 | log "VM renamed to $newVMName" 128 | } 129 | catch 130 | { 131 | log "Failed to rename VM to $($newName) - Error: $_" 132 | Exit 0 133 | } 134 | 135 | 136 | # (optional) Autopilot V2 info 137 | if($Autopilot -eq $true) 138 | { 139 | log "Autopilot switch is enabled. Collecting hardware info for APV2 upload..." 140 | $exportPath = "C:\Autopilot" 141 | log "Checking for $($exportPath)..." 142 | if(!(Test-Path $exportPath)) 143 | { 144 | log "$($exportPath) does not exist. Creating..." 145 | mkdir $exportPath 146 | log "$($exportPath) directory created." 147 | } 148 | else 149 | { 150 | log "$($exportPath) directory already exists." 151 | } 152 | $data = "Microsoft Corporation,Virtual Machine,$($serial)" 153 | log "Autopilot V2 data is $($data)" 154 | log "Exporting to CSV..." 155 | Set-Content -Path "$($exportPath)\$($newVMName).csv" -Value $data 156 | log "Exported APV2 data to $($exportPath)\$($newVMName).csv" 157 | } 158 | 159 | -------------------------------------------------------------------------------- /Hyper-V/createVM.ps1: -------------------------------------------------------------------------------- 1 | # Set parameters 2 | Param( 3 | [Parameter(Mandatory=$True,Position=1)] 4 | [string]$name, 5 | 6 | [Parameter(Mandatory=$True,Position=2)] 7 | [string]$version, 8 | 9 | [Parameter(Mandatory=$True,Position=3)] 10 | [string]$CPUcount, 11 | 12 | [Parameter(Mandatory=$False,Position=4)] 13 | [switch]$Autopilot=$False 14 | ) 15 | 16 | # log function 17 | function log() 18 | { 19 | Param( 20 | [Parameter(Mandatory=$True)] 21 | [string]$message 22 | ) 23 | $date = Get-Date -Format "yyyy-MM-dd HH:mm:ss tt" 24 | Write-Output "$date - $message" 25 | } 26 | 27 | # set drive volume and VM paths 28 | $volume = "" # set the volume drive letter, for example "D:" 29 | log "Hyper-V volume is $($volume)" 30 | 31 | $templates = "$($volume)\Templates" 32 | $virtualMachines = "$($volume)\Hyper-V\Virtual machines" 33 | $virtualHardDisks = "$($volume)\Hyper-V\Virtual hard disks" 34 | 35 | log "Checking for $virtualMachines..." 36 | if(!(Test-Path $virtualMachines)) 37 | { 38 | log "$virtualMachines not found; creating directory..." 39 | try 40 | { 41 | mkdir $virtualMachines 42 | log "Directory $virtualMachines created" 43 | } 44 | catch 45 | { 46 | log "Failed to create $($virtualMachines) - Error: $_" 47 | Exit 1 48 | } 49 | } 50 | else 51 | { 52 | log "Directory $virtualMachiens already exists." 53 | } 54 | 55 | log "Checking for $virtualHardDisks..." 56 | if(!(Test-Path $virtualHardDisks)) 57 | { 58 | log "$virtualHardDisks not found; creating directory..." 59 | try 60 | { 61 | mkdir $virtualHardDisks 62 | log "Directory $virtualHardDisks created" 63 | } 64 | catch 65 | { 66 | log "Failed to create $($virtualHardDisks) - Error: $_" 67 | Exit 1 68 | } 69 | } 70 | else 71 | { 72 | log "Directory $virtualHardDisks already exists." 73 | } 74 | 75 | # build the temp VM name 76 | log "Generating temporary VM name..." 77 | $number = Get-Random -Minimum 1000 -Maximum 10000 78 | $numberString = $number.ToString() 79 | $date = Get-Date -Format "MM-dd" 80 | $VMName = $name + "-" + $date + "-" + $numberString 81 | log "Temporarily setting VM name to $VMName" 82 | 83 | # copy the GM disk 84 | log "Copying the $version disk from $templates..." 85 | try 86 | { 87 | Copy-Item -Path "$($tempaltes)\GM-$($version).vhdx" -Destination "$($virtualHardDisks)\$($VMName).vhdx" -Force | Out-Null 88 | log "Disk coppied to $virtualHardDisks" 89 | } 90 | catch 91 | { 92 | log "Failed to copy disk to $($virtualHardDisks) - Error: $_" 93 | Exit 1 94 | } 95 | 96 | # Virtual switch name 97 | $VMSwitchName = "" # set the virtual switch name, for example "Default Switch" 98 | log "Using virtual switch $($VMSwitchName)" 99 | 100 | # build the VM 101 | $vhdxPath = "$($virtualHardDisks)\$($VMName).vhdx" 102 | log "Setting VHDX path to $($vhdxPath)" 103 | 104 | New-VM -Name $VMName -BootDevice VHD -VHDPath $vhdxPath -Path $virtualMachines -Generation 2 -SwitchName $VMSwitchName 105 | Set-VM -VMName $VMName -ProcessorCount $CPUcount 106 | Set-VMMemory -VMName $VMName -StartupBytes 8GB -DynamicMemoryEnabled $false 107 | Set-VMSecurity -VMName $VMName -VirtualizationBasedSecurityOptOut $False 108 | Set-VMKeyProtector -VMName $VMName -NewLocalKeyProtector 109 | Enable-VMTPM -VMName $VMName 110 | Enable-VMIntegrationService -VMName $VMName -Name "Guest Service Interface" 111 | Set-VM -VMName $VMName -AutomaticCheckpointsEnabled $false | Out-Host 112 | 113 | $serial = Get-WmiObject -ComputerName localhost -Namespace root\virtualization\v2 -class Msvm_VirtualSystemSettingData | Where-Object {$_.elementName -eq $VMName} | Select-Object -ExpandProperty BIOSSerialNumber 114 | log "Serial number for $VMName is $($serial)" 115 | 116 | # set the new VM name 117 | log "Renaming VM..." 118 | $subSerial = $serial.Substring(0,4) 119 | log "Trimmed serial number to $subSerial" 120 | 121 | $newVMName = $name + "-" + $date + "-" + $subSerial 122 | log "New VM Name will be $newName" 123 | 124 | try 125 | { 126 | Renamve-VM -Name $VMName -NewName $newVMName 127 | log "VM renamed to $newVMName" 128 | } 129 | catch 130 | { 131 | log "Failed to rename VM to $($newName) - Error: $_" 132 | Exit 0 133 | } 134 | 135 | 136 | # (optional) Autopilot V2 info 137 | if($Autopilot -eq $true) 138 | { 139 | log "Autopilot switch is enabled. Collecting hardware info for APV2 upload..." 140 | $exportPath = "C:\Autopilot" 141 | log "Checking for $($exportPath)..." 142 | if(!(Test-Path $exportPath)) 143 | { 144 | log "$($exportPath) does not exist. Creating..." 145 | mkdir $exportPath 146 | log "$($exportPath) directory created." 147 | } 148 | else 149 | { 150 | log "$($exportPath) directory already exists." 151 | } 152 | $data = "Microsoft Corporation,Virtual Machine,$($serial)" 153 | log "Autopilot V2 data is $($data)" 154 | log "Exporting to CSV..." 155 | Set-Content -Path "$($exportPath)\$($newVMName).csv" -Value $data 156 | log "Exported APV2 data to $($exportPath)\$($newVMName).csv" 157 | } 158 | 159 | -------------------------------------------------------------------------------- /AppToast/ToastAppStatus.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Display Window to keep users informed of apps and policy being applied from Intune. 4 | 5 | .DESCRIPTION 6 | This script is designed to be run as a scheduled task after Autopilot provisioning to keep users informed of apps and policy being applied from Intune. The script will check for assigned applications and display a pop up Window showing status. 7 | 8 | .PARAMETER message 9 | Microsoft Graph API client ID, client secret, and tenant name. 10 | The message to display in the toast notification. 11 | 12 | .EXAMPLE 13 | IntuneToast.ps1 -clientId "12345678-1234-1234-1234-123456789012" -clientSecret "client_secret" -tenantName "tenantName" 14 | 15 | .NOTES 16 | File Name : IntuneToast.ps1 17 | Author : Justin Rice, Steve Weiner 18 | Prerequisite : PowerShell V5 19 | Copyright 2025 - Rubix, LLC. All rights reserved. 20 | #> 21 | 22 | # Log function 23 | function log { 24 | param ( 25 | [string]$message 26 | ) 27 | $time = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 28 | $message = "$time - $message" 29 | Write-Output $message 30 | } 31 | 32 | # Graph authenticate function 33 | function msGraphAuthenticate() 34 | { 35 | [CmdletBinding()] 36 | Param( 37 | [string]$clientId = "", 38 | [string]$clientSecret = "", 39 | [string]$tenantName = "" 40 | ) 41 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 42 | $headers.Add("Content-Type", "application/x-www-form-urlencoded") 43 | $body = "grant_type=client_credentials&scope=https://graph.microsoft.com/.default" 44 | $body += -join ("&client_id=" , $clientId, "&client_secret=", $clientSecret) 45 | $response = Invoke-RestMethod "https://login.microsoftonline.com/$tenantName/oauth2/v2.0/token" -Method 'POST' -Headers $headers -Body $body 46 | # Get token from OAuth response 47 | 48 | $token = -join ("Bearer ", $response.access_token) 49 | 50 | # Reinstantiate headers 51 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 52 | $headers.Add("Authorization", $token) 53 | $headers.Add("Content-Type", "application/json") 54 | $headers = @{'Authorization'="$($token)"} 55 | return $headers 56 | } 57 | 58 | ### Authenticate to Graph API 59 | $Headers = msGraphAuthenticate 60 | 61 | ### Graph base URI 62 | $GraphAPIBase = "https://graph.microsoft.com/beta" 63 | 64 | ### Get assigned apps through registry 65 | 66 | [string]$Win32RegPath = "HKLM:\SOFTWARE\Microsoft\IntuneManagementExtension\Win32Apps" 67 | [string]$GraphAPIBase = "https://graph.microsoft.com/beta" 68 | 69 | 70 | $AppStatusList = @() 71 | 72 | if(Test-Path $Win32RegPath) 73 | { 74 | $AppGUIDs = Get-ChildItem -Path $Win32RegPath | Select-Object -ExpandProperty PSChildName | Where-Object { $_ -match "^[0-9a-fA-F\-]{36}$" } 75 | 76 | foreach ($AppGUID in $APPGUIDs) 77 | { 78 | $AppGUIDPath = "$($Win32RegPath)\$($AppGUID)" 79 | 80 | if(Test-Path $AppGUIDPath) 81 | { 82 | $ParentSubKeys = Get-ChildItem -Path $AppGUIDPath | Select-Object -ExpandProperty PSChildName -ErrorAction SilentlyContinue 83 | 84 | if($ParentSubKeys) 85 | { 86 | $SubKeys = $ParentSubKeys | Where-Object { $_ -match "^[0-9a-fA-F\-]{36}" } 87 | 88 | if ($SubKeys) 89 | { 90 | foreach($SubKey in $SubKeys) 91 | { 92 | if($SubKey -match "^(.*)_1$") 93 | { 94 | $SubKey = $matches[1] 95 | } 96 | else 97 | { 98 | $SubKey = $SubKey 99 | } 100 | $RegPath = "$($AppGUIDPath)\$($SubKey)_1\EnforcementStateMessage" 101 | $RegValue = "EnforcementStateMessage" 102 | 103 | if(Test-Path $RegPath) 104 | { 105 | try 106 | { 107 | $EnforcementStateMessage = Get-ItemProperty -Path $RegPath -Name $RegValue | Select-Object -ExpandProperty $RegValue 108 | $EnforcementStateMessage = $EnforcementStateMessage.Trim() 109 | 110 | if($EnforcementStateMessage -match "^\{") 111 | { 112 | try 113 | { 114 | $EnforcementStateObject = $EnforcementStateMessage | ConvertFrom-Json 115 | $EnforcementState = $EnforcementStateObject.EnforcementState 116 | 117 | } 118 | catch 119 | { 120 | log "Error parsing JSON: $_" 121 | } 122 | } 123 | else 124 | { 125 | log "Error: EnforcementStateMessage is not in JSON format" 126 | } 127 | 128 | 129 | $GraphUri = "$($GraphAPIBase)/deviceAppManagement/mobileApps/$($SubKey)" 130 | $AppDisplayName = (Invoke-RestMethod -Method Get -Uri $GraphUri -Headers $Headers).DisplayName 131 | 132 | $AppStatusList += [PSCustomObject]@{ 133 | DisplayName = $AppDisplayName 134 | AppId = $SubKey 135 | EnforcementState = $EnforcementState 136 | } 137 | } 138 | catch 139 | { 140 | log "Error retrieving EnforcementState for App GUID: $($SubKey) - $_" 141 | } 142 | } 143 | else 144 | { 145 | log "Registry key not found: $RegPath" 146 | } 147 | } 148 | } 149 | else 150 | { 151 | log "No valid subkeys found under: $AppGUIDPath" 152 | } 153 | } 154 | else 155 | { 156 | log "No subkeys found for App GUID: $AppGUID" 157 | } 158 | } 159 | else 160 | { 161 | log "Registry path does not exist: $AppGUIDPath" 162 | } 163 | } 164 | 165 | } 166 | else 167 | { 168 | log "Registry path not found: $Win32RegPath" 169 | } 170 | 171 | 172 | # Check if AppStatus returned value 173 | if($null -eq $AppStatusList) 174 | { 175 | log "No applications found. Exiting..." 176 | # Kill task 177 | Exit 0 178 | } -------------------------------------------------------------------------------- /Autopilot Helper Scripts/AutopilotBranding_STEVE.ps1: -------------------------------------------------------------------------------- 1 | function Log() { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory=$false)] [String] $message 5 | ) 6 | 7 | $ts = get-date -f "yyyy/MM/dd hh:mm:ss tt" 8 | Write-Output "$ts $message" 9 | } 10 | 11 | # If we are running as a 32-bit process on an x64 system, re-launch as a 64-bit process 12 | if ("$env:PROCESSOR_ARCHITEW6432" -ne "ARM64") 13 | { 14 | if (Test-Path "$($env:WINDIR)\SysNative\WindowsPowerShell\v1.0\powershell.exe") 15 | { 16 | & "$($env:WINDIR)\SysNative\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy bypass -NoProfile -File "$PSCommandPath" 17 | Exit $lastexitcode 18 | } 19 | } 20 | 21 | # Create a tag file just so Intune knows this was installed 22 | if (-not (Test-Path "$($env:ProgramData)\Microsoft\AutopilotBranding")) 23 | { 24 | Mkdir "$($env:ProgramData)\Microsoft\AutopilotBranding" 25 | } 26 | Set-Content -Path "$($env:ProgramData)\Microsoft\AutopilotBranding\AutopilotBranding.ps1.tag" -Value "Installed" 27 | 28 | # Start logging 29 | Start-Transcript "$($env:ProgramData)\Microsoft\AutopilotBranding\AutopilotBranding.log" 30 | 31 | # PREP: Load the Config.xml 32 | $installFolder = "$PSScriptRoot\" 33 | Log "Install folder: $installFolder" 34 | Log "Loading configuration: $($installFolder)Config.xml" 35 | [Xml]$config = Get-Content "$($installFolder)Config.xml" 36 | 37 | # STEP 1: Apply custom start menu layout 38 | $ci = Get-ComputerInfo 39 | if ($ci.OsBuildNumber -le 22000) { 40 | Log "Importing layout: $($installFolder)Layout.xml" 41 | Copy-Item "$($installFolder)Layout.xml" "C:\Users\Default\AppData\Local\Microsoft\Windows\Shell\LayoutModification.xml" -Force 42 | } else { 43 | Log "Importing layout: $($installFolder)Start2.bin" 44 | MkDir -Path "C:\Users\Default\AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState" -Force -ErrorAction SilentlyContinue | Out-Null 45 | Copy-Item "$($installFolder)Start2.bin" "C:\Users\Default\AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\Start2.bin" -Force 46 | } 47 | 48 | # STEP 2: Configure background 49 | Log "Setting up Autopilot theme" 50 | Mkdir "C:\Windows\Resources\OEM Themes" -Force | Out-Null 51 | Copy-Item "$installFolder\Autopilot.theme" "C:\Windows\Resources\OEM Themes\Autopilot.theme" -Force 52 | Mkdir "C:\Windows\web\wallpaper\Autopilot" -Force | Out-Null 53 | Copy-Item "$installFolder\Autopilot.png" "C:\Windows\web\wallpaper\Autopilot\Autopilot.png" -Force 54 | Log "Setting Autopilot theme as the new user default" 55 | reg.exe load HKLM\TempUser "C:\Users\Default\NTUSER.DAT" | Out-Host 56 | reg.exe add "HKLM\TempUser\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes" /v InstallTheme /t REG_EXPAND_SZ /d "%SystemRoot%\resources\OEM Themes\Autopilot.theme" /f | Out-Host 57 | reg.exe unload HKLM\TempUser | Out-Host 58 | 59 | # STEP 3: Set time zone (if specified) 60 | if ($config.Config.TimeZone) { 61 | Log "Setting time zone: $($config.Config.TimeZone)" 62 | Set-Timezone -Id $config.Config.TimeZone 63 | } 64 | else { 65 | # Enable location services so the time zone will be set automatically (even when skipping the privacy page in OOBE) when an administrator signs in 66 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\location" -Name "Value" -Type "String" -Value "Allow" -Force 67 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Sensor\Overrides\{BFA794E4-F964-4FDB-90F6-51056BFE4B44}" -Name "SensorPermissionState" -Type "DWord" -Value 1 -Force 68 | Start-Service -Name "lfsvc" -ErrorAction SilentlyContinue 69 | } 70 | 71 | # STEP 4: Remove specified provisioned apps if they exist 72 | Log "Removing specified in-box provisioned apps" 73 | $apps = Get-AppxProvisionedPackage -online 74 | $config.Config.RemoveApps.App | % { 75 | $current = $_ 76 | $apps | ? {$_.DisplayName -eq $current} | % { 77 | try { 78 | Log "Removing provisioned app: $current" 79 | $_ | Remove-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue | Out-Null 80 | } catch { } 81 | } 82 | } 83 | 84 | # STEP 5: Install OneDrive per machine 85 | if ($config.Config.OneDriveSetup) { 86 | Log "Downloading OneDriveSetup" 87 | $dest = "$($env:TEMP)\OneDriveSetup.exe" 88 | $client = new-object System.Net.WebClient 89 | $client.DownloadFile($config.Config.OneDriveSetup, $dest) 90 | Log "Installing: $dest" 91 | $proc = Start-Process $dest -ArgumentList "/allusers" -WindowStyle Hidden -PassThru 92 | $proc.WaitForExit() 93 | Log "OneDriveSetup exit code: $($proc.ExitCode)" 94 | } 95 | 96 | # STEP 6: Don't let Edge create a desktop shortcut (roams to OneDrive, creates mess) 97 | Log "Turning off (old) Edge desktop shortcut" 98 | reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer" /v DisableEdgeDesktopShortcutCreation /t REG_DWORD /d 1 /f /reg:64 | Out-Host 99 | 100 | <# STEP 7: Add language packs 101 | Get-ChildItem "$($installFolder)LPs" -Filter *.cab | % { 102 | Log "Adding language pack: $($_.FullName)" 103 | Add-WindowsPackage -Online -NoRestart -PackagePath $_.FullName 104 | } 105 | 106 | # STEP 8: Change language 107 | if ($config.Config.Language) { 108 | Log "Configuring language using: $($config.Config.Language)" 109 | & $env:SystemRoot\System32\control.exe "intl.cpl,,/f:`"$($installFolder)$($config.Config.Language)`"" 110 | }#> 111 | 112 | # STEP 9: Add features on demand 113 | $currentWU = (Get-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate\AU" -ErrorAction Ignore).UseWuServer 114 | if ($currentWU -eq 1) 115 | { 116 | Log "Turning off WSUS" 117 | Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "UseWuServer" -Value 0 118 | Restart-Service wuauserv 119 | } 120 | if ($config.Config.AddFeatures.Feature.Count -gt 0) 121 | { 122 | $config.Config.AddFeatures.Feature | % { 123 | Log "Adding Windows feature: $_" 124 | Add-WindowsCapability -Online -Name $_ -ErrorAction SilentlyContinue | Out-Null 125 | } 126 | } 127 | if ($currentWU -eq 1) 128 | { 129 | Log "Turning on WSUS" 130 | Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "UseWuServer" -Value 1 131 | Restart-Service wuauserv 132 | } 133 | 134 | <# STEP 10: Customize default apps 135 | if ($config.Config.DefaultApps) { 136 | Log "Setting default apps: $($config.Config.DefaultApps)" 137 | & Dism.exe /Online /Import-DefaultAppAssociations:`"$($installFolder)$($config.Config.DefaultApps)`" 138 | } 139 | 140 | # STEP 11: Set registered user and organization 141 | Log "Configuring registered user information" 142 | reg.exe add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v RegisteredOwner /t REG_SZ /d "$($config.Config.RegisteredOwner)" /f /reg:64 | Out-Host 143 | reg.exe add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v RegisteredOrganization /t REG_SZ /d "$($config.Config.RegisteredOrganization)" /f /reg:64 | Out-Host 144 | 145 | # STEP 12: Configure OEM branding info 146 | if ($config.Config.OEMInfo) 147 | { 148 | Log "Configuring OEM branding info" 149 | 150 | reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation" /v Manufacturer /t REG_SZ /d "$($config.Config.OEMInfo.Manufacturer)" /f /reg:64 | Out-Host 151 | reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation" /v Model /t REG_SZ /d "$($config.Config.OEMInfo.Model)" /f /reg:64 | Out-Host 152 | reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation" /v SupportPhone /t REG_SZ /d "$($config.Config.OEMInfo.SupportPhone)" /f /reg:64 | Out-Host 153 | reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation" /v SupportHours /t REG_SZ /d "$($config.Config.OEMInfo.SupportHours)" /f /reg:64 | Out-Host 154 | reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation" /v SupportURL /t REG_SZ /d "$($config.Config.OEMInfo.SupportURL)" /f /reg:64 | Out-Host 155 | Copy-Item "$installFolder\$($config.Config.OEMInfo.Logo)" "C:\Windows\$($config.Config.OEMInfo.Logo)" -Force 156 | reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation" /v Logo /t REG_SZ /d "C:\Windows\$($config.Config.OEMInfo.Logo)" /f /reg:64 | Out-Host 157 | } 158 | 159 | # STEP 13: Enable UE-V 160 | Log "Enabling UE-V" 161 | Enable-UEV 162 | Set-UevConfiguration -Computer -SettingsStoragePath "%OneDriveCommercial%\UEV" -SyncMethod External -DisableWaitForSyncOnLogon 163 | Get-ChildItem "$($installFolder)UEV" -Filter *.xml | % { 164 | Log "Registering template: $($_.FullName)" 165 | Register-UevTemplate -Path $_.FullName 166 | }#> 167 | 168 | # STEP 14: Disable network location fly-out 169 | Log "Turning off network location fly-out" 170 | reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\Network\NewNetworkWindowOff" /f 171 | 172 | # STEP 15: Disable new Edge desktop icon 173 | Log "Turning off Edge desktop icon" 174 | reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\EdgeUpdate" /v "CreateDesktopShortcutDefault" /t REG_DWORD /d 0 /f /reg:64 | Out-Host 175 | 176 | # STEP 16: Misc. features 177 | 178 | reg.exe load HKLM\TempUser "C:\Users\Default\NTUSER.DAT" | Out-Host 179 | reg.exe add "HKLM\TempUser\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v ShowTaskViewButton /t REG_DWORD /d 0 /f | Out-Host 180 | reg.exe add "HKLM\TempUser\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /V AutoCheckSelect /T REG_DWORD /D 0 /F | Out-Host 181 | reg.exe add "HKLM\TempUser\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /V TaskbarAl /T REG_DWORD /D 0 /F | Out-Host 182 | reg.exe add "HKLM\TempUser\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v HideFileExt /t REG_DWORD /d 0 /f | Out-Host 183 | reg.exe unload HKLM\TempUser | Out-Host 184 | 185 | reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v EnableFirstLogonAnimation /t REG_DWORD /d 0 /f /reg:64 | Out-Host 186 | 187 | reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v SearchOnTaskbarMode /t REG_DWORD /d 1 /f | Out-Host 188 | 189 | # Chocolatey section- I use this personally, but it's not part of the default script. Uncomment if you want to use it. 190 | <# installs chocolatey 191 | Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) 192 | 193 | 194 | # modify list of chocolatey apps 195 | $chocoApps = @( 196 | "notepadplusplus.install", 197 | "googlechrome", 198 | "7zip.install", 199 | "vscode", 200 | "firefox", 201 | "slack", 202 | "vlc", 203 | "microsoftazurestorageexplorer" 204 | ) 205 | 206 | foreach ($app in $chocoApps){ 207 | choco install $app -y 208 | }#> 209 | 210 | Stop-Transcript 211 | 212 | 213 | -------------------------------------------------------------------------------- /AppToast/AppReport.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Display Window to keep users informed of apps and policy being applied from Intune. 4 | 5 | .DESCRIPTION 6 | This script is designed to be run as a scheduled task after Autopilot provisioning to keep users informed of apps and policy being applied from Intune. The script will check for assigned applications and display a pop up Window showing status. 7 | 8 | .PARAMETER message 9 | Microsoft Graph API client ID, client secret, and tenant name. 10 | The message to display in the toast notification. 11 | 12 | .EXAMPLE 13 | IntuneToast.ps1 -clientId "12345678-1234-1234-1234-123456789012" -clientSecret "client_secret" -tenantName "tenantName" 14 | 15 | .NOTES 16 | File Name : IntuneToast.ps1 17 | Author : Justin Rice, Steve Weiner 18 | Prerequisite : PowerShell V5 19 | Copyright 2025 - Rubix, LLC. All rights reserved. 20 | #> 21 | 22 | # Log function 23 | 24 | Add-Type -AssemblyName PresentationFramework 25 | 26 | function log { 27 | param ( 28 | [string]$message 29 | ) 30 | $time = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 31 | $message = "$time - $message" 32 | Write-Output $message 33 | } 34 | 35 | # Graph authenticate function 36 | function msGraphAuthenticate() 37 | { 38 | [CmdletBinding()] 39 | Param( 40 | [string]$clientId = "", 41 | [string]$clientSecret = "", 42 | [string]$tenantName = "" 43 | ) 44 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 45 | $headers.Add("Content-Type", "application/x-www-form-urlencoded") 46 | $body = "grant_type=client_credentials&scope=https://graph.microsoft.com/.default" 47 | $body += -join ("&client_id=" , $clientId, "&client_secret=", $clientSecret) 48 | $response = Invoke-RestMethod "https://login.microsoftonline.com/$tenantName/oauth2/v2.0/token" -Method 'POST' -Headers $headers -Body $body 49 | # Get token from OAuth response 50 | 51 | $token = -join ("Bearer ", $response.access_token) 52 | 53 | # Reinstantiate headers 54 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 55 | $headers.Add("Authorization", $token) 56 | $headers.Add("Content-Type", "application/json") 57 | $headers = @{'Authorization'="$($token)"} 58 | return $headers 59 | } 60 | 61 | ### Authenticate to Graph API 62 | $Headers = msGraphAuthenticate 63 | 64 | ### Graph base URI 65 | $GraphAPIBase = "https://graph.microsoft.com/beta" 66 | 67 | ### Get assigned apps through registry 68 | 69 | [string]$Win32RegPath = "HKLM:\SOFTWARE\Microsoft\IntuneManagementExtension\Win32Apps" 70 | [string]$GraphAPIBase = "https://graph.microsoft.com/beta" 71 | 72 | 73 | $AppStatusList = @() 74 | 75 | if(Test-Path $Win32RegPath) 76 | { 77 | $AppGUIDs = Get-ChildItem -Path $Win32RegPath | Select-Object -ExpandProperty PSChildName | Where-Object { $_ -match "^[0-9a-fA-F\-]{36}$" } 78 | 79 | foreach ($AppGUID in $APPGUIDs) 80 | { 81 | $AppGUIDPath = "$($Win32RegPath)\$($AppGUID)" 82 | 83 | if(Test-Path $AppGUIDPath) 84 | { 85 | $ParentSubKeys = Get-ChildItem -Path $AppGUIDPath | Select-Object -ExpandProperty PSChildName -ErrorAction SilentlyContinue 86 | 87 | if($ParentSubKeys) 88 | { 89 | $SubKeys = $ParentSubKeys | Where-Object { $_ -match "^[0-9a-fA-F\-]{36}" } 90 | 91 | if ($SubKeys) 92 | { 93 | foreach($SubKey in $SubKeys) 94 | { 95 | if($SubKey -match "^(.*)_1$") 96 | { 97 | $SubKey = $matches[1] 98 | } 99 | else 100 | { 101 | $SubKey = $SubKey 102 | } 103 | $RegPath = "$($AppGUIDPath)\$($SubKey)_1\EnforcementStateMessage" 104 | $RegValue = "EnforcementStateMessage" 105 | 106 | if(Test-Path $RegPath) 107 | { 108 | try 109 | { 110 | $EnforcementStateMessage = Get-ItemProperty -Path $RegPath -Name $RegValue | Select-Object -ExpandProperty $RegValue 111 | $EnforcementStateMessage = $EnforcementStateMessage.Trim() 112 | 113 | if($EnforcementStateMessage -match "^\{") 114 | { 115 | try 116 | { 117 | $EnforcementStateObject = $EnforcementStateMessage | ConvertFrom-Json 118 | $EnforcementState = $EnforcementStateObject.EnforcementState 119 | 120 | } 121 | catch 122 | { 123 | log "Error parsing JSON: $_" 124 | } 125 | } 126 | else 127 | { 128 | log "Error: EnforcementStateMessage is not in JSON format" 129 | } 130 | 131 | 132 | $GraphUri = "$($GraphAPIBase)/deviceAppManagement/mobileApps/$($SubKey)" 133 | $AppDisplayName = (Invoke-RestMethod -Method Get -Uri $GraphUri -Headers $Headers).DisplayName 134 | 135 | $AppStatusList += [PSCustomObject]@{ 136 | DisplayName = $AppDisplayName 137 | AppId = $SubKey 138 | EnforcementState = $EnforcementState 139 | } 140 | } 141 | catch 142 | { 143 | log "Error retrieving EnforcementState for App GUID: $($SubKey) - $_" 144 | } 145 | } 146 | else 147 | { 148 | log "Registry key not found: $RegPath" 149 | } 150 | } 151 | } 152 | else 153 | { 154 | log "No valid subkeys found under: $AppGUIDPath" 155 | } 156 | } 157 | else 158 | { 159 | log "No subkeys found for App GUID: $AppGUID" 160 | } 161 | } 162 | else 163 | { 164 | log "Registry path does not exist: $AppGUIDPath" 165 | } 166 | } 167 | 168 | } 169 | else 170 | { 171 | log "Registry path not found: $Win32RegPath" 172 | } 173 | 174 | 175 | # Check if AppStatus returned value 176 | if($null -eq $AppStatusList) 177 | { 178 | log "No applications found. Exiting..." 179 | # Kill task 180 | Exit 0 181 | } 182 | 183 | 184 | [xml]$xaml = @" 185 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 209 | 210 | 211 | 212 | 213 | 214 | "@ 215 | 216 | $reader = (New-Object System.Xml.XmlNodeReader $xaml) 217 | $window = [Windows.Markup.XamlReader]::Load($reader) 218 | 219 | # Map elements 220 | $AppStatusPanel = $window.FindName("AppStatusPanel") 221 | 222 | foreach($App in $AppStatusList) 223 | { 224 | $status = switch($App.EnforcementState) 225 | { 226 | 1000 { "Installed"; [System.Windows.Media.Brushes]::Green } 227 | 2000 { "Pending"; [System.Windows.Media.Brushes]::Orange } 228 | 5000 { "Failed"; [System.Windows.Media.Brushes]::Red } 229 | Default { "Unknown"; [System.Windows.Media.Brushes]::DarkGray } 230 | } 231 | 232 | $statusText = $status[0] 233 | $statusColor = $status[1] 234 | 235 | # Create row stack 236 | $row = New-Object System.Windows.Controls.StackPanel 237 | $row.Orientation = "Horizontal" 238 | $row.Margin = "0,5,0,0" 239 | 240 | # App name 241 | $nameText = New-Object System.Windows.Controls.TextBlock 242 | $nameText.Text = $App.DisplayName 243 | $nameText.Width = 180 244 | $nameText.VerticalAlignment = "Center" 245 | 246 | # Progress bar 247 | $progress = New-Object System.Windows.Controls.ProgressBar 248 | $progress.Width = 250 249 | $progress.Height = 10 250 | $progress.Margin = "10,0,10,0" 251 | $progress.Value = switch($statusText) 252 | { 253 | "Installed" { 100 } 254 | "Pending" { 50 } 255 | "Failed" { 0 } 256 | Default { 0 } 257 | } 258 | 259 | # Status text 260 | $statusBlock = New-Object System.Windows.Controls.TextBlock 261 | $statusBlock.Text = $statusText 262 | $statusBlock.Width = 80 263 | $statusBlock.Foreground = $statusColor 264 | $statusBlock.VerticalAlignment = "Center" 265 | 266 | # Add to row 267 | $row.Children.Add($nameText) 268 | $row.Children.Add($progress) 269 | $row.Children.Add($statusBlock) 270 | 271 | # Add row to panel 272 | $AppStatusPanel.Children.Add($row) 273 | } 274 | 275 | $window.ShowDialog() | Out-Null -------------------------------------------------------------------------------- /Misc Intune/intuneGraph_WinReporting_Script.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | <#NOTES: 4 | This script has been compiled over time based on various reporting needs for Intune-managed devices - 5 | it has a lot of repeated stuff due to development for different needs over time. Please be nice :) 6 | 7 | Once the CSV is exported, you can optionally import the file into an excel template for filtering and conditional formatting. 8 | Like any public script off the interwebs, there are no guarantees expressed or implied for all functions to work properly. 9 | #> 10 | 11 | #Target "All" or a specific security group for reporting 12 | Write-Host "" 13 | $targetDeviceGroupName = Read-Host 'Enter the name of the desired group you would like to target for reporting. You may type "All" to target all devices' 14 | Write-Host "" 15 | 16 | #Target "All" or a specific Windows Feature Update profile 17 | $TARGET_UPDATE_PROFILE = Read-Host "Please enter the display name of your Feature Update profile. You may enter 'All' to get device statuses from all profiles" 18 | Write-Host "`n" 19 | 20 | #Function to properly search all graph results 21 | function Get-GraphPagedResult 22 | { 23 | param ([parameter(Mandatory = $true)]$Headers,[parameter(Mandatory = $true)]$Uri,[Parameter(Mandatory=$false)][switch]$Verb) 24 | $amalgam = @() 25 | $pages = 0 26 | do 27 | { 28 | $results = Invoke-RestMethod $Uri -Method "GET" -Headers $Headers 29 | if ($results.value) 30 | {$amalgam += $results.value} 31 | else 32 | {$amalgam += $results} 33 | $pages += 1 34 | 35 | if($Verb) 36 | {Write-Host "Completed page $pages for url $Uri"} 37 | 38 | $Uri = $results.'@odata.nextlink' 39 | 40 | } until (!($Uri)) 41 | 42 | $amalgam 43 | } 44 | 45 | ############################################################# 46 | ############### App registration / token ################## 47 | 48 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 49 | Add-Type -AssemblyName System.Web 50 | 51 | #APPLICATION-BASED PERMISSIONS NEEDED: 52 | #Device.Read.All 53 | #DeviceManagementConfiguration.ReadWrite.All (ReadWrite needed for export job for Feature Update Reports) 54 | #DeviceManagementManagedDevices.Read.All 55 | #DeviceManagementServiceConfig.Read.All 56 | #Directory.Read.All 57 | #Group.Read.All 58 | #GroupMember.Read.All 59 | 60 | function Connect_To_Graph { 61 | #App registration 62 | $tenant = "primary-or-federated-domain" 63 | $clientId = "APPLICATION-ID" 64 | $clientSecret = "SECRET-VALUE" 65 | $clientSecret = [System.Web.HttpUtility]::UrlEncode($clientSecret) 66 | 67 | #Header and body request variables 68 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 69 | $headers.Add("Content-Type", "application/x-www-form-urlencoded") 70 | $body = "grant_type=client_credentials&scope=https://graph.microsoft.com/.default" 71 | $body += -join("&client_id=" , $clientId, "&client_secret=", $clientSecret) 72 | $response = Invoke-RestMethod "https://login.microsoftonline.com/$tenant/oauth2/v2.0/token" -Method 'POST' -Headers $header -Body $body 73 | $token = -join("Bearer ", $response.access_token) 74 | #Reinstantiate headers 75 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 76 | $headers.Add("Authorization", $token) 77 | $headers.Add("Content-Type", "application/json") 78 | 79 | $headers 80 | Start-Sleep -Seconds 3 81 | } 82 | 83 | $headers = Connect_To_Graph 84 | 85 | #NOTE: If you prefer to use delegated permissions instead, replace lines 61-77 with the following instead: 86 | <# 87 | $tenant = "primary-or-federated-domain" 88 | $clientId = "APPLICATION-ID" 89 | 90 | $AccessToken = Get-MsalToken -TenantId $tenant -ClientId $clientId -ForceRefresh 91 | $authHeader = $AccessToken.CreateAuthorizationHeader() 92 | 93 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 94 | 95 | $headers.Add("Content-Type", "application/json") 96 | $headers.Add("Authorization", "$($authHeader)") 97 | $headers.Add("Accept", "application/json") 98 | #> 99 | 100 | ############################################################# 101 | ############################################################# 102 | 103 | # Start gathering all Intune Windows devices records 104 | 105 | $intuneDevices = Get-GraphPagedResult -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=operatingSystem eq 'Windows'" -Headers $headers -Verb 106 | 107 | # Get targetted group information 108 | 109 | if($targetDeviceGroupName -ne "All") 110 | { 111 | Write-Host "Querying Azure group by display name..." 112 | $groupPayload = Invoke-RestMethod "https://graph.microsoft.com/beta/groups?`$filter=displayName eq '$($targetDeviceGroupName)'" -Method "GET" -Headers $headers 113 | if ($groupPayload.value.Length -eq 0) 114 | { 115 | Write-Error "Group with name $targetDeviceGroupName does not exist. Closing Script..." 116 | exit -1 117 | } 118 | 119 | $theGroup = $groupPayload.value[0] 120 | $groupDeviceIds = @() 121 | Write-Host "Querying members of retrieved Azure group..." 122 | $groupDevices = Get-GraphPagedResult -Uri "https://graph.microsoft.com/beta/groups/$($theGroup.id)/members" -Headers $headers -Verb 123 | 124 | foreach($dev in $groupDevices) 125 | { 126 | $groupDeviceIds += $dev.deviceId 127 | } 128 | } 129 | 130 | # Get Update Ring policies and details 131 | 132 | $updateRingsStatus = @() 133 | 134 | $updateRings = Get-GraphPagedResult -Uri "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations?`$filter=(isof('microsoft.graph.windowsUpdateForBusinessConfiguration'))" -Headers $headers -Verb 135 | $ringCount = 1 136 | 137 | foreach($ring in $updateRings) 138 | { 139 | Write-Host "Getting device details from Ring $ringCount ($($ring.displayName))..." 140 | $ringDeviceStatusAll = Get-GraphPagedResult -Uri "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations/$($ring.id)/deviceStatuses" -Headers $headers -Verb 141 | #Filter to System account, omit UPN entries 142 | $ringDeviceStatus = $ringDeviceStatusAll | Where-Object {$_.UserPrincipalName -eq "System account"} 143 | 144 | Write-Host "Updating status array for ring $ringCount..." 145 | 146 | foreach($device in $ringDeviceStatus) 147 | { 148 | $updateRingsStatus += New-Object -TypeName PSObject -Property @{ 149 | "Ring" = $($ring.displayName); 150 | "Device" = $($device.deviceDisplayName); 151 | "Status" = $($device.status); 152 | } 153 | } 154 | $ringCount += 1 155 | } 156 | 157 | Start-Sleep -Seconds 5 158 | 159 | 160 | # Manual re-authentication to app reg (NOTE: This may not be needed depending on number of profiles and device statuses exported thus far; this was necessary for tenants with 25k+ devices) 161 | $headers = Connect_To_Graph 162 | 163 | 164 | # Get Device Health Attestation Report details (NOTE: Tried previously calling this per device, but graph always returned unknown. This is the call that is made when navigating to Devices -> Monitor -> Windows health attestation report) 165 | Write-Host "Getting Device Health Attestation Report..." 166 | $deviceHealthAttestationReport = Get-GraphPagedResult -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=isof('microsoft.graph.windowsManagedDevice')&`$select=deviceHealthAttestationState,deviceName,operatingSystem,id" -Headers $headers -Verb 167 | 168 | # Get Encryption Report details (NOTE: Filtering by device name does not appear to work, therefore the full report is gathered in advance) 169 | Write-Host "Getting Device Encryption Report..." 170 | $encryptionReport = Get-GraphPagedResult -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDeviceEncryptionStates?`$select=advancedBitLockerStates,deviceName,encryptionReadinessState" -Headers $headers -Verb 171 | 172 | <# This call seems to often timeout when paired with the previous calls. Re-running might work, but repeated authentication on line 160 instead. 173 | if(!($encryptionReport)) 174 | { 175 | Write-Host "Gateway timeout likely occured - re-running call..." 176 | $encryptionReport = Get-GraphPagedResult -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDeviceEncryptionStates?`$select=advancedBitLockerStates,deviceName,encryptionReadinessState" -Headers $headers -Verb 177 | } 178 | #> 179 | 180 | ############################################################## 181 | ############ Get the Feature Update profiles ############### 182 | 183 | # If you are deploying a Feature Update policy, this section will pull a report from Intune's Reports -> Windows Update -> Feature Update report 184 | # NOTE: If you specify 'All' Feature Update profiles, it will take a bit to perform the export job for each profile 185 | 186 | 187 | $profiles = Invoke-RestMethod "https://graph.microsoft.com/beta/deviceManagement/windowsFeatureUpdateProfiles" -Method "GET" -Headers $headers 188 | 189 | if($TARGET_UPDATE_PROFILE -eq 'All') 190 | { 191 | $featureUpdateReport = @() 192 | Write-Host "Please wait - this make take a bit to export all $($profiles.value.count) Feature Update profiles..." 193 | 194 | foreach($profile in $profiles.value) 195 | { 196 | $profileName = $profile.displayName 197 | $profileId = $profile.id 198 | 199 | Write-Output "Now assessing profile $profileName" 200 | $payload = @{ 201 | reportName = "FeatureUpdateDeviceState"; 202 | filter = "(PolicyId eq '$($profileId)')"; 203 | localizationType = "LocalizedValuesAsAdditionalColumn"; 204 | select = @( 205 | "PolicyId", 206 | "PolicyName", 207 | "FeatureUpdateVersion", 208 | "DeviceId", 209 | "AADDeviceId", 210 | "PartnerPolicyId", 211 | "EventDateTimeUTC", 212 | "LastSuccessfulDeviceUpdateStatus", 213 | "LastSuccessfulDeviceUpdateSubstatus", 214 | "LastSuccessfulDeviceUpdateStatusEventDateTimeUTC", 215 | "CurrentDeviceUpdateStatus", 216 | "CurrentDeviceUpdateSubstatus", 217 | "CurrentDeviceUpdateStatusEventDateTimeUTC", 218 | "LatestAlertMessage", 219 | "LatestAlertMessageDescription", 220 | "LatestAlertRecommendedAction", 221 | "LatestAlertExtendedRecommendedAction", 222 | "UpdateCategory", 223 | "WindowsUpdateVersion", 224 | "LastWUScanTimeUTC", 225 | "Build", 226 | "DeviceName", 227 | "OwnerType", 228 | "UPN", 229 | "AggregateState" 230 | ); 231 | } 232 | 233 | $payload = $payload | ConvertTo-Json 234 | 235 | #Generate job to export report 236 | $jobGenResult = Invoke-RestMethod "https://graph.microsoft.com/beta/deviceManagement/reports/exportJobs" -Method "POST" -Headers $headers -Body $payload 237 | Write-Output $jobGenResult 238 | 239 | #Keep polling til the blob link appears 240 | Write-Output "Now starting extraction of report for profile $profileName" 241 | $reportUrl = "" 242 | 243 | while($true) 244 | { 245 | Start-Sleep -Seconds 10 246 | $report = Invoke-RestMethod "https://graph.microsoft.com/beta/deviceManagement/reports/exportJobs('$($jobGenResult.id)')" -Method "GET" -Headers $headers 247 | if($null -ne $report.url) 248 | { 249 | Write-Output "Report extraction successful. Url loaded. Now pulling report..." 250 | $reportUrl = $report.url 251 | break 252 | } 253 | Write-Output "Report unavailable. Trying again in 10 seconds..." 254 | } 255 | 256 | #In case there are special characters, which screws up downloading and creating directory/zip 257 | $trimmedProfileName = $profileName -replace '[^A-Za-z0-9\s]','' 258 | 259 | if(Test-Path "$psscriptroot\data\$trimmedProfileName") 260 | { 261 | Remove-Item "$psscriptroot\data\$trimmedProfileName" -Force -Recurse 262 | } 263 | 264 | New-Item -Path "$psscriptroot\data" -Name "$trimmedProfileName" -ItemType "directory" 265 | Invoke-WebRequest -Uri $reportUrl -OutFile "$psscriptroot\data\$trimmedProfileName\report_$trimmedProfileName.zip" 266 | Write-Output "Report downloaded for profile $profileName" 267 | 268 | Expand-Archive "$psscriptroot\data\$trimmedProfileName\report_$trimmedProfileName.zip" -DestinationPath "$psscriptroot\data\$trimmedProfileName\report_$trimmedProfileName" 269 | 270 | $theFileName = Get-ChildItem -Path "$psscriptroot\data\$trimmedProfileName\report_$trimmedProfileName" | Select -ExpandProperty FullName | Select-Object -first 1 271 | $featureUpdateReport += Import-Csv -Path $theFileName 272 | } 273 | } 274 | 275 | else 276 | { 277 | $theProfile = @() 278 | foreach($p in $profiles.value){ 279 | if ($p.displayName -eq $($TARGET_UPDATE_PROFILE)){ 280 | $theProfile = $p 281 | break 282 | } 283 | } 284 | 285 | $profileName = $theProfile.displayName 286 | Write-Output "Now assessing profile $profileName" 287 | 288 | $payload = @{ 289 | reportName = "FeatureUpdateDeviceState"; 290 | filter = "(PolicyId eq '$($theProfile.id)')"; 291 | localizationType = "LocalizedValuesAsAdditionalColumn"; 292 | select = @( 293 | "PolicyId", 294 | "PolicyName", 295 | "FeatureUpdateVersion", 296 | "DeviceId", 297 | "AADDeviceId", 298 | "PartnerPolicyId", 299 | "EventDateTimeUTC", 300 | "LastSuccessfulDeviceUpdateStatus", 301 | "LastSuccessfulDeviceUpdateSubstatus", 302 | "LastSuccessfulDeviceUpdateStatusEventDateTimeUTC", 303 | "CurrentDeviceUpdateStatus", 304 | "CurrentDeviceUpdateSubstatus", 305 | "CurrentDeviceUpdateStatusEventDateTimeUTC", 306 | "LatestAlertMessage", 307 | "LatestAlertMessageDescription", 308 | "LatestAlertRecommendedAction", 309 | "LatestAlertExtendedRecommendedAction", 310 | "UpdateCategory", 311 | "WindowsUpdateVersion", 312 | "LastWUScanTimeUTC", 313 | "Build", 314 | "DeviceName", 315 | "OwnerType", 316 | "UPN", 317 | "AggregateState" 318 | ); 319 | } 320 | 321 | $payload = $payload | ConvertTo-Json 322 | 323 | #Generate job to export report 324 | $jobGenResult = Invoke-RestMethod "https://graph.microsoft.com/beta/deviceManagement/reports/exportJobs" -Method "POST" -Headers $headers -Body $payload 325 | Write-Output $jobGenResult 326 | 327 | #Keep polling til the blob link appears 328 | Write-Output "Now starting extraction of report for profile $profileName" 329 | $reportUrl = "" 330 | 331 | while($true) 332 | { 333 | Start-Sleep -Seconds 10 334 | $report = Invoke-RestMethod "https://graph.microsoft.com/beta/deviceManagement/reports/exportJobs('$($jobGenResult.id)')" -Method "GET" -Headers $headers 335 | if($null -ne $report.url) 336 | { 337 | Write-Output "Report extraction successful. Url loaded. Now pulling report..." 338 | $reportUrl = $report.url 339 | break 340 | } 341 | Write-Output "Report unavailable. Trying again in 10 seconds..." 342 | } 343 | 344 | #In case there are special characters, which screws up downloading and creating directory/zip 345 | $trimmedProfileName = $profileName -replace '[^A-Za-z0-9\s]','' 346 | 347 | if(Test-Path "$psscriptroot\data\$trimmedProfileName") 348 | { 349 | Remove-Item "$psscriptroot\data\$trimmedProfileName" -Force -Recurse 350 | } 351 | 352 | New-Item -Path "$psscriptroot\data" -Name "$trimmedProfileName" -ItemType "directory" 353 | Invoke-WebRequest -Uri $reportUrl -OutFile "$psscriptroot\data\$trimmedProfileName\report_$trimmedProfileName.zip" 354 | Write-Output "Report downloaded for profile $profileName" 355 | 356 | Expand-Archive "$psscriptroot\data\$trimmedProfileName\report_$trimmedProfileName.zip" -DestinationPath "$psscriptroot\data\$trimmedProfileName\report_$trimmedProfileName" 357 | 358 | $theFileName = Get-ChildItem -Path "$psscriptroot\data\$trimmedProfileName\report_$trimmedProfileName" | Select -ExpandProperty FullName | Select-Object -first 1 359 | $featureUpdateReport = Import-Csv -Path $theFileName 360 | } 361 | 362 | 363 | # Manual re-authentication to app reg (NOTE: This may not be needed depending on number of profiles and device statuses exported thus far) 364 | $headers = Connect_To_Graph 365 | 366 | 367 | ############################################################# 368 | ###### ###### 369 | ###### Begin Tracking Devices ###### 370 | ###### ###### 371 | ############################################################# 372 | 373 | $outarray = @() 374 | $deviceCount = 0 375 | 376 | foreach($device in $intuneDevices) 377 | { 378 | 379 | if($targetDeviceGroupName -ne "All" -and $groupDeviceIds -notcontains $device.azureActiveDirectoryDeviceId) 380 | { 381 | continue 382 | } 383 | $deviceCount += 1 384 | 385 | #DEVICE VARIABLES FOR REPORTING (For reference: https://learn.microsoft.com/en-us/graph/api/intune-devices-manageddevice-list?view=graph-rest-1.0&viewFallbackFrom=graph-rest-beta&tabs=http) 386 | $deviceId = $device.id 387 | $deviceAADid = $device.azureActiveDirectoryDeviceId 388 | $deviceName = $device.deviceName 389 | $deviceLastSync = $device.lastSyncDateTime 390 | $deviceSync = $deviceLastSync.split("T")[0] #This will be in UTC 391 | #$deviceSync = [datetime]::Parse($deviceLastSync).ToString('MM-dd-yyyy') 392 | $deviceUPN = $device.userPrincipalName 393 | $deviceCompliance = $device.complianceState 394 | $deviceJoinType = $device.joinType 395 | $deviceAutopilotEnrolled = $device.autopilotEnrolled 396 | $deviceEnrollmentProfileName = $device.enrollmentProfileName 397 | 398 | #Additional call for hardware details... 399 | $deviceAdditionalInfo = Invoke-RestMethod "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$($deviceId)?`$select=chassisType,ethernetMacAddress,hardwareInformation,physicalMemoryInBytes,processorArchitecture,roleScopeTagIds" -Method "GET" -Headers $headers 400 | $deviceHardwareInfo = $deviceAdditionalInfo.hardwareInformation 401 | [string]$deviceScopeTags = $deviceAdditionalInfo.roleScopeTagIds 402 | $deviceSerial = $device.serialNumber 403 | $deviceChassis = $deviceAdditionalInfo.chassisType 404 | $deviceManufacturer = $deviceHardwareInfo.manufacturer 405 | $deviceModel = $deviceHardwareInfo.model 406 | $deviceBiosVersion = $deviceHardwareInfo.systemManagementBIOSVersion 407 | $deviceOsVersion = $device.osVersion 408 | $deviceOsEdition = $deviceHardwareInfo.operatingSystemEdition 409 | $deviceArchitecture = $deviceAdditionalInfo.processorArchitecture 410 | $deviceTpmSpecVersion = $deviceHardwareInfo.tpmSpecificationVersion 411 | $deviceTpmManufacturer = $deviceHardwareInfo.tpmManufacturer 412 | $deviceTpmMfrVersion = $deviceHardwareInfo.tpmVersion 413 | $deviceIpAddress = $deviceHardwareInfo.ipAddressV4 414 | $deviceIpSubnet = $deviceHardwareInfo.subnetAddress 415 | [string]$deviceWiredIpAddress = $deviceHardwareInfo.wiredIPv4Addresses 416 | $deviceEthernetMacAddress = $deviceAdditionalInfo.ethernetMacAddress 417 | $deviceTotalStorage = $deviceHardwareInfo.totalStorageSpace 418 | $deviceFreeStorage = $deviceHardwareInfo.freeStorageSpace 419 | $deviceLicenseStatus = $deviceHardwareInfo.deviceLicensingStatus 420 | 421 | #additional call for Defender info... 422 | $deviceWinSecurity = Invoke-RestMethod "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$($deviceId)/windowsProtectionState" -Method "GET" -Headers $headers 423 | $deviceMalwareProtection = $deviceWinSecurity.malwareProtectionEnabled 424 | $deviceWinSecurityState = $deviceWinSecurity.deviceStatuses 425 | $deviceRealTimeProtection = $deviceWinSecurity.realTimeProtectionEnabled 426 | $deviceNetworkInspectionEnabled = $deviceWinSecurity.networkInspectionSystemEnabled 427 | $deviceQuickScanOverdue = $deviceWinSecurity.quickScanOverdue 428 | $deviceFullScanOverdue = $deviceWinSecurity.fullScanOverdue 429 | $deviceSignatureUpdateRequired = $deviceWinSecurity.signatureUpdateOverdue 430 | $deviceRebootRequired = $deviceWinSecurity.rebootRequired 431 | $deviceFullScanRequired = $deviceWinSecurity.fullScanRequired 432 | $deviceSecurityEngineVersion = $deviceWinSecurity.engineVersion 433 | $deviceSecuritySignatureVersion = $deviceWinSecurity.signatureVersion 434 | $deviceSecurityAntiMalwareVersion = $deviceWinSecurity.antiMalwareVersion 435 | if($deviceWinSecurity.lastQuickScanDateTime){$deviceSecuritylastQuickScan = ($deviceWinSecurity.lastQuickScanDateTime).split("T")[0]} else {$deviceSecuritylastQuickScan = "unknown"} 436 | if($deviceWinSecurity.lastFullScanDateTime){$deviceSecuritylastFullScan = ($deviceWinSecurity.lastFullScanDateTime).split("T")[0]} else {$deviceSecuritylastFullScan = "unknown"} 437 | $deviceSecuritylastQuickScanSignatureVersion = $deviceWinSecurity.lastQuickScanSignatureVersion 438 | $deviceSecuritylastFullScanSignatureVersion = $deviceWinSecurity.lastFullScanSignatureVersion 439 | if($deviceWinSecurity.lastReportedDateTime){$deviceSecuritylastReported = ($deviceWinSecurity.lastReportedDateTime).split("T")[0]} else {$deviceSecuritylastReported = "unknown"} 440 | $deviceSecurityproductStatus = $deviceWinSecurity.productStatus 441 | $deviceSecurityisVirtualMachine = $deviceWinSecurity.isVirtualMachine 442 | $deviceSecuritytamperProtectionEnabled = $deviceWinSecurity.tamperProtectionEnabled 443 | 444 | #device health attestation info... 445 | $deviceHealthAttestation = $null 446 | foreach($report in $deviceHealthAttestationReport) 447 | { 448 | if($report.deviceName -eq $deviceName) 449 | { 450 | $deviceHealthAttestation = $report.deviceHealthAttestationState 451 | $deviceHealthAttestationStatus = $deviceHealthAttestation.deviceHealthAttestationStatus 452 | $deviceHealthAttestationSupportStatus = $deviceHealthAttestation.healthAttestationSupportStatus 453 | $deviceHealthAttestationKey = $deviceHealthAttestation.attestationIdentityKey 454 | $deviceBitLockerStatus = $deviceHealthAttestation.bitLockerStatus 455 | $deviceSecureBoot = $deviceHealthAttestation.secureBoot 456 | $deviceBootDebugging = $deviceHealthAttestation.bootDebugging 457 | $deviceOsKernelDebugging = $deviceHealthAttestation.operatingSystemKernelDebugging 458 | $deviceCodeIntegrity = $deviceHealthAttestation.codeIntegrity 459 | $deviceTestSigning = $deviceHealthAttestation.testSigning 460 | $deviceSafeMode = $deviceHealthAttestation.safeMode 461 | $devicesWindowsPE = $deviceHealthAttestation.windowsPE 462 | $deviceEarlyLaunchAntiMalwareDriverProtection = $deviceHealthAttestation.earlyLaunchAntiMalwareDriverProtection 463 | $deviceVirtualSecureMode = $deviceHealthAttestation.virtualSecureMode 464 | $deviceAttestationTpmVer = $deviceHealthAttestation.tpmVersion 465 | $deviceMemoryIntegrityProtection = $deviceHealthAttestation.memoryIntegrityProtection 466 | $deviceMemoryAccessProtection = $deviceHealthAttestation.memoryAccessProtection 467 | $deviceVirtualizationBasedSecurity = $deviceHealthAttestation.virtualizationBasedSecurity 468 | $deviceFirmwareProtection = $deviceHealthAttestation.firmwareProtection 469 | $deviceSystemManagementMode = $deviceHealthAttestation.systemManagementMode 470 | $deviceSecuredCorePC = $deviceHealthAttestation.securedCorePC 471 | break 472 | } 473 | } 474 | if(!($deviceHealthAttestation)) 475 | { 476 | $deviceHealthAttestationStatus = "Unknown" 477 | $deviceHealthAttestationSupportStatus = "Unknown" 478 | $deviceHealthAttestationKey = "Unknown" 479 | $deviceBitLockerStatus = "Unknown" 480 | $deviceSecureBoot = "Unknown" 481 | $deviceBootDebugging = "Unknown" 482 | $deviceOsKernelDebugging = "Unknown" 483 | $deviceCodeIntegrity = "Unknown" 484 | $deviceTestSigning = "Unknown" 485 | $deviceSafeMode = "Unknown" 486 | $devicesWindowsPE = "Unknown" 487 | $deviceEarlyLaunchAntiMalwareDriverProtection = "Unknown" 488 | $deviceVirtualSecureMode = "Unknown" 489 | $deviceAttestationTpmVer = "Unknown" 490 | $deviceMemoryIntegrityProtection = "Unknown" 491 | $deviceMemoryAccessProtection = "Unknown" 492 | $deviceVirtualizationBasedSecurity = "Unknown" 493 | $deviceFirmwareProtection = "Unknown" 494 | $deviceSystemManagementMode = "Unknown" 495 | $deviceSecuredCorePC = "Unknown" 496 | } 497 | 498 | #MDM over GPO status, if applied 499 | $deviceMDMoverGPO = $device.preferMdmOverGroupPolicyAppliedDateTime 500 | if($deviceMDMoverGPO = "0001-01-01T00:00:00Z"){ 501 | $deviceMDMoverGPOstring = "Not applied." 502 | } 503 | else 504 | { 505 | $deviceMDMoverGPOstring = $deviceMDMoverGPO 506 | } 507 | 508 | #managed by state (Intune devices query doesn't appear to pull configMgr-only/cloud-attached devices, but including if statement to be safe) 509 | if($device.managementAgent -eq "configurationManagerClientMdm"){ 510 | $deviceManagement = "Co-Managed" 511 | } 512 | elseif($device.managementAgent -eq "mdm") 513 | { 514 | $deviceManagement = "Intune" 515 | } 516 | elseif($device.managementAgent -eq "configurationManagerClient") 517 | { 518 | $deviceManagement = "ConfigMgr" 519 | } 520 | else 521 | { 522 | $deviceManagement = $device.managementAgent 523 | } 524 | 525 | #encryption state 526 | if(($device.isEncrypted) -eq "True"){ 527 | $deviceEncryptionStatus = "Encrypted" 528 | } 529 | else 530 | { 531 | $deviceEncryptionStatus = "Not encrypted" 532 | } 533 | 534 | #Search encryption report for matching device 535 | foreach($erRecord in $encryptionReport) 536 | { 537 | if($erRecord.deviceName -eq $deviceName) 538 | { 539 | $deviceEncryptionReport_ReadinessState = $erRecord.encryptionReadinessState 540 | $deviceEncryptionReport_TPMSpecificationVersion = $erRecord.tpmSpecificationVersion 541 | $deviceEncryptionReport_AdvancedBitLockerStates = $erRecord.advancedBitLockerStates 542 | break 543 | } 544 | } 545 | 546 | #SCCM information for co-managed devices, where applicable 547 | $deviceSCCMclientHealth = $device.configurationManagerClientHealthState.state 548 | $deviceSCCMclientLastSync = $device.configurationManagerClientHealthState.lastSyncDateTime 549 | if($deviceManagement -eq "Intune"){ 550 | $deviceSCCMclientHealth = "Intune-only" 551 | $deviceSCCMclientSync = "N/A" 552 | } 553 | else 554 | { 555 | $deviceSCCMclientSync = $deviceSCCMclientLastSync.split("T")[0] #Will display in UTC 556 | #$deviceSCCMclientSync = [datetime]::Parse($deviceSCCMclientLastSync).ToString('MM-dd-yyyy') 557 | } 558 | $deviceComanagementSettings = $device.configurationManagerClientEnabledFeatures 559 | $deviceComanagementApps = "N/A" 560 | $deviceComanagementResourceAccess = "N/A" 561 | $deviceComanagementDeviceConfig = "N/A" 562 | $deviceComanagementCompliance = "N/A" 563 | $deviceComanagementWindowsUpdate = "N/A" 564 | $deviceComanagementEndpointProtection = "N/A" 565 | $deviceComanagementOfficeApps = "N/A" 566 | 567 | if($deviceManagement -eq "Co-Managed"){ 568 | if($deviceComanagementSettings.ModernApps -eq "True") {$deviceComanagementApps = "Intune"} else {$deviceComanagementApps = "SCCM"} 569 | if($deviceComanagementSettings.resourceAccess -eq "True") {$deviceComanagementResourceAccess = "Intune"} else {$deviceComanagementResourceAccess = "SCCM"} 570 | if($deviceComanagementSettings.deviceConfiguration -eq "True") {$deviceComanagementDeviceConfig = "Intune"} else {$deviceComanagementDeviceConfig = "SCCM"} 571 | if($deviceComanagementSettings.compliancePolicy -eq "True") {$deviceComanagementCompliance = "Intune"} else {$deviceComanagementCompliance = "SCCM"} 572 | if($deviceComanagementSettings.windowsUpdateForBusiness -eq "True") {$deviceComanagementWindowsUpdate = "Intune"} else {$deviceComanagementWindowsUpdate = "SCCM"} 573 | if($deviceComanagementSettings.endpointProtection -eq "True") {$deviceComanagementEndpointProtection = "Intune"} else {$deviceComanagementEndpointProtection = "SCCM"} 574 | if($deviceComanagementSettings.officeApps -eq "True") {$deviceComanagementOfficeApps = "Intune"} else {$deviceComanagementOfficeApps = "SCCM"} 575 | } 576 | 577 | #logged on users 578 | $usersLoggedOn = $device.usersLoggedOn.userId 579 | 580 | #Group memberships of device 581 | write-host "Getting generic device profile of $deviceSerial..." 582 | $deviceNormalProfile = (Invoke-RestMethod "https://graph.microsoft.com/beta/devices?`$filter=displayName eq '$($deviceName)'" -Method "GET" -Headers $headers).value 583 | 584 | if($null -eq $deviceNormalProfile){ 585 | Write-Warning "Azure device object could not be found." 586 | } 587 | elseif($deviceNormalProfile.length -gt 1){ 588 | 589 | foreach($object in $deviceNormalProfile){ 590 | if($object.deviceId -eq $device.azureActiveDirectoryDeviceId){ 591 | $deviceAzureId = $object.deviceId 592 | $deviceAzureObjId = $object.id 593 | break 594 | } 595 | } 596 | } 597 | 598 | else 599 | { 600 | $deviceAzureId = $deviceNormalProfile.deviceId 601 | $deviceAzureObjId = $deviceNormalProfile.id 602 | } 603 | 604 | write-host "Getting group memberships of device $deviceSerial..." 605 | $deviceGroupSearch = Invoke-RestMethod "https://graph.microsoft.com/beta/devices/$($deviceAzureObjId)/memberOf" -Method "GET" -Headers $headers 606 | $deviceGroupValue = $deviceGroupSearch.value 607 | $deviceGroups = @() 608 | $deviceGroupsString = $null 609 | 610 | foreach($group in $deviceGroupValue){ 611 | $deviceGroups += $group.displayName 612 | $deviceGroupsString = $deviceGroups -join "; " 613 | } 614 | 615 | 616 | #Feature Update Status 617 | $deviceFeatureUpdateStatus = $null 618 | foreach($entry in $featureUpdateReport){ 619 | $featUpdate_Status = $entry.CurrentDeviceUpdateSubstatus_loc 620 | $featUpdate_deviceName = $entry.DeviceName 621 | 622 | if($featUpdate_deviceName -eq $deviceName){ 623 | $deviceFeatureUpdateStatus = $featUpdate_Status 624 | break 625 | } 626 | } 627 | 628 | # get list of all compliance policies of this particular device 629 | write-host "Getting compliance policy for device $deviceSerial ..." 630 | $deviceCompliancePolicy = (Invoke-RestMethod "https://graph.microsoft.com/beta/deviceManagement/managedDevices('$deviceId')/deviceCompliancePolicyStates" -Method "GET" -Headers $headers).value 631 | $deviceComplianceStatus_Detailed = $null 632 | $settingArray = @() 633 | $complianceArray = @() 634 | $windowsPolicyFound = $False 635 | $defaultPolicyFound = $True 636 | 637 | # Compliance policy details (please update line 382 below - this way Intune's default compliance state is not reported) 638 | foreach($policy in $deviceCompliancePolicy){ 639 | if($policy.platformType -like "*windows*"){ 640 | 641 | $windowsPolicyFound = $True 642 | $deviceComplianceId = $policy.id 643 | write-host "Getting compliance settings states for device $deviceName..." 644 | $deviceComplianceStatus_Detailed = (Invoke-RestMethod "https://graph.microsoft.com/beta/deviceManagement/managedDevices('$deviceId')/deviceCompliancePolicyStates('$deviceComplianceId')/settingStates" -Method "GET" -Headers $headers).value 645 | $deviceComplianceStatus_Detailed | Select @{n = 'deviceName'; e = { $deviceName } }, state, setting 646 | 647 | if($deviceComplianceStatus_Detailed.Length -eq 0){ 648 | $complianceArray += "Targeted Compliance Policy not evaluated." 649 | } 650 | 651 | else 652 | { 653 | foreach($setting in $deviceComplianceStatus_Detailed) 654 | { 655 | $settingName = $setting.setting 656 | $settingName = $settingName.Split(".") | Select -Index 1 657 | $settingState = $setting.state 658 | $settingUPN = $setting.userPrincipalName 659 | 660 | if($settingArray -notcontains "($settingUPN) $settingName = $settingState"){ 661 | $settingArray += "($settingUPN) $settingName = $settingState" 662 | } 663 | } 664 | 665 | #Only include UPN based states if system and UPN states are present 666 | foreach($result in $settingArray){ 667 | if($result -like "*System account*" -and $settingArray -like "*@*"){ 668 | continue 669 | } 670 | elseif($complianceArray -contains $result){ 671 | continue 672 | } 673 | else 674 | { 675 | $complianceArray += $result 676 | } 677 | } 678 | 679 | } 680 | } 681 | elseif($policy.displayName -like "Default Device Compliance Policy") 682 | { 683 | $defaultPolicyFound = $true 684 | $deviceComplianceId = $policy.id 685 | write-host "Getting compliance settings states for device $deviceName..." 686 | $deviceComplianceStatus_Detailed = (Invoke-RestMethod "https://graph.microsoft.com/beta/deviceManagement/managedDevices('$deviceId')/deviceCompliancePolicyStates('$deviceComplianceId')/settingStates" -Method "GET" -Headers $headers).value 687 | $deviceComplianceStatus_Detailed | Select @{n = 'deviceName'; e = { $deviceName } }, state, setting 688 | 689 | if($deviceComplianceStatus_Detailed.Length -eq 0){ 690 | $complianceArray += "Default Compliance Policy not evaluated." 691 | } 692 | 693 | else 694 | { 695 | foreach($setting in $deviceComplianceStatus_Detailed) 696 | { 697 | $settingName = $setting.setting 698 | $settingName = $settingName.Split(".") | Select -Index 1 699 | $settingState = $setting.state 700 | $settingUPN = $setting.userPrincipalName 701 | 702 | if($settingArray -notcontains "($settingUPN) $settingName = $settingState"){ 703 | $settingArray += "($settingUPN) $settingName = $settingState" 704 | } 705 | } 706 | 707 | #Only include UPN based states if system and UPN states are present 708 | foreach($result in $settingArray){ 709 | if($result -like "*System account*" -and $settingArray -like "*@*"){ 710 | continue 711 | } 712 | elseif($complianceArray -contains $result){ 713 | continue 714 | } 715 | else 716 | { 717 | $complianceArray += $result 718 | } 719 | } 720 | 721 | } 722 | } 723 | 724 | #Combine compliance results into single string 725 | $deviceComplianceStatus_DetailedString = $complianceArray -join "; " 726 | } 727 | 728 | if($windowsPolicyFound -eq $False){ 729 | $deviceComplianceStatus_DetailedString = 'Windows-based Compliance Policy not assigned.' 730 | } 731 | 732 | #Device Update Ring Status 733 | foreach($ring in $updateRingsStatus) 734 | { 735 | if($($ring.Device) -eq $deviceName) 736 | { 737 | $deviceUpdateRing = $($ring.Ring) 738 | $deviceUpdateRingStatus = $($ring.Status) 739 | break 740 | } 741 | } 742 | 743 | ############################################################# 744 | 745 | #Add desired labels and variables to array for csv export 746 | $record = [ordered] @{ 747 | 'Name' = $deviceName 748 | 'Serial' = $deviceSerial 749 | 'Associate UPN' = $deviceUPN 750 | 'Last Device Sync Time' = $deviceSync 751 | 'Managed By' = $deviceManagement 752 | 'OS Version' = $deviceOsVersion 753 | 'OS Edition' = $deviceOsEdition 754 | 'Feature Update Status' = $deviceFeatureUpdateStatus 755 | 'Update Ring' = $deviceUpdateRing 756 | 'Update Ring Status' = $deviceUpdateRingStatus 757 | 'Encryption Status' = $deviceEncryptionStatus 758 | 'BitLocker Status' = $deviceBitLockerStatus 759 | 'Encryption Readiness State' = $deviceEncryptionReport_ReadinessState 760 | 'Encryption Advanced State' = $deviceEncryptionReport_AdvancedBitLockerStates 761 | 'TPM SpecVersion' = $deviceTpmSpecVersion 762 | 'TPM SpecVersion (H.A.)' = $deviceAttestationTpmVer 763 | 'TPM Manufacturer' = $deviceTpmManufacturer 764 | 'TPM Mfr Version' = $deviceTpmMfrVersion 765 | 'BIOS Version' = $deviceBiosVersion 766 | 'Secure Boot' = $deviceSecureBoot 767 | 'Model' = $deviceModel 768 | 'Manufacturer' = $deviceManufacturer 769 | 'Is Virtual Machine' = $deviceSecurityisVirtualMachine 770 | 'Chassis Type' = $deviceChassis 771 | 'Processor Architecture' = $deviceArchitecture 772 | 'Compliance Status' = $deviceCompliance 773 | 'Detailed Compliance Settings' = $deviceComplianceStatus_DetailedString 774 | 'SCCM Client Health' = $deviceSCCMclientHealth 775 | 'SCCM Client Last Sync' = $deviceSCCMclientSync 776 | 'Co-Management: Compliance' = $deviceComanagementCompliance 777 | 'Co-Management: Device Configuration' = $deviceComanagementDeviceConfig 778 | 'Co-Management: Endpoint Protection' = $deviceComanagementEndpointProtection 779 | 'Co-Management: Resource Access' = $deviceComanagementResourceAccess 780 | 'Co-Management: Client Apps' = $deviceComanagementApps 781 | 'Co-Management: Office C2R Apps' = $deviceComanagementOfficeApps 782 | 'Co-Management: Windows Updates' = $deviceComanagementWindowsUpdate 783 | 'MDM over GPO Applied' = $deviceMDMoverGPOstring 784 | 'Intune Device ID' = $deviceId 785 | 'Azure Device ID' = $deviceAzureId 786 | 'Scope Tags' = $deviceScopeTags 787 | 'Group Memberships' = $deviceGroupsString 788 | 'Users Logged On' = $usersLoggedOn 789 | 'IP Address' = $deviceIpAddress 790 | 'IP Subnet' = $deviceIpSubnet 791 | 'Wired IP Address' = $deviceWiredIpAddress 792 | 'Ethernet Mac Address' = $deviceEthernetMacAddress 793 | 'Total Storage' = $deviceTotalStorage 794 | 'Free Storage' = $deviceFreeStorage 795 | 'Health Attestation Status' = $deviceHealthAttestationStatus 796 | 'Health Attestation Support' = $deviceHealthAttestationSupportStatus 797 | } 798 | 799 | $outarray += New-Object PsObject -property $record 800 | 801 | #Each device sets off 6 calls. 1500 devices is 9000 calls. Rest for 10 seconds. Process time for one device is estimated at 1-2 seconds. 802 | #This means 1500 devices take approximately 1500 seconds, or 25 minutes. Graph limit window is 10,000 calls per 10 minutes. Resting is to make the window absolute and to not interfere in company business. 803 | # 804 | if($deviceCount % 1500 -eq 0) 805 | { 806 | Write-Output "$deviceCount devices have been processed so far. Now sleeping for 10 seconds to avoid graph limiting..." 807 | Start-Sleep -Seconds 10 808 | Write-Host "Re-authenticating with app registration..." 809 | $headers = Connect_To_Graph 810 | 811 | 812 | #NOTE: This if statement can be used if you need to re-authenticate after a larger number of devices (needs to be a multiple of the number above) 813 | <#if($deviceCount % 3000 -eq 0) 814 | { 815 | Write-Host "Re-authenticating with app registration..." 816 | $headers = Connect_To_Graph 817 | } 818 | #> 819 | } 820 | 821 | } 822 | 823 | 824 | #Finally, we export the Data :) 825 | Write-Host "Reporting Complete. Exporting the final csv..." 826 | $outarray | export-csv "C:\Users\Public\Desktop\Intune_WindowsPCs_$($targetDeviceGroupName)_Report.csv" -NoTypeInformation -Force 827 | --------------------------------------------------------------------------------