Tuesday, 24 June 2025

SCCM Co-Management Workload Details and Remediation

 

With modern device management relying heavily on co-management, administrators often need insights into how workloads are being distributed between SCCM and Intune. Whether you're troubleshooting, auditing, or planning transitions, having visibility into co-management state is crucial.

In this post, we’ll explore three effective methods to view co-management workload details:

  1. Using SQL Query in SCCM
  2. Using PowerShell with Microsoft Graph
  3. Using the Intune Admin Console

1️ Method 1: View Co-Management Workloads Using SQL Query in SCCM

If you're using SQL Server Reporting Services (SSRS) or the SQL Management Studio, you can run the following query to fetch device-level co-management details:

sql

CopyEdit

SELECT

    s.Netbios_Name0 AS [Computer Name],

    s.Is_Virtual_Machine0,

    s.Client0,

    s.User_Name0,

    c.MDMEnrolled,

    c.MDMWorkloads,

    c.HybridAADJoined,

    c.MDMProvisioned

FROM

    v_R_System s

JOIN

    v_ClientCoManagementState c ON c.ResourceID = s.ResourceID

FULL JOIN

    v_FullCollectionMembership fcm ON s.ResourceID = fcm.ResourceID

WHERE

    fcm.CollectionID = 'collectionID'

This query returns details such as:

  • Whether the device is enrolled in MDM
  • Hybrid Azure AD Join status
  • Which co-management workloads are offloaded to Intune

📌 Reference Workload Values:
For interpreting the MDMWorkloads values, refer to this SystemCenterDudes reference. Each workload corresponds to a specific bitwise value (e.g., 1 = Compliance Policies, 2 = Resource Access, etc.).


2️ Method 2: View Co-Management Status via PowerShell & Microsoft Graph

You can also use PowerShell with Microsoft Graph API to pull co-management details from Intune directly.

🔧 Prerequisites:

  • Azure AD App registration with Graph permissions
  • Certificate-based authentication

📜 PowerShell Script:

powershell

CopyEdit

# Connect to Microsoft Graph

Connect-MgGraph -ClientId '<Client_ID>' -TenantId '<Tenant_ID>' -CertificateThumbprint '<Cert_Thumbprint>'

 

# Get workload status

$workloadStatus = Invoke-MgGraphRequest -Method GET -Uri 'https://graph.microsoft.com/v1.0/deviceManagement/managedDevices'

 

# Display relevant details

$workloadStatus | Select-Object id, deviceName, operatingSystem, complianceState

This will return:

  • Device name
  • OS version
  • Compliance state
  • Managed status (Hybrid, MDM, etc.)

This is useful when you want real-time data or wish to automate reports via scripts.


3️ Method 3: Use Intune Admin Console – Cloud Attach Detail Report

If you prefer a visual interface, Microsoft Intune provides built-in reporting for co-management.

📍 Steps:

  1. Go to the Intune Admin Center: https://intune.microsoft.com
  2. Navigate to:
    Reports
    ➡️ Cloud Attach ➡️ Cloud Attach Detail Preview
  3. Filter by Co-Managed Workloads

This report gives you a snapshot of which workloads are managed by Intune or ConfigMgr for each device.

SCCM Remediation Script – Fix WMI, Client, and Trigger Workloads

When devices are not reporting co-management workload changes, it's often due to WMI corruption or failed client components. This PowerShell script can be deployed as a SCCM Remediation Script or run manually to fix common issues.

📜 Full PowerShell Script

<#

Script Name: Remediate - StateMsg WMI Status

Description: Fixes WMI issues, repairs SCCM client, and triggers co-management workload baselines.

#>

 

# Step 1: Validate StateMsg WMI namespace

$wmiObject = Get-WmiObject -Namespace root\ccm\StateMsg -Query "SELECT * FROM CCM_StateMsg WHERE TopicType='401'"

if ($wmiObject) {

    Write-Host " StateMsg is working"

} else {

    try {

        Start-Process -FilePath "C:\windows\ccm\ccmrepair.exe" -Wait

        Write-Host "⚙️ StateMsg not working, ran ccmrepair.exe"

    } catch {

        Write-Host " StateMsg not working, ccmrepair.exe failed"

    }

}

 

