none
Automating Application creation

    Question

  • I have a bunch of Applications I need to create in SCCM 2012, all as scripted installation. I'm lazy so I want to automate it. Both as initial filling and also as part of an effort to streamline application deployment. I want to do this in PowerShell (but hey, I'm flexible, VBscript is an option too). It took me a some googling (sorry, binging) to get a few pointers and from there I managed to make a PowerShell script which actually works to create Applications in SCCM. That's a good base. However, I'm stuck on the last bit. The installations need a Detection Rule to check for the existence of a registry key. I'm using http://blogs.msdn.com/b/ameltzer/archive/2012/04/25/sdk-how-to-add-an-enhanced-detection-method-into-an-application-deployment-type.aspx and http://blog.lechar.nl/2012/04/03/creating-an-sccm-2012-application-with-powershell/ as guides and below this post is the bit I have sofar (which works, BTW, it creates an Application in SCCM 2012).

    From the first link, I've discovered I need to create an Enhanced Detection Method with a RegistrySettings object along with a few other obscure objects (ugh, OOP sucks). Anyway, I'm stuck getting the right combination of objects created without ending up in a catch-22. And my skills in PowerShell aren't on a level I can dig myself out of it. I can neither make the proper Rule nor a RegistrySettings nor an Expression object at this point because their constructors need information I don't have and cannot figure out how to get (and no, the example in the first link doesn't work because the empty constructor used there for RegistrySettings isn't there (anymore?)). I've tried a bunch of variants out of the plethora of choices in types of Rules and Expressions.

    What I basically need to finish my project at hand is a working example of how to create a Detection Method to simply check for a registry key. This shouldn't be as hard as it seems, really. I've searched high and low and pretty much end up at the webpages I've already used. Time to consult the gurus :)

    Anyone with a working example?

    The code sofar:

    function Import-SCCMAssemblies($SCCMAdminConsolePath){
    if (Test-Path $path) { $t = [System.Reflection.Assembly]::LoadFrom($path)}
    
    $path = "$SCCMAdminConsolePath\Microsoft.ConfigurationManagement.ApplicationManagement.MsiInstaller.dll"
    if (Test-Path $path) { $t = [System.Reflection.Assembly]::LoadFrom($path)}
    
    $path = "$SCCMAdminConsolePath\DcmObjectModel.dll"
    if (Test-Path $path) { $t = [System.Reflection.Assembly]::LoadFrom($path)}
    }
    
    Function Get-AuthoringScopeID{
    [CmdletBinding()]
    PARAM (
    [Parameter(Mandatory=$true, HelpMessage="SCCM Server")][Alias("Server","SmsServer")][System.Object] $SccmServer
    )
    
    PROCESS {
    $identificationClass = [WMICLASS]"\\$($SccmServer.Machine)\$($SccmServer.Namespace):SMS_Identification"
    $cls = Get-WmiObject SMS_Identification -namespace $SccmServer.Namespace -ComputerName $SccmServer.Machine -list
    $tmp = $identificationClass.GetSiteID().SiteID
    return “ScopeId_$($tmp.Substring(1,$tmp.Length -2))”
    }
    }
    
    $con = New-Object System.Object
    $con | Add-member -type NoteProperty -name Machine -Value "<server>"
    $con | Add-member -type NoteProperty -name namespace -Value "root\SMS\site_AA1"
    
    #Loading Assemblies to create an SCCM 2012 application
    Import-SCCMAssemblies "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\"
     
    #Get Authoring ScopeID
    $scopeid = Get-AuthoringScopeID $con
    
    #Create an unique id for the application and the deployment type
    $newApplicationID = "Application_" + [guid]::NewGuid().ToString()
    $newDeploymentTypeID = "DeploymentType_" + [guid]::NewGuid().ToString()    
     
    #Create SCCM 2012 object id for application and deploymenttype
    $newApplicationID = New-Object Microsoft.ConfigurationManagement.ApplicationManagement.ObjectID($scopeid,$newApplicationID)  
    $newDeploymentTypeID = New-Object Microsoft.ConfigurationManagement.ApplicationManagement.ObjectID($scopeid , $newDeploymentTypeID)
         
    #Create all the objects neccessary for the creation of the application
    $newApplication =  New-Object Microsoft.ConfigurationManagement.ApplicationManagement.Application($newApplicationID)
    $newDeploymentType = New-Object  Microsoft.ConfigurationManagement.ApplicationManagement.DeploymentType($newDeploymentTypeID,"Script")
    $newDisplayInfo  =  New-Object Microsoft.ConfigurationManagement.ApplicationManagement.AppDisplayInfo
    $newDetectionMethod = New-Object Microsoft.ConfigurationManagement.ApplicationManagement.EnhancedDetectionMethod
    
    $APP = New-Object System.Object
    $APP | Add-Member -type noteproperty -Name "Title" -Value "TestApp"
    $APP | Add-Member -type noteproperty -Name "Description" -Value "This is a TestApp"
    $APP | Add-Member -type noteproperty -Name "DisplayName" -Value "TestApp"
    $APP | Add-Member -type noteproperty -Name "PRVersion" -Value "1.0"
    $APP | Add-Member -type noteproperty -Name "thinapp" -Value "\\%USERDOMAIN%\app\thinapps\foo\foo.exe"
    
    $thinreg = "\\%USERDOMAIN%\app\thinapps\thinreg.exe"
    
    ##Setting Display Info
    $newDisplayInfo.Title = $APP.DisplayName
    $newDisplayInfo.Language = "en-US"
    $newDisplayInfo.Description = $APP.Description
    $newDisplayInfo.Version = $APP.PRVersion
    
    $newApplication.DisplayInfo.Add($newDisplayInfo)
    
    ##Setting default Language must be set and displayinfo must exist
    $newApplication.DisplayInfo.DefaultLanguage = $newDisplayInfo.Language
    $newApplication.Title = $APP.Title
    $newApplication.Version = 1.0
    
    ##Deployment Type Script installer will be used
    $newDeploymentType.Title = "Deploy $($APP.DisplayName)"
    $newDeploymentType.Version = 1.0
    $newDeploymentType.Installer.InstallCommandLine = "$($thinreg) $($APP.Thinapp)"
    $newDeploymentType.Installer.UninstallCommandLine = "$($thinreg) /u $($APP.Thinapp)"
    $newApplication.DeploymentTypes.Add($newDeploymentType)
    
    #Detectionmethod
    $newDeploymentType.Installer.DetectionMethod = "Enhanced"
    $newDeploymentType.Installer.EnhancedDetectionMethod = $newDetectionMethod
    
    # Below should be the code to create an Enhanced Detection Rule, as found in
    # the console's interface as Rules and Clauses under the Detection Method tab.
    
    ##Serialize the object to an xml file and stuff it into SCCM
    $newApplicationXML = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::SerializeToSTring($newApplication,$true)
    
    $applicationClass = [WMICLASS]"\\$($con.Machine)\$($con.Namespace):SMS_Application"
    $newApplication = $applicationClass.createInstance()
     
    $newApplication.SDMPackageXML = $newApplicationXML
    $tmp = $newApplication.Put()
     
    ##Reload the application to get the data
    $newApplication.Get()

    Wednesday, August 01, 2012 8:06 AM

