Create Collections in ConfigMgr 2012 with Powershell

Not got time to write the usual three-page idiosyncratic blurb, here’s a script which will create four different collections for a pre-existing Application and move the collections to specific Folders (As usual the word wrap has probably screwed it up a little so i have attached the script as ps1 as well here):

Function CreateCollection($collName,$limitingColl,$container,$ud_Scope){

$collInstance = ([wmiclass]”\\server\root\sms\site_001:sms_collection”).CreateInstance()

$collInstance.Name = $collName

$collInstance.LimitToCollectionID = $limitingColl

if($ud_Scope -eq “device”){

$collInstance.CollectionType = 2

}elseif($ud_Scope -eq “user”){

$collInstance.CollectionType = 1

}

if(gwmi -ComputerName server -Namespace root\sms\site_001 -Class sms_collection | ?{$_.Name -eq $collName}){

“Collection $collName already exists”

}else{

$collInstance.Put()

$collInstance.Get()

$containerItemInstance = ([wmiclass]”\\server\root\sms\site_001:sms_objectcontaineritem”).CreateInstance()

$containerItemInstance.ContainerNodeID = $container

$containerItemInstance.InstanceKey = $collInstance.CollectionID

if($ud_Scope -eq “device”){

$containerItemInstance.ObjectType = 5000

$containerItemInstance.ObjectTypeName = ‘SMS_Collection_Device’

}elseif($ud_Scope -eq “user”){

$containerItemInstance.ObjectType = 5001

$containerItemInstance.ObjectTypeName = ‘SMS_Collection_User’

}

$containerItemInstance.SourceSite = ‘001’

$containerItemInstance.Put()

}

}

$scope = Read-Host -Prompt “Enter Scope ID (DEV,INT,PRD)”

gwmi -ComputerName server -Namespace root\sms\site_001 -Class sms_objectcontainernode | ?{$_.Name -eq ‘Software Distribution’} | %{

$parentContainerID = $_.parentContainerNodeID

if(gwmi -ComputerName server -Namespace root\sms\site_001 -Class sms_objectcontainernode | ?{$_.ContainerNodeID -eq $parentContainerID -and $_.Name -eq $scope}){

gwmi -ComputerName server -Namespace root\sms\site_001 -Class sms_objectcontainernode | ?{$_.Name -eq ‘Software Distribution’ -and $_.parentContainerNodeID -eq $parentContainerID -and $_.ObjectTypeName -eq “SMS_Collection_Device”} | %{

$parentContainerID = $_.ContainerNodeID

gwmi -ComputerName server -Namespace root\sms\site_001 -Class sms_objectcontainernode | ?{$_.Name -eq ‘Software Requests’ -and $_.parentContainerNodeID -eq $parentContainerID -and $_.ObjectTypeName -eq “SMS_Collection_Device”} | %{

$devContainerID = $_.ContainerNodeID

}

}

gwmi -ComputerName server -Namespace root\sms\site_001 -Class sms_objectcontainernode | ?{$_.Name -eq ‘Software Distribution’ -and $_.parentContainerNodeID -eq $parentContainerID -and $_.ObjectTypeName -eq “SMS_Collection_User”} | %{

$parentContainerID = $_.ContainerNodeID

gwmi -ComputerName server -Namespace root\sms\site_001 -Class sms_objectcontainernode | ?{$_.Name -eq ‘Software Requests’ -and $_.parentContainerNodeID -eq $parentContainerID -and $_.ObjectTypeName -eq “SMS_Collection_User”} | %{

$usrContainerID = $_.ContainerNodeID

}

}

}

}

$devLimitingCollID = (gwmi -ComputerName server -Namespace root\sms\site_001 -Class sms_collection | ?{$_.Name -eq “Root-$scope”} | Select CollectionID).CollectionID

$usrLimitingCollID = “SMS00002”

 

gwmi -ComputerName server -Namespace root\sms\site_001 -Class sms_applicationlatest | ?{$_.SecuredScopeNames -contains $scope -and $_.LocalizedDisplayName -eq ‘OracleClient’} | select LocalizedDisplayName | %{

$appName = $_.LocalizedDisplayName

$collectionName = “$appName – Install Device”

CreateCollection $collectionName $devLimitingCollID $devContainerID “device”

$collectionName = “$appName – Uninstall Device”

CreateCollection $collectionName $devLimitingCollID $devContainerID “device”

$collectionName = “$appName – Install User”

CreateCollection $collectionName $usrLimitingCollID $usrContainerID “user”

$collectionName = “$appName – Uninstall User”

CreateCollection $collectionName $usrLimitingCollID $usrContainerID “user”

}

Advertisement

Create a Tree View of Applications and Dependencies