# Step 2: Define function to trigger baseline evaluation

function Invoke-CoMgmtBaselineEvaluation {

    param (

        [string]$BaselineName

    )

    Write-Host "🔎 Looking for baseline: $BaselineName"

    $instance = Get-WmiObject -Namespace root\ccm\dcm -Query "SELECT * FROM SMS_DesiredConfiguration WHERE DisplayName = '$BaselineName'"

    if ($instance) {

        Write-Host "🚀 Triggering evaluation for: $BaselineName"

        Invoke-CimMethod -Namespace root\ccm\dcm -ClassName SMS_DesiredConfiguration -MethodName TriggerEvaluation -Arguments @{

            "Name"       = $instance.Name

            "Version"    = $instance.Version

            "PolicyType" = $instance.PolicyType

        }

    } else {

        Write-Warning "⚠️ Baseline '$BaselineName' not found. Ensure it's deployed to this client."

    }

}

 

# Step 3: Trigger standard client actions

$triggerSchedules = @{

    "Machine Policy Retrieval & Evaluation Cycle" = "{00000000-0000-0000-0000-000000000021}"

    "Application Deployment Evaluation Cycle"     = "{00000000-0000-0000-0000-000000000113}"

    "Software Updates Deployment Evaluation"      = "{00000000-0000-0000-0000-000000000114}"

    "Software Update Scan Cycle"                  = "{00000000-0000-0000-0000-000000000026}"

    "State Message Refresh Cycle"                 = "{00000000-0000-0000-0000-000000000121}"

}

foreach ($cycle in $triggerSchedules.GetEnumerator()) {

    Write-Host " Triggering $($cycle.Key)..."

    Invoke-WmiMethod -Namespace root\ccm -Class sms_client -Name TriggerSchedule -ArgumentList $cycle.Value

    Start-Sleep -Seconds 10

}

 

# Step 4: Evaluate co-management workload baselines

$baselineList = @(

    "CoMgmtSettingsPilotWUP",

    "CoMgmtSettingsPilotO365",

    "CoMgmtSettingsPilotCApp",

    "CoMgmtSettingsPilotCP",

    "CoMgmtSettingsPilotDC",

    "CoMgmtSettingsPilotDiskEncryption",

    "CoMgmtSettingsPilotEP",

    "CoMgmtSettingsPilotRAP"

)

 

foreach ($baseline in $baselineList) {

    Invoke-CoMgmtBaselineEvaluation -BaselineName $baseline

}

💡 How to Use:

  • Deploy via SCCM Remediation Script for proactive healing
  • Use as a manual fix tool during support scenarios
  • Schedule it via Configuration Baseline or Task Scheduler on problematic systems

🧠 Final Thoughts

With hybrid environments becoming the norm, keeping your co-managed devices healthy and correctly reporting is more important than ever. Combine visibility (via SQL, Graph, and Console) with proactive remediation (via PowerShell) to stay ahead of issues.

SQL = Detailed backend insight
PowerShell = Scripting/automation flexibility
Intune Console = Simple, visual reporting
Remediation Script = Fixes it all!

Wednesday, 11 June 2025

BitLocker Remediation Script for SCCM & Intune

 

This blog walks you through a PowerShell script that automates BitLocker encryption, validates TPM status, and logs all activity to a file—perfect for automated deployments via SCCM, Intune, or GPO.


Key Features of the Script

  • 🔍 Checks TPM presence and status
  • 🔐 Verifies BitLocker encryption and protection status
  • 🔄 Enables encryption and protection if required
  • ☁️ Backs up BitLocker recovery keys to Azure AD
  • 📃 Logs actions with timestamps and severity levels

🧩 PowerShell Script Breakdown

Below is the complete PowerShell script. You can save this as Enable-BitLocker.ps1 and deploy it as needed.

