Browsed by
Tag: ConfigMgr

Create a Device Collection based on a User Collection

Create a Device Collection based on a User Collection

I had an interesting discussion with a past colleague the other day where he was asking around to find out if it was possible to create a Device Collection based off a User Collection using the Primary Device option. After a bit of back and forth and him providing the query he was using for the user collection we started playing around with SubSelect queries to see if we can even reference User objects in the device collection.

So to start off we create the simple user query of get user x when in domain y like so:

select *

from SMS_R_User

where SMS_R_User.UserPrincipalName Like ‘%hosking%’

and SMS_R_USER.WindowsNTDomain like ‘%domain%’

Which will return my user account into a User Collection let’s call “Steves User Account” with a collection ID of “P0100024” from here we will move over to the device collection area of the console and create a collection called “Steve Primary Computers” and for this collection we will use the following query:

select *

from SMS_R_System

where SMS_R_System.resourceID in

(select SMS_UserMachineRelationship.resourceID

from SMS_R_User

join SMS_UserMachineRelationship on SMS_UserMachineRelationship.UniqueUserName = SMS_R_User.UniqueUserName

where SMS_R_User.UserPrincipalName Like ‘%hosking%’

and SMS_R_USER.WindowsNTDomain like ‘%domain%’)

So what we are doing here is using the SMS_UserMachineRelationship table which holds the information for Primary Computers for each user, and limiting the search for my username and Domain, which we then return the ResourceID, and finally we then return the SMS_R_system Resources which are in the query into the Device collection.

So this works great, but it does mean that I now have the same query in 2 different locations so if I want to change the query in the User Collection, I will then need to update the query in the Computer Collection.

So we looked at just including the User Collection itself in the query, this information is actually captured in a table accessible via WMI in tables that share this type of naming convention: SMS_CM_RES_COLL_<CollectionID> so from here we wrote this query:

select sms_r_system.name

from SMS_R_System

where SMS_R_System.resourceID in

(select SMS_UserMachineRelationship.resourceID

from SMS_CM_RES_COLL_P0100024

join SMS_UserMachineRelationship on SMS_UserMachineRelationship.UniqueUserName = SMS_CM_RES_COLL_P0100024.SMSID)

This query is even easy then the middle one as all we are doing using the UniqueUserName field from the SMS_UserMachineRelationship table and joining it with the SMSID from the collection table and returning the ResourceID like we did in the previous query.

Good Luck

Steve

Capturing installed Windows Features from Client OS into SCCM without touching the MOF

Capturing installed Windows Features from Client OS into SCCM without touching the MOF

Recently I was asked how to capture the Enabled Windows features from client machines, to help identify the crazy cats who have Hyper-V turned on just so they can run an extra OS or 2 on there Surface Pro 3. So this one was an interesting question in the server OS there is the Win32_ServerFeatures WMI class which is great on servers, but doesn’t exist on client OS’s. All of the information is captured in DSIM right, but how do you turn that into a WMI Class for reporting purposes.

I started off by creating a PowerShell Script with plans to run it in DCM to create the Registry keys which we have traditionally used to capture the information in SCCM, which looks like this:

$f = “”

$feature = dism /online /get-features

foreach ($svc in $feature)

{

if ($svc -like “*Feature Name *”)

{

$f = $f + $svc.Replace(“Feature Name : “, “”) + “,”

}

elseif ($svc -like “*State : *”)

{

$f = $f + $svc.Replace(“State : “,“”) + “;”

}

}

 

Push-Location

Set-Location HKLM:

if ((Test-Path .\software\clientFeatures) -eq $false){New-Item -Path .\software -name clientFeatures | out-null}

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

New-ItemProperty .\software\clientFeatures -Name $feat -Value $featset -PropertyType “string” -Force | Out-Null

$feat = “”

$featset = “”

}

}

Pop-Location

So let’s breakdown the script and explain what each of the sections are doing

$feature = dism /online /get-features

This puts the default DISM result into the $feature variable