Here’s a quick code to get a tree view (sort of) of applications in your ConfigMgr 2012 environment that shows dependencies, including dependencies of dependencies. Works as is, for me at least. If you spot any bugs or improvements feel free to let me know. As usual watch out for word-wrapping and typos.

Function Get-Dependency($appCIID,$appNameFunc){

gwmi -ComputerName $server -Namespace “root\sms\site_$code -Class SMS_AppDependenceRelation -Filter “FromApplicationCIID=’$appCIID | %{

if($_.ToApplicationCIID -ne $null){

$ToApplicationCIID = $_.ToApplicationCIID

$dependencyName = Resolve-ApplicationName $ToApplicationCIID    

$dependencyNameSub = ” –>$dependencyName

Write-Host $dependencyNameSub     

Get-SubDependency $ToApplicationCIID $dependencyNameSub   

}

 }

return

}

Function Get-SubDependency($appCIID, $appNameFunc){

$appNamePrefix = ” “ + ($appNameFunc -split “>”)[0] + “>”

gwmi -ComputerName $server -Namespace “root\sms\site_$code -Class SMS_AppDependenceRelation -Filter “FromApplicationCIID=’$appCIID | %{

if($_.ToApplicationCIID -ne $null){

$ToApplicationCIID = $_.ToApplicationCIID

$dependencyName = Resolve-ApplicationName $ToApplicationCIID   

$dependencyName =$appNamePrefix$dependencyName

Write-Host$dependencyName

Get-SubDependency $ToApplicationCIID $dependencyName   

}

}

return

}

Function Resolve-ApplicationName($appCIID){

gwmi -ComputerName $server -Namespace “root\sms\site_$code -Class SMS_ApplicationLatest -Filter “CI_ID = ‘$appCIID‘” | select LocalizedDisplayName | %{

return $_.LocalizedDisplayName

}

}

cls

$server = “serverName”

$code = “001”

gwmi -ComputerName $server -Namespace “root\sms\site_$code -Class SMS_ApplicationLatest | %{

$appName = $_.LocalizedDisplayName

$CIID = $_.CI_ID

Write-Host$appName

Get-Dependency $CIID $appName

}

Enable TPM in a Task Sequence (DELL)

It took a while but I found a way to enable the TPM in the BIOS, then activate.

At first glance it should have been easy. Using the CCTK from Dell, set the TPM to enable from a command line and then a reboot and finally activate and a further reboot. But it would always only enable and never activate.

This is because the TPM would create an ownership on the chip itself and while the chip is ‘owned’ it cannot be activated. At least not by the CCTK.

In the end, after a lot of trial and error, I found a way to guarantee the chip is enabled and activated and ready for BitLocker, so here are the steps:

1. Set BIOS password with CCTK: Create a package from the Dell CCTK in ConfigMgr. Use this Package witha Command Line Step in the Task Sequence to set a setup password “cctk –setuppwd=password”

2. Enable TPM with CCTK: Using the Package again, run CCTK to enable TPM “cctk –tpm=on –valsetuppwd=password”

3. Restart Computer – if you perform these actions in the BIOS itself then you don’t need to restart. But here in a Task Sequence the WMI Classes need to be reloaded in order that we can see the Win32_TPM class.

4. Clean the chip ownership: I use a powershell script here but you can use vbscript. If you use powershell then you need to first set the Execution Policy with a command line step: “powershell.exe -noprofile -command “&{set-executionpolicy unrestricted -force}” then call the following script:

$oTPM = gwmi -Class Win32_TPM -Namespace root\CIMV2\Security\MicrosoftTpm

$oTPM.SetPhysicalPresenceRequest(10)

If(!(($oTPM.IsEndorsementKeyPairPresent()).IsEndorsementKeyPairPresent)){

$oTPM.CreateEndorsementKeyPair()

}

If(($oTPM.IsEndorsementKeyPairPresent()).IsEndorsementKeyPairPresent){

$OwnerAuth=$oTPM.ConvertToOwnerAuth(“customrandompassword”)

$oTPM.Clear($OwnerAuth.OwnerAuth)

$oTPM.TakeOwnership($OwnerAuth.OwnerAuth)

}

5. Restart again, this time you will get a prompt at the BIOS to press F10 to accept the chip changes

6. Activate the TPM with CCTK: “cctk –tpmactivation=activate –valsetuppwd=password”

7. Restart again.

That’s it.

ConfigMgr 2012 – Change Application Source Path

Big thanks to Angel Redondo for helping out with this.

Changing the source path for packages in SCCM 2007 was pretty easy over the SMSProvider.
It’s a bit different in ConfigMgr 2012.