Answers

  • I created a blog post about the RegistrySetting problem (link). I think it is a bug in the DcomObject.dll file. I created a workaround by creating a custom object that creates the object and a function the retrieves the object. The dll can be found in the blog post. 
    Monday, October 01, 2012 7:02 PM
  • What you're seeing isn't a bug in the DCM Object Model or the Application Management SDK, it's a limitation of PowerShell due to the design of this part of the SDK.

    The RegistrySetting class doesn't have a constructor that accepts the PowerShell $null object (I don't know what object type this gets cast to when trying to find a constructor). I couldn't find a way to work around this by I'm not very well versed in PowerShell.

    A workaround is to write a wrapper library (like you did) or a PowerShell cmdlet that could perform similar functionality. I wish there were a better solution at this time. I'd suggest filing a feedback item on Connect about this to make sure it gets back to our product team for consideration in a future SDK release.

    Monday, October 15, 2012 10:12 PM

All replies

  • This might get you started http://blogs.msdn.com/b/ameltzer/archive/2012/04/25/sdk-how-to-add-an-enhanced-detection-method-into-an-application-deployment-type.aspx
    It did get me started. Except that's about where it ended too, as I described. Thanks though.
    Wednesday, August 01, 2012 1:52 PM
  • Can you elaborate on which constructors you are having problems with? I just double checked the sample code I posted in the blog post against the RTM version of the SDK and everything compiled, built, and seemed to run properly (this is in C# though, not PowerShell).

    Thursday, August 02, 2012 10:42 PM
  • Can you elaborate on which constructors you are having problems with? I just double checked the sample code I posted in the blog post against the RTM version of the SDK and everything compiled, built, and seemed to run properly (this is in C# though, not PowerShell).


    I'm trying to do things in order, so after getting the basics to work the next object I need is the RegistrySetting one to define what registry key to check for. According to the example, the constructor for RegistrySetting can be $null:

    $newRegistrySetting = New-Object Microsoft.ConfigurationManagement.DesiredConfigurationManagement.RegistrySetting($null) 

    which is basically the same as:

    $newRegistrySetting = New-Object Microsoft.ConfigurationManagement.DesiredConfigurationManagement.RegistrySetting

    Not that either variant works, by the way, it throws an error:

    Constructor not found. Cannot find an appropriate constructor for type Microsoft.ConfigurationManagement.DesiredConfigurationManagement.RegistrySetting.

    If I look at the constructor for that object, the only one it has is:

    Microsoft.ConfigurationManagement.DesiredConfigurationManagement.RegistrySetting(ConfigurationItem parent)

    I don't have a ConfigurationItem. Maybe I need to create one or find one, but how or which? I don't know.

    I've also tried to recreate the Expression stuff with a CustomCollection and such but I'm not getting very far with that either. I haven't bothered to touch the SettingReference yet as it's pretty much a waste of time if I can't get all the other stuff sorted first. Like I said, my PowerShell skills aren't at a level I can actually build very complex programs with it. I'm a system administrator who just wants to automate some stuff, not an application developer with deep knowledge of C#, PowerShell and OOP even though I can read and understand code just fine. I tend to modify examples to suit my needs

    Thanks for checking the example against the RTM version of SCCM. I'm building against the release, but it shouldn't make much difference. Odd that the object constructor depends on the language it's used from.



    • Edited by PolarWolf Friday, August 03, 2012 7:54 AM
    Friday, August 03, 2012 7:52 AM
  • Thanks for the clarification. I was able to reproduce the issue you were seeing and could not find a workaround (I'm not a PowerShell expert though so this may be a "PEBKAC" class of issue). I think the problem is that PowerShell is handling $null as a System.Object when it's expecting something of type ConfigurationItem which is a pretty complex object in its own right. I tried casting $null to ConfigurationItem but that didn't seem to work either.

    I'll see if I can get more information for you about how to work around this but I'm thinking the solution may be to author a Cmdlet in C# to wrap the construction logic that's required -- definitely not ideal. I'll let you know once I have more information.



    Friday, August 03, 2012 5:50 PM
  • Thanks for the clarification. I was able to reproduce the issue you were seeing and could not find a workaround (I'm not a PowerShell expert though so this may be a "PEBKAC" class of issue). I think the problem is that PowerShell is handling $null as a System.Object when it's expecting something of type ConfigurationItem which is a pretty complex object in its own right. I tried casting $null to ConfigurationItem but that didn't seem to work either.

    I'll see if I can get more information for you about how to work around this but I'm thinking the solution may be to author a Cmdlet in C# to wrap the construction logic that's required -- definitely not ideal. I'll let you know once I have more information.




    Thank you for your reply and effort you're willing to put into this. In the meantime I'll keep messing with it in any downtime I can get.
    Monday, August 06, 2012 7:29 AM
  • Here is some additional information from our team that may help:

    I would try following two things –

    1. Create an instance of ConfigurationItem class and set it to null. Then pass this object (instead of passing directly ‘null’) to CTOR.
    2. Create a dummy ConfigurationItem object (does not matter what type) and supply that the CTOR.

    For example:

    this.valueSetting = new RegistrySetting(null);


    - Dave


    Monday, August 06, 2012 2:18 PM
  • Hi

    Have you had any luck with this? I am having the exact same problem. I am trying to automate the creation of applications in SCCM 2012 and need to create a registry based detection method.

    Technet tells me that since "DesiredConfigurationManagement.RegistrySetting" is a static class, it cannot be created, destroyed or changed. But its methods and properties can be used. Unfortunately I have no idea how to do this.

    Friday, August 10, 2012 1:29 PM
  • No, I haven't been able to spend any more time on this issue.

    There must be a simpler way to do it, I'm sure.

    Monday, August 13, 2012 10:04 AM
  • I created a blog post about the RegistrySetting problem (link). I think it is a bug in the DcomObject.dll file. I created a workaround by creating a custom object that creates the object and a function the retrieves the object. The dll can be found in the blog post. 
    Monday, October 01, 2012 7:02 PM
  • I created a blog post about the RegistrySetting problem (link). I think it is a bug in the DcomObject.dll file. I created a workaround by creating a custom object that creates the object and a function the retrieves the object. The dll can be found in the blog post. 

    Awesome! I'll have a looksee if this solves my problem.

    [edit]

    Pff...well. After some wrenching I finally got the DLL to load. For some reason I'm unable to load it normally using LoadFrom so I had to use a workaround. Either way, I keep getting stuck on this error though (when trying to New-Object a RegistrySettingRob)

    Exception calling ".ctor" with "1" argument(s): "Could not load file or assembly 'DcmObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified."

    My DcmObjectModel.dll seems to be version 5.0.7711.0, would that make much difference?

    Slowly but surely I'm getting severely pissed off and annoyed by anything .NET and PowerShell. It's so friggen hard to do anything productive with it without first having to spend a day working around its stupidities.




    • Edited by PolarWolf Friday, October 05, 2012 1:19 PM
    Friday, October 05, 2012 9:33 AM
  • I know how you feel. Beacause of lack of a clear SDK I had also lots of trouble solving the issue. The dll I provided was created with the DcomObject.dll provided with SCCM 2012 SP1. So therefore it didn't work with your version of SCCM. I updated my blog post where you van find the .cs file and the steps to create you own dll (link). The only thing you have to do is to install visual studio trial and replace the DcomObject.dll. 
    Monday, October 08, 2012 6:26 PM
  • What you're seeing isn't a bug in the DCM Object Model or the Application Management SDK, it's a limitation of PowerShell due to the design of this part of the SDK.

    The RegistrySetting class doesn't have a constructor that accepts the PowerShell $null object (I don't know what object type this gets cast to when trying to find a constructor). I couldn't find a way to work around this by I'm not very well versed in PowerShell.

    A workaround is to write a wrapper library (like you did) or a PowerShell cmdlet that could perform similar functionality. I wish there were a better solution at this time. I'd suggest filing a feedback item on Connect about this to make sure it gets back to our product team for consideration in a future SDK release.

    Monday, October 15, 2012 10:12 PM
  • We have a similar Problem:
    We need an EnhancedDetectionClass with Type Registry. After we translate the C#.Net-Code to Powershell, we receive a generic Error from the Put()-Function.

    Do anyone see an Typo in our code:

    [System.Reflection.Assembly]::LoadFrom("$PWD\RegistrySetting.dll")
    	    $temp = New-Object RegistrySettingNamespace.RegistrySettingRob ""
    	    $registrySetting = $temp.GetRegistrySetting()
    		$registrySetting.RootKey = "LocalMachine"
    	    $registrySetting.Key = "SOFTWARE\MyCompany\MyProduct"
    	    $registrySetting.Is64Bit = $false
    	    $registrySetting.ValueName = "IsInstalled"
    	    $registrySetting.CreateMissingPath = $false
    	    $registrySetting.SettingDataType = [Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.ScalarDataType]::String
    	    
    	    $logicalName = $registrySetting.LogicalName
    	    
    	    $dataType = [Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.ScalarDataType]::GetDataTypeFromDotNetTypeName("String")
    		
    		$DeploymentType.Installer.EnhancedDetectionMethod = new-object Microsoft.ConfigurationManagement.ApplicationManagement.EnhancedDetectionMethod
    		$DeploymentType.Installer.EnhancedDetectionMethod.Settings.Add($registrySetting)
    		
    		$settingSourceType = [Microsoft.ConfigurationManagement.DesiredConfigurationManagement.ConfigurationItemSettingSourceType]::Registry
    		$refName = "SettingRef_" + [Guid]::NewGuid().ToString()
    		$settingReference = new-object Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.SettingReference -ArgumentList $scopeid, $refName, 0, $logicalName, $dataType, $settingSourceType , $false
            $settingReference.MethodType = [Microsoft.ConfigurationManagement.DesiredConfigurationManagement.ConfigurationItemSettingMethodType]::Value
    		
    		$constantValue = new-object Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.ConstantValue -ArgumentList "0", $dataType
    		
    		$operands = new-object "Microsoft.ConfigurationManagement.DesiredConfigurationManagement.CustomCollection``1[Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.ExpressionBase]"
    	    $operands.Add($settingReference)    
    	    $operands.Add($constantValue)
    		
    		$expressionOperator = [Microsoft.ConfigurationManagement.DesiredConfigurationManagement.ExpressionOperators.ExpressionOperator]::NotEquals
        	$rootExp = new-object Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.Expression -ArgumentList $expressionOperator, $operands
    		
    		$DeploymentType.Installer.EnhancedDetectionMethod.Settings.Add($registrySetting)
    		
    		$severity = [Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Rules.NoncomplianceSeverity]::Critical
        	$DeploymentType.Installer.EnhancedDetectionMethod.Rule = new-object Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Rules.Rule -ArgumentList "exp1", $severity , $null, $rootExp

    We also compiled the whole .Net-Code and loaded the .dll, but still the same Problem.

    Thanks for your help...

    Saturday, January 19, 2013 2:06 PM
  • Hi there,

    does anyone knows if there is a way to add to the detection rule of a windows installer also the MSI version with the operator greater than in c# or powershell?

    Cheers

    Stephan

    Friday, February 01, 2013 2:24 PM
  • While I created a registry enhanced detection method from the code and your dll, there is one setting I need to change, and have not been succesfull. I need to selct "the registry setting must exist on the target...", deselecting "the registry settinjg must satisfy the following rule.."

    Does anybody have the code for that?

    Monday, February 03, 2014 5:49 PM
  • Hi TenFan,

    where you ever able to solve the problem ?

    I am working on a solution to create registry detection method via powershell and at the moment im stuck.

    Kind Regards

    Friday, June 13, 2014 6:31 AM
  • Found a way around it which works in PowerShell v2 (and supposedly up but haven't tested) which doesn't require any further shenanigans to make it work.

    Calling the following fails as you pointed out:

    $newRegistrySetting = New-Object Microsoft.ConfigurationManagement.DesiredConfigurationManagement.RegistrySetting($null) 

    But calling this instead works just fine:

    $newRegistrySetting = New-Object Microsoft.ConfigurationManagement.DesiredConfigurationManagement.RegistrySetting(@($null)) 

    I just pass it an array of $null and it works.

    I could be completely off, but I think that $null is PowerShell is sort of the absence of a type, which is why calling it with $null or with no parameters at all returns the same error.

    However, by forcing it to be an array, it is "something" of "nothing" in this case.

    Passing @() doesn't work though, it needs to be @($null)

    • Proposed as answer by JohnUbuntu Friday, August 15, 2014 10:41 AM
    Friday, August 15, 2014 10:41 AM