├── Microsoft ├── Intune │ ├── Get-WindowsAutoPilotInfo │ │ ├── compHash.csv │ │ ├── _readme.txt │ │ ├── GetAutoPilot.cmd │ │ └── Get-WindowsAutoPilotInfo.ps1 │ ├── PR008 - Start URL On Logon │ │ ├── Alternative 1 (URL file) │ │ │ ├── SimonDoes.url │ │ │ ├── StartURLOnLogonRemoval-PlatformScript.ps1 │ │ │ ├── StartURLOnLogon-Detection.ps1 │ │ │ └── StartURLOnLogon-Remediation.ps1 │ │ └── Alternative 2 (Registry) │ │ │ ├── StartURLonLogon-Detection.ps1 │ │ │ └── StartURLonLogon-Remediation.ps1 │ ├── MS Security Baselines │ │ └── README.md │ ├── EdgeAndChromeManagedFavorites.txt │ ├── PR001 - Windows LAPS Account │ │ ├── Create-LocalAdminLAPSDetection.ps1 │ │ └── Create-LocalAdminLAPSRemediation.ps1 │ ├── OfficeIcons-Detect.ps1 │ ├── FortinetVPNProfile-Detect.ps1 │ ├── PR012 - Clean WSUS Settings │ │ ├── Clean WSUS Settings - Detection.ps1 │ │ └── Clean WSUS Settings - Remediation.ps1 │ ├── PR004 - Download Wallpaper │ │ ├── Wallpaper-Detection.ps1 │ │ └── Wallpaper-Remediation.ps1 │ ├── Get-WindowsCorporateIdentifer.ps1 │ ├── CloudLAPS_EventLog_Removal_1_0_0.ps1 │ ├── WPS007 - Set Regional Settings Norway │ │ └── WPS007 - SetRegionalSettingsNorway.ps1 │ ├── RemoveTeamsConsumer-Detect.ps1 │ ├── PR007 - Block AAD Registration to 3rd party tenants │ │ ├── BlockAADReg-Detection.ps1 │ │ └── BlockAADReg-Remediation.ps1 │ ├── RemoveTeamsConsumer-Remediation.ps1 │ ├── PR005 - Java in Adobe Acrobat │ │ ├── DisableJavaScriptOnAdobeAcrobat-Detection.ps1 │ │ └── DisableJavaScriptOnAdobeAcrobat-Remediation.ps1 │ ├── Win11-AddPrintersFromPrintserver.ps1 │ ├── Get-IntuneDevicesEnrolledBy.ps1 │ ├── Win11-SetDesktopTheme.ps1 │ ├── Group Tag Fixer │ │ ├── Add-ApplicationPermissionsToSystemAssignedManagedIdentity.ps1 │ │ └── RB-Grouptag Fixer.ps1 │ ├── HP Connect Authentication.ps1 │ ├── PR009 - Autopatch NoAutoUpdate │ │ ├── Autopatch-NoAutoUpdate-Detection.ps1 │ │ └── Autopatch-NoAutoUpdate-Remediation.ps1 │ ├── Win11-DriveMapping_Q.ps1 │ ├── Win11-DriveMapping_M.ps1 │ ├── PR003 - Branding │ │ ├── Branding-Detection.ps1 │ │ └── Branding-Remediation.ps1 │ ├── Win11-OEMInformation.ps1 │ ├── FortinetVPNProfile-Remediation.ps1 │ ├── Get-ManagedDeviceBeforeIntuneCleanUp.ps1 │ ├── Get-IntuneUsersEnrollingMultipleDevices.ps1 │ ├── Win11-NetworkConfigurationOperators.ps1 │ ├── CloudLAPS_SchTask_Uninstall_1_0_0.ps1 │ ├── Get-IntuneUniqueGroupTags.ps1 │ ├── WPS006 - Show File Extensions in Windows Explorer │ │ └── WPS006 - ShowFileExtensionsWindowsExplorer.ps1 │ ├── Win11-EnableAdobeAcrobatForMicrosoftSensitivityLabels.ps1 │ ├── PR013 - Uninstall N-able Client │ │ ├── Uninstall N-able Client - Detection.ps1 │ │ ├── Uninstall N-able Client - Remediation - alternative.ps1 │ │ └── Uninstall N-able Client - Remediation.ps1 │ ├── Get-IntuneCorporateDeviceIdentiferDevices.ps1 │ ├── WPS008 - TeamViewer Monitor │ │ ├── TeamViewerMonitor-EventTrigger.ps1 │ │ └── TeamViewerMonitor-TimeTrigger.ps1 │ ├── aa-intune-autopatch-group-automation-runbook.ps1 │ ├── Intune-ChangeOfComputerNames-Runbook-ManagedIdentity.ps1 │ ├── Intune-ChangeOfComputerNames-Runbook.ps1 │ ├── Intune-CheckDeviceEnrolledBy.ps1 │ └── OfficeIcons-Remediation.ps1 ├── AD │ ├── ChangeADUserUPNandMail.csv │ ├── ExportADResourcesToCsv.ps1 │ ├── ImportADResourceDetailsFromCSV.ps1 │ ├── ChangeADUserUPNandMail.ps1 │ └── SetReadOnlyOnHomeDrives.ps1 ├── KQL │ ├── Windows Versions.kql │ ├── SigninLogsPrCAPolicy.kql │ └── AADNonInteractiveUserSignInLogs.kql ├── TEAMS │ └── Manage Who Can Create Teams.ps1 ├── Exchange │ ├── Exchange-ListResources.ps1 │ ├── Exchange-ListMailboxes.ps1 │ ├── Exchange-ListSharedMailboxes.ps1 │ └── RUNBOOK-SetExchangeCalendarPermissions.ps1 ├── AAD │ ├── AAD-EnableAzureADB2BIntegrationInSharePoint.ps1 │ ├── AAD-AssignPermissionsToManagedIdentity.ps1 │ ├── AAD-ListAllSKUs.ps1 │ ├── GET-LAPSDevices.ps1 │ ├── AAD-FindObjectsUsingDomain.ps1 │ ├── AAD-Users-ExportAttributesToCSV.ps1 │ ├── AAD-ListApplicationsWithPermissions.ps1 │ ├── EntraID - GetPasskeysInUse.ps1 │ ├── AAD-CA-PersonaGroups.ps1 │ ├── AAD-CA-Apps.ps1 │ ├── AAD-Users-ImportAttributesFromCSV.ps1 │ ├── EntraID - Authenticate Using Certificates and App Registrations.ps1 │ ├── Add-AADLicenseGroups.ps1 │ └── TENANT-DomainCheck.ps1 └── SharePoint │ └── OneDriveForBusiness PreProvisioning.ps1 ├── README.md └── .vscode └── launch.json /Microsoft/Intune/Get-WindowsAutoPilotInfo/compHash.csv: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Microsoft/AD/ChangeADUserUPNandMail.csv: -------------------------------------------------------------------------------- 1 | SamAccountName,EmailAddress,NewUPN 2 | simon,simon.skotheimsvik@contoso.com,simon.skotheimsvik@cloudway.com -------------------------------------------------------------------------------- /Microsoft/Intune/PR008 - Start URL On Logon/Alternative 1 (URL file)/SimonDoes.url: -------------------------------------------------------------------------------- 1 | [{000214A0-0000-0000-C000-000000000046}] 2 | Prop3=19,11 3 | [InternetShortcut] 4 | IDList= 5 | URL=https://skotheimsvik.no/ 6 | HotKey=0 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Community 2 | This repository contains some scripts and ideas shared with the community. They are all mostly the result of practical work and the desire for an efficient and automated workflow. 3 | 4 | No extra charge for the mistakes. All use at your own risk! 5 | -------------------------------------------------------------------------------- /Microsoft/KQL/Windows Versions.kql: -------------------------------------------------------------------------------- 1 | 2 | IntuneDevices 3 | | where OS == "Windows" 4 | | summarize count() by OSVersion 5 | 6 | IntuneDevices 7 | | where OS == "Windows" 8 | | extend OSVersionParts = split(OSVersion, ".") 9 | | extend OSVersionPrefix = strcat(OSVersionParts[0], ".", OSVersionParts[1], ".", OSVersionParts[2]) 10 | | summarize count() by OSVersionPrefix 11 | | render columnchart -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "PowerShell: Launch Current File", 9 | "type": "PowerShell", 10 | "request": "launch", 11 | "script": "${file}", 12 | "args": [] 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /Microsoft/Intune/MS Security Baselines/README.md: -------------------------------------------------------------------------------- 1 | # MS Security Baselines Exports 2 | 3 | This folder contains exported Microsoft Security Baseline policies from Intune. The policies were created in a tenant with default settings. These files can serve as helpful references when using [IntuneDiff.com](https://intunediff.com) to review and compare custom Intune configuration policies. 4 | 5 | The export was performed using Microsoft Edge Developer Tools (F12), following the steps outlined in the [IntuneDiff.com User Guide](https://intunediff.com/user-guide). -------------------------------------------------------------------------------- /Microsoft/TEAMS/Manage Who Can Create Teams.ps1: -------------------------------------------------------------------------------- 1 | # https://learn.microsoft.com/en-us/microsoft-365/solutions/manage-creation-of-groups?view=o365-worldwide 2 | 3 | # Import the required modules 4 | Import-Module Microsoft.Graph.Beta.Identity.DirectoryManagement 5 | Import-Module Microsoft.Graph.Beta.Groups 6 | 7 | # Connect to Microsoft Graph 8 | Connect-MgGraph -Scopes "Directory.ReadWrite.All", "Group.Read.All" 9 | 10 | 11 | # Check configuration 12 | (Get-MgBetaDirectorySetting).Values 13 | 14 | # Get information about the group allowed to create teams 15 | $group = Get-MgBetaGroup -Id 16 | 17 | 18 | -------------------------------------------------------------------------------- /Microsoft/Intune/PR008 - Start URL On Logon/Alternative 1 (URL file)/StartURLOnLogonRemoval-PlatformScript.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | Created on: 26.06.2024 4 | Created by: Simon Skotheimsvik 5 | Filename: StartURLOnLogonRemoval-PlatformScript.ps1 6 | Info: https://skotheimsvik.no 7 | Version: 1.0 8 | 9 | .DESCRIPTION 10 | This Platform script removes alt 1 using the URL file 11 | #> 12 | 13 | 14 | # clean out alternative 1 using URL file, if it exists 15 | $path = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\SimonDoes.url"; if (Test-Path $path) { Remove-Item $path } 16 | 17 | Exit 0 18 | -------------------------------------------------------------------------------- /Microsoft/Intune/Get-WindowsAutoPilotInfo/_readme.txt: -------------------------------------------------------------------------------- 1 | Tool to collect hardware has from computer and import to Autopilot. 2 | 3 | 1. Unbox the device and boot it up 4 | 2. At the first OOBE picture, press SHIFT+F10 to start CMD 5 | 3. Insert USB and CD to D: 6 | Use the following to locate the disk if D is incorrect: 7 | wmic logicaldisk list brief 8 | 4. Decide if you need to delete compHash.csv 9 | 5. Run GetAutoPilot.cmd 10 | Make your choices 11 | 6. Tur of the PC (shutdown /s /t 0) 12 | 7. Import compHash.csv to Autopilot in Intune 13 | 8. Let the device register and assign a profile before booting the device to Autopilot. 14 | 15 | 2020.05.08 - v1.0 - Simon 16 | 2020.09.30 - v1.1 - Simon -------------------------------------------------------------------------------- /Microsoft/Intune/EdgeAndChromeManagedFavorites.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "toplevel_name": "Cloud Limits" 4 | }, 5 | { 6 | "url": "www.google.com", 7 | "name": "Google" 8 | }, 9 | { 10 | "name": "Internal portals", 11 | "children": [ 12 | { 13 | "url": "https://portal.office.com/", 14 | "name": "Microsoft Office" 15 | }, 16 | { 17 | "url": "https://tripletex.no/", 18 | "name": "Tripletex" 19 | } 20 | ] 21 | }, 22 | { 23 | "name": "Partner portals", 24 | "children": [ 25 | { 26 | "url": "https://partner.microsoft.com/en-us/dashboard/home", 27 | "name": "Microsoft Partner Center" 28 | }, 29 | { 30 | "url": "https://learn.microsoft.com/", 31 | "name": "Microsoft Learn" 32 | } 33 | ] 34 | } 35 | ] -------------------------------------------------------------------------------- /Microsoft/Intune/PR001 - Windows LAPS Account/Create-LocalAdminLAPSDetection.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Version: 1.0 3 | Author: 4 | - Joey Verlinden (joeyverlinden.com) 5 | - Andrew Taylor (andrewstaylor.com) 6 | - Florian Slazmann (scloud.work) 7 | - Jannik Reinhard (jannikreinhard.com) 8 | - Simon Skotheimsvik (skotheimsvik.no) 9 | Script: Create-LocalAdmin 10 | Description: 11 | Hint: This is a community script. There is no guarantee for this. Please check thoroughly before running. 12 | Version 1.0: Init 13 | Run as: Admin 14 | Context: 64 Bit 15 | #> 16 | 17 | $localAdminName = "CloudLimitsLocalAdmin" 18 | 19 | if(Get-LocalUser | where-Object Name -eq $localAdminName){ 20 | Write-Host "User does already exist" 21 | Exit 0 22 | }else{ 23 | Write-Host "User does not exist" 24 | Exit 1 25 | } -------------------------------------------------------------------------------- /Microsoft/Intune/OfficeIcons-Detect.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 13.01.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: OfficeIcons-Detect.ps1 7 | Instructions: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will detect if Office icons are present 12 | Based on ideas from the Microsoft EMS Community on Discord and on Redit 13 | #> 14 | 15 | $StartMenuFolder = "$env:ProgramData\Microsoft\Windows\Start Menu\Programs" 16 | $Count = (Get-ChildItem $StartMenuFolder | ? Name -match "Word|Outlook|Powerpoint|Excel").count 17 | 18 | if ($count -ge 4) { 19 | "Installed" 20 | } 21 | 22 | else { 23 | Exit 1 24 | } -------------------------------------------------------------------------------- /Microsoft/Intune/FortinetVPNProfile-Detect.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 27.06.2022 5 | Created by: Simon Skotheimsvik 6 | Filename: FortinetVPNProfile-Detect.ps1 7 | Instructions: https://skotheimsvik.blogspot.com/2022/07/fortinet-vpn-profile-distribution-with.html 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will detect if VPN profile is present 12 | 13 | #> 14 | 15 | # Defining variables for the VPN connection 16 | $VPNName = "Simons VPN" 17 | 18 | if ((Test-Path -LiteralPath "HKLM:\SOFTWARE\Fortinet\FortiClient\Sslvpn\Tunnels\$VPNName") -ne $true) { 19 | Write-Host "Not existing" 20 | Exit 1 21 | } 22 | Else { 23 | Write-Host "OK" 24 | Exit 0 25 | } -------------------------------------------------------------------------------- /Microsoft/Exchange/Exchange-ListResources.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 19.01.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: Exchange-ListResources.ps1 7 | Instructions: https://github.com/SimonSkotheimsvik/Community-By-SSkotheimsvik/ 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will list details on Exchange resource objects on-premises 12 | #> 13 | 14 | Get-Mailbox -ResultSize Unlimited | Where-object { $_.IsResource -eq 'true' } | Select DisplayName,Alias,UserPrincipalName,PrimarySmtpAddress,RecipientType,RecipientTypeDetails,ResourceType,ServerName,Database,@{Name=“EmailAddresses”;Expression={$_.EmailAddresses | Where-Object {$_.PrefixString -ceq “smtp”} | ForEach-Object {$_.SmtpAddress}}} | Export-CSV .\Exchange-ListResources.csv -Encoding UTF8 -NoTypeInformation -------------------------------------------------------------------------------- /Microsoft/Intune/PR012 - Clean WSUS Settings/Clean WSUS Settings - Detection.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Remediation to clean WSUS settings 4 | 5 | .DESCRIPTION 6 | This script detects the WSUS registry settings. 7 | 8 | .NOTES 9 | Author: Simon Skotheimsvik 10 | Version: 1.0.0 - 2025-08-12, Simon Skotheimsvik, Initial version 11 | 12 | license: This script is provided as-is, without warranty of any kind. Use at your own risk. 13 | You may modify and redistribute this script as long as you retain this notice in the code. 14 | #> 15 | 16 | $datetime = Get-Date -Format "yyyy-MM-dd HH:mm" 17 | 18 | if(test-path -Path 'HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate'){ 19 | #registry item exists 20 | Write-Output "Reg item exists. Remediation required, $($datetime)" 21 | Exit 1 22 | } else { 23 | #registry item doesn't exists 24 | Write-Output "Reg item doesn't exist. No remediation required, $($datetime)" 25 | Exit 0 26 | } -------------------------------------------------------------------------------- /Microsoft/Intune/PR004 - Download Wallpaper/Wallpaper-Detection.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 11.07.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: Wallpaper-Detection.ps1 7 | Info: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script detects if wallpaper from Azure Storage exists locally 12 | The script can be assigned as Detection script in Microsoft Intune 13 | #> 14 | 15 | $imageURL = "https://cloudlimits.blob.core.windows.net/intuneresources-public/cloudlimits.jpg" 16 | $imagePATH = "C:\Windows\web\wallpaper\wallpaper.jpg" 17 | 18 | if (!(Test-Path -Path $imagePATH -PathType Leaf)) { 19 | Write-Host "Wallpaper missing on computer" 20 | Exit 1 #File missing 21 | } 22 | else 23 | { 24 | Write-Host "Wallpaper exists on computer" 25 | Exit 0 26 | } -------------------------------------------------------------------------------- /Microsoft/Intune/Get-WindowsCorporateIdentifer.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 04.06.2024 5 | Created by: Simon Skotheimsvik 6 | Filename: Get-WindowsCorporateIdentifier.ps1 7 | Info: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script gets a CSV with corporate identifier data from Windows10 and Windows 11 12 | to be used in Microsoft Intune for Autopilot Device Preparation. 13 | #> 14 | 15 | # Capture the output from WMI objects 16 | $computerSystem = Get-WmiObject -Class Win32_ComputerSystem 17 | $bios = Get-WmiObject -Class Win32_BIOS 18 | 19 | # Combine the results into a single string 20 | $data = "$($computerSystem.Manufacturer),$($computerSystem.Model),$($bios.SerialNumber)" 21 | 22 | # Write the data to a CSV file without headers 23 | Set-Content -Path "system_info.csv" -Value $data -------------------------------------------------------------------------------- /Microsoft/AAD/AAD-EnableAzureADB2BIntegrationInSharePoint.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 20.10.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: AAD-EnableAzureADB2BIntegrationInSharePoint.ps1 7 | Instructions: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will enable Entra ID B2B integration for SharePoint and OneDrive for Business. 12 | #> 13 | 14 | # Get module for SharePoint Online 15 | # install-module -Name Microsoft.Online.SharePoint.Powershell 16 | import-module -Name Microsoft.Online.SharePoint.Powershell 17 | 18 | # Connect to your SharePoint Admin Center 19 | Connect-SPOService -Url https://m365x84289014-admin.sharepoint.com/ 20 | 21 | # Check current setting 22 | Get-SPOTenant | ft EnableAzureADB2BIntegration 23 | 24 | # Sett Azure AD B2B Integration 25 | Set-SPOTenant -EnableAzureADB2BIntegration $true -------------------------------------------------------------------------------- /Microsoft/Intune/CloudLAPS_EventLog_Removal_1_0_0.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script to remove CloudLAPS EventLog. 4 | 5 | .DESCRIPTION 6 | This script will check if the Event Log named "CloudLAPS-Client" exists and then delete it. 7 | 8 | .NOTES 9 | FileName: CloudLAPS_EventLog_Remocal.ps1 10 | Author: Simon Skotheimsvik 11 | Contact: @SSkotheimsvik 12 | Info: https://skotheimsvik.no/migrating-cloud-laps-to-the-new-windows-laps 13 | Created: 2024.01.11 14 | Updated: 2024.01.11 15 | 16 | Version history: 17 | 1.0.0 - (2024.01.11) Script created 18 | 19 | #> 20 | 21 | #region Variables 22 | $eventLogName = "CloudLAPS-Client" 23 | #endregion Variables 24 | 25 | #region EventLog 26 | if (Get-EventLog -LogName $eventLogName -ErrorAction SilentlyContinue) { 27 | # If it exists, delete the EventLog 28 | Remove-EventLog -LogName $eventLogName 29 | Write-Host "EventLog '$eventLogName' has been deleted." 30 | } else { 31 | Write-Host "EventLog '$eventLogName' does not exist." 32 | } 33 | #endregion EventLog -------------------------------------------------------------------------------- /Microsoft/Intune/PR001 - Windows LAPS Account/Create-LocalAdminLAPSRemediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Version: 1.0 3 | Author: 4 | - Joey Verlinden (joeyverlinden.com) 5 | - Andrew Taylor (andrewstaylor.com) 6 | - Florian Slazmann (scloud.work) 7 | - Jannik Reinhard (jannikreinhard.com) 8 | - Simon Skotheimsvik (skotheimsvik.no) 9 | Script: Create-LocalAdmin 10 | Description: Add a local admin with randomized password, ensuring that we do not have an account with a static password across all devices before Windows LAPS takes effect. 11 | Hint: This is a community script. There is no guarantee for this. Please check thoroughly before running. 12 | Version 1.1: Init 13 | Run as: Admin 14 | Context: 64 Bit 15 | #> 16 | 17 | $localAdminName = "CloudLimitsLocalAdmin" 18 | $password = -join ((65..90) + (97..122) + (48..57) + (35..38) + (40..47) | Get-Random -Count 35 | ForEach-Object {[char]$_}) | ConvertTo-SecureString -AsPlainText -Force 19 | 20 | New-LocalUser "$localAdminName" -Password $password -FullName "$localAdminName" -Description "LAPS account" 21 | Add-LocalGroupMember -Group "Administrators" -Member "$localAdminName" -------------------------------------------------------------------------------- /Microsoft/Intune/WPS007 - Set Regional Settings Norway/WPS007 - SetRegionalSettingsNorway.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 06.06.2024 5 | Created by: Simon Skotheimsvik 6 | Filename: WPS007 - SetRegionalSettingsNorway.ps1 7 | Info: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script sets regional settings to Norwegian for Windows10 and Windows 11 12 | The script is assigned as platform script to users in MicrosoftIntune, running as the logged on credentials. 13 | #> 14 | 15 | 16 | # Set system locale to Norwegian (Bokmål, Norway) 17 | Set-WinSystemLocale nb-NO 18 | 19 | # Set user locale to Norwegian (Bokmål, Norway) 20 | Set-WinUserLanguageList nb-NO -Force 21 | 22 | # Set the input method to Norwegian 23 | Set-WinUILanguageOverride nb-NO 24 | 25 | # Set the location to Norway 26 | Set-WinHomeLocation -GeoId 177 27 | 28 | # Set formats to Norwegian (Bokmål, Norway) 29 | Set-Culture -CultureInfo "nb-NO" -------------------------------------------------------------------------------- /Microsoft/Intune/PR008 - Start URL On Logon/Alternative 2 (Registry)/StartURLonLogon-Detection.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | Created on: 26.06.2024 4 | Author: Simon Skotheimsvik 5 | Filename: StartURLonLogon-Detection.ps1 6 | Info: https://skotheimsvik.no 7 | Versions: 8 | 1.0.0, 26.06.2024, Initial version 9 | 1.0.1, 22.09.2025, Updated output 10 | 11 | .DESCRIPTION 12 | This remediation package adds URL to start on logon. 13 | #> 14 | 15 | $RegContent = @" 16 | RegKeyPath,Key,Value,Type 17 | "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run","SimonDoes","explorer https://skotheimsvik.no","String" 18 | "@ 19 | 20 | $RegData = $RegContent | ConvertFrom-Csv -delimiter "," 21 | 22 | foreach ($Reg in $RegData) { 23 | $ExistingValue = (Get-Item -Path $($Reg.RegKeyPath)).GetValue($($Reg.Key)) 24 | if ($ExistingValue -ne $($Reg.Value)) { 25 | Write-Output "$($Reg.Key) Not Equal. $($ExistingValue) should be $($Reg.Value)" 26 | Exit 1 27 | } 28 | else { 29 | # Write-Host $($Reg.Key) "Equal" 30 | } 31 | } 32 | Write-Output "Registry values are correct." 33 | Exit 0 -------------------------------------------------------------------------------- /Microsoft/Intune/RemoveTeamsConsumer-Detect.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 07.02.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: RemoveTeamsConsumer-Detect.ps1 7 | Instructions: https://www.inthecloud247.com/remove-the-built-in-teams-client-and-chat-icon-from-windows-11/ 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will detect if Teams Consumer is present 12 | Based on https://www.inthecloud247.com/remove-the-built-in-teams-client-and-chat-icon-from-windows-11/ 13 | 14 | Detection script: Yes 15 | Remediation script: Yes 16 | Run this script using the logged-on credentials: Yes 17 | Enforce script signature check: No 18 | Run script in 64-bit PowerShell: Yes 19 | 20 | #> 21 | 22 | if ($null -eq (Get-AppxPackage -Name MicrosoftTeams)) { 23 | Write-Host "Microsoft Teams client not found" 24 | exit 0 25 | } 26 | Else { 27 | Write-Host "Microsoft Teams client found" 28 | Exit 1 29 | 30 | } -------------------------------------------------------------------------------- /Microsoft/Intune/PR007 - Block AAD Registration to 3rd party tenants/BlockAADReg-Detection.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 14.02.2024 5 | Created by: Simon Skotheimsvik 6 | Filename: BlockAADReg-Detection.ps1 7 | Info: https://msendpointmgr.com/2021/03/11/are-you-tired-of-allow-my-organization-to-manage-my-device/ 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script block users from adding additional work accounts (Entra ID registered) on corporate Windows devices 12 | 13 | #> 14 | 15 | $RegContent = @" 16 | RegKeyPath,Key,Value 17 | "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WorkplaceJoin","BlockAADWorkplaceJoin","1" 18 | "@ 19 | 20 | $RegData = $RegContent | ConvertFrom-Csv -delimiter "," 21 | 22 | foreach ($Reg in $RegData) { 23 | $ExistingValue = (Get-Item -Path $($Reg.RegKeyPath)).GetValue($($Reg.Key)) 24 | if ($ExistingValue -ne $($Reg.Value)) { 25 | Write-Host $($Reg.Key) "Not Equal" 26 | Exit 1 27 | Exit #Remediation 28 | } 29 | else { 30 | # Write-Host $($Reg.Key) "Equal" 31 | } 32 | } 33 | Exit 0 -------------------------------------------------------------------------------- /Microsoft/Intune/PR012 - Clean WSUS Settings/Clean WSUS Settings - Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Remediation to clean WSUS settings 4 | 5 | .DESCRIPTION 6 | This script removes the WSUS registry settings if they exist. 7 | 8 | .NOTES 9 | Author: Simon Skotheimsvik 10 | Version: 1.0.0 - 2025-08-12, Simon Skotheimsvik, Initial version 11 | 12 | license: This script is provided as-is, without warranty of any kind. Use at your own risk. 13 | You may modify and redistribute this script as long as you retain this notice in the code. 14 | #> 15 | 16 | $datetime = Get-Date -Format "yyyy-MM-dd HH:mm" 17 | 18 | # Stop the Windows Update service 19 | Stop-Service -Name wuauserv -Force 20 | 21 | # Remove the registry key 22 | Remove-Item -Path 'HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate' -Recurse 23 | 24 | # Start the Windows Update service 25 | Start-Service -Name wuauserv 26 | 27 | if(test-path -Path 'HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate'){ 28 | #registry item still exists 29 | Write-Error "Reg item still exists. Remediation failed at $($datetime)" 30 | Exit 1 31 | } else { 32 | #registry item doesn't exists 33 | Write-Output "Remediation succeeded at $($datetime)" 34 | Exit 0 35 | } -------------------------------------------------------------------------------- /Microsoft/Intune/RemoveTeamsConsumer-Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 07.02.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: RemoveTeamsConsumer-Remediation.ps1 7 | Instructions: https://www.inthecloud247.com/remove-the-built-in-teams-client-and-chat-icon-from-windows-11/ 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will remediate if Teams Consumer is present 12 | Based on https://www.inthecloud247.com/remove-the-built-in-teams-client-and-chat-icon-from-windows-11/ 13 | 14 | Detection script: Yes 15 | Remediation script: Yes 16 | Run this script using the logged-on credentials: Yes 17 | Enforce script signature check: No 18 | Run script in 64-bit PowerShell: Yes 19 | 20 | #> 21 | 22 | try { 23 | Get-AppxPackage -Name MicrosoftTeams | Remove-AppxPackage -ErrorAction stop 24 | Write-Host "Microsoft Teams app successfully removed" 25 | 26 | } 27 | catch { 28 | Write-Error "Errorremoving Microsoft Teams app" 29 | } -------------------------------------------------------------------------------- /Microsoft/Intune/PR005 - Java in Adobe Acrobat/DisableJavaScriptOnAdobeAcrobat-Detection.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 07.11.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: DisableJavaScriptOnAdobeAcrobat-Detection.ps1 7 | Info: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | The script can be assigned as Detection script in Microsoft Intune 12 | #> 13 | 14 | $RegSettingContent = @" 15 | RegKeyPath,Key,Value,Type 16 | "HKLM:\SOFTWARE\Policies\Adobe\Acrobat Reader\DC\FeatureLockDown","bDisableJavaScript","1","DWORD" 17 | "HKLM:\SOFTWARE\Policies\Adobe\Adobe Acrobat\DC\FeatureLockDown","bDisableJavaScript","1","DWORD" 18 | "@ 19 | 20 | $RegSetting = $RegSettingContent | ConvertFrom-Csv -delimiter "," 21 | 22 | foreach ($Reg in $RegSetting) { 23 | $ExistingValue = (Get-Item -Path $($Reg.RegKeyPath)).GetValue($($Reg.Key)) 24 | if ($ExistingValue -ne $($Reg.Value)) { 25 | Write-Host $($Reg.Key) "Not Equal" 26 | Exit 1 27 | Exit #Remediation 28 | } 29 | else { 30 | # Write-Host $($Reg.Key) "Equal" 31 | } 32 | } 33 | Exit 0 -------------------------------------------------------------------------------- /Microsoft/Intune/Win11-AddPrintersFromPrintserver.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 23.06.2022 5 | Created by: Simon Skotheimsvik 6 | Filename: Win11-AddPrintersFromPrintserver.ps1 7 | Instructions: https://skotheimsvik.blogspot.com/2022/06/install-printers-from-ad-printserver-on.html 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will connect printers from a printserver to an Azure AD joined 12 | Windows11 device with user signed in with a hybrid identity. 13 | 14 | .EXAMPLE 15 | Win11-AddPrintersFromPrintserver.ps1 16 | #> 17 | 18 | # $Printers = (Get-Printer -ComputerName PrintServer).Name 19 | # $Printers = (Get-Printer -ComputerName PrintServer | Where-Object {$_.Name -like "Simon*"}).Name 20 | $printers = @( 21 | '\\printserver\printer1' 22 | '\\printserver\printer2' 23 | '\\printserver\printer3' 24 | '\\printserver\printer4' 25 | ) 26 | 27 | ForEach ($printer in $printers) { 28 | $IsInstalled = [bool](Get-Printer | Where-Object { $_.Name -eq $printer }) 29 | if (-not $IsInstalled) { 30 | Add-Printer -ConnectionName $printer -ErrorAction Stop 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Microsoft/AAD/AAD-AssignPermissionsToManagedIdentity.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Created on: 19.02.2024 4 | Created by: CloudWay, Simon Skotheimsvik 5 | Info: Entra Role Assignments 6 | 7 | .DESCRIPTION 8 | Routine to add necessary roles to System Assigned Managed Identity in Function App or Automation Account. 9 | #> 10 | 11 | # Install-Module Microsoft.Graph -Scope CurrentUser 12 | Import-Module Microsoft.Graph.Applications 13 | 14 | # Variables 15 | $TenantID = "YOUR VALUE HERE" 16 | $managedIdentityId = "YOUR VALUE HERE" 17 | $roleNames = "User.Read.All","Calendars.ReadWrite" 18 | 19 | # Connect to Graph 20 | Connect-MgGraph -Scopes Application.Read.All, AppRoleAssignment.ReadWrite.All, RoleManagement.ReadWrite.Directory -TenantId $TenantID 21 | 22 | # Get the Microsoft Graph Service Principal 23 | $msgraph = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'" 24 | 25 | # Set the app role assignments 26 | foreach ($roleName in $roleNames) { 27 | write-host $roleName 28 | $role = $Msgraph.AppRoles| Where-Object {$_.Value -eq $roleName} 29 | New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $managedIdentityId -PrincipalId $managedIdentityId -ResourceId $msgraph.Id -AppRoleId $role.Id 30 | } 31 | 32 | Disconnect-MgGraph -------------------------------------------------------------------------------- /Microsoft/AAD/AAD-ListAllSKUs.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Created on: 13.10.2023 4 | Last update: 13.10.2023 5 | Created by: CloudWay, Simon Skotheimsvik 6 | Info: 7 | 8 | .DESCRIPTION 9 | Script to list numbers related to license usages in tenant 10 | #> 11 | 12 | # Install required module 13 | Install-Module -Name Microsoft.Graph 14 | 15 | # Import the module 16 | Import-Module Microsoft.Graph.Identity.DirectoryManagement 17 | 18 | # Authenticate and set Graph endpoint 19 | Connect-MgGraph -Scopes Directory.Read.All 20 | 21 | # Retrieve the friendly SKUnames from the provided URL 22 | $url = "http://bit.ly/SKUFriendlyNames" 23 | $friendlyNames = Invoke-RestMethod -Uri $url -UseBasicParsing | Convertfrom-Csv -Delimiter ';' 24 | 25 | # Get all SKUs 26 | $skus = Get-MgSubscribedSku 27 | 28 | # Display SKUs with product names and available units 29 | $skus | ForEach-Object { 30 | $sku = $_ 31 | $productName = ($friendlyNames | Where-Object { $_.LicenseSKUID -eq $sku.SkuId }).ProductName 32 | 33 | [PSCustomObject]@{ 34 | ProductName = $productName 35 | SkuPartNumber = $sku.SkuPartNumber 36 | SkuId = $sku.SkuId 37 | ConsumedUnits = $sku.ConsumedUnits 38 | AvailableUnits = $sku.PrepaidUnits.Enabled 39 | } 40 | } | Format-Table -------------------------------------------------------------------------------- /Microsoft/Intune/Get-IntuneDevicesEnrolledBy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This scripts list all devices enrolled in Intune by a given user. 4 | 5 | .DESCRIPTION 6 | There is a discussion going on using DEM account with Autopilot. This script will list all devices enrolled in Intune by a given user. 7 | Read this post for a summery: https://call4cloud.nl/using-a-dem-account-windows-autopilot-is-a-bad-idea/ 8 | 9 | .NOTES 10 | Author: Simon Skotheimsvik 11 | Version: 12 | 1.0.0 - 2025-02-25 - Initial release, Simon Skotheimsvik 13 | #> 14 | 15 | # Name to search for, e.g. DEM-Account or deviceuser 16 | $Name = "kim" 17 | 18 | # Connect to Microsoft Graph (ensure you have the necessary permissions) 19 | Connect-MgGraph -Scopes "DeviceManagementManagedDevices.Read.All" 20 | 21 | # Query for devices with management name containing the specified name 22 | $devices = Get-MgDeviceManagementManagedDevice -All | Where-Object { $_.ManagedDeviceName -like "*$($Name)*" } 23 | 24 | # Display the results 25 | if ($devices) { 26 | $devices | Select-Object DeviceName, ManagedDeviceName, UserPrincipalName, OperatingSystem, OSVersion, LastSyncDateTime | Out-GridView 27 | Write-Host "Total devices found: $($devices.Count)" 28 | } else { 29 | Write-Host "No devices found with management name containing '($Name)'" 30 | } 31 | 32 | # Disconnect from Microsoft Graph 33 | Disconnect-MgGraph -------------------------------------------------------------------------------- /Microsoft/Intune/Get-WindowsAutoPilotInfo/GetAutoPilot.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | echo Enabling WinRM 3 | PowerShell -NoProfile -ExecutionPolicy Unrestricted -Command Enable-PSRemoting -SkipNetworkProfileCheck -Force 4 | 5 | :Start 6 | 7 | echo. 8 | echo Which country will this computer primarily be located? 9 | echo. 10 | echo 1 - Sweden 11 | echo 2 - Norway 12 | echo 3 - Denmark 13 | echo 4 - Finland 14 | echo 5 - Deutschland 15 | echo 00 - EXIT 16 | echo 99 - Delete CSV 17 | echo. 18 | 19 | @set /p userinp=Type the number of your choice: 20 | @set userinp=%userinp:~0,2% 21 | @if "%userinp%"=="1" goto 1 22 | @if "%userinp%"=="2" goto 2 23 | @if "%userinp%"=="3" goto 3 24 | @if "%userinp%"=="4" goto 4 25 | @if "%userinp%"=="5" goto 5 26 | @if "%userinp%"=="00" goto 00 27 | @if "%userinp%"=="99" goto 99 28 | 29 | 30 | :1 31 | set grouptag=Device-SE 32 | goto end 33 | 34 | :2 35 | set grouptag=Device-NO 36 | goto end 37 | 38 | :3 39 | set grouptag=Device-DK 40 | goto end 41 | 42 | :4 43 | set grouptag=Device-FI 44 | goto end 45 | 46 | :5 47 | set grouptag=Device-DE 48 | goto end 49 | 50 | :99 51 | del compHash.csv 52 | goto Start 53 | 54 | :end 55 | echo Gathering Azure AD Joined AutoPilot Hash 56 | PowerShell -NoProfile -ExecutionPolicy Unrestricted -Command .\Get-WindowsAutoPilotInfo.ps1 -OutputFile .\compHash.csv -append -GroupTag %grouptag% 57 | echo Done! 58 | 59 | :shutdown 60 | rem shutdown /s /t 5 61 | 62 | :00 63 | pause -------------------------------------------------------------------------------- /Microsoft/KQL/SigninLogsPrCAPolicy.kql: -------------------------------------------------------------------------------- 1 | SigninLogs 2 | | where TimeGenerated >= ago(90d) // Filter logs for the last 90 days 3 | | mv-expand ConditionalAccessPolicy = ConditionalAccessPolicies // Expand the ConditionalAccessPolicies array 4 | | where ConditionalAccessStatus <> "success" 5 | | where ConditionalAccessPolicy.displayName startswith "CA004" 6 | | where ConditionalAccessPolicy.result <> "notApplied" 7 | | project TimeGenerated, UserPrincipalName, UserDisplayName, DeviceDetail, AppDisplayName, ResourceDisplayName, ConditionalAccessPolicy.displayName, Status.failureReason, IPAddress, LocationDetails 8 | //| summarize Users = count() by UserDisplayName 9 | //| summarize AppDisplayName = count() by AppDisplayName 10 | //| summarize ResourceDisplayName = count() by ResourceDisplayName 11 | //| summarize IP = count() by IPAddress 12 | //| summarize Compliant = count() by tostring(DeviceDetail.isCompliant) 13 | //| render piechart 14 | 15 | 16 | 17 | SigninLogs 18 | | where TimeGenerated >= ago(360d) // Filter logs for the last 360 days 19 | | mv-expand ConditionalAccessPolicy = ConditionalAccessPolicies // Expand the ConditionalAccessPolicies array 20 | | where ConditionalAccessStatus <> "success" 21 | | where ConditionalAccessPolicy.displayName startswith "CA004" 22 | | where ConditionalAccessPolicy.result <> "notApplied" 23 | | summarize IncidentCount = count() by bin(TimeGenerated, 1d), ConditionalAccessStatus 24 | | render timechart 25 | -------------------------------------------------------------------------------- /Microsoft/Intune/Win11-SetDesktopTheme.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 24.06.2022 5 | Created by: Simon Skotheimsvik 6 | Filename: Win11-SetDesktopTheme.ps1 7 | Info: https://skotheimsvik.blogspot.com/2022/06/windows-11-custom-theme-with-mem.html 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script downloads the themepack from Azure Blob Storage and activates the themepack for Windows11. 12 | The script can be assigned to devices in Microsoft Endpoint Manager. 13 | 14 | .EXAMPLE 15 | Win11-SetDesktopTheme.ps1 16 | #> 17 | 18 | # Parameters for source and destination for the themepack file 19 | $ThemepackSource = "https://XXXXXXXX.blob.core.windows.net/YYYYYYYYYYYYY/Win11-theme.deskthemepack" 20 | $ThemepackDestinationFolder = "C:\temp\" 21 | $WallpaperDestinationFile = "$ThemepackDestinationFolder\win11-corporate-theme.deskthemepack" 22 | 23 | # Creates the destination folder on the target computer 24 | md $ThemepackDestinationFolder -erroraction silentlycontinue 25 | 26 | # Downloads the image file from the source location 27 | Start-BitsTransfer -Source $ThemepackSource -Destination "$WallpaperDestinationFile" 28 | 29 | # Assign the themepack 30 | start-process -FilePath $WallpaperDestinationFile; timeout /t 3; taskkill /im "systemsettings.exe" /f -------------------------------------------------------------------------------- /Microsoft/Intune/Group Tag Fixer/Add-ApplicationPermissionsToSystemAssignedManagedIdentity.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Created on: 01.06.2023 4 | Modified on: 19.01.2024 5 | Created by: CloudWay, Simon Skotheimsvik 6 | Info: https://skotheimsvik.no/intune-chronicles-the-automation-secrets-of-seamless-group-tags 7 | 8 | .DESCRIPTION 9 | Group Tag Fixer. Routine to add application persmissions to system assigned managed identity. 10 | #> 11 | 12 | #region Variables 13 | $TenantID = "11111111-1111-1111-1111-111111111111" 14 | $ServicePrincipalId = "22222222-2222-2222-2222-222222222222" 15 | #endregion Variables 16 | 17 | #region Connect to Microsoft Graph 18 | Connect-MgGraph -TenantId $TenantID -Scopes "Application.Read.All","AppRoleAssignment.ReadWrite.All","RoleManagement.ReadWrite.Directory" 19 | #endregion connect 20 | 21 | #region Set Service Principal Permissions 22 | $graphStdApp = Get-MgBetaServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'" 23 | $app_roles = "DeviceManagementServiceConfig.ReadWrite.All", "Device.Read.All" 24 | $Permissions = $graphStdApp.AppRoles | Where-Object {$_.value -in $app_roles} 25 | foreach ($Permission in $Permissions) { 26 | New-MgBetaServicePrincipalAppRoleAssignment -ServicePrincipalId $ServicePrincipalId -PrincipalId $ServicePrincipalId -AppRoleId $Permission.Id -ResourceId $graphStdApp.Id 27 | } 28 | 29 | #endregion set 30 | 31 | # Disconnect-MgGraph -------------------------------------------------------------------------------- /Microsoft/Intune/PR004 - Download Wallpaper/Wallpaper-Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 12.07.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: Wallpaper-Remediation.ps1 7 | Info: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script detects if wallpaper from Azure Storage exists locally 12 | The script can be assigned as Remediation script in Microsoft Intune 13 | The downloaded wallpaper can be configured in Intune Settings catalog 14 | Wallpaper Style: (User) Stretch 15 | Wallpaper Name: (User) C:\Windows\web\wallpaper\wallpaper.jpg 16 | Desktop Wallpaper (Use) Enabled 17 | #> 18 | 19 | $imageURL = "https://cloudlimits.blob.core.windows.net/intuneresources-public/cloudlimits.jpg" 20 | $imagePATH = "C:\Windows\web\wallpaper\wallpaper.jpg" 21 | 22 | if (!(Test-Path -Path $imagePATH -PathType Leaf)) { 23 | Write-Host "Wallpaper missing on computer" 24 | try { 25 | Invoke-WebRequest -Uri $imageURL -OutFile $imagePATH -TimeoutSec 10 -UseBasicParsing:$true -ErrorAction SilentlyContinue 26 | Exit 0 27 | } 28 | catch { 29 | Write-Host "An error occurred:" 30 | Write-Host $_ 31 | Exit 1 32 | } 33 | } 34 | else 35 | { 36 | Write-Host "Wallpaper exists on computer" 37 | Exit 0 38 | } -------------------------------------------------------------------------------- /Microsoft/AAD/GET-LAPSDevices.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script to find devices with LAPS passwords. 4 | .DESCRIPTION 5 | This script will find all devices with a LAPS password set. Use this to find the prevalence of LAPS in your environment. 6 | 7 | .NOTES 8 | Version: 1.1 9 | Author: Simon Skotheimsvik 10 | Info: https://skotheimsvik.no/migrating-cloud-laps-to-the-new-windows-laps 11 | Creation Date: 09.06.2023 12 | Version history: 13 | 1.0 - (09.06.2023) Script released 14 | 1.1 - (24.10.2023) Least privilege (Thanks to Sandy Zeng) 15 | 16 | #> 17 | 18 | #region connect 19 | Connect-MgGraph -Scopes "Device.Read.All", "DeviceLocalCredential.ReadBasic.All" 20 | #region connect 21 | 22 | #region variables 23 | $devices = get-mgdevice -Filter "OperatingSystem eq 'Windows'" -All 24 | $NumberOfDevices = $devices.count 25 | $NumberOfDevicesWithLaps = 0 26 | $Counter = 1 27 | #endregion variables 28 | 29 | #region find devices with LAPS passwords 30 | foreach ($device in $devices) { 31 | if (Get-LapsAADPassword -DeviceIds $device.DisplayName -erroraction 'silentlycontinue') {$NumberOfDevicesWithLaps++} 32 | Write-Progress -Activity "Searching device $Counter : $NumberOfDevices for LAPS" -PercentComplete (($Counter / $NumberOfDevices) * 100) 33 | $Counter++ 34 | } 35 | #endregion 36 | 37 | #region write result 38 | Write-Host "$NumberOfDevicesWithLaps of $NumberOfDevices Windows devices have Windows LAPS password." 39 | #end region 40 | -------------------------------------------------------------------------------- /Microsoft/AAD/AAD-FindObjectsUsingDomain.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 13.01.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: FindObjectsUsingDomain.ps1 7 | Instructions: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will find users and groups using a custom domain in AAD 12 | #> 13 | 14 | # Authenticate 15 | $conn = Connect-MgGraph -Scopes "User.Read.All, Group.Read.All" 16 | Select-MgProfile -name beta 17 | 18 | # Define domain name for filtering 19 | $domain = "skotheimsvik.no" 20 | 21 | # Get users filtered on UPN, MAIL, IMADDRESSES and PROXYADDRESSES 22 | $Users = Get-MgUser -All | Where-Object {$_.UserPrincipalName -like "*@$domain" -or $_.Mail -like "*@$domain" -or $_.ImAddresses -like "*@$domain" -or $_.ProxyAddresses -like "*@$domain"} | Select-Object Id, DisplayName, UserPrincipalName, Mail, @{Name=’ImAddresses’;Expression={[string]::join(“;”, ($_.ImAddresses))}}, @{Name=’proxyAddresses’;Expression={[string]::join(“;”, ($_.proxyAddresses))}} 23 | $Users | measure 24 | $Users | Out-GridView 25 | $Users | Export-Csv -Encoding utf8 "C:\Temp\$domain-Users.csv" 26 | 27 | # Get groups filtered on MAIL 28 | $Groups = Get-MgGroup -All | Where-Object {$_.mail -like "*$domain"} | Select-Object Id, DisplayName, Description, mail 29 | $Groups | measure 30 | $Groups | Out-GridView 31 | $Groups | Export-Csv -Encoding utf8 "C:\Temp\$domain-Groups.csv" -------------------------------------------------------------------------------- /Microsoft/Intune/HP Connect Authentication.ps1: -------------------------------------------------------------------------------- 1 | #C:\Program Files\OpenSSL-Win64\bin\ 2 | 3 | # Endorsement Key Certificate 4 | .\openssl.exe req -x509 -nodes -newkey rsa:2048 -keyout C:\temp\SkotheimsvikHPAuthpriv.pem -out c:\temp\SkotheimsvikHPAuth.crt -days 3650 -subj ‘/C=NO/ST=mro/L=Bud/O=Skotheimsvik/OU=Skotheimsvik/CN=hpbiosauth.skotheimsvik.no’ 5 | .\openssl.exe pkcs12 -inkey C:\temp\SkotheimsvikHPAuthpriv.pem -in C:\temp\SkotheimsvikHPAuth.crt -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -out C:\temp\SkotheimsvikHPAuthpriv.pfx -name ‘Skotheimsvik SPM Endorsement Key Certificate’ 6 | #Sekret666 7 | 8 | # Signing Key Certificate 9 | .\openssl.exe req -x509 -nodes -newkey rsa:2048 -keyout c:\temp\SkotheimsvikSKpriv.pem -out c:\temp\SkotheimsvikSK.crt -days 3650 -subj ‘/C=NO/ST=mro/L=Bud/O=Skotheimsvik/OU=Skotheimsvik/CN= hpbiosauth.skotheimsvik.no’ 10 | .\openssl.exe pkcs12 -inkey c:\temp\SkotheimsvikSKpriv.pem -in c:\temp\SkotheimsvikSK.crt -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -out C:\temp\SkotheimsvikSK.pfx -name ‘Skotheimsvik SPM Signing Key Certificate’ 11 | #Sekret666 12 | 13 | # Local Access Key Certificate 14 | .\openssl.exe req -x509 -nodes -newkey rsa:2048 -keyout c:\temp\SkotheimsvikLAK.pem -out c:\temp\SkotheimsvikLAK.crt -days 3650 -subj ‘/C=NO/ST=mro/L=Bud/O=Skotheimsvik/OU=Skotheimsvik/CN= hpbiosauth.Skotheimsvik.no’ 15 | .\openssl.exe pkcs12 -inkey c:\temp\SkotheimsvikLAK.pem -in c:\temp\SkotheimsvikLAK.crt -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -out C:\temp\SkotheimsvikLAK.pfx -name ‘Skotheimsvik SPM Local Access Key Certificate’ 16 | -------------------------------------------------------------------------------- /Microsoft/Intune/PR008 - Start URL On Logon/Alternative 1 (URL file)/StartURLOnLogon-Detection.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | Created on: 26.06.2024 4 | Created by: Simon Skotheimsvik 5 | Filename: StartURLOnLogon-Detection.ps1 6 | Info: https://skotheimsvik.no 7 | Version: 1.0 8 | 9 | .DESCRIPTION 10 | This script will check if shortcut for URL is present in the startup folder. 11 | If file not exist or not equal, it will be downloaded from a webserver. 12 | #> 13 | 14 | # Define URLs and paths 15 | $remoteFileUrl = "https://YOURPATH.blob.core.windows.net/intuneresources-public/SimonDoes.url" 16 | $localFilePath = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\SimonDoes.url" 17 | $tempFilePath = "$($env:temp)\SimonDoes.url" 18 | 19 | # Download the remote file to a temporary location 20 | Invoke-WebRequest -Uri $remoteFileUrl -OutFile $tempFilePath 21 | 22 | # Function to calculate file hash 23 | function Get-FileHash { 24 | param ([string]$filePath) 25 | return (Get-FileHash -Algorithm SHA256 -Path $filePath).Hash 26 | } 27 | 28 | # Check if the local file exists 29 | if (Test-Path $localFilePath) { 30 | # Calculate hashes for both files 31 | $localFileHash = Get-FileHash -Path $localFilePath 32 | $remoteFileHash = Get-FileHash -Path $tempFilePath 33 | 34 | # Compare the hashes 35 | if ($localFileHash -eq $remoteFileHash) { 36 | # Files are the same 37 | exit 0 38 | } 39 | else { 40 | # Files are different 41 | exit 1 42 | } 43 | } 44 | else { 45 | # Local file does not exist 46 | exit 1 47 | } 48 | -------------------------------------------------------------------------------- /Microsoft/AAD/AAD-Users-ExportAttributesToCSV.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 28.09.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: AAD-Users-ExportAttributesToCSV.ps1 7 | Instructions: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will export information about user accounts in AAD to CSV 12 | #> 13 | 14 | 15 | # Install and import the Microsoft Graph module 16 | #Install-Module -Name Microsoft.Graph 17 | Import-Module Microsoft.Graph.Users 18 | 19 | # Authenticate interactively (remember to aka.ms/pim first) 20 | Connect-MgGraph -Scopes "User.Read.All" 21 | 22 | # Define the output CSV file path 23 | $csvFilePath = "c:\temp\EntraID-Users-Attributes.csv" 24 | 25 | # Retrieve all users from Azure AD using Microsoft Graph 26 | $users = Get-MgUser -All -Property Id, UserPrincipalName, GivenName, Surname, JobTitle, Department, CompanyName, MobilePhone, OfficeLocation, PostalCode, City, Country, UsageLocation -Expand Manager | Select-Object Id, UserPrincipalName, GivenName, Surname, JobTitle, Department, CompanyName, MobilePhone, OfficeLocation, PostalCode, City, Country, UsageLocation, @{Name='Manager'; Expression={$_.Manager.AdditionalProperties.userPrincipalName}} 27 | 28 | # Export the user details to a CSV file 29 | $users | Export-Csv -Path $csvFilePath -NoTypeInformation 30 | 31 | # Display a message indicating the export is complete 32 | Write-Host "User details exported to $csvFilePath." 33 | -------------------------------------------------------------------------------- /Microsoft/SharePoint/OneDriveForBusiness PreProvisioning.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 13.08.2024 5 | Created by: Simon Skotheimsvik 6 | Filename: OneDriveForBusiness PreProvisioning.ps1 7 | Info: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | Doing migrations using Migration Wizard to new user accounts, the OneDrive for Business sites must be pre-provisioned. 12 | Requirement for Microsoft.Online.SharePoint.Powershell is Windows PowerShell up to version 5.0 13 | Can be problematic in VSCode. Use Windows PowerShell ISE 14 | #> 15 | 16 | 17 | # Connect to the SharePoint Online service using the admin URL 18 | Connect-SPOService -Url https://yourcompany-admin.sharepoint.com 19 | 20 | # Define an array of user emails for whom OneDrive for Business sites will be provisioned 21 | $users = @( 22 | "user.one@company.com", 23 | "user.two@company.com", 24 | "user.three@company.com", 25 | "user.four@company.com" 26 | ) 27 | 28 | # Loop through each user in the $users array 29 | foreach ($user in $users) { 30 | # Request to pre-provision a OneDrive for Business site for the current user 31 | # The -NoWait parameter allows the command to return immediately without waiting for the operation to complete 32 | Request-SPOPersonalSite -UserEmails $user -NoWait 33 | 34 | # Optional: Provide feedback in the console that the request was made 35 | Write-Host "OneDrive provisioning requested for $user" -ForegroundColor Green 36 | } -------------------------------------------------------------------------------- /Microsoft/AAD/AAD-ListApplicationsWithPermissions.ps1: -------------------------------------------------------------------------------- 1 | # Example PowerShell script to list applications with scope permissions. 2 | 3 | # First, connect to Microsoft Graph with the necessary permissions 4 | Connect-MgGraph -Scopes "Application.Read.All" 5 | 6 | # Define an array of grant scopes 7 | # $GrantScopes = @("mail.read", "calendars.read", "user.read", "user_impersonation") 8 | $GrantScopes = @("ReadWrite", "FullControl", "AccessAsUser", "Write") 9 | 10 | # Get all Enterprise Applications 11 | $EnterpriseApps = Get-MgServicePrincipal 12 | 13 | # Iterate through each Enterprise Application 14 | foreach ($app in $EnterpriseApps) { 15 | # Get the OAuth2PermissionGrants for the current Enterprise Application 16 | $OAuth2PermissionGrants = Get-MgServicePrincipalOAuth2PermissionGrant -ServicePrincipalId $app.Id 17 | 18 | # Initialize a hashtable to track grant scopes for this app 19 | $AppScopes = @{} 20 | 21 | # Check each OAuth2PermissionGrant against the defined scopes 22 | foreach ($grant in $OAuth2PermissionGrants) { 23 | foreach ($scope in $GrantScopes) { 24 | if ($grant.Scope -like "*$scope*") { 25 | $AppScopes[$scope] = $true 26 | } 27 | } 28 | } 29 | 30 | # Output information about the application and its grant scopes 31 | Write-Host "Application Name: $($app.DisplayName)" 32 | foreach ($scope in $GrantScopes) { 33 | if ($AppScopes[$scope]) { 34 | Write-Host " - $scope permission is granted." -ForegroundColor red 35 | } else { 36 | Write-Host " - $scope permission is not granted." -ForegroundColor green 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Microsoft/KQL/AADNonInteractiveUserSignInLogs.kql: -------------------------------------------------------------------------------- 1 | AADNonInteractiveUserSignInLogs 2 | | where TimeGenerated >= ago(30d) 3 | | where UserPrincipalName == "xx@yy.zz" 4 | | summarize Count = count() by bin(TimeGenerated, 1h), UserPrincipalName, AppDisplayName 5 | | order by TimeGenerated asc 6 | | render barchart 7 | 8 | AADNonInteractiveUserSignInLogs 9 | | where TimeGenerated >= ago(30d) 10 | | summarize SignInCount = count() by UserPrincipalName, AppDisplayName 11 | | order by SignInCount desc 12 | | take 50 13 | 14 | 15 | AADNonInteractiveUserSignInLogs 16 | | where TimeGenerated >= ago(30d) 17 | | where UserPrincipalName == "xx@yy.zz" 18 | | extend DeviceName = tostring (parse_json(DeviceDetail).displayName) 19 | | summarize DeviceCount = count() by DeviceName, AppDisplayName 20 | | order by DeviceCount desc 21 | 22 | AADNonInteractiveUserSignInLogs 23 | | where TimeGenerated >= ago(30d) 24 | | where UserPrincipalName == "xx@yy.zz" 25 | | where AppDisplayName == "Windows Sign In" 26 | 27 | 28 | AADNonInteractiveUserSignInLogs 29 | | where TimeGenerated >= ago(30d) 30 | | where UserPrincipalName == "xx@yy.zz" 31 | | extend DeviceName = tostring (parse_json(DeviceDetail).displayName) 32 | | summarize DeviceCount = count() by DeviceName, AppDisplayName 33 | | order by DeviceCount desc 34 | 35 | 36 | AADNonInteractiveUserSignInLogs 37 | | where TimeGenerated >= ago(30d) 38 | | extend DeviceName = tostring (parse_json(DeviceDetail).displayName) 39 | //| summarize Count = count() by AppDisplayName, DeviceName 40 | //| summarize Count = count() by UserPrincipalName, AppDisplayName 41 | | summarize Count = count() by AppDisplayName, UserPrincipalName 42 | | order by Count desc 43 | | take 20 44 | -------------------------------------------------------------------------------- /Microsoft/Intune/PR009 - Autopatch NoAutoUpdate/Autopatch-NoAutoUpdate-Detection.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | Created on: 07.10.2024 4 | Created by: Simon Skotheimsvik 5 | Filename: Autopatch-NoAutoUpdate-Detection.ps1 6 | Info: https://skotheimsvik.no 7 | Version: 1.0.1 8 | 9 | .DESCRIPTION 10 | This detection script checks if the registry key exists and holds a different value than expected. 11 | #> 12 | 13 | $RegContent = @" 14 | RegKeyPath,Key,Value,Type 15 | "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU","NoAutoUpdate","0","Dword" 16 | "@ 17 | 18 | $RegData = $RegContent | ConvertFrom-Csv -delimiter "," 19 | 20 | foreach ($Reg in $RegData) { 21 | # Suppress errors for non-existent registry paths using -ErrorAction SilentlyContinue 22 | $Item = Get-Item -Path $($Reg.RegKeyPath) -ErrorAction SilentlyContinue 23 | if ($Item) { 24 | try { 25 | # Try to get the value for the specified key 26 | $ExistingValue = $Item.GetValue($($Reg.Key), $null) # Returns $null if the key does not exist 27 | if ($ExistingValue -ne $null) { 28 | # Check if the existing value matches the expected value 29 | if ($ExistingValue -ne $($Reg.Value)) { 30 | Write-Host "$($Reg.Key) exist as $($ExistingValue), and is not equal to $($Reg.Value)." 31 | Exit 1 32 | } 33 | else { 34 | # Write-Host $($Reg.Key) "Equal" 35 | } 36 | } 37 | } 38 | catch { 39 | # Do nothing if GetValue encounters an error 40 | } 41 | } 42 | } 43 | Exit 0 -------------------------------------------------------------------------------- /Microsoft/Intune/PR008 - Start URL On Logon/Alternative 1 (URL file)/StartURLOnLogon-Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | Created on: 26.06.2024 4 | Created by: Simon Skotheimsvik 5 | Filename: StartURLOnLogon-Detection.ps1 6 | Info: https://skotheimsvik.no 7 | Version: 1.0 8 | 9 | .DESCRIPTION 10 | This script will check if shortcut for URL is present in the startup folder. 11 | If file not exist or not equal, it will be downloaded from a webserver. 12 | #> 13 | 14 | # Define URLs and paths 15 | $remoteFileUrl = "https://YOURPATH.blob.core.windows.net/intuneresources-public/SimonDoes.url" 16 | $localFilePath = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\SimonDoes.url" 17 | $tempFilePath = "$($env:temp)\SimonDoes.url" 18 | 19 | # Download the remote file to a temporary location 20 | Invoke-WebRequest -Uri $remoteFileUrl -OutFile $tempFilePath 21 | 22 | # Function to calculate file hash 23 | function Get-FileHash { 24 | param ([string]$filePath) 25 | return (Get-FileHash -Algorithm SHA256 -Path $filePath).Hash 26 | } 27 | 28 | # Check if the local file exists 29 | if (Test-Path $localFilePath) { 30 | # Calculate hashes for both files 31 | $localFileHash = Get-FileHash -Path $localFilePath 32 | $remoteFileHash = Get-FileHash -Path $tempFilePath 33 | 34 | # Compare the hashes 35 | if ($localFileHash -ne $remoteFileHash) { 36 | # Files are different, replace the local file 37 | Copy-Item -Path $tempFilePath -Destination $localFilePath -Force 38 | } 39 | } 40 | else { 41 | # Local file does not exist, download the file 42 | Copy-Item -Path $tempFilePath -Destination $localFilePath -Force 43 | } 44 | 45 | # Clean up the temporary file 46 | Remove-Item -Path $tempFilePath 47 | -------------------------------------------------------------------------------- /Microsoft/Intune/Win11-DriveMapping_Q.ps1: -------------------------------------------------------------------------------- 1 | # Get users that are logged in 2 | $Sessions = Get-WmiObject -Class win32_process | Where-Object {$_.name -eq "explorer.exe"} 3 | 4 | # Get SID of users 5 | $sid = $Sessions.GetOwnerSid().sid 6 | $user=$Sessions.GetOwner().User 7 | 8 | # Drivemapping variables 9 | $DriveLetter = "Q" 10 | $DrivePath = "\\server.skotheimsvik.no\common" 11 | $Path = "REGISTRY::HKEY_USERS\$sid\Network" 12 | 13 | # Delete existing drivemapping 14 | Remove-Item -Path "REGISTRY::HKEY_USERS\$sid\Network\$DriveLetter" -Force -ErrorAction SilentlyContinue | Out-Null 15 | 16 | # Add new drivemapping 17 | New-Item -Path $Path -Name $DriveLetter -ErrorAction SilentlyContinue | Out-Null 18 | New-ItemProperty -Path $Path\$DriveLetter\ -Name ConnectFlags -PropertyType DWORD -Value 0 -ErrorAction SilentlyContinue | Out-Null 19 | New-ItemProperty -Path $Path\$DriveLetter\ -Name ConnectionType -PropertyType DWORD -Value 1 -ErrorAction SilentlyContinue | Out-Null 20 | New-ItemProperty -Path $Path\$DriveLetter\ -Name DeferFlags -PropertyType DWORD -Value 4 -ErrorAction SilentlyContinue | Out-Null 21 | New-ItemProperty -Path $Path\$DriveLetter\ -Name ProviderFlags -PropertyType DWORD -Value 1 -ErrorAction SilentlyContinue | Out-Null 22 | New-ItemProperty -Path $Path\$DriveLetter\ -Name ProviderName -PropertyType String -Value "Microsoft Windows Network" -ErrorAction SilentlyContinue | Out-Null 23 | New-ItemProperty -Path $Path\$DriveLetter\ -Name ProviderType -PropertyType DWORD -Value 131072 -ErrorAction SilentlyContinue | Out-Null 24 | New-ItemProperty -Path $Path\$DriveLetter\ -Name RemotePath -PropertyType String -Value "$DrivePath" -ErrorAction SilentlyContinue | Out-Null 25 | New-ItemProperty -Path $Path\$DriveLetter\ -Name UserName -PropertyType String -ErrorAction SilentlyContinue | Out-Null -------------------------------------------------------------------------------- /Microsoft/Intune/Win11-DriveMapping_M.ps1: -------------------------------------------------------------------------------- 1 | # Get users that are logged in 2 | $Sessions = Get-WmiObject -Class win32_process | Where-Object {$_.name -eq "explorer.exe"} 3 | 4 | # Get SID of users 5 | $sid = $Sessions.GetOwnerSid().sid 6 | $user=$Sessions.GetOwner().User 7 | 8 | # Drivemapping variables 9 | $DriveLetter = "M" 10 | $DrivePath = "\\server.skotheimsvik.no\users\$user" 11 | $Path = "REGISTRY::HKEY_USERS\$sid\Network" 12 | 13 | # Delete existing drivemapping 14 | Remove-Item -Path "REGISTRY::HKEY_USERS\$sid\Network\$DriveLetter" -Force -ErrorAction SilentlyContinue | Out-Null 15 | 16 | # Add new drivemapping 17 | New-Item -Path $Path -Name $DriveLetter -ErrorAction SilentlyContinue | Out-Null 18 | New-ItemProperty -Path $Path\$DriveLetter\ -Name ConnectFlags -PropertyType DWORD -Value 0 -ErrorAction SilentlyContinue | Out-Null 19 | New-ItemProperty -Path $Path\$DriveLetter\ -Name ConnectionType -PropertyType DWORD -Value 1 -ErrorAction SilentlyContinue | Out-Null 20 | New-ItemProperty -Path $Path\$DriveLetter\ -Name DeferFlags -PropertyType DWORD -Value 4 -ErrorAction SilentlyContinue | Out-Null 21 | New-ItemProperty -Path $Path\$DriveLetter\ -Name ProviderFlags -PropertyType DWORD -Value 1 -ErrorAction SilentlyContinue | Out-Null 22 | New-ItemProperty -Path $Path\$DriveLetter\ -Name ProviderName -PropertyType String -Value "Microsoft Windows Network" -ErrorAction SilentlyContinue | Out-Null 23 | New-ItemProperty -Path $Path\$DriveLetter\ -Name ProviderType -PropertyType DWORD -Value 131072 -ErrorAction SilentlyContinue | Out-Null 24 | New-ItemProperty -Path $Path\$DriveLetter\ -Name RemotePath -PropertyType String -Value "$DrivePath" -ErrorAction SilentlyContinue | Out-Null 25 | New-ItemProperty -Path $Path\$DriveLetter\ -Name UserName -PropertyType String -ErrorAction SilentlyContinue | Out-Null -------------------------------------------------------------------------------- /Microsoft/Intune/PR003 - Branding/Branding-Detection.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 11.07.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: Branding-Detection.ps1 7 | Info: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script sets the support information for Windows10 and Windows 11 12 | The information is found in "Settings - System - About - Support" and in "WinVer" 13 | The script can be assigned as Detection script in Microsoft Intune 14 | 15 | #> 16 | 17 | $BrandingContent = @" 18 | RegKeyPath,Key,Value 19 | "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation","SupportURL","https://support.cloudlimits.com/" 20 | "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation","Manufacturer","Cloud Limits" 21 | "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation","SupportHours","Standard: 0700-1600, Extended: 24-7-365" 22 | "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation","SupportPhone","+47 12 34 56 78" 23 | "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion","RegisteredOwner","Cloud Limits" 24 | "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion","RegisteredOrganization","Cloud Limits" 25 | "@ 26 | 27 | $Branding = $BrandingContent | ConvertFrom-Csv -delimiter "," 28 | 29 | foreach ($Brand in $Branding) { 30 | $ExistingValue = (Get-Item -Path $($Brand.RegKeyPath)).GetValue($($Brand.Key)) 31 | if ($ExistingValue -ne $($Brand.Value)) { 32 | Write-Host $($Brand.Key) "Not Equal" 33 | Exit 1 34 | Exit #Remediation 35 | } 36 | else { 37 | # Write-Host $($Brand.Key) "Equal" 38 | } 39 | } 40 | Exit 0 -------------------------------------------------------------------------------- /Microsoft/Intune/PR005 - Java in Adobe Acrobat/DisableJavaScriptOnAdobeAcrobat-Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 07.11.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: DisableJavaScriptOnAdobeAcrobat-Remediation.ps1 7 | Info: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | The script can be assigned as Remediation script in Microsoft Intune 12 | #> 13 | 14 | $RegSettingContent = @" 15 | RegKeyPath,Key,Value,Type 16 | "HKLM:\SOFTWARE\Policies\Adobe\Acrobat Reader\DC\FeatureLockDown","bDisableJavaScript","1","DWORD" 17 | "HKLM:\SOFTWARE\Policies\Adobe\Adobe Acrobat\DC\FeatureLockDown","bDisableJavaScript","1","DWORD" 18 | "@ 19 | 20 | $RegSetting = $RegSettingContent | ConvertFrom-Csv -delimiter "," 21 | 22 | foreach ($Reg in $RegSetting) { 23 | 24 | IF (!(Test-Path ($Reg.RegKeyPath))) { 25 | Write-Host ($Reg.RegKeyPath) " does not exist. Will be created." 26 | New-Item -Path $RegKeyPath -Force | Out-Null 27 | } 28 | IF (!(Get-Item -Path $($Reg.Key))) { 29 | Write Host $($Reg.Key) " does not exist. Will be created." 30 | New-ItemProperty -Path $($Reg.RegKeyPath) -Name $($Reg.Key) -Value $($Reg.Value) -PropertyType $($Reg.Type) -Force 31 | } 32 | 33 | $ExistingValue = (Get-Item -Path $($Reg.RegKeyPath)).GetValue($($Reg.Key)) 34 | if ($ExistingValue -ne $($Reg.Value)) { 35 | Write-Host $($Reg.Key) " not correct value. Will be set." 36 | Set-ItemProperty -Path $($Reg.RegKeyPath) -Name $($Reg.Key) -Value $($Reg.Value) -Force 37 | } 38 | else { 39 | Write-Host $($Reg.Key) " is correct" 40 | } 41 | } 42 | 43 | Exit 0 -------------------------------------------------------------------------------- /Microsoft/Exchange/Exchange-ListMailboxes.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 13.01.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: Exchange-ListMailboxes.ps1 7 | Instructions: https://github.com/SimonSkotheimsvik/Community-By-SSkotheimsvik/ 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will list details of all mailboxes on Exchange server 12 | 13 | .EXAMPLE 14 | Exchange-ListMailboxes.ps1 15 | #> 16 | 17 | 18 | $(Foreach ($mailbox in Get-Recipient -ResultSize Unlimited){ 19 | $Stat = $mailbox | Get-MailboxStatistics | Select TotalItemSize,ItemCount,TotalDeletedItemSize,DeletedItemCount,Servername,Database 20 | New-Object PSObject -Property @{ 21 | FirstName = $mailbox.FirstName 22 | LastName = $mailbox.LastName 23 | DisplayName = $mailbox.DisplayName 24 | TotalItemSize = $Stat.TotalItemSize 25 | TotalDeletedItemSize = $Stat.TotalDeletedItemSize 26 | ItemCount = $Stat.ItemCount 27 | DeletedItemCount = $Stat.DeletedItemCount 28 | Servername = $Stat.Servername 29 | Database = $Stat.Database 30 | PrimarySmtpAddress = $mailbox.PrimarySmtpAddress 31 | Alias = $mailbox.Alias 32 | EmailAddresses = $mailbox.EmailAddresses 33 | RecipientType = $mailbox.RecipientType 34 | } 35 | }) | Select RecipientType,FirstName,LastName,DisplayName,TotalItemSize,ItemCount,TotalDeletedItemSize,DeletedItemCount,Servername,Database,PrimarySmtpAddress,Alias,@{Name=“EmailAddresses”;Expression={$_.EmailAddresses | Where-Object {$_.PrefixString -ceq “smtp”} | ForEach-Object {$_.SmtpAddress}}} | Export-CSV .\Exchange-ListMailboxes.csv -Encoding UTF8 -NoTypeInformation -------------------------------------------------------------------------------- /Microsoft/Intune/Win11-OEMInformation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 03.12.2021 5 | Created by: Simon Skotheimsvik 6 | Filename: Win11-OEMInformation.ps1 7 | Info: https://skotheimsvik.blogspot.com 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script sets the support information for Windows10 and Windows 11 12 | The information is found in Settings - System - About - Support 13 | The script can be assigned to devices in Microsoft Endpoint Manager. 14 | 15 | .EXAMPLE 16 | Win11-OEMInformation.ps1 17 | #> 18 | 19 | $RegKeyPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation' 20 | 21 | $SupportURL = "SupportURL" 22 | $Manufacturer = "Manufacturer" 23 | $SupportHours = "SupportHours" 24 | $SupportPhone = "SupportPhone" 25 | 26 | $SupportURLValue = "https://support.cloudlimits.com/" 27 | $ManufacturerValue = "Cloud Limits" 28 | $SupportHoursValue = "Standard: 0700-1600, Extended: 24-7-365" 29 | $SupportPhoneValue = "+47 12 34 56 78" 30 | 31 | 32 | IF(!(Test-Path $RegKeyPath)) 33 | { 34 | New-Item -Path $RegKeyPath -Force | Out-Null 35 | } 36 | 37 | New-ItemProperty -Path $RegKeyPath -Name $SupportURL -Value $SupportURLValue -PropertyType STRING -Force | Out-Null 38 | New-ItemProperty -Path $RegKeyPath -Name $Manufacturer -Value $ManufacturerValue -PropertyType STRING -Force | Out-Null 39 | New-ItemProperty -Path $RegKeyPath -Name $SupportHours -Value $SupportHoursValue -PropertyType STRING -Force | Out-Null 40 | New-ItemProperty -Path $RegKeyPath -Name $SupportPhone -Value $SupportPhoneValue -PropertyType STRING -Force | Out-Null 41 | 42 | 43 | # Clears the error log from powershell before exiting 44 | $error.clear() -------------------------------------------------------------------------------- /Microsoft/Intune/PR007 - Block AAD Registration to 3rd party tenants/BlockAADReg-Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 14.02.2024 5 | Created by: Simon Skotheimsvik 6 | Filename: BlockAADReg-Remediation 7 | Info: https://msendpointmgr.com/2021/03/11/are-you-tired-of-allow-my-organization-to-manage-my-device/ 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script block users from adding additional work accounts (Entra ID registered) on corporate Windows devices 12 | 13 | #> 14 | 15 | $RegContent = @" 16 | RegKeyPath,Key,Value 17 | "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WorkplaceJoin","BlockAADWorkplaceJoin","1" 18 | "@ 19 | 20 | $RegData = $RegContent | ConvertFrom-Csv -delimiter "," 21 | 22 | foreach ($Reg in $RegData) { 23 | 24 | IF (!(Test-Path ($Reg.RegKeyPath))) { 25 | Write-Host ($Reg.RegKeyPath) " does not exist. Will be created." 26 | New-Item -Path $($Reg.RegKeyPath) -Force | Out-Null 27 | } 28 | 29 | IF ((Get-ItemProperty -Path $Reg.RegKeyPath -Name $Reg.Key -ErrorAction SilentlyContinue) -eq $null) { 30 | Write-Host "$($Reg.Key) does not exist. Will be created." 31 | New-ItemProperty -Path $($Reg.RegKeyPath) -Name $($Reg.Key) -Value $($Reg.Value) -PropertyType Dword -Force 32 | } 33 | 34 | $ExistingValue = (Get-Item -Path $($Reg.RegKeyPath)).GetValue($($Reg.Key)) 35 | if ($ExistingValue -ne $($Reg.Value)) { 36 | Write-Host "$($Reg.Key) not correct value. Will be set." 37 | Set-ItemProperty -Path $($Reg.RegKeyPath) -Name $($Reg.Key) -Value $($Reg.Value) -Force 38 | } 39 | else { 40 | Write-Host "$($Reg.Key) is correct" 41 | } 42 | } 43 | 44 | Exit 0 45 | -------------------------------------------------------------------------------- /Microsoft/Intune/FortinetVPNProfile-Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 27.06.2022 5 | Created by: Simon Skotheimsvik 6 | Filename: FortinetVPNProfile-Remediation.ps1 7 | Instructions: https://skotheimsvik.blogspot.com/2022/07/fortinet-vpn-profile-distribution-with.html 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will create a VPN profile 12 | 13 | #> 14 | 15 | # Defining variables for the VPN connection 16 | $VPNName = "Simons VPN" 17 | $Server = "vpn.skotheimsvik.no:443" 18 | 19 | # Install VPN Profiles 20 | New-Item "HKLM:\SOFTWARE\Fortinet\FortiClient\Sslvpn\Tunnels\$VPNName" -force -ea SilentlyContinue; 21 | New-ItemProperty -LiteralPath "HKLM:\SOFTWARE\Fortinet\FortiClient\Sslvpn\Tunnels\$VPNName" -Name 'Description' -Value $VPNName -PropertyType String -Force -ea SilentlyContinue; 22 | New-ItemProperty -LiteralPath "HKLM:\SOFTWARE\Fortinet\FortiClient\Sslvpn\Tunnels\$VPNName" -Name 'Server' -Value $Server -PropertyType String -Force -ea SilentlyContinue; 23 | New-ItemProperty -LiteralPath "HKLM:\SOFTWARE\Fortinet\FortiClient\Sslvpn\Tunnels\$VPNName" -Name 'promptusername' -Value 1 -PropertyType DWord -Force -ea SilentlyContinue; 24 | New-ItemProperty -LiteralPath "HKLM:\SOFTWARE\Fortinet\FortiClient\Sslvpn\Tunnels\$VPNName" -Name 'promptcertificate' -Value 0 -PropertyType DWord -Force -ea SilentlyContinue; 25 | New-ItemProperty -LiteralPath "HKLM:\SOFTWARE\Fortinet\FortiClient\Sslvpn\Tunnels\$VPNName" -Name 'ServerCert' -Value '1' -PropertyType String -Force -ea SilentlyContinue; 26 | 27 | if ((Test-Path -LiteralPath "HKLM:\SOFTWARE\Fortinet\FortiClient\Sslvpn\Tunnels\$VPNName") -ne $true) { 28 | $exitCode = -1 29 | } 30 | else { 31 | $exitCode = 0 32 | } 33 | 34 | exit $exitCode -------------------------------------------------------------------------------- /Microsoft/Exchange/Exchange-ListSharedMailboxes.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 19.01.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: Exchange-ListSharedMailboxes.ps1 7 | Instructions: https://github.com/SimonSkotheimsvik/Community-By-SSkotheimsvik/ 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will list Shared mailboxes on Exchange on-premises 12 | #> 13 | 14 | $CSVFile = ".\Exchange-ListSharedMailboxes.csv" 15 | if (Test-Path $CSVFile) { 16 | Remove-Item $CSVFile 17 | } 18 | 19 | 20 | $SharedMailboxes = Get-Mailbox -RecipientTypeDetails SharedMailbox -ResultSize Unlimited | select DisplayName,Alias,UserPrincipalName,PrimarySmtpAddress,ServerName,Database,@{Name=“EmailAddresses”;Expression={$_.EmailAddresses | Where-Object {$_.PrefixString -ceq “smtp”} | ForEach-Object {$_.SmtpAddress}}} 21 | 22 | $(Foreach ($mailbox in $SharedMailboxes){ 23 | $Permissions = Get-MailboxPermission $mailbox.UserPrincipalName | select user,@{Name=“accessrights”;Expression={$_.accessrights }},IsInherited,Deny | where { ($_.User -notlike 'NT AUTHORITY\*') } 24 | $(Foreach ($Permission in $Permissions){ 25 | New-Object PSObject -Property @{ 26 | DisplayName = $mailbox.DisplayName 27 | Alias = $mailbox.Alias 28 | UserPrincipalName = $mailbox.UserPrincipalName 29 | PrimarySmtpAddress = $mailbox.PrimarySmtpAddress 30 | EmailAddresses = $mailbox.EmailAddresses 31 | ServerName = $mailbox.ServerName 32 | Database = $mailbox.Database 33 | User = $Permission.user 34 | AccessRights = $Permission.accessrights 35 | } | Select DisplayName,Alias,UserPrincipalName,PrimarySmtpAddress,ServerName,Database,User,AccessRights,EmailAddresses | Export-CSV $CSVFile -Encoding UTF8 -NoTypeInformation -Append 36 | }) 37 | }) -------------------------------------------------------------------------------- /Microsoft/Intune/Get-ManagedDeviceBeforeIntuneCleanUp.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script lists all devices that have not synced with Intune for the last 55 days. 4 | Use this script in conjunction with the Intune cleanup process to identify devices that have not synced recently. 5 | 6 | .DESCRIPTION 7 | This script lists all devices that have not synced with Intune for the last 55 days. The script uses the Microsoft.Graph.Beta.DeviceManagement module to connect to Microsoft Graph and retrieve the devices. 8 | The script then filters the devices based on the LastSyncDateTime property and calculates the number of days since the last sync. The script outputs the devices to an Out-GridView window. 9 | 10 | .NOTES 11 | Author: Simon Skotheimsvik 12 | Version: 13 | 1.0.0 - 2024-10-29 - Initial release, Simon Skotheimsvik 14 | #> 15 | 16 | # Define the threshold date (55 days ago) 17 | $thresholdDate = (Get-Date).AddDays(-55) 18 | 19 | # Import modules and connect to Microsoft Graph 20 | Import-Module Microsoft.Graph.Beta.DeviceManagement 21 | Connect-MgGraph -Scopes "DeviceManagementManagedDevices.Read.All" -NoWelcome 22 | 23 | # Get devices from Microsoft Graph, filter by LastSyncDateTime, and add DaysSinceLastSync 24 | Get-MgBetaDeviceManagementManagedDevice | 25 | Where-Object { $_.LastSyncDateTime -lt $thresholdDate } | 26 | Select-Object LastSyncDateTime, 27 | @{Name="DaysSinceLastSync"; Expression={(Get-Date) - $_.LastSyncDateTime | Select-Object -ExpandProperty Days}}, 28 | UserPrincipalName, 29 | DeviceName, 30 | Manufacturer, 31 | Model, 32 | OperatingSystem, 33 | OSVersion, 34 | ComplianceState, 35 | ManagementState, 36 | ManagedDeviceOwnerType, 37 | JoinType, 38 | ManagementCertificateExpirationDate | 39 | Sort-Object LastSyncDateTime | Out-GridView -------------------------------------------------------------------------------- /Microsoft/Intune/Get-IntuneUsersEnrollingMultipleDevices.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This scripts list users enrolling many devices in Intune. 4 | 5 | .DESCRIPTION 6 | There is a discussion going on using DEM account with Autopilot. This script will list all users enrolling many devices in Intune. 7 | Read this post for a summery: https://call4cloud.nl/using-a-dem-account-windows-autopilot-is-a-bad-idea/ 8 | 9 | .NOTES 10 | Author: Simon Skotheimsvik 11 | Version: 12 | 1.0.0 - 2025-02-25 - Initial release, Simon Skotheimsvik 13 | #> 14 | 15 | 16 | # Connect to Microsoft Graph 17 | Connect-MgGraph -Scopes "DeviceManagementManagedDevices.Read.All" 18 | 19 | # Get all managed devices 20 | $devices = Get-MgDeviceManagementManagedDevice -All 21 | 22 | # Process devices and create custom objects 23 | $deviceData = $devices | ForEach-Object { 24 | $enrollmentUser = $_.ManagedDeviceName -split '_' | Select-Object -First 1 25 | [PSCustomObject]@{ 26 | EnrollmentUser = $enrollmentUser 27 | DeviceName = $_.DeviceName 28 | ManagedDeviceName = $_.ManagedDeviceName 29 | UserPrincipalName = $_.UserPrincipalName 30 | OperatingSystem = $_.OperatingSystem 31 | OSVersion = $_.OSVersion 32 | Model = $_.Model 33 | Manufacturer = $_.Manufacturer 34 | SerialNumber = $_.SerialNumber 35 | LastSyncDateTime = $_.LastSyncDateTime 36 | } 37 | } 38 | 39 | # Group by enrollment user and filter for those with more than 5 devices 40 | $enrollmentUsersWithDevices = $deviceData | Group-Object EnrollmentUser | 41 | Where-Object { $_.Count -gt 5 } | 42 | ForEach-Object { 43 | $_.Group | Add-Member -MemberType NoteProperty -Name DeviceCount -Value $_.Count -PassThru 44 | } | 45 | Sort-Object EnrollmentUser, LastSyncDateTime -Descending 46 | 47 | # Display results in GridView 48 | $enrollmentUsersWithDevices | Out-GridView -Title "Enrollment Users with More Than 5 Devices and Device Details" 49 | 50 | # Disconnect from Microsoft Graph 51 | Disconnect-MgGraph -------------------------------------------------------------------------------- /Microsoft/Intune/Win11-NetworkConfigurationOperators.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 02.03.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: Win11-NetworkConfigurationOperators.ps1 7 | Info: https://skotheimsvik.no 8 | Reference: https://call4cloud.nl/2021/04/dude-wheres-my-admin/#changing 9 | =========================================================================== 10 | 11 | .DESCRIPTION 12 | This script adds users to the local group "Network Configuration Operators". 13 | Members in this group can have some administrative privileges to manage 14 | configuration of networking features. 15 | 16 | The script can be distributed through Intune and targeted to a group of 17 | users qualifying to get this right "AZ-Device-Role-Local Network Configuration Operators" 18 | The script should run under system context. It will create a local scheduled task 19 | running at each user logon. 20 | 21 | #> 22 | 23 | $content = @' 24 | $loggedonuser = Get-WMIObject -class Win32_ComputerSystem | Select-Object -ExpandProperty username 25 | Add-LocalGroupMember -Group "Network Configuration Operators" -Member $loggedonuser 26 | '@ 27 | 28 | # create custom folder and write PS script 29 | $path = $(Join-Path $env:ProgramData CustomScripts) 30 | if (!(Test-Path $path)) 31 | { 32 | New-Item -Path $path -ItemType Directory -Force -Confirm:$false 33 | } 34 | Out-File -FilePath $(Join-Path $env:ProgramData CustomScripts\NetworkOperatorGroup.ps1) -Encoding unicode -Force -InputObject $content -Confirm:$false 35 | 36 | # register script as scheduled task 37 | $Time = New-ScheduledTaskTrigger -AtLogOn 38 | $User = "SYSTEM" 39 | $Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ex bypass -file `"C:\ProgramData\CustomScripts\NetworkOperatorGroup.ps1`"" 40 | Register-ScheduledTask -TaskName "AddUserToNetworkOperatorGroup" -Trigger $Time -User $User -Action $Action -Force -------------------------------------------------------------------------------- /Microsoft/Intune/PR008 - Start URL On Logon/Alternative 2 (Registry)/StartURLonLogon-Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | Created on: 26.06.2024 4 | Author: Simon Skotheimsvik 5 | Filename: StartURLonLogon-Remediation.ps1 6 | Info: https://skotheimsvik.no 7 | Versions: 8 | 1.0.0, 26.06.2024, Initial version 9 | 1.0.1, 22.09.2025, Updated output 10 | 11 | .DESCRIPTION 12 | This remediation package adds URL to start on logon. 13 | #> 14 | 15 | $RegContent = @" 16 | RegKeyPath,Key,Value,Type 17 | "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run","SimonDoes","explorer https://skotheimsvik.no","String" 18 | "@ 19 | 20 | $RegData = $RegContent | ConvertFrom-Csv -delimiter "," 21 | $RemediationFailed = $false 22 | 23 | foreach ($Reg in $RegData) { 24 | try { 25 | IF (!(Test-Path ($Reg.RegKeyPath))) { 26 | Write-Output "$($Reg.RegKeyPath) does not exist. Will be created." 27 | New-Item -Path $($Reg.RegKeyPath) -Force | Out-Null 28 | } 29 | 30 | IF ((Get-ItemProperty -Path $Reg.RegKeyPath -Name $Reg.Key -ErrorAction SilentlyContinue) -eq $null) { 31 | Write-Output "$($Reg.Key) does not exist. Will be created." 32 | New-ItemProperty -Path $($Reg.RegKeyPath) -Name $($Reg.Key) -Value $($Reg.Value) -PropertyType $($Reg.Type) -Force 33 | } 34 | 35 | $ExistingValue = (Get-Item -Path $($Reg.RegKeyPath)).GetValue($($Reg.Key)) 36 | if ($ExistingValue -ne $($Reg.Value)) { 37 | Write-Output "$($Reg.Key) not correct value. Will be set." 38 | Set-ItemProperty -Path $($Reg.RegKeyPath) -Name $($Reg.Key) -Value $($Reg.Value) -Force 39 | } 40 | else { 41 | Write-Output "$($Reg.Key) is correct" 42 | } 43 | } 44 | catch { 45 | Write-Output "Failed to process $($Reg.Key) at $($Reg.RegKeyPath): $_" 46 | $RemediationFailed = $true 47 | } 48 | } 49 | 50 | if ($RemediationFailed) { 51 | # At least one of the remediation steps failed 52 | Exit 1 53 | } 54 | else { 55 | Exit 0 56 | } 57 | -------------------------------------------------------------------------------- /Microsoft/Intune/PR009 - Autopatch NoAutoUpdate/Autopatch-NoAutoUpdate-Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | Created on: 07.10.2024 4 | Created by: Simon Skotheimsvik 5 | Filename: Autopatch-NoAutoUpdate-Remediation.ps1 6 | Info: https://skotheimsvik.no 7 | Version: 1.0.5 8 | 9 | .DESCRIPTION 10 | This remediation script checks if the registry key exists and holds a different value than expected. If the value is different, the script will clean up the registry key. 11 | #> 12 | 13 | $RegKeyPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" 14 | $RegKey = "NoAutoUpdate" 15 | $ExpectedValue = 0 16 | 17 | try { 18 | if (Test-Path -Path $RegKeyPath) { 19 | try { 20 | $ExistingValue = Get-ItemPropertyValue -Path $RegKeyPath -Name $RegKey -ErrorAction SilentlyContinue 21 | if ($ExistingValue -eq $null) { 22 | Write-Host "Registry key does not exist. No remediation needed." 23 | Exit 0 # No changes required 24 | } elseif ($ExistingValue -ne $ExpectedValue) { 25 | # Stop the Windows Update service 26 | Stop-Service -Name wuauserv -Force 27 | 28 | Set-ItemProperty -Path $RegKeyPath -Name $RegKey -Value $ExpectedValue -ErrorAction SilentlyContinue 29 | Write-Host "Set key: $RegKey in path $RegKeyPath to the expected value $ExpectedValue." 30 | 31 | # Start the Windows Update service 32 | Start-Service -Name wuauserv 33 | 34 | Exit 1 # Remediation successful 35 | } else { 36 | Write-Host "$($RegKey) in path $($RegKeyPath) equals $($ExistingValue). No remediation needed." 37 | Exit 0 # No changes required 38 | } 39 | } catch { 40 | Write-Host "$_. No remediation needed." 41 | Exit 0 # No changes required 42 | } 43 | } else { 44 | Write-Host "Registry path does not exist. No remediation needed." 45 | Exit 0 # No changes required 46 | } 47 | } catch { 48 | Write-Host "Error accessing registry path ${RegKeyPath}: $_" 49 | Exit 1 # Error occurred 50 | } -------------------------------------------------------------------------------- /Microsoft/Intune/CloudLAPS_SchTask_Uninstall_1_0_0.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script to uninstall CloudLAPS solution for environments not licensed for Proactive Remediations where the solution has been added as a Scheduled task. 4 | 5 | .DESCRIPTION 6 | This script will check if the scheduled task exists and then delete it. The script will also remove the folder holding the script for the scheduled 7 | task and delete the old local cloudlaps administrator account. 8 | 9 | .NOTES 10 | FileName: CloudLAPS_SchTask_Uninstall.ps1 11 | Author: Simon Skotheimsvik 12 | Contact: @SSkotheimsvik 13 | Info: https://skotheimsvik.no/migrating-cloud-laps-to-the-new-windows-laps 14 | Created: 2024.01.09 15 | Updated: 2024.01.09 16 | 17 | Version history: 18 | 1.0.0 - (2024.01.09) Script created 19 | 20 | #> 21 | 22 | #region Variables 23 | $scheduledTaskName = "CloudLAPS Rotation" 24 | $folderPath = "C:\ProgramData\CloudLAPS Client" 25 | $userAccount = "LocalAdmin" 26 | #endregion Variables 27 | 28 | #region Scheduled Task 29 | $scheduledTask = Get-ScheduledTask -TaskName $scheduledTaskName -ErrorAction SilentlyContinue 30 | 31 | if ($scheduledTask -ne $null) { 32 | # Delete the scheduled task 33 | Unregister-ScheduledTask -TaskName $scheduledTaskName -Confirm:$false 34 | Write-Host "Scheduled task '$scheduledTaskName' deleted." 35 | } else { 36 | Write-Host "Scheduled task '$scheduledTaskName' not found." 37 | } 38 | #endregion Scheduled Task 39 | 40 | 41 | #region Script and folder 42 | if (Test-Path $folderPath -PathType Container) { 43 | Remove-Item -Path $folderPath -Recurse -Force 44 | Write-Host "Folder '$folderPath' deleted." 45 | } else { 46 | Write-Host "Folder '$folderPath' not found." 47 | } 48 | #endregion Script and folder 49 | 50 | 51 | #region Local Administrator account 52 | $userExists = Get-LocalUser -Name $userAccount -ErrorAction SilentlyContinue 53 | 54 | if ($userExists -ne $null) { 55 | Remove-LocalUser -Name $userAccount -Confirm:$false 56 | Write-Host "User account '$userAccount' deleted." 57 | } else { 58 | Write-Host "User account '$userAccount' not found." 59 | } 60 | #endregion Local Administrator account 61 | -------------------------------------------------------------------------------- /Microsoft/AD/ExportADResourcesToCsv.ps1: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # # 3 | # Script for exporting AD resources in given OU's to CSV file # 4 | # # 5 | ############################################################################### 6 | # # 7 | # 2014.06.18 - Simon Skotheimsvik # 8 | # # 9 | ############################################################################### 10 | 11 | Import-Module ac* 12 | 13 | $path = "C:\Source\Scripts\" 14 | $ADServer = "DC01.domain.local" 15 | $OU = "ou=users,ou=mybusiness,dc=domain,dc=local" 16 | $CSVUsers = $path + "ExportADResourcesToCsv-Users.csv" 17 | $CSVComputers = $path + "ExportADResourcesToCsv-Computers.csv" 18 | 19 | # USERS 20 | Get-ADUser -server $ADServer -SearchBase $OU -Filter * -ResultSetSize 5000 -Properties EmailAddress, Company, Department, Title, Office, OfficePhone, MobilePhone, ipPhone, physicalDeliveryOfficeName, manager, msRTCSIP-Line, msRTCSIP-PrimaryUserAddress, homeDrive, homeDirectory, distinguishedName, proxyAddresses, lastLogonTimeStamp | Select givenName, surName, Name, SamAccountName, EmailAddress, Company, Department, Title, Office, OfficePhone, MobilePhone, ipPhone, physicalDeliveryOfficeName, manager, msRTCSIP-Line, msRTCSIP-PrimaryUserAddress, homeDrive, homeDirectory, distinguishedName, @{n = "lastLogonDate"; e = { [datetime]::FromFileTime($_.lastLogonTimestamp) } }, @{Name = "proxyAddresses"; expression = { $_.proxyAddresses -join ";" } } | export-csv $CSVUsers -Encoding "unicode" 21 | 22 | # COMPUTERS 23 | Get-ADComputer -server $ADServer -SearchBase $OU -Filter * -ResultSetSize 5000 -Properties cn, description, distinguishedName, operatingSystem, operatingSystemVersion, whenCreated, whenChanged, lastLogonTimestamp | Select @{n = "lastLogonDate"; e = { [datetime]::FromFileTime($_.lastLogonTimestamp) } }, cn, description, distinguishedName, DNSHostname, Enabled, operatingSystem, operatingSystemVersion, whenCreated, whenChanged | export-csv $CSVComputers -Encoding "unicode" -------------------------------------------------------------------------------- /Microsoft/Intune/PR003 - Branding/Branding-Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 11.07.2023 5 | Last updated: 16.04.2024 6 | Created by: Simon Skotheimsvik 7 | Filename: Branding-Remediation.ps1 8 | Info: https://skotheimsvik.no 9 | =========================================================================== 10 | 11 | .DESCRIPTION 12 | This script sets the support information for Windows10 and Windows 11 13 | The information is found in Settings - System - About - Support 14 | The script can be assigned as a Remediation script in Microsoft Intune 15 | 16 | #> 17 | 18 | $BrandingContent = @" 19 | RegKeyPath,Key,Value 20 | "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation","SupportURL","https://support.cloudlimits.com/" 21 | "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation","Manufacturer","Cloud Limits" 22 | "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation","SupportHours","Standard: 0700-1600, Extended: 24-7-365" 23 | "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation","SupportPhone","+47 12 34 56 78" 24 | "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion","RegisteredOwner","Cloud Limits" 25 | "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion","RegisteredOrganization","Cloud Limits" 26 | "@ 27 | 28 | $Branding = $BrandingContent | ConvertFrom-Csv -delimiter "," 29 | 30 | foreach ($Brand in $Branding) { 31 | 32 | IF (!(Test-Path ($Brand.RegKeyPath))) { 33 | Write-Host ($Brand.RegKeyPath) " does not exist. Will be created." 34 | New-Item -Path $RegKeyPath -Force | Out-Null 35 | } 36 | IF ((Get-ItemProperty -Path $Brand.RegKeyPath -Name $Brand.Key -ErrorAction SilentlyContinue) -eq $null) { 37 | Write Host $($Brand.Key) " does not exist. Will be created." 38 | New-ItemProperty -Path $($Brand.RegKeyPath) -Name $($Brand.Key) -Value $($Brand.Value) -PropertyType STRING -Force 39 | } 40 | 41 | $ExistingValue = (Get-Item -Path $($Brand.RegKeyPath)).GetValue($($Brand.Key)) 42 | if ($ExistingValue -ne $($Brand.Value)) { 43 | Write-Host $($Brand.Key) " not correct value. Will be set." 44 | Set-ItemProperty -Path $($Brand.RegKeyPath) -Name $($Brand.Key) -Value $($Brand.Value) -Force 45 | } 46 | else { 47 | Write-Host $($Brand.Key) " is correct" 48 | } 49 | } 50 | 51 | Exit 0 52 | -------------------------------------------------------------------------------- /Microsoft/Intune/Get-IntuneUniqueGroupTags.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script to get unique Group Tags from Autopilot devices 4 | .DESCRIPTION 5 | Script to get unique Group Tags from Autopilot devices in Microsoft Intune using Microsoft Graph PowerShell SDK. 6 | .EXAMPLE 7 | 8 | .NOTES 9 | Author: Simon Skotheimsvik 10 | Contact: skotheimsvik.no 11 | Version history: 12 | 1.0 - (09.09.2025) Script released, Simon Skotheimsvik 13 | 14 | #> 15 | 16 | 17 | # Install required module if not present 18 | if (-not (Get-Module Microsoft.Graph.DeviceManagement.Enrollment -ListAvailable)) { 19 | Install-Module Microsoft.Graph.DeviceManagement.Enrollment -Scope CurrentUser -Force 20 | } 21 | 22 | Import-Module-Module Microsoft.Graph.DeviceManagement.Enrollment 23 | 24 | # Connect to Microsoft Graph 25 | Connect-MgGraph -Scopes "DeviceManagementServiceConfig.Read.All" 26 | 27 | # Get all Autopilot devices 28 | $devices = Get-MgDeviceManagementWindowsAutopilotDeviceIdentity -All 29 | 30 | # Extract and list unique Group Tags 31 | $uniqueGroupTags = $devices | Select-Object -ExpandProperty GroupTag | Where-Object { $_ -ne $null -and $_ -ne "" } | Sort-Object -Unique 32 | 33 | # Display results 34 | Write-Host "Unique Group Tags in Autopilot Devices:" 35 | $uniqueGroupTags 36 | 37 | # Display count for each Group Tag 38 | $groupTagCounts = $devices | Group-Object -Property GroupTag | Sort-Object Name 39 | Write-Host "`nGroup Tag Counts:" 40 | $groupTagCounts | ForEach-Object { Write-Host "$($_.Name): $($_.Count)" } 41 | 42 | # Display different enrollmentstates for Autopilot devices. 43 | $devices | Group-Object -Property EnrollmentState | Select-Object Name, Count 44 | 45 | # Display numbers for enrollment states for each grouptag 46 | $devices | Group-Object -Property GroupTag, EnrollmentState | Select-Object @{Name='GroupTag';Expression={$_.Values[0]}}, @{Name='EnrollmentState';Expression={$_.Values[1]}}, Count | Sort-Object GroupTag, EnrollmentState | Format-Table -AutoSize 47 | 48 | $devices | measure 49 | 50 | $devices[3] | fl 51 | 52 | # Get number of devices assigned to each Autopilot profile 53 | Import-Module Microsoft.Graph.Beta.DeviceManagement.Enrollment 54 | $profileAssignments = @(); foreach ($profile in $profiles) { $assignedDevices = Get-MgBetaDeviceManagementWindowsAutopilotDeploymentProfileAssignedDevice -WindowsAutopilotDeploymentProfileId $profile.Id -All; $profileAssignments += [PSCustomObject]@{ ProfileName = $profile.DisplayName; DeviceCount = ($assignedDevices | Measure-Object).Count } }; $profileAssignments | Format-Table -AutoSize -------------------------------------------------------------------------------- /Microsoft/AAD/EntraID - GetPasskeysInUse.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script to investigate passkeys in use in Entra ID 4 | .DESCRIPTION 5 | The script will connect to Microsoft Graph and retrieve user registration details for passkey device-bound methods. 6 | This script will list all the unique AaGuids for FIDO2 methods in use in Entra ID. 7 | It will then retrieve the unique AaGuids for FIDO2 methods and output them in a table format with details on each user having passkeys. 8 | .PARAMETER None 9 | This script does not accept any parameters. 10 | 11 | .NOTES 12 | Author: Simon Skotheimsvik 13 | Info: https://skotheimsvik.no 14 | Creation Date: 2024.08.19 15 | Version history: 16 | 1.0 - (2024.08.19) Script released, Simon Skotheimsvik 17 | #> 18 | 19 | #region Connect 20 | # Install the Microsoft Graph module 21 | # Install-Module Microsoft.Graph 22 | 23 | # Connect to Microsoft Graph with the required scopes 24 | Connect-MgGraph -Scope AuditLog.Read.All, UserAuthenticationMethod.Read.All 25 | #endregion 26 | 27 | #region Get Users 28 | # Retrieve and process user registration details for passkey device-bound methods 29 | $users = Get-MgReportAuthenticationMethodUserRegistrationDetail -Filter "methodsRegistered/any(i:i eq 'passKeyDeviceBound')" -All 30 | $userIds = $users.Id 31 | #endregion 32 | 33 | #region Unique AaGuids 34 | # Get unique AaGuids for FIDO2 methods 35 | $aaGuids = $userIds | ForEach-Object { Get-MgUserAuthenticationFido2Method -UserId $_ -All } | Select-Object -ExpandProperty AaGuid -Unique 36 | 37 | # Output the unique AaGuids 38 | Write-Host "Unique AaGuids for FIDO2 methods in use in Entra ID:" -ForegroundColor Green 39 | $aaGuids 40 | #endregion 41 | 42 | #region User Passkey Details 43 | # Initialize an array to store the results 44 | $result = @() 45 | 46 | # Loop through each user and get their FIDO2 methods 47 | foreach ($user in $users) { 48 | $fidoMethods = Get-MgUserAuthenticationFido2Method -UserId $user.Id -All 49 | foreach ($method in $fidoMethods) { 50 | $result += [PSCustomObject]@{ 51 | Username = $user.UserPrincipalName 52 | DisplayName = $user.DisplayName 53 | AaGuid = $method.AaGuid 54 | Model = $method.Model 55 | DislplayName = $method.DisplayName 56 | CreatedDateTime = $method.CreatedDateTime 57 | } 58 | } 59 | } 60 | 61 | # Output the results in a table format 62 | Write-Host "User passkey details in Entra ID:" -ForegroundColor Green 63 | $result | ft 64 | #endregion -------------------------------------------------------------------------------- /Microsoft/Intune/WPS006 - Show File Extensions in Windows Explorer/WPS006 - ShowFileExtensionsWindowsExplorer.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 06.06.2024 5 | Created by: Simon Skotheimsvik 6 | Filename: WPS006 - ShowFileExtensionsWindowsExplorer.ps1 7 | Info: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script enables file extensions in Windows Explorer for Windows10 and Windows 11 12 | The setting is found in "View - Show - File name extensions" 13 | The script is assigned as platform script to users in MicrosoftIntune. 14 | #> 15 | 16 | #region - Get information about signed in user. 17 | # Routine inspired by Rudy Ooms: https://call4cloud.nl/2020/03/how-to-deploy-hkcu-changes-while-blocking-powershell/#part4 18 | 19 | # Get information of current user 20 | $currentUser = (Get-Process -IncludeUserName -Name explorer | Select-Object -First 1 | Select-Object -ExpandProperty UserName).Split("\")[1] 21 | 22 | $Data = $currentUser 23 | $Keys = GCI "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\" -Recurse 24 | Foreach ($Key in $Keys) { 25 | IF (($key.GetValueNames() | % { $key.GetValue($_) }) -match "\b$CurrentUser\b" ) { $sid = $key } 26 | } 27 | 28 | # Add SID of current user to a variable 29 | $sid = $sid.pschildname 30 | 31 | New-PSDrive HKU Registry HKEY_USERS | out-null 32 | #endregion 33 | 34 | #region RegistryContent 35 | 36 | $RegistryContent = @" 37 | RegKeyPath,Key,Value,Type 38 | "HKU:\$sid\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced","HideFileExt","0", "DWord" 39 | "@ 40 | 41 | $RegistryC = $RegistryContent | ConvertFrom-Csv -delimiter "," 42 | 43 | 44 | foreach ($Content in $RegistryC) { 45 | 46 | IF (!(Test-Path ($Content.RegKeyPath))) { 47 | Write-Host ($Content.RegKeyPath) " does not exist. Will be created." 48 | New-Item -Path $RegKeyPath -Force | Out-Null 49 | } 50 | IF ((Get-ItemProperty -Path $Content.RegKeyPath -Name $Content.Key -ErrorAction SilentlyContinue) -eq $null) { 51 | Write Host $($Content.Key) " does not exist. Will be created." 52 | New-ItemProperty -Path $($Content.RegKeyPath) -Name $($Content.Key) -Value $($Content.Value) -PropertyType $($Content.Type) -Force 53 | } 54 | 55 | $ExistingValue = (Get-Item -Path $($Content.RegKeyPath)).GetValue($($Content.Key)) 56 | if ($ExistingValue -ne $($Content.Value)) { 57 | Write-Host $($Content.Key) " not correct value. Will be set." 58 | Set-ItemProperty -Path $($Content.RegKeyPath) -Name $($Content.Key) -Value $($Content.Value) -Force 59 | } 60 | else { 61 | Write-Host $($Content.Key) " is correct" 62 | } 63 | } 64 | 65 | 66 | # Clears the error log from powershell before exiting 67 | $error.clear() 68 | 69 | #endregion -------------------------------------------------------------------------------- /Microsoft/Intune/Win11-EnableAdobeAcrobatForMicrosoftSensitivityLabels.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 20.12.2022 5 | Created by: Simon Skotheimsvik 6 | Filename: Win11-EnableAdobeAcrobatForMicrosoftSensitivityLabels.ps1 7 | Info: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script sets registry information in Windows10 and Windows11 12 | to enable Adobe Acrobat to work with Microsoft Sensitivity labels 13 | defined in Microsoft Purview as defined by Nikki Chapple in her blog 14 | https://nikkichapple.com/how-to-use-sensitivity-labels-with-your-pdf-files/ 15 | 16 | The script can be assigned to users in Microsoft Intune. 17 | 18 | .EXAMPLE 19 | Win11-EnableAdobeAcrobatForMicrosoftSensitivityLabels.ps1 20 | #> 21 | 22 | #region - Get information about signed in user. 23 | # Routine inspired by Rudy Ooms: https://call4cloud.nl/2020/03/how-to-deploy-hkcu-changes-while-blocking-powershell/#part4 24 | 25 | # Get information of current user 26 | $currentUser = (Get-Process -IncludeUserName -Name explorer | Select-Object -First 1 | Select-Object -ExpandProperty UserName).Split("\")[1] 27 | 28 | $Data = $currentUser 29 | $Keys = GCI "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\" -Recurse 30 | Foreach ($Key in $Keys) { 31 | IF (($key.GetValueNames() | % { $key.GetValue($_) }) -match "\b$CurrentUser\b" ) { $sid = $key } 32 | } 33 | 34 | # Add SID of current user to a variable 35 | $sid = $sid.pschildname 36 | 37 | New-PSDrive HKU Registry HKEY_USERS | out-null 38 | #endregion 39 | 40 | #region Variables for sensitivity labels in Adobe Acrobat 41 | 42 | $RegKeyPath1 = "HKLM:\SOFTWARE\WOW6432Node\Policies\Adobe\Adobe Acrobat\DC\FeatureLockDown" 43 | $RegKeyPath2 = "HKU:\$sid\SOFTWARE\Adobe\Adobe Acrobat\DC\MicrosoftAIP" 44 | 45 | $bMIPCheckPolicyOnDocSave = "bMIPCheckPolicyOnDocSave" 46 | $bMIPCheckPolicyOnDocSaveValue = 1 47 | 48 | $bMIPLabelling = "bMIPLabelling" 49 | $bMIPLabellingValue = 1 50 | 51 | $bShowDMB = "bShowDMB" 52 | $bShowDMBValue = 1 53 | #endregion 54 | 55 | #region Implementation of registry settings 56 | IF (!(Test-Path $RegKeyPath1)) { 57 | New-Item -Path $RegKeyPath1 -Force | Out-Null 58 | } 59 | 60 | IF (!(Test-Path $RegKeyPath2)) { 61 | New-Item -Path $RegKeyPath2 -Force | Out-Null 62 | } 63 | 64 | New-ItemProperty -Path $RegKeyPath1 -Name $bMIPCheckPolicyOnDocSave -Value $bMIPCheckPolicyOnDocSaveValue -PropertyType DWord -Force | Out-Null 65 | New-ItemProperty -Path $RegKeyPath1 -Name $bMIPLabelling -Value $bMIPLabellingValue -PropertyType DWord -Force | Out-Null 66 | New-ItemProperty -Path $RegKeyPath2 -Name $bShowDMB -Value $bShowDMBValue -PropertyType DWord -Force | Out-Null 67 | 68 | # Clears the error log from powershell before exiting 69 | $error.clear() 70 | 71 | #endregion -------------------------------------------------------------------------------- /Microsoft/Intune/PR013 - Uninstall N-able Client/Uninstall N-able Client - Detection.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Remediation to uninstall N-Able client 4 | 5 | .DESCRIPTION 6 | This script searches for the N-Able client in the registry and uninstalls it if found. 7 | 8 | .NOTES 9 | Author: Simon Skotheimsvik 10 | Version: 1.0.0 - 2025-08-12, Simon Skotheimsvik, Initial version 11 | Version: 1.0.1 - 2025-08-12, Simon Skotheimsvik, Changed application to uninstall. 12 | Version: 1.0.2 - 2025-08-12, Simon Skotheimsvik, Fixed error handling and exit codes. 13 | 14 | license: This script is provided as-is, without warranty of any kind. Use at your own risk. 15 | You may modify and redistribute this script as long as you retain this notice in the code. 16 | #> 17 | 18 | 19 | # Define log file path in the IME extension logs folder 20 | $imeLogFolder = "C:\ProgramData\Microsoft\IntuneManagementExtension\Logs" 21 | if (-not (Test-Path $imeLogFolder)) { 22 | New-Item -Path $imeLogFolder -ItemType Directory -Force | Out-Null 23 | } 24 | 25 | # Function to log messages 26 | function Write-Log { 27 | param([string]$Message) 28 | $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 29 | Add-Content -Path $logFile -Value "$timestamp $Message" 30 | } 31 | 32 | # variables 33 | $name = "Windows Agent" 34 | $publisher = "N-able Technologies" 35 | $logFile = Join-Path $imeLogFolder "uninstall_log_$($name).log" 36 | $datetime = Get-Date -Format "yyyy-MM-dd HH:mm" 37 | $found = $false 38 | 39 | # Define registry paths to search 40 | $registryPaths = @( 41 | "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall", 42 | "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" 43 | ) 44 | 45 | foreach ($path in $registryPaths) { 46 | Get-ChildItem -Path $path | ForEach-Object { 47 | $displayName = (Get-ItemProperty -Path $_.PSPath -ErrorAction SilentlyContinue).DisplayName 48 | $publisherName = (Get-ItemProperty -Path $_.PSPath -ErrorAction SilentlyContinue).Publisher 49 | if ($displayName -eq $name -and $publisherName -eq $publisher) { 50 | $found = $true 51 | Write-Log "Found key: $($_.Name)" 52 | # Extract the GUID from the registry key name 53 | if ($_.Name -match '{[A-F0-9\-]+}') { 54 | $guid = $matches[0] 55 | Write-Log "Found MSI with GUID: $guid" 56 | Write-Output "Found $($name) from $($publisherName) with guid $($guid), $($datetime)" 57 | Exit 1 58 | 59 | } else { 60 | Write-Log "No GUID found in key: $($_.Name)" 61 | Write-Output "No GUID found for $($name) from $($publisherName), $($datetime)." 62 | Exit 0 63 | } 64 | } 65 | } 66 | } 67 | 68 | if (-not $found) { 69 | Write-Log "No registry key found for $name from $publisher." 70 | Write-Output "No $($name) from $($publisher) found, no remediation required, $($datetime)." 71 | Exit 0 72 | } -------------------------------------------------------------------------------- /Microsoft/AAD/AAD-CA-PersonaGroups.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script to create Entra ID CA Persona groups. 4 | .DESCRIPTION 5 | Script to create role-assignable Entra ID CA Persona groups used for assigning users to Personas targeting Conditional Access policies. 6 | .EXAMPLE 7 | 8 | .NOTES 9 | Author: Simon Skotheimsvik 10 | Info: https://skotheimsvik.no 11 | Versions: 12 | 1.0.0 - (23.05.2023) Script released 13 | 1.0.1 - (12.03.2024) Convert to Microsoft Graph PowerShell SDK V2 module, Simon Skotheimsvik 14 | 1.0.2 - (10.09.2024) Added new CA Persona for Service Accounts, Simon Skotheimsvik 15 | 1.0.3 - (10.09.2024) Bugfix in finding existing groups, Simon Skotheimsvik 16 | #> 17 | 18 | #region Variables 19 | # Insert groups to be created. Security defined after ";" 20 | $GroupNames = @( 21 | "AZ-Persona-CA-BreakGlassAccounts;secure" 22 | "AZ-Persona-CA-Admins;secure" 23 | "AZ-Persona-CA-AzureServiceAccounts;secure" 24 | "AZ-Persona-CA-Exclude-Block LegacyAuth;secure" 25 | "AZ-Persona-CA-Global-BaseProtection-Exclusions;secure" 26 | "AZ-Persona-CA-Internals;normal" 27 | ) 28 | $Date = Get-Date -Format "yyyy.MM.dd" 29 | #endregion Variables 30 | 31 | #region connect 32 | Connect-MgGraph -Scopes "Directory.Read.All", "Group.ReadWrite.All", "RoleManagement.ReadWrite.Directory" 33 | #Install-Module Microsoft.Graph.Beta.Groups 34 | Import-Module Microsoft.Graph.Beta.Groups 35 | #endregion connect 36 | 37 | #region script 38 | # Create groups in Tenant if not existing 39 | foreach ($Group in $GroupNames) { 40 | # Create description 41 | $GroupName = $Group.Split(";")[0] 42 | $GroupSecurity = $Group.Split(";")[1] 43 | if ($GroupSecurity -like "secure") { $IsAssignableToRole = $true } 44 | else { $IsAssignableToRole = $false } 45 | 46 | $GroupDescription = $GroupName.Split("-")[-1] 47 | $GroupType = $GroupName.Split("-")[1] 48 | if ($GroupType -like "Persona") { 49 | $Description = "This group is used to assign user account to the CA Persona: $GroupDescription - $Date, CloudWay." 50 | } 51 | elseif ($GroupType -like "APP") { 52 | $Description = "This group is used to assign user account to CA APP Deviation for the app $GroupDescription - $Date, CloudWay." 53 | } 54 | $MailNickName = $grouptype + "." + (($GroupDescription).Replace(" ", "")).Replace("(Hybrid)", "Hybrid") 55 | 56 | # Find group, create it if not existing 57 | 58 | if ($ExistingGroup = Get-MgBetaGroup -Filter "DisplayName eq '$($GroupName)'") { 59 | Write-Output """$GroupName"" exists" 60 | } 61 | else { 62 | Write-Output """$GroupName"" does not exist. Will be created now as $GroupSecurity." 63 | $NewGroup = New-MgBetaGroup -DisplayName $GroupName -Description $Description -MailEnabled:$false -MailNickName $MailNickName -SecurityEnabled -IsAssignableToRole:$IsAssignableToRole 64 | } 65 | } 66 | #endregion script -------------------------------------------------------------------------------- /Microsoft/AAD/AAD-CA-Apps.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script to register missing Azure Enterprise Applications. 4 | .DESCRIPTION 5 | Script to register missing Azure Enterprise Applications for use in CA policies. 6 | .EXAMPLE 7 | 8 | .NOTES 9 | Version: 1.2 10 | Author: CloudWay, Simon Skotheimsvik 11 | Info: https://cloudway.com 12 | Creation Date: 23.05.2023 13 | Version history: 14 | 1.0.0 - (23.05.2023) Script released 15 | 1.1.0 - (29.06.2023) Script updated with Universal Store Service APIs and Web Application 16 | 1.2.0 - (12.02.2024) Convert to Microsoft Graph PowerShell SDK V2 module, Simon Skotheimsvik 17 | 1.2.1 - (11.08.2025) Added the My Staff application ID, Simon Skotheimsvik 18 | 1.2.2 - (10.09.2025) Added Azure Credential Configuration Endpoint Service application ID, Simon Skotheimsvik 19 | 1.2.3 - (24.10.2025) Added Resco MobileCRM application ID, Simon Skotheimsvik 20 | #> 21 | 22 | #region Variables 23 | # Insert APP IDs for Enterprise Applications to be registered 24 | $APPIDs = @( 25 | "9cdead84-a844-4324-93f2-b2e6bb768d07" # Azure Virtual Desktop 26 | "0af06dc6-e4b5-4f28-818e-e78e62d137a5" # Windows 365 27 | "d4ebce55-015a-49b5-a083-c84d1797ae8c" # Microsoft Intune Enrollment 28 | "45a330b1-b1ec-4cc1-9161-9f03992aa49f" # Windows Store for Business 29 | "a4a365df-50f1-4397-bc59-1a1564b8bb9c" # Microsoft Remote Desktop 30 | "ba9ff945-a723-4ab5-a977-bd8c9044fe61" # My Staff 31 | "ea890292-c8c8-4433-b5ea-b09d0668e1a6" # Azure Credential Configuration Endpoint Service. Source: https://nathanmcnulty.com/blog/2025/09/improving-passkey-registration-experiences/ 32 | "a116bf70-75fe-41c2-9f9f-7f3d0faff4bb" # Resco MobileCRM 33 | ) 34 | #endregion Variables 35 | 36 | #region connect 37 | # disconnect-mggraph 38 | Connect-MgGraph -Scopes "Application.ReadWrite.All" 39 | Import-Module Microsoft.Graph.Beta.Applications 40 | #endregion connect 41 | 42 | #region script 43 | # Register Enterprise applications 44 | foreach ($ID in $APPIDs) { 45 | # Finds Enterprise Applications, register it if not existing 46 | if ($ExistingApp = Get-MgBetaServicePrincipal -all | Where-Object { $_.AppId -like $ID }) { 47 | $DisplayName = $ExistingApp.DisplayName 48 | Write-Output "App with ID ""$ID"" exists as $DisplayName." 49 | } 50 | else { 51 | Write-Output "App with ID ""$ID"" does not exist. Will be created now." 52 | $ServicePrincipalID = @{ 53 | "AppId" = "$ID" 54 | } 55 | New-MgBetaServicePrincipal -BodyParameter $ServicePrincipalId | Format-List id, DisplayName, AppId, SignInAudience 56 | } 57 | } 58 | #endregion script 59 | 60 | 61 | #Investigate sign-in logs for a specific app 62 | $events = Get-MgBetaAuditLogSignIn -Filter "conditionalAccessAudiences/any(i:i eq 'ea890292-c8c8-4433-b5ea-b09d0668e1a6')" -All 63 | $events | Select-Object appId,appDisplayName,clientAppUsed,resourceDisplayName,resourceId,servicePrincipalId | Group-Object AppDisplayName | Format-List -------------------------------------------------------------------------------- /Microsoft/Intune/PR013 - Uninstall N-able Client/Uninstall N-able Client - Remediation - alternative.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Remediation to uninstall N-Able client 4 | 5 | .DESCRIPTION 6 | This script searches for the N-Able client in the registry and uninstalls it if found. 7 | 8 | .NOTES 9 | Author: Simon Skotheimsvik 10 | Version: 1.0.0 - 2025-08-12, Simon Skotheimsvik, Initial version 11 | 12 | license: This script is provided as-is, without warranty of any kind. Use at your own risk. 13 | You may modify and redistribute this script as long as you retain this notice in the code. 14 | #> 15 | 16 | 17 | # Define log file path in the IME extension logs folder 18 | $imeLogFolder = "C:\ProgramData\Microsoft\IntuneManagementExtension\Logs" 19 | if (-not (Test-Path $imeLogFolder)) { 20 | New-Item -Path $imeLogFolder -ItemType Directory -Force | Out-Null 21 | } 22 | 23 | # Function to log messages 24 | function Write-Log { 25 | param([string]$Message) 26 | $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 27 | Add-Content -Path $logFile -Value "$timestamp $Message" 28 | } 29 | 30 | # variables 31 | $name = "Patch Management Service Controller" 32 | $publisher = "N-able" 33 | $logFile = Join-Path $imeLogFolder "uninstall_log_$($name).log" 34 | 35 | # Define registry paths to search 36 | $registryPaths = @( 37 | "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall", 38 | "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" 39 | ) 40 | 41 | foreach ($path in $registryPaths) { 42 | Get-ChildItem -Path $path | ForEach-Object { 43 | $displayName = (Get-ItemProperty -Path $_.PSPath -ErrorAction SilentlyContinue).DisplayName 44 | $publisherName = (Get-ItemProperty -Path $_.PSPath -ErrorAction SilentlyContinue).Publisher 45 | $QuietUninstallString = (Get-ItemProperty -Path $_.PSPath -ErrorAction SilentlyContinue).QuietUninstallString 46 | if ($displayName -eq $name -and $publisherName -eq $publisher) { 47 | Write-Log "Found key: $($_.Name)" 48 | # Extract the GUID from the registry key name 49 | if ($_.Name -match '{[A-F0-9\-]+}') { 50 | $guid = $matches[0] 51 | Write-Log "Starting uninstall of $($name) from $($publisherName) with guid $($guid) using $($QuietUninstallString)." 52 | Write-Output "Starting uninstall of $($name) from $($publisherName) with guid $($guid) using $($QuietUninstallString)." 53 | # Start-Process -FilePath "msiexec.exe" -ArgumentList "/X$($guid) /qn" -Wait 54 | # Hardcoding the uninstall string found in registry 55 | Start-Process "C:\Program Files (x86)\MspPlatform\PME\unins000.exe" -ArgumentList "/SILENT" -Wait 56 | Write-Output "$($name) from $($publisherName) with guid $($guid) uninstalled using $($QuietUninstallString)." 57 | Exit 1 58 | 59 | } else { 60 | Write-Log "No GUID found in key: $($_.Name)" 61 | Write-Output "No GUID found for $($name) from $($publisherName). Cannot uninstall." 62 | Exit 0 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Microsoft/Exchange/RUNBOOK-SetExchangeCalendarPermissions.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Name: RUNBOOK-SetExchangeCalendarPermissions.ps1 4 | Author: Alexander Holmeset, https://alexholmeset.blog, https://x.com/AlexHolmeset 5 | Contributor: Simon Skotheimsvik, http://skotheimsvik.no, https://x.com/SSkotheimsvik 6 | Versions: 1.0 - 2017.10.30 - Simon Skotheimsvik, initial version using ExchangeOnline module 7 | 1.1 - 2024.06.12 - Alexander Holmeset, updated to use Microsoft Graph PowerShell SDK 8 | 1.2 - 2025.04.08 - Simon Skotheimsvik, Bugfix for users with more than 10 defined calender permissions 9 | 10 | .DESCRIPTION 11 | This script sets the default calendar permissions for all users in a Microsoft 365 organization to LimitedRead. 12 | #> 13 | 14 | .DESCRIPTION 15 | This script sets the default calendar permissions for all users in a Microsoft 365 organization to LimitedRead. 16 | #> 17 | 18 | # Set the permission level to be set on the calendars. 19 | # Options can be found here: https://learn.microsoft.com/en-us/graph/api/resources/calendarpermission?view=graph-rest-1.0#calendarroletype-values 20 | $Permission = "limitedRead" 21 | 22 | # Connects to Microsoft Graph with the specified scopes 23 | Connect-MgGraph -Identity 24 | 25 | # Generates a list of all licensed users in the Microsoft 365 organization with a mailbox 26 | $users = Get-MgUser -All -Property "id", "AssignedLicenses", "UserPrincipalName", "Mail" | Where-Object { $_.AssignedLicenses.Count -gt 0 -and $_.Mail -ne $null} 27 | #$users = Get-MgUser -All -Property "id", "AssignedLicenses", "UserPrincipalName", "Mail" | Where-Object { $_.AssignedLicenses.Count -gt 0 -and $_.Mail -ne $null -and $_.UserPrincipalName -eq "per.skeid@elinett.no"} 28 | 29 | # Sets default access to LimitedRead for all calendars in each user's mailbox 30 | foreach ($user in $users) { 31 | 32 | # Prints the user currently in focus 33 | Write-Output "User in focus = $($user.userprincipalname)" 34 | 35 | # Initializes the variables to store calendar permissions 36 | $CalenderPermissions = @() 37 | $CalenderPermissions = Get-MgUserCalendarPermission -All -UserId $user.id 38 | 39 | # If the user has any calendar permissions, update them 40 | if ($CalenderPermissions) { 41 | $CalenderPermissionsMyOrg = @() 42 | $CalenderPermissionsMyOrg = $CalenderPermissions | Where-Object { $_.EmailAddress.Name -eq "My Organization" } 43 | # Information about the number of calendar permissions for the user 44 | Write-Output "- Calendar permissions: $($CalenderPermissions.count)" 45 | Write-Output "- Calendar permissions MyOrg: $($CalenderPermissionsMyOrg.count)" 46 | 47 | # Updates the calendar permissions for the user 48 | if ($CalenderPermissionsMyOrg.Role -ne $Permission) { 49 | Write-Warning "- Changing MyOrg-permission on calendar for $($user.userprincipalname) from $($CalenderPermissionsMyOrg.Role) to $($Permission)." 50 | Update-MgUserCalendarPermission -UserId $user.id -Role $Permission -CalendarPermissionId $CalenderPermissionsMyOrg.id 51 | } 52 | # If the permission is already set, print a message 53 | else { 54 | Write-Output "- MyOrg-permission already set to $($Permission) on calendar for $($user.userprincipalname)." 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Microsoft/Intune/PR013 - Uninstall N-able Client/Uninstall N-able Client - Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Remediation to uninstall N-Able client 4 | 5 | .DESCRIPTION 6 | This script searches for the N-Able client in the registry and uninstalls it if found. 7 | 8 | .NOTES 9 | Author: Simon Skotheimsvik 10 | Version: 1.0.0 - 2025-08-12, Simon Skotheimsvik, Initial version 11 | Version: 1.0.1 - 2025-08-12, Simon Skotheimsvik, Changed application to uninstall. Extended logging. 12 | Version: 1.0.2 - 2025-08-12, Simon Skotheimsvik, Fixed error handling and exit codes. 13 | Version: 1.0.3 - 2025-08-12, Simon Skotheimsvik, Calling msiexec directly to ensure exit code is returned. 14 | 15 | license: This script is provided as-is, without warranty of any kind. Use at your own risk. 16 | You may modify and redistribute this script as long as you retain this notice in the code. 17 | #> 18 | 19 | 20 | # Define log file path in the IME extension logs folder 21 | $imeLogFolder = "C:\ProgramData\Microsoft\IntuneManagementExtension\Logs" 22 | if (-not (Test-Path $imeLogFolder)) { 23 | New-Item -Path $imeLogFolder -ItemType Directory -Force | Out-Null 24 | } 25 | 26 | # Function to log messages 27 | function Write-Log { 28 | param([string]$Message) 29 | $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 30 | Add-Content -Path $logFile -Value "$timestamp $Message" 31 | } 32 | 33 | # variables 34 | $name = "Windows Agent" 35 | $publisher = "N-able Technologies" 36 | $logFile = Join-Path $imeLogFolder "uninstall_log_$($name).log" 37 | $datetime = Get-Date -Format "yyyy-MM-dd HH:mm" 38 | 39 | # Define registry paths to search 40 | $registryPaths = @( 41 | "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall", 42 | "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" 43 | ) 44 | 45 | foreach ($path in $registryPaths) { 46 | Get-ChildItem -Path $path -ErrorAction SilentlyContinue | ForEach-Object { 47 | $itemProps = Get-ItemProperty -Path $_.PSPath -ErrorAction SilentlyContinue 48 | if ($itemProps.DisplayName -eq $name -and $itemProps.Publisher -eq $publisher) { 49 | Write-Log "Found key: $($_.Name)" 50 | 51 | if ($_.Name -match '{[A-F0-9\-]+}') { 52 | $guid = $matches[0] 53 | Write-Log "Attempting uninstall: $name ($guid) from $publisher." 54 | 55 | # Run uninstall 56 | # Start-Process -FilePath "msiexec.exe" -ArgumentList "/X$guid /qn" -Wait 57 | & "msiexec.exe" /X$guid /qn 58 | $exitCode = $LASTEXITCODE 59 | 60 | if ($exitCode -eq 0) { 61 | Write-Log "Uninstall successful for $name ($guid)." 62 | Write-Output "$name uninstalled successfully." 63 | $success = $true 64 | } else { 65 | Write-Log "Uninstall FAILED for $name ($guid). MSI exit code: $exitCode" 66 | Write-Output "Failed to uninstall $name. MSI exit code: $exitCode" 67 | Exit 1 68 | } 69 | } 70 | else { 71 | Write-Log "No GUID found in registry key: $($_.Name)" 72 | Write-Output "Cannot uninstall $name - GUID not found." 73 | # Exit 1 74 | } 75 | } 76 | } 77 | } 78 | 79 | if (-not $success) { 80 | Write-Log "Application not found. Nothing to uninstall." 81 | Write-Output "$name not found. Nothing to do." 82 | } 83 | 84 | Write-Log "=== Script finished at $(Get-Date -Format 'yyyy-MM-dd HH:mm') ===" 85 | Exit 0 -------------------------------------------------------------------------------- /Microsoft/AAD/AAD-Users-ImportAttributesFromCSV.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 28.09.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: AAD-Users-ImportAttributesFromCSV.ps1 7 | Instructions: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will import information about user accounts in AAD from CSV 12 | #> 13 | 14 | # Install and import the Microsoft Graph module 15 | #Install-Module -Name Microsoft.Graph 16 | Import-Module Microsoft.Graph.Users 17 | 18 | # Authenticate interactively (remember to aka.ms/pim first) 19 | Connect-MgGraph -Scopes "User.ReadWrite.All" 20 | 21 | # Define the CSV file path 22 | $csvFilePath = "c:\temp\EntraID-Users-Attributes.csv" 23 | 24 | # Read the CSV file 25 | $csvUsers = Import-Csv -Path $csvFilePath -Delimiter ";" 26 | 27 | # Attributes to check and update 28 | $attributesToUpdate = @("GivenName", "Surname", "JobTitle", "Department", "CompanyName", "MobilePhone", "OfficeLocation", "PostalCode", "City", "Country", "UsageLocation", "Manager", "Id") 29 | 30 | # Iterate through each user in the CSV and update Azure AD if needed 31 | foreach ($csvUser in $csvUsers) { 32 | $userPrincipalName = $csvUser.UserPrincipalName 33 | write-host "Focus on $userPrincipalName" 34 | 35 | # Retrieve the Azure AD user with all necessary attributes 36 | $azureADUser = Get-MgUser -Filter "userPrincipalName eq '$userPrincipalName'" -Property $attributesToUpdate 37 | 38 | if ($azureADUser) { 39 | # Get the user's Id 40 | $userId = $azureADUser.Id 41 | 42 | # Compare and update attributes 43 | $NoUpdatesForUser = $true 44 | 45 | foreach ($attribute in $attributesToUpdate) { 46 | if ($csvUser.$attribute -ne "") { # Check if the CSV value is not empty 47 | if ($azureADUser.$attribute -ne $csvUser.$attribute) { 48 | if ($attribute -eq "Manager") { 49 | # Retrieve the manager's user object to get the manager's Id 50 | $managerUser = Get-MgUser -Filter "userPrincipalName eq '$($csvUser.$attribute)'" -Property Id 51 | $NewManager = @{ 52 | "@odata.id"="https://graph.microsoft.com/v1.0/users/$($managerUser.Id)" 53 | } 54 | 55 | Set-MgUserManagerByRef -UserId $UserId -BodyParameter $NewManager 56 | Write-Host "Updated $attribute for user: $userPrincipalName to $($csvUser.$attribute)" -ForegroundColor DarkYellow 57 | } 58 | else { 59 | # All other attributes than the manager 60 | $attributeValue = $csvUser.$attribute 61 | $params = @{ 62 | "UserId" = $userId 63 | } 64 | $params[$attribute] = $attributeValue 65 | Update-MgUser @params 66 | Write-Host "Updated $attribute for user: $userPrincipalName to $attributeValue" -ForegroundColor DarkYellow 67 | } 68 | $NoUpdatesForUser = $false 69 | } 70 | } 71 | } 72 | 73 | if ($NoUpdatesForUser -eq $true) { 74 | Write-Host "No updates for user: $userPrincipalName" -ForegroundColor Green 75 | } 76 | 77 | } 78 | else { 79 | Write-Host "User not found in Azure AD: $userPrincipalName" -ForegroundColor Cyan 80 | } 81 | } 82 | 83 | # Display a message indicating the update is complete 84 | Write-Host "Userlist processing complete." -ForegroundColor Blue 85 | 86 | # https://learn.microsoft.com/en-us/answers/questions/1245345/how-can-i-prevent-getting-prompted-multiple-times 87 | #Disconnect-MgGraph 88 | #Remove-Item "$env:USERPROFILE\.graph" -Recurse -Force -------------------------------------------------------------------------------- /Microsoft/AAD/EntraID - Authenticate Using Certificates and App Registrations.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script holds the code for setting up and using self-signed certificates for authentication with Microsoft Entra ID (Azure AD) and Microsoft Graph API. 4 | 5 | .DESCRIPTION 6 | The script holds two main sections: 7 | 1. Setting up self-signed certificates, including creating, exporting, and importing them. 8 | 2. Authenticating to Microsoft Graph using the created certificates and executing a sample workload. 9 | 10 | The script is designed to be run in a PowerShell environment with the necessary permissions and modules installed, such as the Microsoft Graph PowerShell SDK. 11 | It is important to replace placeholder values like `$Password`, `$tenantID`, and `$clientID` with actual values before running the script. 12 | The script is structured to be modular, allowing for easy adjustments and reuse in different environments or scenarios. 13 | The script is intended to be run manually as an example of how to authenticate using certificates in PowerShell. 14 | 15 | .NOTES 16 | Author: Simon Skotheimsvik 17 | Version: 1.0.0 - 2025-08-04, Simon Skotheimsvik, Initial version 18 | 19 | license: This script is provided as-is, without warranty of any kind. Use at your own risk. 20 | You may modify and redistribute this script as long as you retain this notice in the code. 21 | #> 22 | 23 | 24 | ###################################### 25 | ### SET UP THE CERTIFICATES ### 26 | ###################################### 27 | 28 | #region Variables 29 | $CertificateName = "GraphAutomationCert" 30 | $CertificatePath = "C:\Temp\GraphAutomationCert" 31 | $Password = "YourStrongPassword" 32 | $CertificateType = "LocalMachine" # Options: "CurrentUser" or "LocalMachine" 33 | #endregion 34 | 35 | #region Create Self-Signed Certificate on the server 36 | $LifeTimeMonths = "18" 37 | $cert = New-SelfSignedCertificate -Subject "CN=$CertificateName" -CertStoreLocation "Cert:\$CertificateType\My" -KeyExportPolicy Exportable -KeySpec Signature -NotAfter (Get-Date).AddMonths($LifeTimeMonths) 38 | #endregion 39 | 40 | #region Export Self-Signed Certificate for the Entra ID App 41 | Write-Host "Exporting certificate to: $CertificatePath.cer" -ForegroundColor Green 42 | Export-Certificate -Cert $cert -FilePath "$CertificatePath.cer" 43 | #endregion 44 | 45 | #region Backup/Export Self-Signed Certificate with Private Key for use on other computer 46 | Write-Host "Exporting certificate to: $CertificatePath.pfx" -ForegroundColor Green 47 | $pwd = ConvertTo-SecureString -String $Password -Force -AsPlainText 48 | Export-PfxCertificate -Cert $cert -FilePath "$CertificatePath.pfx" -Password $pwd 49 | #endregion 50 | 51 | #region Restore/Import Self-Signed Certificate 52 | $certPath = "$CertificatePath.pfx" 53 | $certPassword = ConvertTo-SecureString -String $Password -Force -AsPlainText 54 | 55 | $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 56 | $cert.Import($certPath, $certPassword, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable) 57 | #endregion 58 | 59 | 60 | ###################################### 61 | ### USE THE CERTIFICATES ### 62 | ###################################### 63 | 64 | #region Authenticate Using Certificate 65 | # Variables 66 | $tenantId = "11111111-2222-3333-4444-555555555555" 67 | $clientId = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" 68 | $CertThumbprint = (Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Subject -like "*CN=GraphAutomationMachineCert*" }).Thumbprint 69 | 70 | # Load the certificate from the store 71 | $cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Thumbprint -eq $CertThumbprint } 72 | 73 | # Connect to Microsoft Graph 74 | Write-Host "Connecting to Graph using certificate with thumbprint: $($cert.Thumbprint)" -ForegroundColor Yellow 75 | Connect-MgGraph -ClientId $clientId -TenantId $tenantId -Certificate $cert -NoWelcome 76 | #endregion 77 | 78 | #region PS Workload 79 | Write-Host "Executing Microsoft Graph workload..." -ForegroundColor Green 80 | Get-MgUser -Top 5 81 | #endregion -------------------------------------------------------------------------------- /Microsoft/Intune/Get-IntuneCorporateDeviceIdentiferDevices.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script to get devices registered with Corporate Device Identifiers. 4 | .DESCRIPTION 5 | This script retrieves and summarizes information about devices registered with corporate device identifiers in Microsoft Intune. 6 | There is also a section to compare these devices with Autopilot registered devices to find potential conflicts. 7 | .EXAMPLE 8 | 9 | .NOTES 10 | Author: Simon Skotheimsvik 11 | Contact: skotheimsvik.no 12 | Version history: 13 | 1.0.0 - (09.09.2025) Script released, Simon Skotheimsvik 14 | 1.0.1 - (10.09.2025) Added section to compare with Autopilot devices, Simon Skotheimsvik 15 | 16 | #> 17 | 18 | 19 | # Install required module if not present 20 | if (-not (Get-Module Microsoft.Graph.Beta.DeviceManagement.Enrollment -ListAvailable)) { 21 | Install-Module Microsoft.Graph.Beta.DeviceManagement.Enrollment -Force 22 | } 23 | 24 | Import-Module-Module Microsoft.Graph.Beta.DeviceManagement.Enrollment 25 | 26 | 27 | # Connect to Microsoft Graph Beta if not already connected 28 | if (-not (Get-MgContext)) { 29 | Connect-MgGraph -Scopes "DeviceManagementServiceConfig.Read.All" 30 | } 31 | 32 | #region Query Corporate Device Identifiers 33 | # Get all imported device identities (Autopilot devices) from Beta endpoint 34 | $importedDevices = Get-MgBetaDeviceManagementImportedDeviceIdentity -All 35 | 36 | # Output relevant identifiers (e.g., Id, SerialNumber, ProductKey, HardwareIdentifier) 37 | $importedDevices | Select-Object ImportedDeviceIdentifier, CreatedDateTime, LastContactedDateTime, EnrollmentState 38 | 39 | $deviceCount = $importedDevices.Count 40 | $enrolledCount = ($importedDevices | Where-Object { $_.EnrollmentState -eq "enrolled" }).Count 41 | $notContactedCount = ($importedDevices | Where-Object { $_.EnrollmentState -eq "notContacted" }).Count 42 | 43 | Write-Host "Total imported devices: $deviceCount" 44 | Write-Host "Enrolled devices: $enrolledCount" 45 | Write-Host "Not contacted devices: $notContactedCount" 46 | 47 | $createdDates = $importedDevices | Select-Object -ExpandProperty CreatedDateTime | Sort-Object 48 | if ($createdDates.Count -gt 0) { 49 | Write-Host "First CreatedDateTime: $($createdDates[0])" 50 | Write-Host "Last CreatedDateTime: $($createdDates[-1])" 51 | } else { 52 | Write-Host "No devices found." 53 | } 54 | #endregion Query Corporate Device Identifiers 55 | 56 | 57 | #region Find devices also registered as Autopilot devices 58 | # Get all Autopilot devices 59 | $autopilotDevices = Get-MgDeviceManagementWindowsAutopilotDeviceIdentity -All 60 | 61 | # Create a lookup table for Autopilot serial numbers 62 | $autopilotSerials = $autopilotDevices | Select-Object -ExpandProperty SerialNumber 63 | 64 | # Check which imported devices are also registered as Autopilot devices, and get GroupTag if available 65 | $CompareAutopilotAndCorporateDeviceIdentifier = $importedDevices | ForEach-Object { 66 | $parts = $_.ImportedDeviceIdentifier -split ',' 67 | $serial = $parts[2] 68 | $isAutopilot = $autopilotSerials -contains $serial 69 | 70 | # Try to find matching Autopilot device for GroupTag 71 | $autopilotDevice = $autopilotDevices | Where-Object { $_.SerialNumber -eq $serial } | Select-Object -First 1 72 | 73 | [PSCustomObject]@{ 74 | DevicePrepImportedDeviceIdentifier = $_.ImportedDeviceIdentifier 75 | DevicePrepSerialNumber = $serial 76 | DevicePrepCreatedDateTime = $_.CreatedDateTime 77 | DevicePrepEnrollmentState = $_.EnrollmentState 78 | IsAutopilotDevice = $isAutopilot 79 | GroupTag = $autopilotDevice.GroupTag 80 | LastContactedDateTime = $autopilotDevice.LastContactedDateTime 81 | Manufacturer = $autopilotDevice.Manufacturer 82 | Model = $autopilotDevice.Model 83 | } 84 | } 85 | 86 | 87 | $CompareAutopilotAndCorporateDeviceIdentifier| Where-Object { $_.IsAutopilotDevice -eq $true } | Out-GridView -Title "Corporate Device Identifiers also registered as Autopilot devices" 88 | #endregion Find devices also registered as Autopilot devices -------------------------------------------------------------------------------- /Microsoft/AD/ImportADResourceDetailsFromCSV.ps1: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # # 3 | # Script made for setting new variables in Active Directory # 4 | # # 5 | ############################################################################### 6 | # # 7 | # 2016.10.25 - Simon Skotheimsvik # 8 | # # 9 | ############################################################################### 10 | 11 | 12 | #Import the Active Directory Module 13 | Import-Module ac* 14 | 15 | $path = "C:\Source\Scripts" 16 | $importfile = "2023-07-06-ExportADResourcesToCsv-Users-updated.csv" 17 | $fullpath = $path + "\" + $importfile 18 | write-host "- Importing Users from "$fullpath 19 | 20 | $testinputfile = test-path $fullpath 21 | 22 | if ($testinputfile -eq $false) { 23 | write-host "- $fullpath is not found please create this file before continuing" -foregroundcolor red -backgroundcolor black 24 | exit 0 25 | } 26 | 27 | $users = $null 28 | $users = import-csv $fullpath 29 | 30 | if ($users -eq $null) { 31 | write-host "- No Users Found in Input File" -foregroundcolor red -backgroundcolor black 32 | exit 0 33 | } 34 | else { 35 | $count = $users | Measure-Object 36 | $count = $count.count 37 | write-host "- We have found " $count "Users to import" 38 | } 39 | 40 | ############################################################################### 41 | # CSV file with users found - Processing users # 42 | ############################################################################### 43 | 44 | write-host "- Processing Users.....`n" 45 | $index = 1 46 | 47 | Foreach ($user in $users) { 48 | 49 | write-host "- Processing User " $index " of " $count -foregroundcolor Yellow -backgroundcolor Black 50 | # Decalaring User Variables from CSV file 51 | $givenName = $user.givenName 52 | $surName = $user.surName 53 | $displayName = $user.Name 54 | $SamAccountName = $user.SamAccountName 55 | $EmailAddress = ($user.EmailAddress).ToLower() 56 | if ($user.EmailAddress -ne "N/A") { $EmailAddress = $user.EmailAddress } else { $EmailAddress = $null } 57 | if ($user.Company -ne "N/A") { $company = $user.Company } else { $company = $null } 58 | if ($user.Department -ne "N/A") { $department = $user.Department } else { $department = $null } 59 | if ($user.Title -ne "N/A") { $title = $user.Title } else { $title = $null } 60 | if ($user.Office -ne "N/A") { $Office = $user.Office } else { $Office = $null } 61 | if ($user.Office -eq "Dublin") { 62 | $c = "IE" 63 | $CO = "Ireland" 64 | $countryCode = "372" 65 | $l = "Dublin" 66 | } else { 67 | $c = $null 68 | $CO = $null 69 | $countryCode = $null 70 | $l = $null 71 | } 72 | if ($user.physicalDeliveryOfficeName -ne "N/A") { $physicalDeliveryOfficeName = $user.physicalDeliveryOfficeName } else { $physicalDeliveryOfficeName = $null } 73 | if ($user.manager -ne "N/A") { $manager = $user.manager } else { $manager = $null } 74 | 75 | 76 | write-host "- Testing if $SamAccountName exists in AD" 77 | $adexist = get-aduser -identity $SamAccountName 78 | if ($adexist -ne $null) { 79 | write-host `t "- User" $SamAccountName "exists in AD" 80 | write-host `t "- Setting user variables $givenName, $surName, $displayName, $EmailAddress, $company, $department, $title, $Office, $manager " 81 | # set-aduser -identity $SamAccountName -givenName $givenName -surName $surName -DisplayName $displayName -EmailAddress $EmailAddress -company $company -Department $department -Title $title -Office $Office -Manager $manager #-Replace @{manager = $manager} 82 | set-aduser -identity $SamAccountName -givenName $givenName -surName $surName -DisplayName $displayName -company $company -Department $department -Title $title -Office $Office -Manager $manager -Replace @{c = $c;CO = $CO;countryCode = $countryCode;l = $l} 83 | 84 | } 85 | $index++ 86 | } 87 | 88 | 89 | -------------------------------------------------------------------------------- /Microsoft/Intune/WPS008 - TeamViewer Monitor/TeamViewerMonitor-EventTrigger.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script adds a scheduled task which checks if TeamViewer has terminated and starts the process again. 4 | 5 | .DESCRIPTION 6 | This script adds a scheduled task which checks for Event ID 4689 in the Security log. If the event is triggered by TeamViewer.exe, the script starts the TeamViewer process again. 7 | The scheduled task is set to run as the current user to ensure TeamViewer Host is running as the user. 8 | 9 | .NOTES 10 | Author: Simon Skotheimsvik 11 | Filename: TeamViewerMonitor-EventTrigger.ps1 12 | Info: https://skotheimsvik.no 13 | Versions: 14 | 1.0.0 - 11.09.2024 - Initial Release, Simon Skotheimsvik 15 | 1.0.1 - 12.09.2024 - Changed to Event Trigger, Simon Skotheimsvik 16 | #> 17 | 18 | #region Variables 19 | $taskName = "TeamViewerMonitor" 20 | $tempPath = [System.IO.Path]::GetTempPath() 21 | $xmlFilePath = "$tempPath" + "TeamViewerMonitor.xml" 22 | $installDate = Get-Date -Format "yyyy-MM-dd" 23 | #endregion 24 | 25 | #region Create XML Content 26 | # Create the XML content for the scheduled task 27 | $xmlContent = @" 28 | 29 | 30 | 31 | 2024-09-11T23:45:36.3491732 32 | Simon Does: skotheimsvik.no 33 | This is a routine from SimonDoes to ensure TeamViewer Host is running on the system. `nInstalled on $installDate by Intune. 34 | \Event Viewer Tasks\Security_Microsoft-Windows-Security-Auditing_4689_$($taskName) 35 | 36 | 37 | 38 | true 39 | <QueryList><Query Id="0" Path="Security"><Select Path="Security">*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and (EventID=4689)]]and 40 | *[EventData[(Data='C:\Program Files (x86)\TeamViewer\TeamViewer.exe')]]</Select></Query></QueryList> 41 | 42 | 43 | 44 | 45 | S-1-5-32-545 46 | LeastPrivilege 47 | 48 | 49 | 50 | IgnoreNew 51 | true 52 | true 53 | true 54 | false 55 | false 56 | 57 | true 58 | false 59 | 60 | true 61 | true 62 | false 63 | false 64 | false 65 | PT72H 66 | 7 67 | 68 | 69 | 70 | TeamViewer.exe 71 | C:\Program Files (x86)\TeamViewer\ 72 | 73 | 74 | 75 | "@ 76 | #endregion 77 | 78 | #region Write XML Content to File 79 | # Write the XML content to the file 80 | Set-Content -Path $xmlFilePath -Value $xmlContent 81 | #endregion 82 | 83 | #region Register Scheduled Task from XML 84 | # Check if the scheduled task exists and delete it if it does 85 | $taskExists = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue 86 | 87 | if ($taskExists) { 88 | Unregister-ScheduledTask -TaskName $taskName -Confirm:$false 89 | Write-Output "Scheduled task '$taskName' deleted." 90 | } 91 | 92 | # Register the scheduled task from the XML file 93 | Register-ScheduledTask -TaskName $taskName -Xml (Get-Content -Path $xmlFilePath -Raw) 94 | 95 | Write-Output "Scheduled task '$taskName' created successfully from XML." 96 | #endregion 97 | 98 | #region Clean Up 99 | # Remove the XML file after the task is registered 100 | Remove-Item -Path $xmlFilePath -Force 101 | Write-Output "Temporary XML file '$xmlFilePath' deleted." 102 | #endregion -------------------------------------------------------------------------------- /Microsoft/Intune/aa-intune-autopatch-group-automation-runbook.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script separates Windows devices based on their operating system version and adds them to respective Entra ID groups. 4 | 5 | .DESCRIPTION 6 | This script retrieves Windows devices from Intune, separates them into Windows 10 and Windows 11 devices based on their OS version, and adds them to the corresponding Entra ID groups. 7 | 8 | .NOTES 9 | Author: Simon Skotheimsvik 10 | Version: 11 | 1.0.0 - 2024-09-23 - Initial release, Simon Skotheimsvik 12 | 1.0.1 - 2024-09-24 - Added the ability to run the script in Azure Automation using managed identity, Simon Skotheimsvik 13 | #> 14 | 15 | # Define the Entra ID group names 16 | $windows10GroupName = "Windows 10 Autopatch - Device Registration" 17 | $windows11GroupName = "Windows 11 Autopatch - Device Registration" 18 | 19 | # Import the module 20 | $RequiredModules = @('Microsoft.Graph.Authentication', 'Microsoft.Graph.DeviceManagement', 'Microsoft.Graph.Groups', 'Microsoft.Graph.Identity.DirectoryManagement') 21 | foreach ($RequiredModule in $RequiredModules) { 22 | try { 23 | Write-Host "Importing $RequiredModule" 24 | Import-Module $RequiredModule -Force 25 | } 26 | catch { 27 | Write-Error -Exception $_.Exception.Message 28 | } 29 | } 30 | 31 | # Authenticate to Microsoft Graph interactively 32 | # Connect-MgGraph -Scopes "DeviceManagementManagedDevices.Read.All", "GroupMember.ReadWrite.All", "Device.Read.All" -NoWelcome 33 | 34 | # Authenticate to Microsoft Graph using managed identity in Runbook 35 | Connect-MgGraph -Identity -NoWelcome 36 | 37 | # Get group IDs based on group names 38 | $windows10Group = Get-MgGroup -Filter "DisplayName eq '$windows10GroupName'" 39 | $windows11Group = Get-MgGroup -Filter "DisplayName eq '$windows11GroupName'" 40 | 41 | $windows10GroupId = $windows10Group.Id 42 | $windows11GroupId = $windows11Group.Id 43 | 44 | # Initialize arrays 45 | $windows10Devices = @() 46 | $windows11Devices = @() 47 | 48 | # Get all Windows devices from Intune 49 | $devices = Get-MgDeviceManagementManagedDevice | Where-Object { $_.OperatingSystem -like "Windows*" } 50 | 51 | # Separate devices based on OS version 52 | foreach ($device in $devices) { 53 | if ($device.OSVersion -lt "10.0.22000") { 54 | $windows10Devices += $device 55 | } 56 | elseif ($device.OSVersion -ge "10.0.22000") { 57 | $windows11Devices += $device 58 | } 59 | } 60 | 61 | # Retrieve all current group members 62 | $windows10GroupMembers = @() 63 | $windows11GroupMembers = @() 64 | $windows10GroupMembers = Get-MgGroupMember -GroupId $windows10GroupId -All | Select-Object -ExpandProperty Id 65 | $windows11GroupMembers = Get-MgGroupMember -GroupId $windows11GroupId -All | Select-Object -ExpandProperty Id 66 | 67 | # Add devices to respective Entra ID groups 68 | 69 | # Add Windows 11 devices to the Windows 11 group 70 | foreach ($device in $windows11Devices) { 71 | $deviceId = (Get-MgDevice -Filter "DeviceID eq '$($device.AzureAdDeviceId)'").id 72 | if ($windows11GroupMembers -notcontains $deviceId) { 73 | try { 74 | New-MgGroupMember -GroupId $windows11GroupId -DirectoryObjectId $deviceId 75 | Write-Warning "Added $($device.DeviceName) to Windows 11 group." 76 | } 77 | catch { 78 | Write-Error "Failed to add $($device.DeviceName) to Windows 11 group: $_" 79 | } 80 | } 81 | else { 82 | Write-Output "$($device.DeviceName) is already a member of the Windows 11 group." 83 | } 84 | } 85 | 86 | # Loop through Windows 10 devices and check if it is not part of the Win10 or Win11 group, then add them to the Windows 10 group 87 | # If the device is part of the Windows 11 group, remove it from the Windows 10 group. 88 | # This can happen if devices are moved manually between groups in Entra ID to do autopatching. 89 | foreach ($device in $windows10Devices) { 90 | $deviceId = (Get-MgDevice -Filter "DeviceID eq '$($device.AzureAdDeviceId)'").id 91 | if ($windows10GroupMembers -notcontains $deviceId -and $windows11GroupMembers -notcontains $deviceId) { 92 | try { 93 | New-MgGroupMember -GroupId $windows10GroupId -DirectoryObjectId $deviceId 94 | Write-Warning "Added $($device.DeviceName) to Windows 10 group." 95 | } 96 | catch { 97 | Write-Error "Failed to add $($device.DeviceName) to Windows 10 group: $_" 98 | } 99 | } 100 | elseif ($windows11GroupMembers -contains $deviceId -and $windows10GroupMembers -contains $deviceId) { 101 | try { 102 | write-host $deviceId 103 | Remove-MgGroupMemberByRef -GroupId $windows10GroupId -DirectoryObjectId $deviceId 104 | Write-Warning "Removed $($device.DeviceName) from Windows 10 group as it is a member of the Windows 11 group." 105 | } 106 | catch { 107 | Write-Error "Failed to remove $($device.DeviceName) from Windows 10 group: $_" 108 | } 109 | } 110 | else { 111 | Write-Output "$($device.DeviceName) is already a member of the Windows 10 or Windows 11 group." 112 | } 113 | } -------------------------------------------------------------------------------- /Microsoft/AAD/Add-AADLicenseGroups.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script to create Azure AD License groups 4 | .DESCRIPTION 5 | Script to create Azure AD License groups for available SKUs used for assigning licenses to users in the tenant. 6 | .EXAMPLE 7 | 8 | .NOTES 9 | Version: 1.1 10 | Author: Simon Skotheimsvik 11 | Contact: 12 | Version history: 13 | 1.0 - (08.11.2022) Script released 14 | 1.1 - (07.07.2023) Adding users with existing licenses to the groups 15 | 1.2 - (13.02.2023) Convert to Microsoft Graph PowerShell SDK V2 module, Simon Skotheimsvik 16 | 1.3 - (05.09.2025) Fix for UTF8 BOM issue when importing CSV from Microsoft, Simon Skotheimsvik 17 | 18 | #> 19 | 20 | #region functions 21 | function RenameDisplayName { 22 | Param ( 23 | [Parameter(Mandatory = $true)] 24 | [string]$DisplayName 25 | ) 26 | foreach ($Translation in $DisplayNameTranslations.GetEnumerator()) { 27 | $DisplayName = $DisplayName.Replace($Translation.Name, $Translation.Value) 28 | } 29 | $DisplayName 30 | }# end function 31 | #endregion functions 32 | 33 | #region Variables 34 | # Insert SKUs not manageable by groups: Get-MgBetaSubscribedSku | Where-Object { $_.SkuPartNumber -notin $SKUsNotToManage } 35 | $SKUsNotToManage = @( 36 | "WINDOWS_STORE" 37 | "RMSBASIC" 38 | "MICROSOFT_REMOTE_ASSIST" 39 | "AAD_PREMIUM_P2_FACULTY" 40 | "Dynamics_365_Guides_vTrial" 41 | "FLOW_FREE" 42 | "WIN10_ENT_A5_FAC" 43 | "STREAM" 44 | ) 45 | 46 | # Insert translations used to shorten group names 47 | $DisplayNameTranslations = @{ 48 | "Azure Active Directory" = 'AAD' 49 | "Microsoft 365" = 'M365' 50 | "Office 365" = 'O365' 51 | "Enterprise Mobility + Security" = 'EMS' 52 | "Windows 365" = 'W365' 53 | } 54 | 55 | # Import Microsoft CSV file with friendly display name and SKU Partnumber, https://learn.microsoft.com/en-us/entra/identity/users/licensing-service-plan-reference 56 | $licenseCsvURL = 'https://download.microsoft.com/download/e/3/e/e3e9faf2-f28b-490a-9ada-c6089a1fc5b0/Product%20names%20and%20service%20plan%20identifiers%20for%20licensing.csv' 57 | $response = Invoke-WebRequest -Uri $licenseCsvURL 58 | 59 | # Decode the byte array as UTF8 text 60 | $csvText = [System.Text.Encoding]::UTF8.GetString($response.Content) 61 | 62 | # Strip BOM if present 63 | $csvText = $csvText -replace '^\uFEFF','' 64 | 65 | # Now parse CSV 66 | $csv = $csvText | ConvertFrom-Csv 67 | 68 | # Build your hashtable 69 | $skuHashTable = @{} 70 | $csv | ForEach-Object { 71 | $skuHashTable[$_.String_Id] = @{ 72 | "SkuId" = $_.GUID 73 | "SkuPartNumber" = $_.String_Id 74 | "DisplayName" = $_.Product_Display_Name 75 | } 76 | } 77 | 78 | #endregion Variables 79 | 80 | #region connect 81 | Connect-MgGraph -Scopes "Directory.Read.All", "Group.ReadWrite.All" 82 | Import-Module Microsoft.Graph.Beta.Identity.DirectoryManagement 83 | Import-Module Microsoft.Graph.Users 84 | Import-Module Microsoft.Graph.Beta.Groups 85 | #endregion connect 86 | 87 | #region script 88 | 89 | # Gather all SKUs in Tenant 90 | $SKUsToManage = Get-MgBetaSubscribedSku | Where-Object { $_.SkuPartNumber -notin $SKUsNotToManage } 91 | 92 | # Create groups for SKUs in Tenant 93 | foreach ($SKU in $SKUsToManage) { 94 | # Get friendly shortened GroupDisplayName 95 | $SKUpartno = $SKU.SkuPartNumber 96 | $GroupDisplayName = RenameDisplayName -DisplayName "AZ-LIC-$(($skuHashTable["$SKUpartno"]).DisplayName)" 97 | 98 | # Finds group, create it if not existing 99 | if ($Group = Get-MgBetaGroup -All | Where-Object { $_.DisplayName -like $GroupDisplayName }) { 100 | Write-Output """$GroupDisplayName"" exists" 101 | } 102 | else { 103 | Write-Output """$GroupDisplayName"" does not exist" 104 | $Group = New-MgBetaGroup -DisplayName $GroupDisplayName -Description "This group is used to assign $(($skuHashTable["$SKUpartno"]).DisplayName) licenses." -MailEnabled:$false -SecurityEnabled -MailNickName ($GroupDisplayName).Replace(" ", "") #-IsAssignableToRole:$true 105 | } 106 | 107 | # Add license to group 108 | $params = @{ 109 | AddLicenses = @( 110 | @{ 111 | SkuId = $Sku.SkuId 112 | } 113 | ) 114 | RemoveLicenses = @( 115 | ) 116 | } 117 | Set-MgBetaGroupLicense -GroupId $Group.Id -BodyParameter $params 118 | 119 | #Get all users with the specified license 120 | Write-Output "Getting all users with the $(($skuHashTable["$SKUpartno"]).DisplayName) license" 121 | $users = Get-MgUser -All | Where-Object { $_.AssignedLicenses.SkuId -contains $($Sku.SkuId) } 122 | 123 | # Add users to the Azure security group 124 | Write-Output "Adding $($users.count) users to the $($GroupDisplayName) group" 125 | foreach ($user in $users) { 126 | $params = @{ 127 | "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/{$($user.Id)}" 128 | } 129 | 130 | New-MgBetaGroupMemberByRef -GroupId $Group.Id -BodyParameter $params 131 | } 132 | } 133 | #endregion script -------------------------------------------------------------------------------- /Microsoft/Intune/Intune-ChangeOfComputerNames-Runbook-ManagedIdentity.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 09.05.2022 5 | Modified on: 25.05.2023 6 | Created by: Simon Skotheimsvik 7 | Filename: Intune-ChangeOfComputerNames-Runbook-ManagedIdentity.ps1 8 | Instructions: https://skotheimsvik.no/rename-computers-with-countrycode-in-intune 9 | =========================================================================== 10 | 11 | .DESCRIPTION 12 | This script uses the Graph API to bulk rename Windows devices. It can 13 | be used in a scenario where autopilot default naming has been used and 14 | a new standardised naming convention has been agreed upon. 15 | This Script will use the Country Code from the owning users Azure Account. 16 | It can be modified to use other user variables as well. 17 | 18 | The script is designed to run unattended in an Azure Runbook. 19 | #> 20 | 21 | #region Variables 22 | $GLOBAL:DebugPreference = "Continue" 23 | $TenantId = Get-AutomationVariable -Name 'CWS-ComputerNamingStandards-TenantId' 24 | 25 | $Countries = @{ 26 | Norway = "NO" 27 | Vietnam = "VN" 28 | Brazil = "BR" 29 | Chile = "CL" 30 | Croatia = "HR" 31 | India = "IN" 32 | Italy = "IT" 33 | Poland = "PL" 34 | Romania = "RO" 35 | Singapore = "SG" 36 | Canada = "CA" 37 | "United States" = "US" 38 | } 39 | #endregion Variables 40 | 41 | #region Connect to Graph with Managed Identity Token 42 | Connect-AzAccount -Identity -ErrorAction Stop | Out-Null 43 | 44 | $token = (Get-AzAccessToken -ResourceTypeName MSGraph).token # Get-PnPAccessToken 45 | $Headers = @{ 46 | "Content-Type" = "application/json" 47 | Authorization = "Bearer $token" 48 | } 49 | 50 | write-output "Authentication finished" 51 | #endregion Connect 52 | 53 | #region Routine for renaming users Autopilot devices 54 | foreach ($Country in $Countries.keys) { 55 | write-output "Working on country $Country" 56 | $ISO3166Alpha2CountryCode = $($Countries[$Country]) 57 | $MaxSerialLength = (15 - $ISO3166Alpha2CountryCode.get_Length()) - 1 #Max 15 characters allowed in devicename. Calculate length of serial# part. 58 | $userList = $Null 59 | 60 | # Get all users with the current country code. Use paging in order to get more than 999 which is max pr query 61 | $UsersURL = 'https://graph.microsoft.com/v1.0/users?$filter=startswith(country,''' + $Country + ''')&$top=999' 62 | While ($UsersURL -ne $Null) { 63 | $data = (Invoke-WebRequest -Headers $Headers -Uri $UsersURL -UseBasicParsing) | ConvertFrom-Json 64 | $userList += $data.Value 65 | $UsersURL = $data.'@Odata.NextLink' 66 | } 67 | 68 | # Get all managed devices for each user 69 | foreach ($User in $UserList) { 70 | $upn = $User.userPrincipalName 71 | write-output "- Focus on user $upn" 72 | $DeviceList = $Null 73 | $deviceURL = 'https://graph.microsoft.com/v1.0/users/' + $User.userPrincipalName + '/managedDevices?$filter=startswith(operatingSystem,''Windows'')' 74 | $DeviceList = (Invoke-RestMethod -Uri $deviceURL -Headers $Headers).value 75 | $NoOfDevices = $DeviceList.Count 76 | write-output "- $NoOfDevices device(s) found" 77 | 78 | foreach ($Device in $DeviceList) { 79 | $CurrentDeviceName = $Device.deviceName 80 | write-output "--- Focus on device $CurrentDeviceName" 81 | $OS = $Device.operatingSystem 82 | $DeviceID = $Device.id 83 | $FullSerial = $Device.serialNumber 84 | 85 | # Max 15 characters allowed in devicename - Some devices have to long serialnumber 86 | if ($FullSerial.get_Length() -gt $MaxSerialLength) { 87 | $DeviceSerial = $FullSerial.substring($FullSerial.get_Length() - $MaxSerialLength) 88 | write-output "---- Serial too long - shortened!" 89 | } 90 | else { 91 | $DeviceSerial = $FullSerial 92 | } 93 | # Calculates new devicename in format NO-12345678 94 | $CalculatedDeviceName = $ISO3166Alpha2CountryCode.ToUpper() + '-' + $DeviceSerial 95 | 96 | # Virtual computers have the text "SerialNumber" as serialnumber... 97 | if (($CurrentDeviceName -ne $CalculatedDeviceName) -and ($DeviceSerial -ne "SerialNumber")) { 98 | write-warning "---- Device $CurrentDeviceName will be renamed to $CalculatedDeviceName" 99 | $URI = "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$DeviceID/setDeviceName" 100 | $JSONPayload = @{ 101 | "deviceName" = $CalculatedDeviceName 102 | } 103 | $convertedJSONPayLoad = $JSONPayload | ConvertTo-Json 104 | 105 | Invoke-RestMethod -Uri $URI -Method POST -Body $convertedJSONPayLoad -Headers $Headers 106 | } 107 | else { 108 | write-output "---- $CurrentDeviceName will not be renamed" 109 | } 110 | } 111 | } 112 | } 113 | #endregion Routine -------------------------------------------------------------------------------- /Microsoft/AD/ChangeADUserUPNandMail.ps1: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # # 3 | # Script made for setting new UPN, SIP and Mail addresses in Active Directory # 4 | # Main purpose is to get a clean AD before introducing O365 integration # 5 | # https://github.com/SimonSkotheimsvik/Community-By-SSkotheimsvik # 6 | # # 7 | ############################################################################### 8 | # # 9 | # Tested on customer running AD with DirSync integration on O365 # 10 | # 1. Export AD users to CSV and let customer manually build new UPN/SIP/Mail # 11 | # 2. Run this script with modified CSV to update users with new UPN/SIP/Mail # 12 | # # 13 | ############################################################################### 14 | # # 15 | # 2016.10.25 - Simon Skotheimsvik # 16 | # # 17 | ############################################################################### 18 | 19 | 20 | #Import the Active Directory Module 21 | Import-Module ac* 22 | 23 | $path = "C:\Source\Scripts" 24 | $importfile = "ChangeADUserUPNandMail.csv" 25 | $fullpath = $path + "\" + $importfile 26 | write-host "- Importing Users from "$fullpath 27 | 28 | 29 | $testinputfile = test-path $fullpath 30 | 31 | if ($testinputfile -eq $false) { 32 | write-host "- $fullpath is not found please create this file before continuing" -foregroundcolor red -backgroundcolor black 33 | exit 0 34 | } 35 | 36 | $users = $null 37 | $users = import-csv $fullpath 38 | 39 | if ($users -eq $null) { 40 | write-host "- No Users Found in Input File" -foregroundcolor red -backgroundcolor black 41 | exit 0 42 | } 43 | else { 44 | $count = $users.count 45 | write-host "- We have found " $count "Users to import" 46 | } 47 | 48 | ############################################################################### 49 | # CSV file with users found - Processing users # 50 | ############################################################################### 51 | 52 | write-host "- Processing Users.....`n" 53 | $index = 1 54 | 55 | Foreach ($user in $users) { 56 | 57 | write-host "- Processing User " $index " of " $count -foregroundcolor Yellow -backgroundcolor Black 58 | # Decalaring User Variables from CSV file 59 | $SamAccountName = $user.SamAccountName 60 | $EmailAddress = $user.EmailAddress 61 | $NewUPN = $user.NewUPN 62 | $NewUPNPrefix = $NewUPN.Split("@")[0] 63 | $NewUPNSuffix = "@" + $NewUPN.Split("@")[1] 64 | $NewSIP = "sip:" + $NewUPN 65 | $NewSMTPproxy = "SMTP:" + $NewUPN 66 | $oldSMTPproxy = "SMTP:" + $EmailAddress 67 | 68 | 69 | write-host "- Testing if $samaccountname exists in AD" 70 | $adexist = get-aduser -identity $SamAccountName 71 | if ($adexist -ne $null) { 72 | write-host `t"- User " $samaccountname " exists in AD" 73 | 74 | # Decalaring the new ProxyAddresses and add them to an Array 75 | $ProxyAddresses = Get-ADUser -Identity $SamAccountName -Properties ProxyAddresses | Select-Object ProxyAddresses -ExpandProperty ProxyAddresses 76 | $ProxyAddressesArray = @() #Declare the array 77 | 78 | Foreach ($Paddress in $ProxyAddresses) { 79 | # Finding Proxy Address Type 80 | $PaddressType = $Paddress.Split(":")[0] 81 | 82 | if ($PaddressType -eq "sip") { 83 | #Change to new SIP address 84 | $ProxyAddressesArray += $NewSIP 85 | } 86 | elseif ($PaddressType -clike "SMTP") { 87 | #Set old-Primary as secondary address if its not equal the New SMTPProxy 88 | if ($Paddress -ne $NewSMTPproxy) { 89 | $ProxyAddressesArray += $Paddress.tolower() 90 | } 91 | #Change to new primary address 92 | $ProxyAddressesArray += $NewSMTPproxy 93 | } 94 | elseif ($Paddress -clike $NewSMTPproxy.tolower()) { 95 | #Remove "new primary address" as a secondary address 96 | } 97 | else { 98 | $ProxyAddressesArray += $Paddress 99 | } 100 | } 101 | 102 | write-host "------------------------" 103 | 104 | write-host "Setting new ProxyAddresses" 105 | # remove old proxy addresses from AD 106 | set-aduser -identity $SamAccountName -Clear proxyAddresses 107 | # iterate array and add new proxyAddresses 108 | Foreach ($PaddressA in $ProxyAddressesArray) { 109 | set-aduser -identity $SamAccountName -Add @{proxyAddresses = "$PaddressA" } 110 | write-host `t $PaddressA 111 | } 112 | 113 | write-host "Setting new Mail Address " 114 | set-aduser -identity $SamAccountName -email $NewUPN 115 | write-host `t $NewUPN 116 | 117 | write-host "Setting new UPN" 118 | set-aduser -identity $SamAccountName -UserPrincipalName $NewUPN 119 | write-host `t $NewUPN 120 | #write-host `t`t $NewUPNPrefix 121 | #write-host `t`t $NewUPNSuffix 122 | 123 | write-host "Setting new SIP" 124 | set-aduser -identity $SamAccountName -replace @{'msRTCSIP-PrimaryUserAddress' = $NewSIP } 125 | write-host `t $NewSIP 126 | } 127 | $index++ 128 | } 129 | 130 | 131 | -------------------------------------------------------------------------------- /Microsoft/Intune/Group Tag Fixer/RB-Grouptag Fixer.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Created on: 01.06.2023 4 | Modified on: 12.01.2024 5 | Created by: Simon Skotheimsvik 6 | Info: https://skotheimsvik.no 7 | Version: 1.1.3 8 | 9 | .DESCRIPTION 10 | Automatic set Group tag on Autopilot devices based on computer name. 11 | Script designed for running in Azure Automation Account using managed identity.#> 12 | 13 | #region Variables 14 | $TenantId = Get-AutomationVariable -Name 'aa-no-grouptag-tenantid' 15 | 16 | $csvContent = @" 17 | DevicePrefix;GroupTag 18 | SE-;DeviceSE 19 | NO-;DeviceNO 20 | DK-;DeviceDK 21 | FI-;DeviceFI 22 | DE-;DeviceDE 23 | "@ 24 | 25 | $GroupTagInfo = $csvContent | ConvertFrom-Csv -delimiter ";" 26 | 27 | #endregion Variables 28 | 29 | #region Authentication 30 | Connect-MgGraph -Identity #-TenantId $TenantId 31 | write-output "Authentication finished" 32 | #endregion Authentication 33 | 34 | #region Device traversal 35 | foreach ($Group in $GroupTagInfo) { 36 | # Get variables from CSV for current group 37 | $DevicePrefix = $Group.DevicePrefix 38 | $DeviceTargetGroupTag = $Group.GroupTag 39 | 40 | # Search all Autopilot devices starting with Prefix and has Zero Touch Device ID (Autopilot devices) 41 | $Devices = Get-MgBetaDevice -Filter "startsWith(displayName, '$DevicePrefix')" -ConsistencyLevel eventual -All | Where-Object { $_.PhysicalIds -match '\[ZTDID\]' } 42 | 43 | if ($Devices.count -gt 0) { 44 | Write-Output "$($Devices.count) devices with prefix $DevicePrefix" 45 | 46 | # Iterate alle devices found where name starting with Prefix 47 | foreach ($Device in $Devices) { 48 | # Get variables from the Autopilot device 49 | $ReturnBodyTemp = New-Object -TypeName PSObject # New JSON object for logging 50 | $DeviceDisplayName = $Device.DisplayName 51 | $AutopilotZTDID = ($Device.PhysicalIds | Select-String -Pattern '\[ZTDID\]:(.*)').Matches.Groups[1].Value 52 | $ReturnBodyTemp | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value "$DeviceDisplayName" -Force # Add value for logging 53 | $ReturnBodyTemp | Add-Member -MemberType NoteProperty -Name "Target Group tag" -Value "$DeviceTargetGroupTag" -Force # Add value for logging 54 | $ReturnBodyTemp | Add-Member -MemberType NoteProperty -Name "ZTDID" -Value "$AutopilotZTDID" -Force # Add value for logging 55 | try { 56 | $DeviceGroupTag = ($Device.PhysicalIds | Select-String -Pattern '\[OrderId\]:(.*)').Matches.Groups[1].Value 57 | $ReturnBodyTemp | Add-Member -MemberType NoteProperty -Name "OrderId" -Value "$DeviceGroupTag" -Force # Add value for logging 58 | } 59 | catch { 60 | Write-Error "$($DeviceDisplayName) is missing grouptag." 61 | $ReturnBodyTemp | Add-Member -MemberType NoteProperty -Name "OrderId" -Value "-" -Force # Add value for logging 62 | $DeviceGroupTag = "" 63 | } 64 | 65 | # Check if current Group tag is Ok 66 | if ($DeviceTargetGroupTag -ne $DeviceGroupTag) { 67 | Write-Warning "$($DeviceDisplayName) has grouptag ""$($DeviceGroupTag)"" not matching target grouptag ""$($DeviceTargetGroupTag)"". Device will be updated." 68 | $ReturnBodyTemp | Add-Member -MemberType NoteProperty -Name "Action" -Value "Wrong Group tag" -Force # Add value for logging 69 | 70 | # Update Autopilot device with new Group tag 71 | $params = @{ 72 | groupTag = $DeviceTargetGroupTag 73 | } 74 | try { 75 | Update-MgBetaDeviceManagementWindowsAutopilotDeviceIdentityDeviceProperty -WindowsAutopilotDeviceIdentityId $AutopilotZTDID -BodyParameter $params 76 | $ReturnBodyTemp | Add-Member -MemberType NoteProperty -Name "Status" -Value "Succeed" -Force # Add value for logging 77 | write-warning "Device $($DeviceDisplayName), Group tag set to $($DeviceTargetGroupTag)" 78 | } 79 | catch { 80 | $ReturnBodyTemp | Add-Member -MemberType NoteProperty -Name "Status" -Value "Failed" -Force # Add value for logging 81 | write-warning "Device $($DeviceDisplayName), Failed setting Group tag to $($DeviceTargetGroupTag)" 82 | } 83 | 84 | } 85 | else { 86 | Write-Output "$($DeviceDisplayName) has grouptag ""$($DeviceGroupTag)"". No change required." 87 | $ReturnBodyTemp | Add-Member -MemberType NoteProperty -Name "Action" -Value "OK Group tag" -Force # Add value for logging 88 | 89 | } 90 | 91 | #write output logs in JSON format 92 | $LogOutputMsg = $ReturnBodyTemp | ConvertTo-Json 93 | Write-Output $LogOutputMsg 94 | 95 | $Device = $null 96 | $DeviceDisplayName = $null 97 | $DeviceGroupTag = $null 98 | $AutopilotZTDID = $null 99 | } 100 | } 101 | else { 102 | Write-Output "$($Devices.count) devices with prefix $DevicePrefix" 103 | } 104 | $Devices = $null 105 | } 106 | #endregion Device traversal -------------------------------------------------------------------------------- /Microsoft/Intune/Intune-ChangeOfComputerNames-Runbook.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 09.05.2022 5 | Created by: Simon Skotheimsvik 6 | Filename: Intune-ChangeOfComputerNames-Runbook.ps1 7 | Instructions: https://skotheimsvik.no/rename-computers-with-countrycode-in-intune 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script uses the Graph API to bulk rename Windows devices. It can for 12 | example be used in a scenario where autopilot default naming has been used 13 | and a new standardised naming convention has been agreed upon. This Script 14 | will use the Country Code from the owning users Azure Account. It can be 15 | modified to use other user variables as well. 16 | 17 | The script is designed to run unattended in an Azure Runbook. 18 | #> 19 | 20 | $GLOBAL:DebugPreference = "Continue" 21 | 22 | $Countries = @{ 23 | Norway = "NO" 24 | Vietnam = "VN" 25 | Brazil = "BR" 26 | Chile = "CL" 27 | Croatia = "HR" 28 | India = "IN" 29 | Italy = "IT" 30 | Poland = "PL" 31 | Romania = "RO" 32 | Singapore = "SG" 33 | Canada = "CA" 34 | } 35 | 36 | # CONNECT TO GRAPH WITH AZURE APP-REGISTRATION 37 | $TenantId = Get-AutomationVariable -Name 'Computer_Rename_TenantID' 38 | $ClientId = Get-AutomationVariable -Name 'Computer_Rename_ClientID' 39 | $ClientSecret = Get-AutomationVariable -Name 'Computer_Rename_ClientSecret' 40 | 41 | # Create a hashtable for the body, the data needed for the token request 42 | # The variables used are explained above 43 | $Body = @{ 44 | 'tenant' = $TenantId 45 | 'client_id' = $ClientId 46 | 'scope' = 'https://graph.microsoft.com/.default' 47 | 'client_secret' = $ClientSecret 48 | 'grant_type' = 'client_credentials' 49 | } 50 | 51 | # Assemble a hashtable for splatting parameters, for readability 52 | # The tenant id is used in the uri of the request as well as the body 53 | $Params = @{ 54 | 'Uri' = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" 55 | 'Method' = 'Post' 56 | 'Body' = $Body 57 | 'ContentType' = 'application/x-www-form-urlencoded' 58 | } 59 | 60 | $AuthResponse = Invoke-RestMethod @Params 61 | 62 | $Headers = @{ 63 | 'Authorization' = "Bearer $($AuthResponse.access_token)" 64 | } 65 | 66 | # Connect-MgGraph with Token in order to be able to post a computer renaming 67 | $connection = Invoke-RestMethod ` 68 | -Uri https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token ` 69 | -Method POST ` 70 | -Body $body 71 | 72 | $token = $connection.access_token 73 | Connect-MgGraph -AccessToken $token 74 | 75 | write-output "Authentication finished" 76 | 77 | ############################################################ 78 | # ROUTINE FOR RENAMING USERS AUTOPILOT DEVICES 79 | ############################################################ 80 | 81 | foreach ($CountryCode in $Countries.keys) { 82 | write-output "Working on country $CountryCode" 83 | $Country = $CountryCode 84 | $CountryCode = $($Countries[$Country]) 85 | $MaxSerialLength = (15 - $CountryCode.get_Length()) - 1 #Max 15 characters allowed in devicename. Calculate length of serial# part. 86 | $userList = $Null 87 | 88 | # Get all users with the current country code. Use paging in order to get more than 999 which is max pr query 89 | $UsersURL = 'https://graph.microsoft.com/v1.0/users?$filter=startswith(country,''' + $Country + ''')&$top=999' 90 | While ($UsersURL -ne $Null) { 91 | $data = (Invoke-WebRequest -Headers $Headers -Uri $UsersURL -UseBasicParsing) | ConvertFrom-Json 92 | $userList += $data.Value 93 | $UsersURL = $data.'@Odata.NextLink' 94 | } 95 | 96 | # Get all managed devices for each user 97 | foreach ($User in $UserList) { 98 | $upn = $User.userPrincipalName 99 | write-output "- Focus on user $upn" 100 | $DeviceList = $Null 101 | $deviceURL = 'https://graph.microsoft.com/v1.0/users/' + $User.userPrincipalName + '/managedDevices?$filter=startswith(operatingSystem,''Windows'')' 102 | $DeviceList = (Invoke-RestMethod -Uri $deviceURL -Headers $Headers).value 103 | $NoOfDevices = $DeviceList.Count 104 | write-output "- $NoOfDevices device(s) found" 105 | 106 | foreach ($Device in $DeviceList) { 107 | $CurrentDeviceName = $Device.deviceName 108 | write-output "--- Focus on device $CurrentDeviceName" 109 | $OS = $Device.operatingSystem 110 | $DeviceID = $Device.id 111 | $FullSerial = $Device.serialNumber 112 | 113 | # Max 15 characters allowed in devicename - Some devices have to long serialnumber 114 | if ($FullSerial.get_Length() -gt $MaxSerialLength) { 115 | $DeviceSerial = $FullSerial.substring($FullSerial.get_Length() - $MaxSerialLength) 116 | write-output "---- Serial too long - shortened!" 117 | } 118 | else { 119 | $DeviceSerial = $FullSerial 120 | } 121 | # Calculates new devicename in format NO-12345678 122 | $CalculatedDeviceName = $CountryCode.ToUpper() + '-' + $DeviceSerial 123 | 124 | # Virtual computers have the text "SerialNumber" as serialnumber... 125 | if (($CurrentDeviceName -ne $CalculatedDeviceName) -and ($DeviceSerial -ne "SerialNumber")) { 126 | write-warning "---- Device $CurrentDeviceName needs to be renamed to $CalculatedDeviceName" 127 | # Calculate graph api url's 128 | $Resource = "deviceManagement/managedDevices/$DeviceID/setDeviceName" 129 | $GraphApiVersion = "beta" 130 | $URI = "https://graph.microsoft.com/$GraphApiVersion/$($Resource)" 131 | 132 | $JSONPayload = @{ 133 | "deviceName" = $CalculatedDeviceName 134 | } 135 | 136 | $convertedJSONPayLoad = $JSONPayload | ConvertTo-Json 137 | 138 | #Send change to Graph. 139 | Invoke-MgGraphRequest -Uri $URI -Method POST -Body $convertedJSONPayLoad -Verbose -ErrorAction Continue 140 | } 141 | else { 142 | write-output "---- $CurrentDeviceName will not be renamed" 143 | } 144 | } 145 | } 146 | } 147 | 148 | -------------------------------------------------------------------------------- /Microsoft/Intune/Intune-CheckDeviceEnrolledBy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script is used to check the health state for user principal names and enrolled-by user principal names for Windows devices in Intune. 4 | 5 | .DESCRIPTION 6 | Connects to Microsoft Graph, queries Windows devices for primary user and enrolled by user. 7 | Several alternative ways of reporting on the data. 8 | 9 | .NOTES 10 | Author: Simon Skotheimsvik 11 | Versions: 12 | 1.0.0 - 2025.08.11 - Initial version, Simon Skotheimsvik 13 | 1.0.1 - 2025.08.28 - Better routine for prerequisites, Simon Skotheimsvik 14 | 1.0.2 - 2025.09.03 - Added progress bar, Simon Skotheimsvik 15 | 1.0.3 - 2025.09.09 - Added enrollmentProfileName to the report to find DEM accounts used with Autopilot, Simon Skotheimsvik 16 | Copyright: (c) 2025 Simon Skotheimsvik 17 | License: MIT 18 | #> 19 | 20 | #region prerequisites 21 | try { 22 | Import-Module Microsoft.Graph.DeviceManagement -ErrorAction Stop 23 | Import-Module Microsoft.Graph.Authentication -ErrorAction Stop 24 | Write-Host "Required modules imported successfully" -ForegroundColor Green 25 | } 26 | catch { 27 | Write-Host "Failed to import required modules. Please install Microsoft.Graph.DeviceManagement module." -ForegroundColor Red 28 | Write-Host "Run: Install-Module Microsoft.Graph.DeviceManagement -Force" -ForegroundColor Yellow 29 | exit 1 30 | } 31 | #endregion 32 | 33 | #region Connect to Microsoft Graph 34 | if (-not (Get-MgContext)) { 35 | Connect-MgGraph -Scopes "DeviceManagementManagedDevices.Read.All" 36 | } 37 | #endregion 38 | 39 | #region Query Devices 40 | # Step 1: Get all Windows devices 41 | $allWindowsDevices = Get-MgDeviceManagementManagedDevice -Filter "operatingSystem eq 'Windows'" -All 42 | # Step 2: Filter locally for Entra ID joined devices (can be heavy in some tenants filtering locally) 43 | $devices = $allWindowsDevices | Where-Object { $_.deviceEnrollmentType -eq 'windowsAzureADJoin' } 44 | #endregion 45 | 46 | #region Prepare output array 47 | $deviceReport = @() 48 | 49 | $deviceReport = foreach ($device in $devices) { 50 | $deviceId = $device.Id 51 | 52 | # Progress bar 53 | $currentIndex = [array]::IndexOf($devices, $device) + 1 54 | $percentComplete = [int](($currentIndex / $devices.Count) * 100) 55 | Write-Progress -Activity "Processing devices" -Status "$currentIndex of $($devices.Count)" -PercentComplete $percentComplete 56 | 57 | # Query full device object by ID 58 | # Heavy to run in large environments, but had to be done to get full details 59 | $uri = "https://graph.microsoft.com/beta/deviceManagement/managedDevices('$deviceId')?`$select=deviceName,userPrincipalName,enrolledByUserPrincipalName,enrollmentProfileName" 60 | $details = Invoke-MgGraphRequest -Method GET -Uri $uri -OutputType PSObject 61 | 62 | # Compare UPNs 63 | $mismatch = if ($details.userPrincipalName -ne $details.enrolledByUserPrincipalName) { "Yes" } else { "No" } 64 | 65 | # Add to report 66 | [PSCustomObject]@{ 67 | DeviceName = $details.deviceName 68 | UserPrincipalName = $details.userPrincipalName 69 | EnrolledByUserPrincipalName = $details.enrolledByUserPrincipalName 70 | EnrollmentProfileName = $details.enrollmentProfileName 71 | Mismatch = $mismatch 72 | } 73 | } 74 | Write-Progress -Activity "Processing devices" -Completed 75 | Write-Host "Number of devices processed: $($deviceReport.Count)" -ForegroundColor Cyan 76 | #endregion 77 | 78 | #region Display results 79 | $TotalDevices = $devices.Count 80 | # Show all devices 81 | $deviceReport | Out-GridView -Title "Device Report" 82 | 83 | # Show only mismatched entries 84 | $deviceCount = ($deviceReport | Where-Object { $_.Mismatch -eq "Yes" }).Count 85 | $deviceReport | Where-Object { $_.Mismatch -eq "Yes" } | Out-GridView -Title "Mismatch on $($deviceCount) of $($TotalDevices) devices" 86 | 87 | # Show only devices enrolled by john.doe@contoso.com 88 | $EnrollmentUser = "john.doe@contoso.com" 89 | $deviceReport | Where-Object { $_.EnrolledByUserPrincipalName -eq $EnrollmentUser } | Format-Table -AutoSize 90 | $deviceReport | Where-Object { $_.EnrolledByUserPrincipalName -eq $EnrollmentUser } | Measure 91 | 92 | # Show only devices where primary user is john.doe@contoso.com 93 | $UserPrincipalName = "john.doe@contoso.com" 94 | $deviceReport | Where-Object { $_.UserPrincipalName -eq $UserPrincipalName } | Format-Table -AutoSize 95 | $deviceReport | Where-Object { $_.UserPrincipalName -eq $UserPrincipalName } | Measure 96 | 97 | # Show details on named device 98 | $deviceReport | Where-Object { $_.DeviceName -eq "PC-123456789Z" } | Format-Table -AutoSize 99 | 100 | # Count number of devices enrolled by each user 101 | $deviceReport | 102 | Group-Object -Property EnrolledByUserPrincipalName | 103 | Sort-Object -Property Count -Descending | 104 | Select-Object Name, Count | 105 | Out-GridView -Title "Device count by EnrolledByUserPrincipalName" 106 | 107 | # Count number of devices enrolled by each user, include the computernames and show users with multiple devices enrolled 108 | $deviceReport | 109 | Group-Object -Property EnrolledByUserPrincipalName | 110 | Where-Object { $_.Count -gt 1 } | 111 | Sort-Object -Property Count -Descending | 112 | ForEach-Object { 113 | [PSCustomObject]@{ 114 | EnrolledByUser = $_.Name 115 | DeviceCount = $_.Count 116 | DeviceNames = ($_.Group | Select-Object -ExpandProperty DeviceName) -join ", " 117 | } 118 | } | Out-GridView -Title "Devices Enrolled by User" 119 | 120 | # Find all devices enrolled by a specific user, show enrollment profile name to find DEM accounts used with Autopilot 121 | $EnrollmentUser = "john.doe@contoso.com" 122 | $deviceReport | Where-Object { $_.EnrolledByUserPrincipalName -eq $EnrollmentUser } | Select-Object DeviceName, EnrolledByUserPrincipalName, EnrollmentProfileName | Out-GridView -Title "Devices enrolled by $EnrollmentUser with enrollment profile name" 123 | 124 | # Count number of devices by EnrollmentProfileName where EnrolledByUserPrincipalName is $EnrollmentUser 125 | $EnrollmentUser = "john.doe@contoso.com" 126 | $deviceReport | 127 | Where-Object { $_.EnrolledByUserPrincipalName -eq $EnrollmentUser } | 128 | Group-Object -Property EnrollmentProfileName | 129 | Sort-Object -Property Count -Descending | 130 | Select-Object Name, Count | 131 | Out-GridView -Title "Device count by EnrollmentProfileName for $EnrollmentUser" 132 | 133 | # Count number of devices by EnrollmentProfileName 134 | $deviceReport | 135 | Group-Object -Property EnrollmentProfileName | 136 | Sort-Object -Property Count -Descending | 137 | Select-Object Name, Count | 138 | Out-GridView -Title "Device count by EnrollmentProfileName" 139 | 140 | #endregion -------------------------------------------------------------------------------- /Microsoft/Intune/WPS008 - TeamViewer Monitor/TeamViewerMonitor-TimeTrigger.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script adds a scheduled task which checks if TeamViewer is running. If TeamViewer is not running, the script starts the TeamViewer process and logs an event to the Application Log. 4 | 5 | .DESCRIPTION 6 | This script adds a scheduled task which checks if TeamViewer Host is running. If TeamViewer is not running, the script starts the TeamViewer process and logs an event to the Application Log. 7 | The script utilizes the PSInvoker tool from MSEndpointMgr to run the PowerShell script as a scheduled task without having av PowerShell window displaying for the end user. 8 | The scheduled task is set to run at logon as the current user to ensure TeamViewer Host is running as the user. 9 | 10 | .NOTES 11 | Author: Simon Skotheimsvik 12 | Filename: TeamViewerMonitor-TimeTrigger.ps1 13 | Info: https://skotheimsvik.no 14 | Reference: https://github.com/MSEndpointMgr/PSInvoker/tree/master 15 | Versions: 16 | 1.0.0 - 11.09.2024 - Initial Release, Simon Skotheimsvik 17 | #> 18 | 19 | 20 | 21 | #region Variables 22 | $taskName = "TeamViewerMonitor" 23 | $SimonDoesPath = "C:\Program Files\SimonDoes" 24 | $xmlFilePath = "$SimonDoesPath\TeamViewerMonitor.xml" 25 | $scriptPath = "$SimonDoesPath\CheckStartAndLogTeamViewer.ps1" 26 | $zipFilePath = "$SimonDoesPath\PSInvoker.zip" 27 | $installDate = Get-Date -Format "yyyy-MM-dd" 28 | $Interval = 15 #Interval for scheduled task in minutes 29 | #endregion 30 | 31 | #region Download and Extract PSInvoker from MSEndpointMGR GitHub 32 | # Define the URL and the destination path for the ZIP file 33 | $zipUrl = "https://github.com/MSEndpointMgr/PSInvoker/releases/download/1.0.1/PSInvoker.zip" 34 | 35 | # Download the ZIP file 36 | Invoke-WebRequest -Uri $zipUrl -OutFile $zipFilePath 37 | 38 | # Ensure the script directory exists 39 | if (-not (Test-Path $SimonDoesPath)) { 40 | New-Item -Path $SimonDoesPath -ItemType Directory 41 | } 42 | 43 | # Extract the ZIP file to the script directory 44 | Expand-Archive -Path $zipFilePath -DestinationPath $SimonDoesPath -Force 45 | 46 | # Remove the ZIP file after extraction 47 | Remove-Item -Path $zipFilePath 48 | #endregion 49 | 50 | 51 | #region Create PowerShell Script 52 | # Create the PowerShell script to check and log TeamViewer status 53 | $scriptContent = @' 54 | $processName = "TeamViewer" 55 | $teamViewerPath = "C:\Program Files (x86)\TeamViewer\TeamViewer.exe" 56 | 57 | if (Test-Path $teamViewerPath) { 58 | $process = Get-Process -Name $processName -ErrorAction SilentlyContinue 59 | if ($process -eq $null) { 60 | # Log an event to the Application Log 61 | if (-not (Get-EventLog -LogName Application -Source "TeamViewerMonitor" -ErrorAction SilentlyContinue)) { 62 | New-EventLog -LogName Application -Source "TeamViewerMonitor" 63 | } 64 | Write-EventLog -LogName Application -Source "TeamViewerMonitor" -EventId 1001 -EntryType Information -Message "TeamViewer has stopped and is being restarted by TeamViewerMonitor from SimonDoes." 65 | 66 | # Start TeamViewer 67 | Start-Process $teamViewerPath 68 | } 69 | } else { 70 | # Log an event if TeamViewer is not installed 71 | if (-not (Get-EventLog -LogName Application -Source "TeamViewerMonitor" -ErrorAction SilentlyContinue)) { 72 | New-EventLog -LogName Application -Source "TeamViewerMonitor" 73 | } 74 | Write-EventLog -LogName Application -Source "TeamViewerMonitor" -EventId 1002 -EntryType Warning -Message "TeamViewer is not installed on this system." 75 | } 76 | '@ 77 | #endregion 78 | 79 | #region Ensure Script Directory Exists 80 | # Ensure the script directory exists 81 | if (-not (Test-Path "C:\Program Files\SimonDoes")) { 82 | New-Item -Path "C:\Program Files\SimonDoes" -ItemType Directory 83 | } 84 | #endregion 85 | 86 | #region Write Script Content to File 87 | # Write the script content to the file 88 | Set-Content -Path $scriptPath -Value $scriptContent 89 | #endregion 90 | 91 | #region Create XML Content 92 | # Create the XML content for the scheduled task 93 | $xmlContent = @" 94 | 95 | 96 | 97 | 5337D3DB-D8D3-4\WDAGUtilityAccount 98 | This is a routine from SimonDoes to ensure TeamViewer host is running on the system. 99 | Installed on $installDate by Intune. 100 | \TeamViewerMonitor 101 | 102 | 103 | 104 | 105 | PT$($Interval)M 106 | false 107 | 108 | true 109 | 110 | 111 | 112 | 113 | S-1-5-32-545 114 | LeastPrivilege 115 | 116 | 117 | 118 | IgnoreNew 119 | false 120 | false 121 | true 122 | true 123 | false 124 | 125 | true 126 | false 127 | 128 | true 129 | true 130 | false 131 | false 132 | false 133 | true 134 | false 135 | PT72H 136 | 7 137 | 138 | 139 | 140 | PSInvoker.exe 141 | CheckStartAndLogTeamViewer.ps1 --ExecutionPolicy Bypass 142 | $SimonDoesPath 143 | 144 | 145 | 146 | "@ 147 | #endregion 148 | 149 | #region Write XML Content to File 150 | # Write the XML content to the file 151 | Set-Content -Path $xmlFilePath -Value $xmlContent 152 | #endregion 153 | 154 | #region Register Scheduled Task from XML 155 | # Check if the scheduled task exists and delete it if it does 156 | $taskExists = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue 157 | 158 | if ($taskExists) { 159 | Unregister-ScheduledTask -TaskName $taskName -Confirm:$false 160 | Write-Output "Scheduled task '$taskName' deleted." 161 | } 162 | 163 | # Register the scheduled task from the XML file 164 | Register-ScheduledTask -TaskName $taskName -Xml (Get-Content -Path $xmlFilePath -Raw) 165 | 166 | Write-Output "Scheduled task '$taskName' created successfully from XML." 167 | #endregion -------------------------------------------------------------------------------- /Microsoft/AAD/TENANT-DomainCheck.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script to check domains in a Microsoft 365 tenant. 4 | .DESCRIPTION 5 | This script will check domains and DNS records for all domains in a Microsoft 365 tenant. 6 | MX, SPF, DKIM, DMARC, Autodiscover, Skype for Business, and other records will be checked. 7 | .EXAMPLE 8 | 9 | .NOTES 10 | Author: Simon Skotheimsvik 11 | Info: https://skotheimsvik.no 12 | Creation Date: 24.06.2024 13 | Version history: 14 | 1.0 - 24.06.2024 - Simon - Script released 15 | 16 | #> 17 | 18 | # Import the Microsoft Graph PowerShell module 19 | Import-Module Microsoft.Graph.Identity.DirectoryManagement 20 | 21 | # Connect to Microsoft Graph 22 | Connect-MgGraph -Scopes "Domain.Read.All" 23 | 24 | # Retrieve all domains from the Microsoft tenant 25 | $domains = Get-MgDomain 26 | 27 | # Function to check DNS records using Resolve-DnsName 28 | function Get-DnsRecords { 29 | param ( 30 | [string]$Domain, 31 | [string]$RecordType 32 | ) 33 | 34 | try { 35 | $dnsRecords = Resolve-DnsName -Name $Domain -Type $RecordType -ErrorAction Stop 36 | return $dnsRecords 37 | } 38 | catch { 39 | return $null 40 | } 41 | } 42 | 43 | # Function to check DNS records for MX, SPF, DKIM, DMARC, Autodiscover, Skype for Business, and other records 44 | function Check-DnsRecords { 45 | param ( 46 | [string]$Domain 47 | ) 48 | 49 | $result = [PSCustomObject]@{ 50 | Domain = $Domain 51 | MX = $null 52 | SPF = $null 53 | DKIMSelector1 = $null 54 | DKIMSelector2 = $null 55 | DMARC = $null 56 | Autodiscover = $null 57 | SIP = $null 58 | LyncDiscover = $null 59 | SipTls = $null 60 | SipFederationTlsTcp = $null 61 | EnterpriseRegistration = $null 62 | EnterpriseEnrollment = $null 63 | } 64 | 65 | # Check MX records 66 | $mxRecords = Get-DnsRecords -Domain $Domain -RecordType "MX" 67 | if ($mxRecords) { 68 | $result.MX = $mxRecords | ForEach-Object { $_.Exchange -join "," } 69 | } 70 | 71 | # Check SPF record 72 | $spfRecords = Get-DnsRecords -Domain $Domain -RecordType "TXT" 73 | if ($spfRecords) { 74 | foreach ($record in $spfRecords) { 75 | foreach ($string in $record.Strings) { 76 | if ($string -match "^v=spf1") { 77 | $result.SPF = $string 78 | break 79 | } 80 | } 81 | } 82 | } 83 | 84 | # Check DKIM records 85 | $dkimSelector1 = "selector1._domainkey.$Domain" 86 | $dkimSelector2 = "selector2._domainkey.$Domain" 87 | 88 | $dkimRecord1 = Get-DnsRecords -Domain $dkimSelector1 -RecordType "CNAME" 89 | $dkimRecord2 = Get-DnsRecords -Domain $dkimSelector2 -RecordType "CNAME" 90 | 91 | if ($dkimRecord1) { 92 | $result.DKIMSelector1 = $dkimRecord1.NameHost 93 | } 94 | if ($dkimRecord2) { 95 | $result.DKIMSelector2 = $dkimRecord2.NameHost 96 | } 97 | 98 | # Check DMARC record 99 | $dmarcRecords = Get-DnsRecords -Domain "_dmarc.$Domain" -RecordType "TXT" 100 | if ($dmarcRecords) { 101 | foreach ($record in $dmarcRecords) { 102 | foreach ($string in $record.Strings) { 103 | if ($string -match "^v=DMARC1") { 104 | $result.DMARC = $string 105 | break 106 | } 107 | } 108 | } 109 | } 110 | 111 | # Check Autodiscover CNAME 112 | $autodiscoverRecord = Get-DnsRecords -Domain "autodiscover.$Domain" -RecordType "CNAME" 113 | if ($autodiscoverRecord) { 114 | $result.Autodiscover = $autodiscoverRecord.NameHost 115 | } 116 | 117 | # Check Skype for Business CNAMEs 118 | $SIPRecord = Get-DnsRecords -Domain "sip.$Domain" -RecordType "CNAME" 119 | if ($SIPRecord) { 120 | $result.SIP = $SIPRecord.NameHost 121 | } 122 | 123 | $LyncDiscoverRecord = Get-DnsRecords -Domain "lyncdiscover.$Domain" -RecordType "CNAME" 124 | if ($LyncDiscoverRecord) { 125 | $result.LyncDiscover = $LyncDiscoverRecord.NameHost 126 | } 127 | 128 | # Check Skype for Business SRV records 129 | $SipTlsRecord = Get-DnsRecords -Domain "_sip._tls.$Domain" -RecordType "SRV" 130 | if ($SipTlsRecord) { 131 | $result.SipTls = $SipTlsRecord | ForEach-Object { $_.NameTarget } 132 | } 133 | 134 | $SipFederationTlsTcpRecord = Get-DnsRecords -Domain "_sipfederationtls._tcp.$Domain" -RecordType "SRV" 135 | if ($SipFederationTlsTcpRecord) { 136 | $result.SipFederationTlsTcp = $SipFederationTlsTcpRecord | ForEach-Object { $_.NameTarget } 137 | } 138 | 139 | # Check Enterprise Registration and Enrollment CNAMEs 140 | $EnterpriseRegistrationRecord = Get-DnsRecords -Domain "enterpriseregistration.$Domain" -RecordType "CNAME" 141 | if ($EnterpriseRegistrationRecord) { 142 | $result.EnterpriseRegistration = $EnterpriseRegistrationRecord.NameHost 143 | } 144 | 145 | $EnterpriseEnrollmentRecord = Get-DnsRecords -Domain "enterpriseenrollment.$Domain" -RecordType "CNAME" 146 | if ($EnterpriseEnrollmentRecord) { 147 | $result.EnterpriseEnrollment = $EnterpriseEnrollmentRecord.NameHost 148 | } 149 | 150 | return $result 151 | } 152 | 153 | # Array to hold results 154 | $results = @() 155 | 156 | # Check DNS records for each domain and include additional parameters 157 | foreach ($domain in $domains) { 158 | Write-Host "Checking DNS records for domain: $($domain.Id)" 159 | $dnsResult = Check-DnsRecords -Domain $domain.Id 160 | 161 | $result = [PSCustomObject]@{ 162 | Domain = $domain.Id 163 | AuthenticationType = $domain.AuthenticationType 164 | IsAdminManaged = $domain.IsAdminManaged 165 | IsDefault = $domain.IsDefault 166 | IsInitial = $domain.IsInitial 167 | IsRoot = $domain.IsRoot 168 | IsVerified = $domain.IsVerified 169 | PasswordNotificationWindowInDays = $domain.PasswordNotificationWindowInDays 170 | PasswordValidityPeriodInDays = $domain.PasswordValidityPeriodInDays 171 | SupportedServices = $domain.SupportedServices 172 | MX = $dnsResult.MX 173 | SPF = $dnsResult.SPF 174 | DKIMSelector1 = $dnsResult.DKIMSelector1 175 | DKIMSelector2 = $dnsResult.DKIMSelector2 176 | DMARC = $dnsResult.DMARC 177 | Autodiscover = $dnsResult.Autodiscover 178 | SIP = $dnsResult.SIP 179 | LyncDiscover = $dnsResult.LyncDiscover 180 | SipTls = $dnsResult.SipTls -join "," 181 | SipFederationTlsTcp = $dnsResult.SipFederationTlsTcp -join "," 182 | EnterpriseRegistration = $dnsResult.EnterpriseRegistration 183 | EnterpriseEnrollment = $dnsResult.EnterpriseEnrollment 184 | } 185 | 186 | $results += $result 187 | Write-Host "----------------------------------------" 188 | } 189 | 190 | # Sort results alphabetically by Domain 191 | $results = $results | Sort-Object Domain 192 | 193 | # Output results to CSV 194 | $results | Export-Csv -Path "DomainDnsReport.csv" -NoTypeInformation 195 | 196 | # Convert results to JSON for display 197 | # $results | ConvertTo-Json 198 | 199 | # Disconnect from Microsoft Graph 200 | Disconnect-MgGraph 201 | 202 | Write-Host "DNS report exported to DomainDnsReport.csv" 203 | -------------------------------------------------------------------------------- /Microsoft/Intune/Get-WindowsAutoPilotInfo/Get-WindowsAutoPilotInfo.ps1: -------------------------------------------------------------------------------- 1 | <#PSScriptInfo 2 | 3 | .VERSION 2.1 4 | 5 | .GUID ebf446a3-3362-4774-83c0-b7299410b63f 6 | 7 | .AUTHOR Michael Niehaus 8 | 9 | .COMPANYNAME Microsoft 10 | 11 | .COPYRIGHT 12 | 13 | .TAGS Windows AutoPilot 14 | 15 | .LICENSEURI 16 | 17 | .PROJECTURI 18 | 19 | .ICONURI 20 | 21 | .EXTERNALMODULEDEPENDENCIES 22 | 23 | .REQUIREDSCRIPTS 24 | 25 | .EXTERNALSCRIPTDEPENDENCIES 26 | 27 | .RELEASENOTES 28 | Version 1.0: Original published version. 29 | Version 1.1: Added -Append switch. 30 | Version 1.2: Added -Credential switch. 31 | Version 1.3: Added -Partner switch. 32 | Version 1.4: Switched from Get-WMIObject to Get-CimInstance. 33 | Version 1.5: Added -GroupTag parameter. 34 | Version 1.6: Bumped version number (no other change). 35 | Version 2.0: Added -Online parameter. 36 | Version 2.1: Bug fix. 37 | #> 38 | 39 | <# 40 | .SYNOPSIS 41 | Retrieves the Windows AutoPilot deployment details from one or more computers 42 | .DESCRIPTION 43 | This script uses WMI to retrieve properties needed by the Microsoft Store for Business to support Windows AutoPilot deployment. 44 | .PARAMETER Name 45 | The names of the computers. These can be provided via the pipeline (property name Name or one of the available aliases, DNSHostName, ComputerName, and Computer). 46 | .PARAMETER OutputFile 47 | The name of the CSV file to be created with the details for the computers. If not specified, the details will be returned to the PowerShell 48 | pipeline. 49 | .PARAMETER Append 50 | Switch to specify that new computer details should be appended to the specified output file, instead of overwriting the existing file. 51 | .PARAMETER Credential 52 | Credentials that should be used when connecting to a remote computer (not supported when gathering details from the local computer). 53 | .PARAMETER Partner 54 | Switch to specify that the created CSV file should use the schema for Partner Center (using serial number, make, and model). 55 | .PARAMETER GroupTag 56 | An optional tag value that should be included in a CSV file that is intended to be uploaded via Intune (not supported by Partner Center or Microsoft Store for Business). 57 | .PARAMETER Online 58 | Add computers to Windows Autopilot via the Intune Graph API 59 | .EXAMPLE 60 | .\Get-WindowsAutoPilotInfo.ps1 -ComputerName MYCOMPUTER -OutputFile .\MyComputer.csv 61 | .EXAMPLE 62 | .\Get-WindowsAutoPilotInfo.ps1 -ComputerName MYCOMPUTER -OutputFile .\MyComputer.csv -GroupTag Kiosk 63 | .EXAMPLE 64 | .\Get-WindowsAutoPilotInfo.ps1 -ComputerName MYCOMPUTER -OutputFile .\MyComputer.csv -Append 65 | .EXAMPLE 66 | .\Get-WindowsAutoPilotInfo.ps1 -ComputerName MYCOMPUTER1,MYCOMPUTER2 -OutputFile .\MyComputers.csv 67 | .EXAMPLE 68 | Get-ADComputer -Filter * | .\GetWindowsAutoPilotInfo.ps1 -OutputFile .\MyComputers.csv 69 | .EXAMPLE 70 | Get-CMCollectionMember -CollectionName "All Systems" | .\GetWindowsAutoPilotInfo.ps1 -OutputFile .\MyComputers.csv 71 | .EXAMPLE 72 | .\Get-WindowsAutoPilotInfo.ps1 -ComputerName MYCOMPUTER1,MYCOMPUTER2 -OutputFile .\MyComputers.csv -Partner 73 | .EXAMPLE 74 | .\GetWindowsAutoPilotInfo.ps1 -Online 75 | 76 | #> 77 | 78 | [CmdletBinding()] 79 | param( 80 | [Parameter(Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True,Position=0)][alias("DNSHostName","ComputerName","Computer")] [String[]] $Name = @("localhost"), 81 | [Parameter(Mandatory=$False)] [String] $OutputFile = "", 82 | [Parameter(Mandatory=$False)] [String] $GroupTag = "", 83 | [Parameter(Mandatory=$False)] [Switch] $Append = $false, 84 | [Parameter(Mandatory=$False)] [System.Management.Automation.PSCredential] $Credential = $null, 85 | [Parameter(Mandatory=$False)] [Switch] $Partner = $false, 86 | [Parameter(Mandatory=$False)] [Switch] $Force = $false, 87 | [Parameter(Mandatory=$False)] [Switch] $Online = $false 88 | ) 89 | 90 | Begin 91 | { 92 | # Initialize empty list 93 | $computers = @() 94 | 95 | # If online, make sure we are able to authenticate 96 | if ($Online) { 97 | 98 | # Make sure we can connect 99 | $module = Import-Module WindowsAutopilotIntune -PassThru -ErrorAction Ignore 100 | if (-not $module) { 101 | Write-Host "Installing module WindowsAutopilotIntune" 102 | Install-Module WindowsAutopilotIntune -Force 103 | } 104 | Import-Module WindowsAutopilotIntune -Scope Global 105 | $graph = Connect-MSGraph 106 | Write-Host "Connected to tenant $($graph.TenantId)" 107 | 108 | # Force the output to a file 109 | if ($OutputFile -eq "") 110 | { 111 | $OutputFile = "$($env:TEMP)\autopilot.csv" 112 | } 113 | } 114 | } 115 | 116 | Process 117 | { 118 | foreach ($comp in $Name) 119 | { 120 | $bad = $false 121 | 122 | # Get a CIM session 123 | if ($comp -eq "localhost") { 124 | $session = New-CimSession 125 | } 126 | else 127 | { 128 | $session = New-CimSession -ComputerName $comp -Credential $Credential 129 | } 130 | 131 | # Get the common properties. 132 | Write-Verbose "Checking $comp" 133 | $serial = (Get-CimInstance -CimSession $session -Class Win32_BIOS).SerialNumber 134 | 135 | # Get the hash (if available) 136 | $devDetail = (Get-CimInstance -CimSession $session -Namespace root/cimv2/mdm/dmmap -Class MDM_DevDetail_Ext01 -Filter "InstanceID='Ext' AND ParentID='./DevDetail'") 137 | if ($devDetail -and (-not $Force)) 138 | { 139 | $hash = $devDetail.DeviceHardwareData 140 | } 141 | else 142 | { 143 | $bad = $true 144 | $hash = "" 145 | } 146 | 147 | # If the hash isn't available, get the make and model 148 | if ($bad -or $Force) 149 | { 150 | $cs = Get-CimInstance -CimSession $session -Class Win32_ComputerSystem 151 | $make = $cs.Manufacturer.Trim() 152 | $model = $cs.Model.Trim() 153 | if ($Partner) 154 | { 155 | $bad = $false 156 | } 157 | } 158 | else 159 | { 160 | $make = "" 161 | $model = "" 162 | } 163 | 164 | # Getting the PKID is generally problematic for anyone other than OEMs, so let's skip it here 165 | $product = "" 166 | 167 | # Depending on the format requested, create the necessary object 168 | if ($Partner) 169 | { 170 | # Create a pipeline object 171 | $c = New-Object psobject -Property @{ 172 | "Device Serial Number" = $serial 173 | "Windows Product ID" = $product 174 | "Hardware Hash" = $hash 175 | "Manufacturer name" = $make 176 | "Device model" = $model 177 | } 178 | # From spec: 179 | # "Manufacturer Name" = $make 180 | # "Device Name" = $model 181 | 182 | } 183 | elseif ($GroupTag -ne "") 184 | { 185 | # Create a pipeline object 186 | $c = New-Object psobject -Property @{ 187 | "Device Serial Number" = $serial 188 | "Windows Product ID" = $product 189 | "Hardware Hash" = $hash 190 | "Group Tag" = $GroupTag 191 | } 192 | } 193 | else 194 | { 195 | # Create a pipeline object 196 | $c = New-Object psobject -Property @{ 197 | "Device Serial Number" = $serial 198 | "Windows Product ID" = $product 199 | "Hardware Hash" = $hash 200 | } 201 | } 202 | 203 | # Write the object to the pipeline or array 204 | if ($bad) 205 | { 206 | # Report an error when the hash isn't available 207 | Write-Error -Message "Unable to retrieve device hardware data (hash) from computer $comp" -Category DeviceError 208 | } 209 | elseif ($OutputFile -eq "") 210 | { 211 | $c 212 | } 213 | else 214 | { 215 | $computers += $c 216 | } 217 | 218 | Remove-CimSession $session 219 | } 220 | } 221 | 222 | End 223 | { 224 | if ($OutputFile -ne "") 225 | { 226 | if ($Append) 227 | { 228 | if (Test-Path $OutputFile) 229 | { 230 | $computers += Import-CSV -Path $OutputFile 231 | } 232 | } 233 | if ($Partner) 234 | { 235 | $computers | Select "Device Serial Number", "Windows Product ID", "Hardware Hash", "Manufacturer name", "Device model" | ConvertTo-CSV -NoTypeInformation | % {$_ -replace '"',''} | Out-File $OutputFile 236 | } 237 | elseif ($GroupTag -ne "") 238 | { 239 | $computers | Select "Device Serial Number", "Windows Product ID", "Hardware Hash", "Group Tag" | ConvertTo-CSV -NoTypeInformation | % {$_ -replace '"',''} | Out-File $OutputFile 240 | } 241 | else 242 | { 243 | $computers | Select "Device Serial Number", "Windows Product ID", "Hardware Hash" | ConvertTo-CSV -NoTypeInformation | % {$_ -replace '"',''} | Out-File $OutputFile 244 | } 245 | } 246 | if ($Online) 247 | { 248 | Import-AutopilotCSV -csvFile $OutputFile 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /Microsoft/AD/SetReadOnlyOnHomeDrives.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script retrieves home directories of users from Active Directory, lists unique servers and paths, counts users per path, verifies path existence, and modifies access rights on the home directories. 4 | 5 | .DESCRIPTION 6 | This script retrieves home directories of users from Active Directory, lists unique servers and paths, counts users per path, verifies path existence, and modifies access rights on the home directories. 7 | There are several sections in the script that can be uncommented to run specific parts of the script. The script is designed to be run in a controlled environment where the user has the necessary permissions to modify access rights on the home directories. 8 | 9 | .NOTES 10 | Author: Simon Skotheimsvik 11 | Version: 12 | 1.0.0 - 2024-11-06 - Initial release, Simon Skotheimsvik 13 | #> 14 | 15 | #region Variables 16 | $OU = "OU=users,DC=domain,DC=com" 17 | $domain = "Your Domain" 18 | 19 | # Path to log CSV 20 | $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" 21 | $JsonLogPath = "C:\temp\homedrive-removereadrights-log_$timestamp.json" 22 | $TxtLogPath = "C:\temp\homedrive-removereadrights-log_$timestamp.txt" 23 | $log = @() # Initialize an array to hold log entries 24 | #endregion Variables 25 | 26 | 27 | #region Manual testing 28 | <#### List all homefolder paths 29 | Get-ADUser -Filter * -SearchBase $OU -Properties homeDirectory | ForEach-Object { 30 | $_.homeDirectory 31 | } | out-gridview 32 | #> 33 | 34 | <##### List all unique servers 35 | $servers = Get-ADUser -Filter * -SearchBase $OU -Properties homeDirectory | ForEach-Object { 36 | # Extract the server name from the UNC path 37 | if ($_.homeDirectory -match "^\\\\([^\\]+)\\") { 38 | $matches[1] # This is the server name from the UNC path (e.g., \\ServerName\Share) 39 | } 40 | } 41 | 42 | $uniqueServers = $servers | Sort-Object -Unique 43 | $uniqueServers 44 | #> 45 | 46 | <# 47 | ##### List all unique homefolder paths 48 | $serverPaths = Get-ADUser -Filter * -SearchBase $OU -Properties homeDirectory | ForEach-Object { 49 | # Remove the last segment (username) from the home directory path 50 | if ($_.homeDirectory -match "^(.*)\\[^\\]+$") { 51 | # This captures everything up to the last backslash 52 | $matches[1] # This results in the path without the username 53 | } 54 | } 55 | 56 | $uniqueServerPaths = $serverPaths | Sort-Object -Unique 57 | $uniqueServerPaths 58 | #> 59 | 60 | 61 | <#### Count users adressing each unique homefolder path and verify if the path is alive 62 | $UsersInOu = ($serverPaths = Get-ADUser -Filter * -SearchBase $OU -Properties homeDirectory).count 63 | $serverPaths = Get-ADUser -Filter * -SearchBase $OU -Properties homeDirectory | ForEach-Object { 64 | # Remove the last segment (username) from the home directory path 65 | if ($_.homeDirectory -match "^(.*)\\[^\\]+$") { 66 | # This captures everything up to the last backslash 67 | $matches[1] # This results in the path without the username 68 | } 69 | } 70 | 71 | # Get unique server paths 72 | $uniqueServerPaths = $serverPaths | Sort-Object -Unique 73 | 74 | # Initialize an array to store custom objects for each path 75 | $serverPathUserCountArray = @() 76 | 77 | # Loop through each unique server path and count users, and check if the path is alive 78 | foreach ($path in $uniqueServerPaths) { 79 | # Count the number of users for this path 80 | $userCount = ($serverPaths | Where-Object { $_ -eq $path }).Count 81 | 82 | # Check if the folder path exists (is alive) 83 | $isPathAlive = Test-Path $path 84 | 85 | # Create a custom object to store path, user count, and alive status 86 | $serverPathUserCountArray += [PSCustomObject]@{ 87 | Path = $path 88 | UserCount = $userCount 89 | IsAlive = $isPathAlive 90 | } 91 | } 92 | 93 | # Display the result 94 | $serverPathUserCountArray 95 | $totalUsersWithHomeDir = ($serverPathUserCountArray | Measure-Object -Property UserCount -Sum).Sum 96 | Write-Host "$($totalUsersWithHomeDir) of $($UsersInOu) users in $($OU) have a homedir" 97 | #> 98 | 99 | 100 | <#### List all users homedrives and driveletters 101 | Get-ADUser -Filter * -SearchBase $OU -Properties UserPrincipalName, homeDirectory, homeDrive | ForEach-Object { 102 | $userPrincipalName = $_.UserPrincipalName 103 | $homeDirectory = $_.homeDirectory 104 | $driveLetter = $_.homeDrive 105 | 106 | # Create a custom object for each user 107 | [PSCustomObject]@{ 108 | UserPrincipalName = $userPrincipalName 109 | HomeDirectory = $homeDirectory 110 | DriveLetter = $driveLetter 111 | } 112 | } | Sort-Object -Property HomeDirectory -Descending | out-gridview 113 | #> 114 | #endregion Manual testing 115 | 116 | #region Change rights on homedrives 117 | #### Traversing each user and modify rights on the homedrive folders 118 | # $users = Get-ADUser -Filter {Name -eq "simons"} -SearchBase $OU -Properties homeDirectory 119 | $users = Get-ADUser -Filter * -SearchBase $OU -Properties homeDirectory 120 | 121 | foreach ($user in $users) { 122 | $homeDir = $user.HomeDirectory 123 | if (Test-Path -Path $homeDir) { 124 | Write-Host "Homedir for $($user.Name) found at $($homeDir)" 125 | 126 | # Get current ACL before change 127 | $ACL = Get-Acl -Path $homeDir 128 | 129 | # Get the user identity 130 | $userIdentity = New-Object System.Security.Principal.NTAccount($domain, $user.SamAccountName) 131 | 132 | # Get current ACL for the user on Home directory 133 | $aclUserAccessBefore = $ACL.Access | Where-Object { $_.IdentityReference -eq $userIdentity } 134 | 135 | # Extract ACL details into a cleaner format 136 | $permissionsBefore = $aclUserAccessBefore | Select-Object -Property IdentityReference, FileSystemRights, AccessControlType, IsInherited | ForEach-Object { 137 | [PSCustomObject]@{ 138 | IdentityReference = $_.IdentityReference.ToString() 139 | FileSystemRights = $_.FileSystemRights 140 | AccessControlType = $_.AccessControlType 141 | IsInherited = $_.IsInherited 142 | } 143 | } 144 | 145 | # Define the new ACL 146 | #$ACL.setAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule($userIdentity, "Read", "ContainerInherit,ObjectInherit", "none", "allow"))) 147 | $ACL.setAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule($userIdentity, "Read, ReadAndExecute, ListDirectory", "ContainerInherit,ObjectInherit", "none", "allow"))) 148 | 149 | # Set the new ACL 150 | # Set-Acl -Path $homeDir $ACL 151 | 152 | # Get current ACL for the user on Home directory after the change 153 | $ACLnew = Get-Acl -Path $homeDir 154 | $aclUserAccessAfter = $ACLnew.Access | Where-Object { $_.IdentityReference -eq $userIdentity } 155 | 156 | # Extract ACL details after the change 157 | $permissionsAfter = $aclUserAccessAfter | Select-Object -Property IdentityReference, FileSystemRights, AccessControlType, IsInherited | ForEach-Object { 158 | [PSCustomObject]@{ 159 | IdentityReference = $_.IdentityReference.ToString() 160 | FileSystemRights = $_.FileSystemRights 161 | AccessControlType = $_.AccessControlType 162 | IsInherited = $_.IsInherited 163 | } 164 | } 165 | 166 | # Log the details to the array 167 | $logEntry = [PSCustomObject]@{ 168 | UserSamAccountName = $user.SamAccountName 169 | HomeDirectory = $homeDir 170 | Status = "ACL updated" 171 | ACLBefore = $permissionsBefore 172 | ACLAfter = $permissionsAfter 173 | } 174 | 175 | $log += $logEntry # Add entry to log array 176 | 177 | } else { 178 | Write-Host "Homedir for $($user.Name) not found at $($homeDir)" 179 | 180 | # Log the details to the array 181 | $logEntry = [PSCustomObject]@{ 182 | UserSamAccountName = $user.SamAccountName 183 | HomeDirectory = $homeDir 184 | Status = "Directory not found" 185 | ACLBefore = $null 186 | ACLAfter = $null 187 | } 188 | 189 | $log += $logEntry # Add entry to log array 190 | 191 | } 192 | } 193 | 194 | # Convert log array to JSON and export it to the specified path 195 | $log | ConvertTo-Json -Depth 7 | Out-File -FilePath $JsonLogPath 196 | $log | Out-String | Set-Content -Path $TxtLogPath 197 | 198 | #endregion Change rights on homedrives -------------------------------------------------------------------------------- /Microsoft/Intune/OfficeIcons-Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | =========================================================================== 4 | Created on: 13.01.2023 5 | Created by: Simon Skotheimsvik 6 | Filename: OfficeIcons-Remediation.ps1 7 | Instructions: https://skotheimsvik.no 8 | =========================================================================== 9 | 10 | .DESCRIPTION 11 | This script will remediate the missing Office 12 | Based on ideas from the Microsoft EMS Community on Discord, Reddit and Rudy Ooms 13 | 14 | #> 15 | 16 | 17 | # Office Alternative1 - Repair 18 | # Start-Process "C:\Program Files\Microsoft Office 15\ClientX64\OfficeClickToRun.exe" -ArgumentList "scenario=Repair", "system=x64", "culture=en-us", "RepairType=QuickRepair", "DisplayLevel=False" -Wait 19 | 20 | # Office Alternative2 - Recreate icons the Rudy Ooms way 21 | 22 | if(!(Test-Path -Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Word.lnk")){ 23 | 24 | if(Test-Path -Path "C:\Program Files\Microsoft Office 15\ClientX64\OfficeClickToRun.exe"){ 25 | 26 | #Restore Shortcuts to Public desktop 27 | $ComObj = New-Object -ComObject WScript.Shell 28 | $ShortCut = $ComObj.CreateShortcut("C:\Users\Public\desktop\Microsoft Excel.lnk") 29 | $ShortCut.TargetPath = "C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE" 30 | $ShortCut.Description = "Excel" 31 | $ShortCut.FullName 32 | $ShortCut.WindowStyle = 1 33 | # $ShortCut.Save() 34 | 35 | $ComObj = New-Object -ComObject WScript.Shell 36 | $ShortCut = $ComObj.CreateShortcut("C:\Users\Public\desktop\Microsoft Outlook.lnk") 37 | $ShortCut.TargetPath = "C:\Program Files\Microsoft Office\root\Office16\Outlook.exe" 38 | $ShortCut.Description = "Outlook" 39 | $ShortCut.FullName 40 | $ShortCut.WindowStyle = 1 41 | # $ShortCut.Save() 42 | 43 | $ComObj = New-Object -ComObject WScript.Shell 44 | $ShortCut = $ComObj.CreateShortcut("C:\Users\Public\desktop\Microsoft Word.lnk") 45 | $ShortCut.TargetPath = "C:\Program Files\Microsoft Office\root\Office16\Winword.EXE" 46 | $ShortCut.Description = "Word" 47 | $ShortCut.FullName 48 | $ShortCut.WindowStyle = 1 49 | # $ShortCut.Save() 50 | 51 | $ComObj = New-Object -ComObject WScript.Shell 52 | $ShortCut = $ComObj.CreateShortcut("C:\Users\Public\desktop\Microsoft Powerpoint.lnk") 53 | $ShortCut.TargetPath = "C:\Program Files\Microsoft Office\root\Office16\PowerPNT.exe" 54 | $ShortCut.Description = "PowerPoint" 55 | $ShortCut.FullName 56 | $ShortCut.WindowStyle = 1 57 | # $ShortCut.Save() 58 | 59 | if(!(Test-Path -Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Powerpoint.lnk")){ 60 | $ComObj = New-Object -ComObject WScript.Shell 61 | $ShortCut = $ComObj.CreateShortcut("C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Powerpoint.lnk") 62 | $ShortCut.TargetPath = "C:\Program Files\Microsoft Office\root\Office16\PowerPNT.exe" 63 | $ShortCut.Description = "PowerPoint" 64 | $ShortCut.FullName 65 | $ShortCut.WindowStyle = 1 66 | $ShortCut.Save() 67 | } 68 | 69 | if(!(Test-Path -Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Word.lnk")){ 70 | $ComObj = New-Object -ComObject WScript.Shell 71 | $ShortCut = $ComObj.CreateShortcut("C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Word.lnk") 72 | $ShortCut.TargetPath = "C:\Program Files\Microsoft Office\root\Office16\Winword.EXE" 73 | $ShortCut.Description = "Word" 74 | $ShortCut.FullName 75 | $ShortCut.WindowStyle = 1 76 | $ShortCut.Save() 77 | } 78 | 79 | if(!(Test-Path -Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Outlook.lnk")){ 80 | $ComObj = New-Object -ComObject WScript.Shell 81 | $ShortCut = $ComObj.CreateShortcut("C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Outlook.lnk") 82 | $ShortCut.TargetPath = "C:\Program Files\Microsoft Office\root\Office16\Outlook.exe" 83 | $ShortCut.Description = "Outlook" 84 | $ShortCut.FullName 85 | $ShortCut.WindowStyle = 1 86 | $ShortCut.Save() 87 | } 88 | 89 | if(!(Test-Path -Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Excel.lnk")){ 90 | $ComObj = New-Object -ComObject WScript.Shell 91 | $ShortCut = $ComObj.CreateShortcut("C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Excel.lnk") 92 | $ShortCut.TargetPath = "C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE" 93 | $ShortCut.Description = "Excel" 94 | $ShortCut.FullName 95 | $ShortCut.WindowStyle = 1 96 | $ShortCut.Save() 97 | } 98 | 99 | 100 | 101 | } elseif(Test-PAth -Path "C:\Program Files\Microsoft Office 15\ClientX32\OfficeClickToRun.exe"){ 102 | 103 | #Restore Shortcuts to Public desktop 104 | $ComObj = New-Object -ComObject WScript.Shell 105 | $ShortCut = $ComObj.CreateShortcut("C:\Users\Public\desktop\Microsoft Excel.lnk") 106 | $ShortCut.TargetPath = "C:\Program Files (x86)\Microsoft Office\root\Office16\EXCEL.EXE" 107 | $ShortCut.Description = "Excel" 108 | $ShortCut.FullName 109 | $ShortCut.WindowStyle = 1 110 | # $ShortCut.Save() 111 | 112 | $ComObj = New-Object -ComObject WScript.Shell 113 | $ShortCut = $ComObj.CreateShortcut("C:\Users\Public\desktop\Microsoft Outlook.lnk") 114 | $ShortCut.TargetPath = "C:\Program Files (x86)\Microsoft Office\root\Office16\Outlook.exe" 115 | $ShortCut.Description = "Outlook" 116 | $ShortCut.FullName 117 | $ShortCut.WindowStyle = 1 118 | # $ShortCut.Save() 119 | 120 | $ComObj = New-Object -ComObject WScript.Shell 121 | $ShortCut = $ComObj.CreateShortcut("C:\Users\Public\desktop\Microsoft Word.lnk") 122 | $ShortCut.TargetPath = "C:\Program Files (x86)\Microsoft Office\root\Office16\Winword.EXE" 123 | $ShortCut.Description = "Word" 124 | $ShortCut.FullName 125 | $ShortCut.WindowStyle = 1 126 | #$ShortCut.Save() 127 | 128 | $ComObj = New-Object -ComObject WScript.Shell 129 | $ShortCut = $ComObj.CreateShortcut("C:\Users\Public\desktop\Microsoft Powerpoint.lnk") 130 | $ShortCut.TargetPath = "C:\Program Files (x86)\Microsoft Office\root\Office16\PowerPNT.exe" 131 | $ShortCut.Description = "PowerPoint" 132 | $ShortCut.FullName 133 | $ShortCut.WindowStyle = 1 134 | #$ShortCut.Save() 135 | 136 | if(!(Test-Path -Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Powerpoint.lnk")){ 137 | $ComObj = New-Object -ComObject WScript.Shell 138 | $ShortCut = $ComObj.CreateShortcut("C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Powerpoint.lnk") 139 | $ShortCut.TargetPath = "C:\Program Files (x86)\Microsoft Office\root\Office16\PowerPNT.exe" 140 | $ShortCut.Description = "PowerPoint" 141 | $ShortCut.FullName 142 | $ShortCut.WindowStyle = 1 143 | $ShortCut.Save() 144 | } 145 | 146 | if(!(Test-Path -Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Word.lnk")){ 147 | $ComObj = New-Object -ComObject WScript.Shell 148 | $ShortCut = $ComObj.CreateShortcut("C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Word.lnk") 149 | $ShortCut.TargetPath = "C:\Program Files (x86)\Microsoft Office\root\Office16\Winword.EXE" 150 | $ShortCut.Description = "Word" 151 | $ShortCut.FullName 152 | $ShortCut.WindowStyle = 1 153 | $ShortCut.Save() 154 | } 155 | 156 | if(!(Test-Path -Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Outlook.lnk")){ 157 | $ComObj = New-Object -ComObject WScript.Shell 158 | $ShortCut = $ComObj.CreateShortcut("C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Outlook.lnk") 159 | $ShortCut.TargetPath = "C:\Program Files (x86)\Microsoft Office\root\Office16\Outlook.exe" 160 | $ShortCut.Description = "Outlook" 161 | $ShortCut.FullName 162 | $ShortCut.WindowStyle = 1 163 | $ShortCut.Save() 164 | } 165 | 166 | if(!(Test-Path -Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Excel.lnk")){ 167 | $ComObj = New-Object -ComObject WScript.Shell 168 | $ShortCut = $ComObj.CreateShortcut("C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Excel.lnk") 169 | $ShortCut.TargetPath = "C:\Program Files (x86)\Microsoft Office\root\Office16\EXCEL.EXE" 170 | $ShortCut.Description = "Excel" 171 | $ShortCut.FullName 172 | $ShortCut.WindowStyle = 1 173 | $ShortCut.Save() 174 | } 175 | 176 | 177 | } 178 | }else{ 179 | write-host "nothing to repair"} 180 | 181 | #Restore Other Shortcuts to Public desktop 182 | 183 | if(!(Test-Path -Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk")){ 184 | $ComObj = New-Object -ComObject WScript.Shell 185 | $ShortCut = $ComObj.CreateShortcut("C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk") 186 | $ShortCut.TargetPath = "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" 187 | $ShortCut.Description = "Edge" 188 | $ShortCut.FullName 189 | $ShortCut.WindowStyle = 1 190 | $ShortCut.Save() 191 | 192 | $ComObj = New-Object -ComObject WScript.Shell 193 | $ShortCut = $ComObj.CreateShortcut("C:\Users\Public\desktop\Microsoft Edge.lnk") 194 | $ShortCut.TargetPath = "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" 195 | $ShortCut.Description = "Edge" 196 | $ShortCut.FullName 197 | $ShortCut.WindowStyle = 1 198 | # $ShortCut.Save() 199 | } 200 | 201 | 202 | $StartMenuFolder = "$env:ProgramData\Microsoft\Windows\Start Menu\Programs" 203 | $Count = (Get-ChildItem $StartMenuFolder | ? Name -match "Word|Outlook|Powerpoint|Excel").count 204 | 205 | if ($count -ge 4) { 206 | "Installed" 207 | $exitCode = 0 208 | } 209 | 210 | else { 211 | $exitCode = -1 212 | } 213 | 214 | 215 | exit $exitCode --------------------------------------------------------------------------------