Looking through the WMI Classes you’ll find SMS_Application, SMS_DeploymentType, and eventually SMS_Content. This last one has the source path for the application content but you’ll soon find out you can’t manipulate this because it is a system class and only allowed to be used by the provider itself.

What you need to do is leverage the dlls provided with the AdminConsole. Then you need to understand that information in ConfigMgr is handled completely differently from 2007. That being data is stored in XML format in WMI and SQL to allow for SQL Replication to look after much of the hierarchical and global replication. It is really good and much more efficient but might take a couple of attempts to get used to. Also the content source is defined on the Deployment Type object.

To alter Applications, in particular something like the source path, you need to connect to the Application class, the instance and then deserialize the XML data. Then you can edit this information, serialize it and inject it back into the WMI object.

I am assuming Powershell and that the Admin Console is installed on the machine running the script.
First of all get your ConfigMgr assemblies into your script:
#import assemblies
[System.Reflection.Assembly]::LoadFrom(“C:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\Microsoft.ConfigurationManagement.ApplicationManagement.dll”)
[System.Reflection.Assembly]::LoadFrom(“C:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\Microsoft.ConfigurationManagement.ApplicationManagement.Extender.dll”)
[System.Reflection.Assembly]::LoadFrom(“C:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\Microsoft.ConfigurationManagement.ApplicationManagement.MsiInstaller.dll”)

#Specify your site server and sitecode:
$server = “configmgr01”
$code = “001”

#Get the applications (i’m only doing the latest version here):
#gwmi -ComputerName $server -Namespace root\sms\site_$code -class sms_application | ?{$_.IsLatest -eq $true}

#Either save this an object or loop on the fly, i’m looping on the fly:

gwmi -ComputerName $server -Namespace root\sms\site_$code -class sms_application | ?{$_.IsLatest -eq $true} | %{
#get the instance of the application
$app = [wmi]$_.__PATH
#deserialize the XML data
$appXML = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::DeserializeFromString($app.SDMPackageXML,$true)
#loop through the deployment types
foreach($dt in $appXML.DeploymentTypes){
#find the installer element of the XML
$installer = $dt.Installer
#the content for each installer is stored as an single element array
$content = $installer.Contents[0]
“Current Path: ” + $content.Location
#use a regular expression to modify the existing path to your new location
$newPath = $content.Location -replace ‘\\\\domain.loc\\unc\\share’,’\\namespace.loc\dfs’
“New Path : ” + $newPath
if($newPath -ne $content.Location){
“Setting new path”
#this creates a new instance of content which will overwrite the older, this works cleaner than modifying the existing content path
#modifying existing tends to corrupt the data
$newContent = [Microsoft.ConfigurationManagement.ApplicationManagement.ContentImporter]::CreateContentFromFolder($newPath)
$newContent.FallbackToUnprotectedDP = $true
$newContent.OnFastNetwork = [Microsoft.ConfigurationManagement.ApplicationManagement.ContentHandlingMode]::Download
$newContent.OnSlowNetwork = [Microsoft.ConfigurationManagement.ApplicationManagement.ContentHandlingMode]::Download
$newContent.PeerCache = $false
$newContent.PinOnClient = $false
#set the content ID to the new content information you have created
$installer.Contents[0].ID = $newContent.ID
#inject the new data into the deserialized XML data
$installer.Contents[0] = $newContent
}
}
#reserialize the XML
$updatedXML = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::SerializeToString($appXML, $true)
#add the serialized XML data to the application object
$app.SDMPackageXML = $updatedXML
#put the changes to the instance
$app.Put()
}

Adding Roles and Features in SCCM Task Sequence

Should be simple. And I have done it before and it was, dead simple. This post kinda overlaps with running powershell scripts, batch files, and command lines from Task Sequence, and the example here is Roles and Features.

Sticking with deploying Server 2008 R2 for simplicity, I want to add Roles and Features after the OS is installed: .NET Framework, and RDS, for XenApp Servers.

Previously, you could use ServerManagerCmd.exe for this, so when deploying 2008 or earlier then you need to modify accordingly. ServerManagerCmd is deprecated in R2 so you need to use Powershell, and the servermanager module.

First thing is to set the exec policy: i use a command-line step in the Task Sequence:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noprofile -command “&{Set-ExecutionPolicy Unrestricted -Force}”

But you can also use a command line with Reg:

reg add HKLM\Software\Microsoft\Powershell\1\ShellIds\Microsoft.Powershell /v ExecutionPolicy /t REG_SZ /d Unrestricted /f