I know your sitting there like me and wondering how you can turn that into a registry entry.

Well that’s where the next phase of the script comes in and creates a single string

foreach ($svc in $feature)

{

if ($svc -like “*Feature Name *”)

{

$f = $f + $svc.Replace(“Feature Name : “, “”) + “,”

}

elseif ($svc -like “*State : *”)

{

$f = $f + $svc.Replace(“State : “,“”) + “;”

}

}

Which steps through each of the lines in the result of the DISM command, and finds the lines which have “Feature Names” and “State” then writes into string called $f which we will use later. As we have put the , after the Feature name and the ; after the state, we can then split these in the next phase of the script. Which I will now explain what we are doing there:

Push-Location

Set-Location HKLM:

if ((Test-Path .\software\clientFeatures) -eq $false){New-Item -Path .\software -name clientFeatures | out-null}

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

New-ItemProperty .\software\clientFeatures -Name $feat -Value $featset -PropertyType “string” -Force | Out-Null

$feat = “”

$featset = “”

}

}

Pop-Location

At a high level we are connecting to HKEY_LOCAL_MACHINE and created a new Key called ClientFeatures under the Software key, then creating a new REG_SZ for each of the features with the states as the value of the key. At this point I was really happy I had all of the features in the registry in a format I could then create a MOF from, this was short lived as I noticed I had over 100 features in the client OS which would create a heartache when creating the MOF file as it would be around 300 lines of boring code. So I went to myself, self why don’t you just create the WMI class with PowerShell then you don’t have to worry about getting the MOF file right, which I responded to self with that’s a great idea I’m amazed I didn’t think of it myself. Below you will see the resulting script:

if ((Get-WmiObject -Class Win32_ClientFeatures -ErrorAction SilentlyContinue).__PROPERTY_COUNT -lt 1)

{

$newClass = New-Object System.Management.ManagementClass(“root\cimv2”, [String]::Empty, $null)

$newClass[“__CLASS”] = “Win32_ClientFeatures”

$newClass.Qualifiers.Add(“Static”, $true)

$newClass.Properties.Add(“key”,[System.Management.CimType]::String, $false)

$newClass.Properties[“key”].Qualifiers.Add(“Key”, $true)

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

$newClass.Properties.Add($feat,[System.Management.CimType]::String, $false)

$newClass.Put() | Out-Null

$feat = “”

$featset = “”

}

}

}

if ((Get-WmiObject -Class Win32_ClientFeatures).__path -eq $null){$new = $true} else {$inst = (Get-WmiObject -Class Win32_ClientFeatures).key}

if ($new) {$classinstance = $newClass.CreateInstance()}

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

if ($new)

{

$classinstance.$feat = $featset

$classinstance.put() | Out-Null

}

else

{

$fe = Get-WmiObject -Class Win32_clientFeatures

$fe.$feat = $featset

$fe.put() | Out-Null

}

$feat = “”

$featset = “”

}

}

Let’s dive into the breakdown of the slab of code,

if ((Get-WmiObject -Class Win32_ClientFeatures -ErrorAction SilentlyContinue).__PROPERTY_COUNT -lt 1)

This if statement is checking to see if the WMI Class called Win32_ClientFeatures has any properties assigned to it, if the count is less than 1 then we will go ahead and create the WMI Class and create a Property for each of the Windows Features which is this slab of code:

{

$newClass = New-Object System.Management.ManagementClass(“root\cimv2”, [String]::Empty, $null)

$newClass[“__CLASS”] = “Win32_ClientFeatures”

$newClass.Qualifiers.Add(“Static”, $true)

$newClass.Properties.Add(“key”,[System.Management.CimType]::String, $false)

$newClass.Properties[“key”].Qualifiers.Add(“Key”, $true)

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

$newClass.Properties.Add($feat,[System.Management.CimType]::String, $false)

$newClass.Put() | Out-Null

$feat = “”

$featset = “”

}

}

}