Function Get-LoggedInUser {

    [CmdletBinding()]

    param(

        [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]

        [string[]] $ComputerName = $env:COMPUTERNAME,

 

        [Parameter(Mandatory = $false)]

        [Alias("SamAccountName")]

        [string]   $UserName

    )

    PROCESS {

        foreach ($Computer in $ComputerName) {

            try {

                $Computer = $Computer.ToUpper()

                $SessionList = quser /Server:$Computer 2>$null

                if ($SessionList) {

                    $UserInfo = foreach ($Session in ($SessionList | select -Skip 1)) {

                        $Session = $Session.ToString().trim() -replace '\s+', ' ' -replace '>', ''

                        if ($Session.Split(' ')[3] -eq 'Active') {

                            [PSCustomObject]@{

                                ComputerName = $Computer

                                UserName     = $session.Split(' ')[0]

                                SessionName  = $session.Split(' ')[1]

                                SessionID    = $Session.Split(' ')[2]

                                SessionState = $Session.Split(' ')[3]

                                IdleTime     = $Session.Split(' ')[4]

                                LogonTime    = $session.Split(' ')[5, 6, 7] -as [string] -as [datetime]

                            }

                        } else {

                            [PSCustomObject]@{

                                ComputerName = $Computer

                                UserName     = $session.Split(' ')[0]

                                SessionName  = $null

                                SessionID    = $session.Split(' ')[1]

                                SessionState = 'Disconnected'

                                IdleTime     = $Session.Split(' ')[3]

                                LogonTime    = $session.Split(' ')[4, 5, 6] -as [string] -as [datetime]

                            }

                        }

                    }

                    if ($PSBoundParameters.ContainsKey('Username')) {

                        $UserInfo | Where-Object {$_.UserName -eq $UserName}

                    } else {

                        $UserInfo | Sort-Object LogonTime

                    }

                }

            } catch {

                Write-Error $_.Exception.Message

            }

        }

    }

}

 

Function Out-LogFile {

    Param(

        [Parameter(Mandatory = $false)] $Text,

        $Mode,

        [Parameter(Mandatory = $false)][ValidateSet(1, 2, 3, 'Information', 'Warning', 'Error')]$Severity = 1

    )

 

    switch ($Severity) {

        'Information' {$Severity = 1}

        'Warning' {$Severity = 2}

        'Error' {$Severity = 3}

    }

 

    $clientpath = 'C:\Windows'

    $Logfile = "$clientpath\temp\BitlockerAzure.log"

 

    foreach ($item in $text) {

        $item = '<![LOG[' + $item + ']LOG]!>'

        $time = 'time="' + (Get-Date -Format HH:mm:ss.fff) + '+000"'

        $date = 'date="' + (Get-Date -Format MM-dd-yyyy) + '"'

        $component = 'component="BitlockerScript"'

        $context = 'context=""'

        $type = 'type="' + $Severity + '"'

        $thread = 'thread="' + $PID + '"'

        $file = 'file=""'

        $logblock = ($time, $date, $component, $context, $type, $thread, $file) -join ' '

        $logblock = '<' + $logblock + '>'

        $item + $logblock | Out-File -Encoding utf8 -Append $logFile

    }

}

 

function Get-TPMStatus {

    try {

        $tpm = Get-WmiObject -Namespace "Root\CIMv2\Security\MicrosoftTpm" -Class Win32_Tpm

        if ($tpm) {

            if ($tpm.IsEnabled -eq $false) {

                return "TPM is turned off"

            } elseif ($tpm.IsPresent -eq $false) {

                return "No TPM present"

            } else {

                return "TPM is enabled"

            }

        } else {

            return "No TPM information available"

        }

    } catch {

        return "Error retrieving TPM status"

    }

}


 

💡 Deployment Tips

  • Run as Administrator – TPM and BitLocker commands require elevated privileges.
  • Log Review – Review C:\Windows\Temp\BitlockerAzure.log for audit and debugging.
  • Use with Intune or Task Scheduler for zero-touch deployments.
  • Test on a VM or staging environment before deploying widely.

Wednesday, 9 April 2025

SCCM Collection Relationships Using SQL Queries

 In System Center Configuration Manager (SCCM), collections are used to group systems or devices based on specific criteria for easier management. But how do these collections relate to each other? In this blog post, we’ll explore how to find relationships between collections, such as including, excluding, or limiting systems in source collections using SQL queries in SCCM.

Understanding Collection Relationships in SCCM

SCCM allows administrators to manage collections by creating dependencies between them. These dependencies determine how one collection impacts another. You may have scenarios where one collection is a subset of another (limited), one collection is included in another (include), or one collection excludes another collection (exclude).

