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 MigrationProject Overview

This project aims to migrate all applications from SCCM to Intune. This involved: 

  1. Identifying applications, packages, and task sequences in SCCM that were actively deployed. 
  1. 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). 
  1. 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 :

SCCM to Microsoft Intune Migration

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:

SCCM to Microsoft Intune Migration

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:

SCCM to Microsoft Intune Migration

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. 

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!

Comments (0)