Of course you need to run from command line for the posh variant because you can’t use a powershell script to set the execution policy when the policy already restricts scripts from running – a chicken and egg analogy. Incidentally, the egg came first. A reptile egg presumably, but it hatched into a genetic mutation which in turn laid a mutant egg and this process eventually led to to the chicken being hatched from a non-chicken egg. So perhaps the chicken did come first then if you mean which came first, chicken or chicken’s egg? Otherwise it’s egg. Maybe better to think how you might get your car keys out of your locked car. That’s a simpler conundrum.

Anyway, onward. Another command-line:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noprofile -command “&{Import-Module -Name C:\Windows\System32\WindowsPowerShell\v1.0\Modules\ServerManager\servermanager.psd1;Add-WindowsFeature AS-NET-Framework -includeAllSubFeature}”

The option with this last command-line is that you can put it in a script with a whole host of other post install tasks or changes, package it and call from the task sequence. Obviously your script only needs the part in the “{}”. Interestingly I was getting errors last time with paths, hence the full paths to the exe and psd1, but it does work without these so long as working dir etc is correct.

So that’s a pretty easy way to install a role or feature. But you could also use the MDT integration with SCCM to create a MDT Task Sequence.

For MDT you need to configure the MDT integration, and create an MDT Tools Package: when you create a new MDT TS you can create the Tools package in the wizard.

Be careful though, for some reason if you use a standard SCCM TS and add MDT steps they don’t always work. It’s easier to create a whole MDT TS and modify it accordingly, particularly for Roles and Features. For installing Language Packs, you can use a standard SCCM TS and add the Use MDT Toolkit step, then Language Pack Offline step and it works fine. Not so with Roles/Features. Funny.

Err on the side of caution I say, create the MDT TS and remove the extraneous steps and groups. It’ll save you time. Time enough to figure out how to get your keys…

Configure ConfigMgr Pre-Reqs with Powershell – Update 2012

No WebDav…. The following script will set up all the neccessary roles and features for a SCCM 2012 Site Server:

# add Windows Features Required for SCCM Site Server
# add Server Manager CMDLETS
Import-Module servermanager

# add Windows Features Required for SCCM Site Server
Add-WindowsFeature WAS-Process-Model
Add-WindowsFeature WAS-Config-APIs
Add-WindowsFeature WAS-Net-Environment
Add-WindowsFeature Web-Server
Add-WindowsFeature Web-ISAPI-Ext
Add-WindowsFeature Web-ISAPI-Filter
Add-WindowsFeature Web-Net-Ext
Add-WindowsFeature Web-ASP-Net
Add-WindowsFeature Web-ASP
Add-WindowsFeature Web-Windows-Auth
Add-WindowsFeature Web-Basic-Auth
Add-WindowsFeature Web-URL-Auth
Add-WindowsFeature Web-IP-Security
Add-WindowsFeature Web-Scripting-Tools
Add-WindowsFeature Web-Mgmt-Service
Add-WindowsFeature Web-Lgcy-Scripting
Add-WindowsFeature Web-Lgcy-Mgmt-Console
Add-WindowsFeature Web-Stat-Compression
Add-WindowsFeature Web-Metabase
Add-WindowsFeature Web-WMI
Add-WindowsFeature Web-HTTP-Redirect
Add-WindowsFeature Web-Log-Libraries
Add-WindowsFeature Web-HTTP-Tracing
Add-WindowsFeature BITS-IIS-Ext
Add-WindowsFeature Net-Framework-Core
Add-WindowsFeature RDC

SCCM Add Computer Associations with Powershell – Download Executable

Ok, here is an exe version of this tool. The code inside is also a bit tidier.
Download the zip file from here:
https://sites.google.com/site/andrewcraigsccm/ACATSV409.zip

Extract and copy the files to a location of choice but make a note of where.
Run the exe from a powershell or command window, or create a shortcut. You must use the following command line syntax:

powershell.exe [pathToExecutable] -Arguments -ConfigFile [pathToXML]
For Example:
powershell.exe F:\scripts\sccm\ACATSV409.exe -Arguments -ConfigFile “F:\scripts\SCCM”

When run from a powershell window of course you don’t need powershell.exe at the front.
At first run you will get a message to insert site provider (fqdn of sccm site server) and then site code (xxx).

That’s it, have fun. The IP Address and Firmware functions on the front page just set Machine Variables – in order to use these you need to create Task Sequences with Conditions.

SCCM Add Computer Associations With Powershell – Code

Below is the source code for this tool. I would like to compile it a bit better so it is more dynamic: it uses an ini file called acatsv4t.ini which should really be in a proper ini format with tags to allow config of the available options, for example available language and OS. Once I have the ini config sorted i’ll compile as an exe, with an install/self-extract routine.

Code is provided as is. No official support provided and i accept no responsibility for misuse. Other than that, hope it comes in handy.

Andy
addComputerAssociationToSCCM_v401_Tabbed.ps1