SCCM maintains collection dependencies in the vSMS_CollectionDependencies table, which stores the relationship between a source collection and a dependent collection.

Key Columns Involved

  • SourceCollectionID: The ID of the collection that is the source of the dependency.
  • DependentCollectionID: The ID of the collection that is dependent on the source collection.
  • RelationshipType: The type of relationship, where:
    • 1 = Limited To
    • 2 = Include
    • 3 = Exclude

Using SQL to Query Collection Relationships

You can use SQL queries to extract details about collection relationships in SCCM. Below is a SQL query that will help you retrieve the relationships for a specific source collection

SELECT

    v_Collection.name,

    v_Collections.CollectionName AS [Source Collection Name],

    SourceCollectionID,

    CASE

        WHEN vSMS_CollectionDependencies.relationshiptype = 1 THEN 'Limited To ' + SourceCollectionID

        WHEN vSMS_CollectionDependencies.relationshiptype = 2 THEN 'Include ' + SourceCollectionID

        WHEN vSMS_CollectionDependencies.relationshiptype = 3 THEN 'Exclude ' + SourceCollectionID

    END AS "Type of Relationship"

FROM

    v_Collection

JOIN

    vSMS_CollectionDependencies ON vSMS_CollectionDependencies.DependentCollectionID = v_Collection.CollectionID

JOIN

    v_Collections ON v_Collections.SiteID = vSMS_CollectionDependencies.SourceCollectionID

WHERE

    vSMS_CollectionDependencies.SourceCollectionID LIKE 'CollectionID';

Conclusion

Using SQL queries to analyze collection relationships in SCCM can save time and improve your ability to manage your environment efficiently. The query shared in this blog will help you gain insights into how collections are interdependent and ensure that your configuration management policies are applied correctly.

Friday, 4 April 2025

Windows LAPS with Intune


Windows Local Administrator Password Solution (LAPS) has been a crucial tool for securing local administrator accounts in managed Windows devices. With the recent Windows 24H2 update, Microsoft has introduced several enhancements to LAPS, empowering IT administrators with new options for automatic account management and increased flexibility for password security policies. One of the standout improvements is the ability to create managed accounts and configure automatic account management directly from Intune, making it easier than ever to enforce security standards across your devices.

 

Key Features of LAPS in Windows 24H2

With the release of Windows 24H2, Microsoft has made several improvements to LAPS, including the ability to define policies that streamline the management of local administrator accounts through Intune. The primary update is the inclusion of new policies for **Automatic Account Management**, which makes it easier to automate and enforce the creation, management, and maintenance of local administrator accounts. Additionally, administrators can now randomize the account name for an added layer of security.

 

New LAPS Policies in Intune

With these updates, administrators can now leverage the **Configuration Service Provider (CSP)** to define policies for LAPS in Microsoft Intune. Below are the essential CSPs that are now available to configure LAPS policies:

 

1. **`./Device/Vendor/MSFT/LAPS/Policies/AutomaticAccountManagementEnabled`** 

   This policy allows administrators to enable or disable automatic management of the local administrator account.

 

2. **`./Device/Vendor/MSFT/LAPS/Policies/AutomaticAccountManagementEnableAccount`** 

   This policy controls whether or not the local administrator account is automatically created and managed via LAPS.

 

3. **`./Device/Vendor/MSFT/LAPS/Policies/AutomaticAccountManagementNameOrPrefix`** 

   Here, administrators can define a name or prefix for the local administrator account. The flexibility to define custom account names is crucial for organizations with specific naming conventions.

 

4. **`./Device/Vendor/MSFT/LAPS/Policies/AutomaticAccountManagementRandomizeName`** 

   This policy allows the administrator to randomize the local administrator account name, which enhances security by reducing the predictability of the account name.

 

5. **`./Device/Vendor/MSFT/LAPS/Policies/AutomaticAccountManagementTarget`** 

   This policy defines the target device group or specific devices that will be subject to LAPS management.

 

6. **`./Device/Vendor/MSFT/LAPS/Policies/BackupDirectory`** 

   This policy specifies the backup directory for LAPS password storage, ensuring that backup copies of passwords are safely stored for recovery when needed.

 