We have also created a key called “key” inventive I know, but it works. So we now have a WMI Class with all of the properties the next step is to create a WMI instance of the state of each of the Windows Features like so:

if ((Get-WmiObject -Class Win32_ClientFeatures).__path -eq $null){$new = $true} else {$inst = (Get-WmiObject -Class Win32_ClientFeatures).key}

if ($new) {$classinstance = $newClass.CreateInstance()}

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

if ($new)

{

$classinstance.$feat = $featset

$classinstance.put() | Out-Null

}

else

{

$fe = Get-WmiObject -Class Win32_clientFeatures

$fe.$feat = $featset

$fe.put() | Out-Null

}

$feat = “”

$featset = “”

}

}

The if statement is checking to see if the there is an existing instance in the WMI object and selects that instance to update, otherwise we will go ahead and create a new instance. As you can see in the script these are 2 different groups of code.

So I now have a great WMI class which I can query with PowerShell and it returns like so:

We are now sitting there really happy with our new WMI class which captures all of the Windows Features settings for the computer great. The next step is to import the new WMI class into SCCM Hardware Inventory, and this is where it became interesting, as I got this error message:

I started doing some digging into this and found that the Remote Server Administration Tools features all started the same along with the language packs, so I tweaked the script to handle this and is still didn’t correct the issue. At about this point I came to the realisation that as easy as it would be from a reporting point of view I would be next to impossible to maintain as each OS version has different features, and then you need to include the RSAT features, and Language packs, which would result of in hundreds of properties required in WMI for each OS just to capture the information.

To get around this we then need to change the WMI class to have 2 properties called Feature which we will make as the Key for the instances, and value which will contain the state for the Feature. To complete this we run the following script:

$f = “”

$new = $true

$feature = dism /online /get-features

foreach ($svc in $feature)

{

if ($svc -like “*Feature Name *”)

{

$f = $f + $svc.Replace(“Feature Name : “, “”) + “,”

}

elseif ($svc -like “*State : *”)

{

$f = $f + $svc.Replace(“State : “,“”) + “;”

}

}

if ((Get-WmiObject -Class ClientFeatures -ErrorAction SilentlyContinue).__PROPERTY_COUNT -lt 1)

{

$newClass = New-Object System.Management.ManagementClass(“root\cimv2”, [String]::Empty, $null)

$newClass[“__CLASS”] = “Win32_ClientFeatures”

$newClass.Qualifiers.Add(“Static”, $true)

$newClass.Properties.Add(“Feature”,[System.Management.CimType]::String, $false)

$newClass.Properties[“Feature”].Qualifiers.Add(“Key”, $true)

$newClass.Properties.Add(“value”,[System.Management.CimType]::String, $false)

$newClass.Put() | out-null

}

 

foreach ($r in $f.Split(“;”))

{

 

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

if ((Get-WmiObject -Class Win32_ClientFeatures).feature -eq $feat)

{

$fe = Get-WmiObject -query “select * from win32_clientFeatures where feature = ‘$feat‘”

$fe.value = $featset

$fe.put() | Out-Null

}

else

{

$classinstance = $newClass.CreateInstance()

$classinstance.feature = $feat

$classinstance.value = $featset

$classinstance.put() | Out-Null

}

$feat = “”

$featset = “”

}

}

The first part is the same as above, but once we get to defining the WMI class it changes a little, now rather than stepping through each of windows features and creating a property for each feature we are now just creating 2 properties called “Feature” and “Value”, only if the WMI class doesn’t exist, as defined in the below script:

if ((Get-WmiObject -Class ClientFeatures -ErrorAction SilentlyContinue).__PROPERTY_COUNT -lt 1)

{

$newClass = New-Object System.Management.ManagementClass(“root\cimv2”, [String]::Empty, $null)

$newClass[“__CLASS”] = “Win32_ClientFeatures”

$newClass.Qualifiers.Add(“Static”, $true)

$newClass.Properties.Add(“Feature”,[System.Management.CimType]::String, $false)

$newClass.Properties[“Feature”].Qualifiers.Add(“Key”, $true)

$newClass.Properties.Add(“value”,[System.Management.CimType]::String, $false)

$newClass.Put() | out-null

}

