Install User-targeted Applications during OS Deployment via PowerShell and ConfigMgr 2012

Let’s start my first post of this great new year with another nice PowerShell script. This post will be about deploying required user targeted applications, to the device of the primary user, during OS deployment. Yes, I know the setting of Pre-deploy software to the user’s primary device, but that doesn’t give enough control. After deployment, the device has to be completely ready for use.

A bit more than a year ago I already did a post about pre-provisioning user applications during OS deployment via Orchestrator and ConfigMgr 2012. This time I wanted to make less assumptions. I also wanted to be sure that a user is a member of a collection and what the application is that is deployed to the collection. I first tried to achieve this goal by adjusting my runbook in Orchestrator and I made it work (and it still works), but to make it work I had to use all custom PowerShell activities. This made me think why I still wanted this and I couldn’t come up with something better than “because I can”. So I decided to make one PowerShell script to find the applications and to create the task sequence variables.

Script

The main part of this script is gathering data and filtering it. In short I could say this script consists of five queries and an action. The following six steps make sure that I only get the applications, that are required for the primary user of the device, to be installed during the OS deployment.

Step 1 – Get the Primary User

The first step is to get the primary user of the device that’s being deployed. That information can be retrieved in WMI in the class SMS_UserMachineRelationship. This class shows the relationship of a user with a device, even when it’s only a suggestion yet. The properties of Sources and Types can be used to see how the primary user is defined and to see if it’s a suggestion or a “real” affinity. I know that, in my case, all the device affinities are administrator defined. So to get the user name of the primary user of a device I use the following code snippet (format is <Domain>\<User>:

$PrimaryUser = (Get-WmiObject -ComputerName $SiteServer ` -Class SMS_UserMachineRelationship ` -Namespace root\SMS\Site_$SiteCode ` -Filter "ResourceName='$ResourceName'").UniqueUserName

Step 2 – Get the Container Node

The second step is to get the container node of the application deployment collections. This will be used to make sure that only collections used for application deployments will be queried. This information can be retrieved in WMI in the class SMS_ObjectContainerNode. This class shows the different folders in the console and its location. The property ObjectTypeName can be used to see the type of objects in the folder. In my case, I occasionally use identical folder names. So to get the container node information that I need, I use the following code snippet:

$ContainerNodeId = (Get-WmiObject -ComputerName $SiteServer ` -Class SMS_ObjectContainerNode ` -Namespace root/SMS/site_$SiteCode ` -Filter "Name='$Container' and ` ObjectTypeName='SMS_Collection_User'").ContainerNodeId

Step 3 – Get the Collections

The third step is to get the collections within the container. This information can be retrieved in WMI in the class SMS_ObjectContainerItem. This class shows the relation between an container and the objects within an container. So to get the collections within the container I use the following code snippet:

$InstanceKeys = (Get-WmiObject -ComputerName $SiteServer ` -Class SMS_ObjectContainerItem ` -Namespace root/SMS/site_$SiteCode ` -Filter "ContainerNodeID='$ContainerNodeId'").InstanceKey

Step 4 – Filter the Collections

The fourth step is to filter the collections on a specific collection member. This will make sure that only collections used for application deployments AND with the specific collection member will be queried later on.  This information can be found in WMI in the class SMS_FullCollectionMembership. This class shows the different collection members and their memberships. The best thing, the property SMSID shows the collection member in exactly exactly the same format as I have the primary user of the device. So to filter the collections I use the following code snippet:

$CollectionId = (Get-WmiObject -ComputerName $SiteServer ` -Class SMS_FullCollectionMembership ` -Namespace root/SMS/site_$SiteCode ` | Where-Object {$_.CollectionID -eq $InstanceKey -and ` $_.SMSID -eq $PrimaryUser}).CollectionId

Note: For an unknown reason, to me, a normal filter did not work together with the property SMSID. That’s why I had to use an where-object statement.

Step 5 – Get the targeted Applications

The fifth step is to get the applications that are targeted to the filtered collection. This makes sure that only applications deployed to collections, of which the primary user of the device is a member, will be filtered. This information can be found in WMI in the class SMS_ApplicationAssignment. The property OfferTypeID can be used to see if the deployment is required or available. I only want to have the required applications. So this makes that I use the following code snippet:

$ApplicationNames = (Get-WmiObject -ComputerName $SiteServer ` -Class SMS_ApplicationAssignment ` -Namespace root/SMS/site_$SiteCode ` -Filter "TargetCollectionID='$CollectionId' and ` OfferTypeID='0'").ApplicationName

Step 6 – Create the Task Sequence Variables

The sixth, and last step, is to create task sequence variables for the applications that have to be installed during the OS deployment. For every application I create a task sequence variable named APPIdXX with the value of the application. To achieve this I use the following code snippet:

foreach ($ApplicationName in $ApplicationNames) { $Id = "{0:D2}" -f $Count $AppId = "APPId$Id" $TSEnv.Value($AppId) = $ApplicationName $Count = $Count + 1 }

Note: In the complete script I already created a variable $Count with the value 0 and an object named $TSEnv of Microsoft.SMS.TSEnvironment.

>> The complete script is available via download here on the TechNet Galleries! <<

Usage

Now download the PowerShell script via the link above and add the PowerShell script to an old-school Package, so it will be available for a task sequence. Then create a standard Install an existing image package task sequence. Now edit the task sequence and make sure the following steps are included:

  • imageAdd a step Run PowerShell Script with the following settings:
    • Package: <NameOfPackageThatContainsTheScript>
    • Script name: <NameOfTheScript>
    • Parameters: %_SMSTSMachineName%
      • Note: The script needs more input parameters, but I usually add those parameters directly in the script as they are “static” per environment.
    • PowerShell execution policy: Bypass
  • Add a step Install Application with the following settings:
    • Select Install applications according to dynamic variable list
    • Base variable name: APPId

Note: The computer account running the script needs read access to ConfigMgr. So in most cases this would mean that the Domain Computers need read access to ConfigMgr. This can be achieved via the build-in role of Read-only Analyst.

70 thoughts on “Install User-targeted Applications during OS Deployment via PowerShell and ConfigMgr 2012”

  1. in regards to Chris’s response – “I just figured this out, I need to force the UDA relationship before OSD makes this happen at the end of my build. Doing that the query pulls the correct information.”

    How would you “Force” this relationship before the end of the build?

  2. Hello Peter,

    Thank you for the script. I am getting an incorrect function when initializing the script. I was wondering if you could include a screenshot of the Get Application Task Sequence Step so I can ensure that I am including the parameters correctly. Thanks.

    Andy

  3. Hi,

    I get the following error when running the script in a task sequences, I have granted “domain computers” read only access to SCCM as per your suggestion. If I run the script interactively it does retrieve assigned applications.
    Any suggestions on how to fix this would be very helpful.

    Get-WmiObject : Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)) RunPowerShellScript 14/02/2019 09:44:51 1644 (0x066C)
    At C:\_SMSTaskSequence\Packages\UKP006C9\Get-TargetedApplications_User.ps1:21 char:22 RunPowerShellScript 14/02/2019 09:44:51 1644 (0x066C)
    + … aryUsers = (Get-WmiObject -ComputerName $SiteServer -Class SMS_UserMa … RunPowerShellScript 14/02/2019 09:44:51 1644 (0x066C)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ RunPowerShellScript 14/02/2019 09:44:51 1644 (0x066C)
    + CategoryInfo : NotSpecified: (:) [Get-WmiObject], UnauthorizedAccessException RunPowerShellScript 14/02/2019 09:44:51 1644 (0x066C)
    + FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.GetWmiObjectCommand RunPowerShellScript 14/02/2019 09:44:51 1644 (0x066C)
    RunPowerShellScript 14/02/2019 09:44:51 1644 (0x066C)

    Thanks,
    Martin.

  4. Hi Peter,

    Yes at the point the user app install step run (in the TS) the computer is joined to the domain.

    Regards,
    Martin.

  5. Hi Peter,

    I should first state that I have copied one of the posters and amended the script itself with the following changes:

    [CmdletBinding()]
    $tsenv=New-Object -ComObject Microsoft.SMS.TSEnvironment
    $ResourceName=$tsenv.Value(“_SMSTSMACHINENAME”)
    $SiteCode=””
    $SiteServer=””
    $Container=”Approved Managed Applications”

    I have a run PowerShell step in the TS the references the script, this step has the following set up:
    Package: PKGID, Get-TargetedApplications
    Script name: Get-TargetedApplications_User.ps1
    Parameters: empty due to changes made to script
    PowerShell execution policy: Bypass

    The next step is Install Application and this is configured as per your screen shot, the only difference being that I have enable the option “if an application installation fails, continue installing other applications in the list.

    The TS is standard MDT one modified to fit our requirements, I know machines are domain joined prior to these steps as the TS includes a step to install Microsoft LAPS, this will only install on domain joined systems.

    Thanks, Martin.

  6. Hi Peter,

    Success, I had to grant the domain group “Domain Computers” direct permissions on the site_ WMI Namespace. This is a bit odd as by adding “Domain Computers” to the SCCM read only role the group has been added to the local group “SMS Admins” which already has the correct access rights on the Namespace.

    Martin.

  7. Is there a way to achieve this without giving domain computers read access to the wmi?
    For example by using a service account and in the ts use the option to run the powershells with that service account?

  8. Hi Peter,

    Excellent to have this script available yet I am running into numerous obstacles and keep hitting the below errors. I have verified all the required permissions, followed your steps as you have highlighted but still cannot get tot the bottom of this. If you can provide any help at all it would be much appreciated as I am under pressure to get this into production.

    Many Thanks in advance

    Jon

    You cannot call a method on a null-valued expression.
    At C:\temp\Get-TargetedApplications_User.ps1:8 char:5
    + $PrimaryUsers = (Get-WmiObject -ComputerName $SiteServer -Class S …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
    At C:\temp\Get-TargetedApplications_User.ps1:31 char:9
    + $TSEnv.Value(“SkipApplications”) = “True”
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OperationStopped: (:) [], UnauthorizedAccessException
    + FullyQualifiedErrorId : System.UnauthorizedAccessException

    SMSTS.LOG

    No Env variable with specified basename APPId and suffix ’01’ is found. No applications installed.

    Failed to run the action: Install Application.
    Unspecified error (Error: 80004005; Source: Windows)

  9. Hi Peter,

    Thanks for your response, it was my fault, I had set the script to run under my account, that was the issue !! 🙂
    Just a final question, only about 1% of applications out of 500 are set to “required”, is there any way to bypass this and still force previously installed applications to install ?

    Many Thanks in advance

    Jon

  10. Hello Peter,
    I am running into the problem that after the OS deployment TS, the user/device affinity is gone/empty from the object. So userbased apps will no longer get pre-deployed.
    Regards, Hans

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.