MSEndpointMgr

Detect and remove application from dependent task sequences with PowerShell

I  believe most of us use install application steps during operating system deployment, unless you are using fully dynamic scripts to install applications. We often do changes in our task sequence, we made a copy of the original task sequence, made it as backup, then start modify the new ones. In some point, we updated some of applications, and we might want to delete those old applications, but oops, cannot delete, because of number of dependent task sequences is not 0.

This popup message doesn’t tell what are the dependent task sequences, it is not so easy to find out which task sequence it is when you have lots of them. Luckily we have PowerShell. Here is a very simple PowerShell script can do it for you. You can also import ConfigMgr PowerShell module what is mentioned in Jordan’s post How to import ConfigMgr powershell cmdlets

You can find this script from GitHub

# Site configuration
$ApplicationName = "7-zip" #Name of the application
$SiteServer = "CM01.ZIT.local" #Your site server name
$SiteCode ="T01" #Your site code

# Customizations
$initParams = @{}

# Import the ConfigurationManager.psd1 module 
if((Get-Module ConfigurationManager) -eq $null) {
    Import-Module "$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1" @initParams 
}

# Connect to the site's drive if it is not already present
if((Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue) -eq $null) {
    New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName @initParams
}

# Set the current location to be the site code.
Set-Location "$($SiteCode):\" @initParams

#Get SMS_ApplicationLatest WMI object
$Application = Get-CMApplication -Name "$ApplicationName"

#Get Application Model Names
$ApplicationModelName = $Application.ModelName
$TaskSequencePackageIDs = (Get-WmiObject -Namespace "root\SMS\site_$($SiteCode)" -Class "SMS_TaskSequenceAppReferencesInfo" -ComputerName $SiteServer -Filter "RefAppModelName like '$ApplicationModelName'").PackageID
foreach ($TaskSequencePackageId in $TaskSequencePackageIds)
{
    $TaskSquenceName = (Get-CMTaskSequence -TaskSequencePackageId $TaskSequencePackageId).Name
    Write-host "Application name: $($Application.LocalizedDisplayName). Dependent TaskSequence is: $TaskSquenceName." -ForegroundColor Green
}

If you want to run this remotely without using Configuration Manager cmdlets, use this script from GitHub.

Finally, this script will remove the application from dependent task sequences, it use same technique as Nickolaj’s post Modify Task Sequence steps in ConfigMgr with PowerShell , please read Nickolaj’s post for reference.

You will find the script from GitHub

IMPORTANT: As always, running any kind of script in your ConfigMgr server is dangerous without testing, so please do test these scripts in your test environment before run it in production.

THE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND”

(Maybe we will make a GUI for this. Let’s see.. πŸ˜€ )

Sandy Zeng

Sandy is an Enterprise Mobility MVP since 2018. She is an experienced Information Technology Specialist for over 10 years. Skilled in Microsoft Endpoint Manager (ConfigMgr and Intune), Windows 10 and security. Sandy's interests are mostly related to Microsoft Technologies, she has passions learning new skill sets to improve her professional career and also as her hobbies. She uses her expertise to help customers achieve their goals and solve their issues.

Sandy founded the https://sandyzeng.com blog and is now a blogger on MSEndPointMgr.