The next step is to create a new instance for each features, defining the feature name and the state like so:

foreach ($r in $f.Split(“;”))

{

 

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

if ((Get-WmiObject -Class Win32_ClientFeatures).feature -eq $feat)

{

$fe = Get-WmiObject -query “select * from win32_clientFeatures where feature = ‘$feat‘”

$fe.value = $featset

$fe.put() | Out-Null

}

else

{

$classinstance = $newClass.CreateInstance()

$classinstance.feature = $feat

$classinstance.value = $featset

$classinstance.put() | Out-Null

}

$feat = “”

$featset = “”

}

}

We are also checking to see if the instance already exists for the feature, if it does exist, we will need to update the value, otherwise we create a new class instance.

Now when we import the WMI class into the SCCM console it doesn’t throw an error, and we can see the v_GS_ClientFeatures in SQL Reporting Services as an option to report upon now, so how do I get the list of computers which have Hyper-V Enabled, well you create a SQL Report with the following query:

select rsys.Name0

from v_R_System as rsys join

v_GS_CLIENT_FEATURES as feat on rsys.ResourceID = feat.ResourceID

where Feature0 = ‘Microsoft-Hyper-V’ and

value0 = ‘enabled’

And the WMI Query for a collection looks like this:

select * from SMS_R_System inner join SMS_G_System_CLIENT_FEATURES on SMS_G_System_CLIENT_FEATURES.ResourceId = SMS_R_System.ResourceId where SMS_G_System_CLIENT_FEATURES.Feature = “Microsoft-Hyper-V” and SMS_G_System_CLIENT_FEATURES.value = “Enabled”

The DCM detection PowerShell script looks like this:

$compliance = “Compliant”

$f = “”

$feature = dism /online /get-features

foreach ($svc in $feature)

{

if ($svc -like “*Feature Name *”)

{

$f = $f + $svc.Replace(“Feature Name : “, “”) + “,”

}

elseif ($svc -like “*State : *”)

{

$f = $f + $svc.Replace(“State : “,“”) + “;”

}

}

if ((Get-WmiObject -Class win32_ClientFeatures -ErrorAction SilentlyContinue).__PROPERTY_COUNT -eq 2)

{

foreach ($r in $f.Split(“;”))

{

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

$cls = Get-WmiObject -query “select * from win32_clientFeatures where feature = ‘$feat‘”

if ($cls.value -ne $featset) {$compliance = “Non-Compliant”}

$feat = “”

$featset = “”

}

}

}

else

{

$compliance = “Non-Compliant”

}

$compliance

And the DCM remediation PowerShell script looks like this:

$f = “”

$new = $true

$feature = dism /online /get-features

foreach ($svc in $feature)

{

if ($svc -like “*Feature Name *”)

{

$f = $f + $svc.Replace(“Feature Name : “, “”) + “,”

}

elseif ($svc -like “*State : *”)

{

$f = $f + $svc.Replace(“State : “,“”) + “;”

}

}

if ((Get-WmiObject -Class ClientFeatures -ErrorAction SilentlyContinue).__PROPERTY_COUNT -lt 1)

{

$newClass = New-Object System.Management.ManagementClass(“root\cimv2”, [String]::Empty, $null)

$newClass[“__CLASS”] = “Win32_ClientFeatures”

$newClass.Qualifiers.Add(“Static”, $true)

$newClass.Properties.Add(“Feature”,[System.Management.CimType]::String, $false)

$newClass.Properties[“Feature”].Qualifiers.Add(“Key”, $true)

$newClass.Properties.Add(“value”,[System.Management.CimType]::String, $false)

$newClass.Put() | out-null

}

 

foreach ($r in $f.Split(“;”))

