

Many organizations are transitioning from SCCM to Microsoft Intune as they modernize their device management strategies. This shift is often a broader move toward cloud-based management and streamlined operations. However, SCCM to Microsoft Intune Migration can be a complex process, especially when dealing with a large amount of content like applications, packages, and task sequences.
In this blog post, I’ll share my experience working on a project to migrate SCCM content to Intune. I’ll walk you through the steps I took to generate the list of applications, packages, and task sequences for investigation/take decision, and how I used SQL queries and PowerShell scripts to streamline the process.
SCCM to Microsoft Intune Migration – Project Overview
This project aims to migrate all applications from SCCM to Intune. This involved:
- Identifying applications, packages, and task sequences in SCCM that were actively deployed.
- Assessing which of these should be migrated to Intune and which could be decommissioned. (I am not covering this part as it is an internal process and usage behaviour of the apps).
- Ensuring that no new applications or packages were created in SCCM during the migration process. All new content should be in Intune.
To achieve this, I used a combination of SQL queries to extract relevant data from SCCM and PowerShell scripts to automate certain tasks, such as disabling deployments and moving obsolete content to a designated “Decom” folder.
Step 1: Prevent New Content Creation in SCCM
Before starting the migration, it’s important to make sure that no new applications, packages, or task sequences are created in SCCM (unless Intune has limitation or so). This avoids duplication of effort and ensures that all new deployments are managed through Intune.
Solution: Create an SCCM Report to Track New Content
To monitor new content creation, I created an SCCM report using SQL Server Reporting Services (SSRS). This report tracks applications, packages, and task sequences created within a specified time frame (e.g., the last 30 days).
The report uses the following SQL views to retrieve the necessary data:
- v_Application: Tracks application details.
- v_Package: Tracks package details.
- v_TaskSequencePackage: Tracks task sequence details.
I’ve uploaded the RDL files for these reports to my GitHub repository. You can download them, import them into SSRS, modify the data source, and run the reports.
Step 2: Identify Applications, Packages, and Task Sequences with Active Deployments
The next step is to identify which applications, packages, and task sequences have active deployments in SCCM. This helps determine what needs to be migrated to Intune and what can be decommissioned.
Organize Content into Folders
To simplify the process, I created a new folder in a respective category called “Decom” (applications, packages, and task sequences). This folder serves as a holding area for content that won’t be migrated to Intune.
SQL Queries to Extract Deployment Data
I used SQL queries (feel free to adjust them according to your needs) to generate lists of applications, packages, and task sequences with active deployments. These queries filter content based on specific criteria, such as folder paths and collection folders.
1. Applications with Deployments
The following SQL query retrieves a list of applications with active deployments. It includes details such as the application name, folder, deployment type, collection name, and whether the application is part of a task sequence.
select
ParentApp.displayname [App Name],F.Name [Folder],p.PackageID,
case when ds.Enabled='1' then 'Yes' else 'No' End as 'DT Enabled',
fmc.Name [CollName],
case when fmc.CollectionType='2' then 'Device' else 'User' end as 'CollType',
case when fmc.CollectionID not in (select CRQ.collectionid from v_CollectionRuleQuery CRQ) and collg.Flags in ('2','4','6') then 'Static' else 'Dynamic' end as 'Query',
fmc.MemberCount [Count],
parentapp.CreatedBy [Created By],ParentApp.DateCreated,parentapp.DateLastModified,datediff(dd,parentapp.DateLastModified,GETDATE()) as 'Age of App (days)',
case when c.OfferTypeID = 2 then 'Available'
when c.OfferTypeID = 0 then 'Required'
else cast(C.OfferTypeID as nvarchar)
end as 'OptionalOrRequired',
case when parentapp.IsEnabled='1' then 'Yes' else 'No' end as IsAppEnabled,
case when parentapp.IsDeployed='1' then 'Yes' else 'No' end as IsAppDeployed,
parentapp.NumberOfDeploymentTypes [No of DT],
count(distinct ChildApp.DisplayName) AS [No of Dependencies],
ISNULL((
select count(distinct apps.CI_UniqueID) From dbo.fn_ListApplicationCIs(1033) apps
inner Join fn_ListDeploymentTypeCIs(1033) appDT ON appDT.AppModelName = apps.ModelName and appdt.IsLatest = 1
inner Join vSMS_AppRelation_Flat appRel on appRel.FromApplicationCIID = apps.CI_ID
inner Join fn_ListApplicationCIs_List(1033) ChildApp on ChildApp.CI_ID = appRel.ToApplicationCIID And ChildApp.IsLatest = 1 and apps.IsDeployed = 1
where apps.IsLatest=1 and childapp.CI_ID = parentapp.CI_ID
group by childapp.CI_ID, childapp.DisplayName
), 0) dependentdeployments,
parentapp.NumberOfDevicesWithApp,
parentapp.NumberOfDevicesWithFailure,
case when parentapp.CI_ID in (select RefAppCI_ID from v_TaskSequenceAppReferencesInfo) then 'Yes' else 'No' end as 'Part of TS'
From dbo.fn_ListApplicationCIs(1033) ParentApp
Left Join fn_ListDeploymentTypeCIs(1033) ParentApp_DT ON ParentApp_DT.AppModelName = ParentApp.ModelName
Left Join vSMS_AppRelation_Flat R on R.FromApplicationCIID = ParentApp.CI_ID
Left Join fn_ListApplicationCIs_List(1033) ChildApp on ChildApp.CI_ID = R.ToApplicationCIID And ChildApp.IsLatest = 1
inner Join vFolderMembers FM on FM.InstanceKey =ParentApp.ModelName
inner Join vSMS_Folders F on F.ContainerNodeID = FM.ContainerNodeID
left join fn_DeploymentSummary(1033) ds on ParentApp.CI_ID=ds.CI_ID
left join v_Collection fmc on fmc.CollectionID=ds.CollectionID
left join v_Collections_G collg on collg.SiteID=fmc.CollectionID
inner join v_CIAssignment c on c.CollectionID=ds.CollectionID
inner join v_Package p on p.Name=ParentApp.DisplayName
inner join v_Collections coll on coll.SiteID=fmc.CollectionID
where
( fm.ObjectPath like '/Workstation%' )
and ( fm.ObjectPath not like '/Workstation/Decom%' )
--and coll.ObjectPath like '%Workstation%')
and ds.Enabled='1'
group by parentapp.DisplayName,parentapp.CreatedBy,parentapp.DateLastModified,parentapp.NumberOfDeploymentTypes,parentapp.NumberOfDeployments,
parentapp.NumberOfDevicesWithApp,
ds.Enabled,
c.AssignmentName, c.AssignmentEnabled,
parentapp.NumberOfDevicesWithFailure,
parentapp.IsEnabled,
parentapp.IsDeployed,
parentapp.CI_ID,
F.Name,fmc.Name,ParentApp.DateCreated,ParentApp.CI_ID,
fmc.CollectionType,fmc.CollectionID,
collg.Flags,
fmc.MemberCount,c.OfferTypeID,
p.PackageID
order by 2
This is the result of the query :