20 comments

  • I am unable to run this script. My Provider resides on my SQL server, not on my site server. Any suggestions on getting this script to run? Here is the error code I get when running it on my site server or on my SQL server which hosts the Provider role:

    Change this Path? (y/n): n
    Get-WmiObject : Invalid namespace “root\SMS\site_EM1”
    At C:\Scripts\DetectAppInTS.PS1:45 char:28
    + … ckageIDs = (Get-WmiObject -Namespace “root\SMS\site_$($SiteCode)” -Cl …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidArgument: (:) [Get-WmiObject], ManagementException
    + FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand

  • I’m struggling to get this working whenever i run the script i just get
    ——————————————————
    Application name: Java 8 Update 231
    No mention of what TS is referencing the application. I’ve trawled through all my TS’s and can’t see that it is referenced anywhere but when trying to delete the Application it says it’s used in 2 TS’s so frustrating.

  • Hi,

    Thanks a lot for sharing this however; I am not able to run it. Probably I am being dumb but unable to run this script. Can anyone please help?

    Rome

  • Question?… could the delete script be edited to “Replace” the app?

    so, if the TS has old flash XX that is now flash YY, can it delete XX and add YY to the TS?

    • @Russ Ring
      Yep…. that’s what I’ve been working on (and I think cracked) in this script below. Hope it helps you or someone else, as this site has helped me no end.

      Script is bastardised from this one and elsewhere, then tweaked.

      Import-Module “$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1” # Import the ConfigurationManager.psd1 module
      $CurrentLocation = get-location | select -ExpandProperty path # gets current user location
      Set-Location “AB1:” # Set the current location to be the site code.

      [array]$DropDownArrayItemsOLD = Get-CMApplication | select -ExpandProperty localizeddisplayname
      [array]$DropDownArrayOLD = $DropDownArrayItemsOLD | sort

      [array]$DropDownArrayItemsNEW = Get-CMApplication | select -ExpandProperty localizeddisplayname
      [array]$DropDownArrayNEW = $DropDownArrayItemsNEW | sort

      # This Function Returns the Selected Value and Closes the Form

      function Return-DropDown {
      if ($DropDownOLD.SelectedItem -eq $null -or $DropDownNew.SelectedItem -eq $null){
      $DropDownOLD.SelectedItem = $DropDownOLD.Items[0]
      $DropDownNEW.SelectedItem = $DropDownNEW.Items[0]
      $script:ChoiceOLD = $DropDownOLD.SelectedItem.ToString()
      $script:ChoiceNEW = $DropDownNEW.SelectedItem.ToString()
      $Form.Close()
      }
      else{
      $script:ChoiceOLD = $DropDownOLD.SelectedItem.ToString()
      $script:ChoiceNEW = $DropDownNEW.SelectedItem.ToString()
      $Form.Close()
      }
      }

      function SelectApplication{
      [void] [System.Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”)
      [void] [System.Reflection.Assembly]::LoadWithPartialName(“System.Drawing”)

      $Form = New-Object System.Windows.Forms.Form

      $Form.width = 300
      $Form.height = 150
      $Form.Text = “Replace an Application in Dependant TS”

      $DropDownOLD = new-object System.Windows.Forms.ComboBox
      $DropDownOLD.Location = new-object System.Drawing.Size(100,10)
      $DropDownOLD.Size = new-object System.Drawing.Size(130,30)

      $DropDownNEW = new-object System.Windows.Forms.ComboBox
      $DropDownNEW.Location = new-object System.Drawing.Size(100,50)
      $DropDownNEW.Size = new-object System.Drawing.Size(130,30)

      ForEach ($Item in $DropDownArrayOLD) {
      [void] $DropDownOLD.Items.Add($Item)
      }

      ForEach ($Item in $DropDownArrayNEW) {
      [void] $DropDownNEW.Items.Add($Item)
      }

      $Form.Controls.Add($DropDownOLD)
      $Form.Controls.Add($DropDownNEW)

      $DropDownOLDLabel = new-object System.Windows.Forms.Label
      $DropDownOLDLabel.Location = new-object System.Drawing.Size(10,10)
      $DropDownOLDLabel.size = new-object System.Drawing.Size(100,40)
      $DropDownOLDLabel.Text = “Select Current Application:”
      $Form.Controls.Add($DropDownOLDLabel)

      $DropDownNEWLabel = new-object System.Windows.Forms.Label
      $DropDownNEWLabel.Location = new-object System.Drawing.Size(10,50)
      $DropDownNEWLabel.size = new-object System.Drawing.Size(100,40)
      $DropDownNEWLabel.Text = “Select New Application:”
      $Form.Controls.Add($DropDownNEWLabel)

      $Button = new-object System.Windows.Forms.Button
      $Button.Location = new-object System.Drawing.Size(120,80)
      $Button.Size = new-object System.Drawing.Size(100,20)
      $Button.Text = “Confirm Replace”
      $Button.Add_Click({Return-DropDown})
      $form.Controls.Add($Button)
      $form.ControlBox = $false

      $Form.Add_Shown({$Form.Activate()})
      [void] $Form.ShowDialog()

      return $script:choiceOLD
      return $script:choiceNEW
      }

      $Group = $null
      $Group = SelectApplication
      while ($Group -like “”){
      $Group = SelectApplication
      }

      $CurrentApp = $script:choiceOLD #Name of the Current application
      $NewApp = $script:choiceNEW #Name of the New application
      $SiteServer = “Site.Server” #Your site server name
      $SiteCode =”AB1″ #Your site code
      # Customizations

      #Get SMS_ApplicationLatest WMI object
      $Current = Get-WmiObject -Namespace “root\SMS\site_$($SiteCode)” -Class “SMS_ApplicationLatest” -ComputerName $SiteServer -Filter “LocalizedDisplayName=’$CurrentApp’ and IsExpired=’False’ ”
      $New = Get-WmiObject -Namespace “root\SMS\site_$($SiteCode)” -Class “SMS_ApplicationLatest” -ComputerName $SiteServer -Filter “LocalizedDisplayName=’$NewApp’ and IsExpired=’False’ ”

      Write-host “——————————————————” -ForegroundColor Gray
      Write-host “Current Application name: ” -fore gray -NoNewline; Write-Host $($Current.LocalizedDisplayName) -ForegroundColor Yellow
      Write-host “New Application name: ” -fore gray -NoNewline; Write-Host $($new.LocalizedDisplayName) -ForegroundColor Green
      Write-host “——————————————————” -ForegroundColor Gray

      #Get Application Model Names
      $CurrentAppModelName = $Current.ModelName
      $NewAppModelName = $New.ModelName

      #Get SMS_TaskSequenceAppReferencesInfo WMI Object
      $TaskSequencePackageIDs = (Get-WmiObject -Namespace “root\SMS\site_$($SiteCode)” -Class “SMS_TaskSequenceAppReferencesInfo” -ComputerName $SiteServer -Filter “RefAppModelName Like ‘$CurrentAppModelName'”).PackageID
      Foreach ($TaskSequencePackageId in $TaskSequencePackageIds){
      If ($TaskSequencePackageIds.length -cgt 1){

      # Get SMS_TaskSequencePackage WMI object
      $TaskSequencePackage = Get-WmiObject -Namespace “root\SMS\site_$($SiteCode)” -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Filter “PackageID like ‘$TaskSequencePackageId'”
      $TaskSequencePackage.Get()

      #Get Task SequenceName
      $TaskSquenceName = $TaskSequencePackage.Name
      write-host “`r`r`r”
      Write-host ” Dependent TaskSequence is: ” -fore gray -NoNewline; Write-Host $TaskSquenceName -ForegroundColor Cyan
      write-host “`r`r`r”
      Write-host ” Step Name: ” -fore gray

      # Get SMS_TaskSequence WMI object from TaskSequencePackage
      $TaskSequence = Invoke-WmiMethod -Namespace “root\SMS\site_$($SiteCode)” -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name “GetSequence” -ArgumentList $TaskSequencePackage

      # Convert WMI object to XML
      $TaskSequenceResult = Invoke-WmiMethod -Namespace “root\SMS\site_$($SiteCode)” -Class SMS_TaskSequence -ComputerName $SiteServer -Name “SaveToXml” -ArgumentList $TaskSequence.TaskSequence
      $TaskSequenceXML = [xml]$TaskSequenceResult.ReturnValue

      # Amend variable for Install applications, only replace those applications that we need to
      $InstallApplicationVariableNodes = Select-Xml -Xml $TaskSequenceXML -XPath “//variable[contains(@name,’ApplicationName’)]”
      foreach ($InstallApplicationVariableNode in $InstallApplicationVariableNodes)
      {
      $InstallApplicationVariableNode.Node.’#text’ = $InstallApplicationVariableNode.Node.’#text’ -replace “$CurrentAppModelName,”, “$NewAppModelName,” -replace “,$CurrentAppModelName”, “,$NewAppModelName” -replace “$CurrentAppModelName”, “$NewAppModelName”
      }

      # Amend command line for Install other applications, only replace those applications that we need to
      $InstallApplicationCommandLineNodes = Select-Xml -Xml $TaskSequenceXML -XPath “//step[contains(@type,’SMS_TaskSequence_InstallApplicationAction’)]”
      foreach ($InstallApplicationCommandLineNode in $InstallApplicationCommandLineNodes)
      {
      if ($InstallApplicationCommandLineNode.Node.action -match $CurrentAppModelName) {
      $InstallApplicationCommandLineNode.Node.action = $InstallApplicationCommandLineNode.Node.action -replace “$CurrentAppModelName,”, “$NewAppModelName,” -replace “,$CurrentAppModelName”, “,$NewAppModelName” -replace “$CurrentAppModelName”, “$NewAppModelName”
      Write-Host ” $($InstallApplicationCommandLineNode.Node.name) ” -ForegroundColor Cyan -NoNewline; write-host “– ” -fore White -NoNewline; Write-Host $($Current.LocalizedDisplayName) -fore Yellow -NoNewline; Write-Host ” replaced with ” -fore gray -NoNewline; Write-Host $($New.LocalizedDisplayName) -fore Green
      }
      }

      #Replace Application from Task Sequence step
      Try
      {
      # Convert XML back to SMS_TaskSequencePackage WMI object
      $TaskSequenceResult = Invoke-WmiMethod -Namespace “root\SMS\site_$($SiteCode)” -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name “ImportSequence” -ArgumentList $TaskSequenceXML.OuterXml

      # Update SMS_TaskSequencePackage WMI object
      Invoke-WmiMethod -Namespace “root\SMS\site_$($SiteCode)” -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name “SetSequence” -ArgumentList @($TaskSequenceResult.TaskSequence, $TaskSequencePackage) | Out-Null
      write-host “`r`r`r”
      Write-Host ” Application ” -fore Gray -NoNewline; Write-Host $($New.LocalizedDisplayName) -fore Green -NoNewline; Write-Host ” has replaced ” -fore Gray -NoNewline; Write-Host $($Current.LocalizedDisplayName) -fore Yellow -NoNewline; Write-Host “, In Task Sequence ” -fore Gray -NoNewline; Write-Host $TaskSquenceName -fore Cyan
      write-host “`r`r`r”
      Write-Host ” Operation Succeded!” -ForegroundColor Green
      Write-host ” ——————” -ForegroundColor Green
      write-host “`r`r`r”

      }
      catch
      {
      Write-Host “Remove application $($Current.LocalizedDisplayName) and replace with $($New.LocalizedDisplayName) in task sequence $TaskSquenceName failed .Error message: $($_.Exception.Message)” -ForegroundColor red
      }
      }
      }
      If ($TaskSequencePackageIds.length -cgt 1){
      Write-host “——————————————————” -ForegroundColor Gray
      }

      If ($TaskSequencePackageIds -eq $null)
      {
      Write-host “——————————————————” -ForegroundColor Gray
      Write-host “There are No Dependant TaskSequences for — ” -fore gray -NoNewline; Write-Host $($Current.LocalizedDisplayName) -fore Yellow -NoNewline; Write-Host ” to be replaced in” -fore gray
      Write-host “——————————————————” -ForegroundColor Gray
      }
      Set-location $CurrentLocation

      Ta!

      • Hello, thanks for posting this. Scripts format might goes wrong in comments, do you mind upload that to your github or OneDrive and share the link?

  • Excellent post. Would it be possible to do a similar thing for Packages instead of Applications?
    I tried something like:
    $package = Get-CMPackage -Name “Google Chrome 71.0.1234.56”
    $TaskSequencePackageIDs = (Get-WmiObject -Namespace “root\SMS\site_$($SiteCode)” -Class “SMS_TaskSequenceAppRe
    ferencesInfo” -ComputerName $SiteServer -Filter “RefAppModelName like ‘$packageName'”).PackageID

    But $TaskSequencePackageIDs keeps coming back blank, even though I know the package I’m naming as a test exists in multiple task sequences.

  • Thanks for this, it’s just what I need. The first script to show the dependent task sequences works flawlessly. I am running into an issue when I run the script to actually remove the applications from the TSes though. When trying to remove a particular app (in this case, an old version of Android Studio) I get something like:

    ——————————————————
    Application name: Android Studio 143.2915827
    —–
    Dependent TaskSequence is: Client Dev Win7x64
    Select-Xml : Cannot bind parameter ‘Xml’.

    …which is then followed by the xml for the entire TS that I’ll cut for brevity (but can post if needed), then ending with:

    ” value of type “System.String” to type “System.Xml.XmlNode”.
    At D:\SysAdmin\Scripts\SCCM\RemoveApplicationReferencedinTS.ps1:36 char:59
    + … pplicationCommandLineNodes = Select-Xml -Xml $TaskSequenceXML -XPath …
    + ~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidArgument: (:) [Select-Xml], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.SelectXmlCommand

    Invoke-WmiMethod : Generic failure
    At D:\SysAdmin\Scripts\SCCM\RemoveApplicationReferencedinTS.ps1:48 char:31
    + … nceResult = Invoke-WmiMethod -Namespace “root\SMS\site_$($SiteCode)” …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [Invoke-WmiMethod], ManagementException
    + FullyQualifiedErrorId : InvokeWMIManagementException,Microsoft.PowerShell.Commands.InvokeWmiMethod

    Invoke-WmiMethod : Generic failure
    At D:\SysAdmin\Scripts\SCCM\RemoveApplicationReferencedinTS.ps1:51 char:9
    + Invoke-WmiMethod -Namespace “root\SMS\site_$($SiteCode)” -Cla …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [Invoke-WmiMethod], ManagementException
    + FullyQualifiedErrorId : InvokeWMIManagementException,Microsoft.PowerShell.Commands.InvokeWmiMethod

    Remove application Android Studio 143.2915827 from Task sequence Client Dev Win7x64 succeded

    It repeats for the additional four TSes it finds the app in and doesn’t actually succeed. Anyone else run into anything similar?

    • Hello, I just test that in my test lab, it still works without errors. When you run into errors, does it give you any values for $TaskSequence, $TaskSequenceResult, $TaskSequenceXML and $InstallApplicationVariableNodes? Looks like it failed in Select-Xml. Did you modified the script? Because in your error log Invoke-WmiMethod -Namespace β€œroot\SMS\site_$($SiteCode) is in line 48 and 51, I have different line numbers in my original script. Can you post your script to OneDrive or any download location?

      • Hi Zeng, thanks for your reply. I think the line numbers might be different because the formatting removed the blank lines when I originally copied the script over, but I haven’t edited any other way (at least not intentionally) apart from setting the variables at the top. I just copied it over again and I still seem to get the same error (albeit now with different line/character for that portion). By running ($Error[#]).InvocationInfo.Line (there’s probably a better way to do this) I could expand the error a bit and find errors at the following variables (including ones for $TaskSequenceResult and $InstallApplicationVariableNodes) but it doesn’t appear to provide me with much info. The only part I cut off in my earlier post just includes the XML for the TS (which I can’t attach without editing first due to it containing credentials in plain text):

        Invoke-WmiMethod -Namespace “root\SMS\site_$($SiteCode)” -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name “SetSequence” -ArgumentList @($TaskSequenceResult.TaskSequence, $TaskSequencePa
        ckage) | Out-Null

        $TaskSequenceResult = Invoke-WmiMethod -Namespace “root\SMS\site_$($SiteCode)” -Class SMS_TaskSequencePackage -ComputerName $SiteServer -Name “ImportSequence” -ArgumentList $TaskSequenceXML.OuterXml

        $InstallApplicationCommandLineNodes = Select-Xml -Xml $TaskSequenceXML -XPath “//step[contains(@type,’SMS_TaskSequence_InstallApplicationAction’)]”

        $InstallApplicationVariableNodes = Select-Xml -Xml $TaskSequenceXML -XPath “//variable[contains(@name,’ApplicationName’)]”

        Here’s a link to the script I’m running: https://onedrive.live.com/?authkey=%21APGeglw8zide5kM&cid=088783C0238CB2AD&id=88783C0238CB2AD%2112107&parId=88783C0238CB2AD%2112104&o=OneUp

        Incidentally we are currently on 1806 and upgrading tomorrow. Thanks again for the response!

      • Hello Tim, sorry for the delay. Will you try what Yuchi suggested?

      • I apologize for my slow response, but I can confirm that Yuchi’s fix, adding [xml] to the $TaskSequenceXML variable, worked great (and as a bonus, I improved my Powershell skill a bit in the process of going through the script too, which is always welcome). And wow, what an elegant script it is.

        Thanks again for this incredibly helpful script and great blog. I don’t comment often but SCConfigMgr has helped me many times since we’ve implemented SCCM, and it’s very much appreciated.

      • Hello Tim, thanks for spotting out this issue and testing it. We are very happy that we have such strong community to help each other, thanks again for supporting us and your kindly comments.

      • Thank you Zeng so much. I’m learning so much on your Website and this script is very useful. Thank you all @SCConfigMgr.

        I had the same error as Tim mention above. It seems to be the variable $TaskSequenceXML isn’t parsing the data as XML so I added the [XML] under the # Convert WMI object to XML section.

        $TaskSequenceXML = [xml]$TaskSequenceResult.ReturnValue

      • Thank you Yuchi for you help! I will update my script. Glad to hear you like our blog!

  • Thank you so much for posting this. As a Senior SCCM Administrator managing (157) Distribution Points…..SCCONFIGMGR is the most helpful site on the Web. I can’t thank you all enough!

    • That’s a lot of Distribution Points. πŸ™‚ We are glad that we can help and share, thanks for supporting us!

Sponsors