{

 

if ($r -ne “”)

{

$feat = $r.Split(“,”)[0].ToString()

$featset = $r.Split(“,”)[1].ToString()

if ((Get-WmiObject -Class Win32_ClientFeatures).feature -eq $feat)

{

$fe = Get-WmiObject -query “select * from win32_clientFeatures where feature = ‘$feat‘”

$fe.value = $featset

$fe.put() | Out-Null

}

else

{

$classinstance = $newClass.CreateInstance()

$classinstance.feature = $feat

$classinstance.value = $featset

$classinstance.put() | Out-Null

}

$feat = “”

$featset = “”

}

}

$compliance = “Compliant”

$compliance

For the DCM you will need to do a detection on a string that equals “Compliant” and remediate if it not.

As an recap of the whole blog, you can see there is a couple of ways to capture the information, depending upon the amount of properties it might make sense to have a single WMI instance in the WMI class, but in other cases it might make sense to have a large number of WMI instances in the WMI class, each have their advantages and disadvantages for the methods. The nice part of this solution is we don’t need to worry about making any changes to the MOF which makes life so much easier.

Good Luck

Steve

The Easy way to Deploy Windows 10 Tech Preview using SCCM 2012 R2

The Easy way to Deploy Windows 10 Tech Preview using SCCM 2012 R2

We have all heard the news of the “Awesome” new features which we will love with Windows 10; for the most part I really do love the awesome new features. For this post I’m going to focus on the “keep everything” upgrade of Windows. Yes you herd me correctly, you can keep everything, which includes all of the tweaks you have made to your applications… how awesome is that! The best thing about the whole solution is that you can empower your staff to complete this deployment using the Application Deployment model from SCCM 2012 which I will detail below:

DISCLAIMER: this is NOT officially supported in production yet so you should really test this process in a non-production environment.

First off we need to get the ISO for Windows 10 from Microsoft which if you don’t already have it you can download it from here or MSDN if you are lucky enough to have a subscription.

We then need to mount the ISO and copy the contents into your source directory.

Once copied we then head into SCCM and browse to Software Library\ Application Management\ Applications and Create Application

Select Manually Specify the application information then Next

Fill out the pertinent General information and select Next

Complete the Application Catalog page using the Icon from setup.exe (not required but makes it look pretty) and select Next

Add a Deployment Type following the below:

Change the type to Script Installer which will automatically change it to Manually Specify the Deployment information then select Next

Fill out the name of the Deployment Type, Select Next

Define the location of your source and point to setup.exe as the installation program (for ease of documenting this I haven’t used an unattended.xml but you can in your environment if you want to reduce the user input, use /? On setup.exe to find the details for it.) Select Next

Now on the Detection Method we need to work out how to see if Windows 10.0.9926 is already installed, and confirm that it was successful after the fact to do that we are going to use a PowerShell Script which looks like this:

if (((Get-WmiObject -Class win32_operatingsystem).version).startswith(“10.0.9926”)) {write-host $true} else {}

 The version number can be changed to whatever the target version of windows is.

Select Ok

Then Next

Update the User Experience tab to look like this (making sure to extend the Max allowed
run time), then select Next

For this demo I’m going to leave the Requirements and Dependencies
empty (you might want to limit the version of windows it can target and alike), so Next, Next

Select Next on the Summary Page

Then select Close which will return you to the Create Application Wizard

Which you can select Next, Next, close to complete the creation of the application.

To deploy the application to your test users, select the Application, then Deploy from the Ribbon

As this is a test environment I’m going to use the All Users Collection (in production this should rarely happen) and then select Next

As we have just created the Application the content hasn’t been deployed, so add a Distribution Point, and select Next

Leave the Defaults for the rest of the Wizard, select Next until the end and then Close

You can now browse to your Application Catalog and select the Application we have just deployed from the Catalog.

Which will then appear in Software Center on the client, once the files download the user will be prompted to complete the standard Windows 10 upgrade prompts.

Wait an hour or depending upon how powerful your machine is, then the computer will be running the latest version of Windows 10.

Good Luck

Steve