2. Packages with Deployments
Similarly, the following query retrieves a list of packages with active deployments:
SELECT
p.Name,F.Name [Pkg Folder],
p.SourceDate [Pkg Created],
fmc.Name [CollName],
case when fmc.CollectionType='2' then 'Device' else 'User' end as 'CollType',
case when fmc.CollectionID not in (select CRQ.collectionid from v_CollectionRuleQuery CRQ) and collg.Flags in ('2','4','6') then 'Static' else 'Dynamic' end as 'Query',
fmc.MemberCount [Count],
A.AdvertisementName AS [Adv Name],
A.PresentTime AS 'Adv Created',
MAX(CAS.LastStatusTime) AS [Recent App installed],
datediff(dd,MAX(CAS.LastStatusTime),GETDATE()) as 'Age of recent Install (days)',
p.packageID,
A.ProgramName,
case when p.PackageID NOT IN (SELECT ReferencePackageID FROM v_TaskSequenceReferencesInfo) then 'No' else 'Yes' end as 'Part of TS'
FROM
v_Package P
left JOIN v_Advertisement A ON P.PackageID = A.PackageID
INNER JOIN v_ClientAdvertisementStatus CAS ON A.AdvertisementID = CAS.AdvertisementID
inner Join vFolderMembers FM on FM.InstanceKey =p.PackageID and fm.ObjectType='2'
inner Join vSMS_Folders F on F.ContainerNodeID = FM.ContainerNodeID
left join v_Collection fmc on fmc.CollectionID=a.CollectionID
left join v_Collections_G collg on collg.SiteID=fmc.CollectionID
join v_Collections coll on coll.SiteID=fmc.CollectionID
WHERE
p.Packagetype=0
and fm.ObjectPath like '/Workstation%'
and ( fm.ObjectPath not like '/Workstation/Decom%' )
--coll.ObjectPath like '%Workstation%'
GROUP BY
A.AdvertisementName,A.PresentTime,A.CollectionID,P.PkgSourcePath,P.Name,P.PackageID,A.ProgramName,
P.Description,fmc.Name,fmc.CollectionType,collg.CollectionID,collg.Flags,fmc.CollectionID,fmc.MemberCount,F.Name,p.SourceDate
ORDER BY
A.AdvertisementName,
A.PresentTime
This is the result of the query:

