├── 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
--------------------------------------------------------------------------------