Configuring LAPS Policies in Intune

To create a new LAPS policy via Intune, administrators can use the aforementioned CSPs in the **Device Configuration** section. These settings can be pushed to managed Windows devices, allowing for centralized control over the local administrator account security.

 

1. **Navigate to Intune > Devices > Configuration Profiles** in the Intune portal.

2. **Create a New Profile** and select **Windows 10 and later** as the platform.

3. **Choose the Profile Type** as **Custom**, and under **OMA-URI Settings**, you can input the appropriate CSPs based on your requirements.

 

Once configured, these settings will be applied to the managed devices, ensuring the local administrator account is automatically created and secured according to the defined policies.

 

Backup and Recovery Considerations

As part of the improved LAPS functionality, administrators are now encouraged to set up a backup directory for storing passwords securely. This ensures that in the event of an emergency or a recovery scenario, administrators can retrieve the local administrator password for troubleshooting and remediation.

 

For more detailed information on configuring backup directories and additional LAPS policy options, refer to the official [Microsoft documentation on LAPS

CSP](https://learn.microsoft.com/en-us/windows/client-management/mdm/laps-csp#policiesbackupdirectory).

 

Conclusion

The introduction of **Automatic Account Management** in Windows LAPS (available in the latest Windows 24H2 update) represents a significant step forward in securing local administrator accounts in a streamlined, automated manner. With the ability to manage account names, randomize credentials, and enforce automatic updates directly through Intune, organizations can enhance their security posture while reducing the administrative overhead of managing these critical accounts.


Thursday, 20 March 2025

Automating Email Alerts for Non-Compliant Devices in Intune via PowerShell and Graph API

Managing devices through Microsoft Intune requires efficient tracking of device compliance, and one key task for administrators is to keep users informed about the status of their devices. In some cases, users may have devices that are not compliant with your organization’s policies. To automate the process of notifying these users, you can use the Microsoft Graph API in combination with PowerShell to send an email to users whose devices are non-compliant.

In this blog post, we'll walk through a PowerShell script that pulls non-compliant device data from Intune via the Microsoft Graph API and sends a notification email to the user informing them about the non-compliant status.

Prerequisites:

Before running the script, ensure the following prerequisites are met:

  1. Microsoft Graph PowerShell SDK is installed. You can install it with the following command:

                                Install-Module Microsoft.Graph -Scope CurrentUser

  1. Permissions: Make sure your account has the necessary Microsoft Graph API permissions to read device data and send emails. The required permissions are:
    • DeviceManagementManagedDevices.ReadWrite.All to manage devices.
    • User.Read to read user information.
  2. SMTP Server Setup: You need access to an SMTP server (in this case, Office 365) to send emails.
  3. PowerShell Version: Ensure you are using PowerShell 7.x or a version that supports the required modules.

PowerShell Script to Send Emails to Non-Compliant Users

Here is a simple PowerShell script to get the non-compliant devices and send an email to the respective users:

# Connect to Microsoft Graph

# Connect to Microsoft Graph

Connect-MgGraph -Scopes "DeviceManagementManagedDevices.ReadWrite.All", "User.Read"

 

# Get the list of non-compliant devices

$nonCompliantDevices = Get-MgDeviceManagementManagedDevice -Filter "complianceState eq 'noncompliant'"

 

# Check if we found any non-compliant devices

if ($nonCompliantDevices -eq $null -or $nonCompliantDevices.Count -eq 0) {

    Write-Host "No non-compliant devices found."

    exit

}

 

# Loop through each non-compliant device and send an email

foreach ($device in $nonCompliantDevices) {

    # Display device details in console (for debugging)

    Write-Host "User: $($device.userPrincipalName)"

    Write-Host "Device Name: $($device.deviceName)"

    Write-Host "Compliance State: $($device.complianceState)"

    Write-Host "----------------------------------------------------------"

 

    # Prepare email subject and body

    $subject = "Non-Compliant Device"

    $body = "Device $($device.deviceName) assigned to $($device.userPrincipalName) is not currently compliant."

   

    # Email settings

    $smtpServer = "smtp.office365.com"

    $from = "IntuneAdmin@domain.com"

    $to = $device.userPrincipalName

    $port = 587

 

    # Ensure email address is valid

    if ($to -match "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$") {

        try {

            # Send email using SMTP

            Send-MailMessage -To $to -Subject $subject -Body $body -SmtpServer $smtpServer -From $from -UseSsl -Port $port

            Write-Host "Email sent to: $to"

        } catch {

            Write-Error "Failed to send email to $to. Error: $_"

        }

    } else {

        Write-Host "Invalid email address for user: $($device.userPrincipalName)"

    }

}

 

# Optional: Disconnect from the Graph API after the operation

Disconnect-MgGraph

Useful References:

  1. Microsoft Graph PowerShell SDK documentation: https://learn.microsoft.com/en-us/powershell/microsoftgraph/intune/introduction

Conclusion:

By using the Microsoft Graph API and PowerShell, you can easily automate the process of identifying non-compliant devices in Microsoft Intune and sending out notifications to users. This script helps improve compliance management and ensures that users are promptly informed of any issues with their devices.

 

Wednesday, 19 March 2025

SCCM Audit: 3 Methods to Identify Audit Status

 When managing an SCCM (System Center Configuration Manager) environment, it’s crucial to regularly audit your deployments, installations, and overall system health. Fortunately, SCCM provides various options to identify audit statuses and troubleshoot potential issues. In this blog post, we'll explore three methods you can use to identify the audit status in SCCM: Status Filter Rule, Default Report, and SQL Query. We'll also provide a sample SQL query to help you get the audit status based on Message ID and strings.

1. Using Status Filter

The first method to track audit status is by using the Status Filter in the SCCM console. This option allows you to filter messages based on criteria such as message type, severity, and date.

To use the Status Filter:

  • Navigate to Monitoring in the SCCM Console.
  • Under System Status, select Status Messages.
  • Use the filtering options to narrow down to the specific status you're looking for.

This is a quick and easy way to check the status of various tasks within SCCM, including package deployments and client health.

2. Using Default Report

SCCM also provides a set of default built-in reports, which are useful for auditing purposes. These reports provide visibility into package deployments, software updates, client activity, and more.

To access default reports:

  • Go to the Monitoring workspace in the SCCM Console.
  • Under Reporting, select Reports.
  • Browse the list of available reports and select the one relevant to your audit.

These reports are pre-configured to give you insights into various aspects of your environment, such as package distribution, client status, and software update compliance.

3. Using SQL Query

If you want more flexibility and advanced querying capabilities, SQL queries are a powerful tool. You can directly query the SCCM database to retrieve audit information based on specific message IDs and strings.

Below is an example SQL query to help you get audit status based on the Message ID and Message String:

SELECT *

FROM vStatusMessagesWithStrings

WHERE MessageID = '30000'

AND InsStrValue3 LIKE '%packagename%'

In this query:

  • vStatusMessagesWithStrings is the SCCM view that contains status messages along with their associated strings.
  • MessageID = '30000' filters the messages for a specific ID, in this case, 30000 (this ID represents a specific status event such as successful package deployment).
  • InsStrValue3 LIKE '%packagename%' further narrows the results to only show messages containing the package name.

Microsoft Technet Blog on Status Message IDs

For further information on the different Message IDs in SCCM, you can refer to the Microsoft Technet Blog. The blog provides an extensive list of SCCM Status Message IDs and their titles. This will help you understand what each message ID represents and how you can use them for better auditing.

Here is the link to the Microsoft Technet Blog that contains detailed information on status message IDs.

Conclusion

By using Status Filter, Default Reports, or SQL Queries, you can effectively track and monitor the audit status of your SCCM environment. SQL queries, in particular, offer advanced flexibility for detailed audits. The sample query provided above is just one example, and you can modify it according to your specific needs. For further reference, the Microsoft Technet Blog provides a complete list of status message IDs and their explanations, which will be extremely helpful for identifying and troubleshooting issues within your SCCM environment.

Stay on top of your SCCM environment's health, and leverage these methods for effective auditing and troubleshooting

SCCM Co-Management Workload Details and Remediation

  With modern device management relying heavily on co-management , administrators often need insights into how workloads are being distribut...