3. Task Sequences with Deployments
Finally, this query retrieves a list of task sequences with active deployments:
SELECT
P.Name,
fmc.Name [CollName],
max(coll.ObjectPath) [Folder],
case when fmc.CollectionType='2' then 'Device' else 'User' end as 'CollType',
case when fmc.CollectionID not in (select CRQ.collectionid from v_CollectionRuleQuery CRQ) and collg.Flags in ('2','4','6') then 'Static' else 'Dynamic' end as 'Query',
fmc.MemberCount [Count],
case when fmc.Name like 'APP-P%' then 'Yes' else 'No' End 'Orchestrated',
A.AdvertisementName AS [Adv Name],
A.PresentTime AS 'Adv Created',
MAX(CAS.LastStatusTime) AS [Recent App installed],
datediff(dd,MAX(CAS.LastStatusTime),GETDATE()) as 'Age of recent Install (days)',
p.packageID
FROM
v_Advertisement A
left JOIN v_Package P ON P.PackageID = A.PackageID
INNER JOIN v_ClientAdvertisementStatus CAS ON A.AdvertisementID = CAS.AdvertisementID
inner Join vFolderMembers FM on FM.InstanceKey =p.PackageID -- and fm.ObjectType='2'
inner Join vSMS_Folders F on F.ContainerNodeID = FM.ContainerNodeID
left join v_Collection fmc on fmc.CollectionID=a.CollectionID
left join v_Collections_G collg on collg.SiteID=fmc.CollectionID
join v_Collections coll on coll.SiteID=fmc.CollectionID
WHERE
-- (CAS.LastStateName != 'Accepted - No Further Status') AND
A.PackageID IN (SELECT PackageID FROM v_TaskSequencePackage)
and coll.ObjectPath like '%Workstation%'
and f.FolderPath not like '/Workstation/Decom'
and f.ObjectTypeName='SMS_TaskSequencePackage'
GROUP BY
A.AdvertisementName,A.PresentTime,A.CollectionID,P.PkgSourcePath,P.Name,P.PackageID,A.ProgramName,
P.Description,fmc.Name,fmc.CollectionType,collg.CollectionID,collg.Flags,fmc.CollectionID,fmc.MemberCount ,coll.ObjectPath ,F.Name ,p.SourceDate
ORDER BY
A.AdvertisementName,
A.PresentTime
This is the result of the query:

Step 3: Migrate or Decommission Identified Content
Once you’ve identified the content to be migrated, you can use Intune to upload and deploy the applications. For content that won’t be migrated, you can disable its active deployments in SCCM and move it to the “Decom” folder.
Automating the Process with PowerShell
To streamline this process, I used PowerShell scripts to:
- Disable active deployments for applications that won’t be migrated.
- Move obsolete applications, packages, and task sequences to the “Decom” folder.
- You can download these scripts and SQL queries from my GitHub repository.
Conclusion:
Migrating from SCCM to Intune is a significant undertaking, but with the right tools and processes, it can be managed effectively. By using SQL queries to identify active deployments and PowerShell scripts to automate decommissioning tasks, you can ensure a smooth transition to Intune.
If you found this post helpful or have additional tips for migrating from SCCM to Intune, feel free to share your thoughts in the comments below!
Only authorized users can leave comments
Log In