Tuesday, 1 July 2025

SQL Script to List SCCM Applications Details



📘 Overview

In SCCM (Microsoft Endpoint Configuration Manager), gaining a clear view of your applications, their deployment types, source content, and install/uninstall behaviors is critical for maintaining a healthy environment. Whether you are auditing your environment, troubleshooting deployment issues, or preparing for a migration, having a detailed application inventory is a must.

In this blog post, we’ll walk through a powerful SQL query that pulls application metadata, deployment information, content size, install/uninstall commands, and more — directly from your SCCM database.


🔍 What This Query Does

This SQL query retrieves:

  • Application name and description
  • Deployment type details
  • Source path and size
  • Status (Active/Retired)
  • Install and uninstall command lines
  • Detection method
  • Admin comments and metadata

All tied together using SCCM’s AppModel and CI relationships.


📋 The SQL Query

DECLARE @LocaleID INT = (SELECT LocaleID FROM vSMSData);

 

WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/SystemCenterConfigurationManager/2009/AppMgmtDigest')

SELECT

    app.DisplayName AS ApplicationName,

    pkg.PackageID,

    CASE

        WHEN dtt.IsExpired LIKE '1' THEN 'Retired'

        ELSE 'Active'

    END AS [Application Status],

    cp.SourceSize AS [Source Size in KB],

    app.Description,

    ad.AdminComments,

    Dtt.DisplayName AS [DeploymentTypeName],

    Dtt.Technology,

    dt.CI_ID,

    dt.SDMPackageDigest.value('(/AppMgmtDigest/DeploymentType/Title)[1]', 'nvarchar(max)') AS [DeploymentTypeName],

    ab.NumberOfDeploymentTypes AS [No of Deployments],

    dt.SDMPackageDigest.value('(/AppMgmtDigest/DeploymentType/Installer/Contents/Content/Location)[1]', 'nvarchar(max)') AS [SourcePath],

    dt.SDMPackageDigest.value('(/AppMgmtDigest/DeploymentType/Installer/InstallAction/Args/Arg)[1]', 'nvarchar(max)') AS [InstallCommandLine],

    dt.SDMPackageDigest.value('(/AppMgmtDigest/DeploymentType/Installer/UninstallAction/Args/Arg)[1]', 'nvarchar(max)') AS [UninstallCommandLine],

    dt.SDMPackageDigest.value('(/AppMgmtDigest/DeploymentType/DetectionMethod/Setting)[1]', 'nvarchar(max)') AS DetectionSetting

FROM v_ConfigurationItems dt

INNER JOIN vSMS_CIRelation rel ON dt.CI_ID = rel.ToCIID

INNER JOIN fn_ListLatestApplicationCIs_List(@LocaleID) app ON app.CI_ID = rel.FromCIID

INNER JOIN v_Package p ON p.SecurityKey = app.ModelName

INNER JOIN vSMS_ContentPackage cp ON cp.PkgID = p.PackageID

INNER JOIN dbo.Fn_Listdeploymenttypecis(1033) AS Dtt ON Dtt.AppModelName = app.ModelName

INNER JOIN fn_ListLatestApplicationCIs(1033) AS ab ON ab.ModelName = app.ModelName

INNER JOIN v_Package pkg ON pkg.SecurityKey = ab.ModelName

INNER JOIN v_Applications ad ON ad.ModelID = app.ModelID

WHERE dt.CIType_ID = 21 -- Deployment Type

  AND dt.IsLatest = 1;  -- Latest version only

 


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.


SQL Script to List SCCM Applications Details

📘 Overview In SCCM (Microsoft Endpoint Configuration Manager), gaining a clear view of your applications, their deployment types...