How to Create a Basic App using the Configuration Manager 2012 Beta 2 SDK

With System Center Configuration Manager 2012 comes a new powerful way to manage apps called the Application Model. The new Application Model (App Model) unlocks the vision of User Centric computing or user targeting. User targeting in Configuration Manager 2007 was technically possible however it was not practical as platform specific issues had to be managed at the collection level. The App Model solves this problem along with many other short comings of what is now called Classic Software Distribution. A.K.A Packages and Programs which we are all familiar with. There are a lot of pieces to the new App Model to discuss. I want to focus on the most basic scenario in this post and show how to create a simple Application with a single deployment type.

Quick note: The App Model SDK is still under development and some of the class referenced below are bound to change between Beta 2 and RTM.

Start with Application

To get started you’ll need to add a reference to the following assemblies found in the CM 2012 Console install directory:

  • Microsoft.ConfigurationManagement.ApplicationManagement.dll
  • Microsoft.ConfigurationManagement.ApplicationManagement.MsiInstaller.dll

The first step in creating your first Application in the 2010 App Model is to create an instance of Microsoft.ConfigurationManagement.ApplicationManagement.Application.

 //creat an instance of Application 
ObjectId appId = new ObjectId("Authoring_Scope_Id",
 "Application_"+Guid.NewGuid().ToString());
Application app = new Application(appId);
app.Title = "New App";
app.Version = 1; 

The constructor for Application takes an instance of ObjectId which indicates the Authoring Scope and Logical Name of the Application. These are in the form of “ScopeId_5D0545B8-46E1-4F48-B165-B8870DAE1C1C" and “Application_b3628303-eeb6-41af-a15c-175c12f3bd6a” respectively. The Authoring scope is unique per Primary Site Server and you can get that from WMI. Supplying a fake Authoring Scope ID should work just fine for testing purposes. If you plan on deploying this to a client you should do the work to get the actual Site Scope Id. It should also be noted that the “Version” property does not refer to the version of the Application that your are creating. It indicates the revision of the app in terms of Application revisions which is a new feature in 2012.

Add Localized Display Info

The various Application properties such as Title are what we see in the Admin Console. The end user via Software Center or the Software Catalog see different information. The end user sees information contained in an instance of DisplayInfo that can be localized to the language of the end user. This is done by adding an instance of DisplayInfo along with localized metadata for each language you want to support.

 //cerate DisplayInfo //this is the localizable info that the end user will see in Software Center 
AppDisplayInfo displayInfo = new AppDisplayInfo(); 
displayInfo.Title = "My App"; 
displayInfo.Language = CultureInfo.CurrentCulture.Name; 
displayInfo.Description = "My first App"; 
app.DisplayInfo.Add(displayInfo);

Here I am using the same language as the current executing process “en-US”, but you could use any language identifier.

Add a Deployment Type

For those not familiar with the App Model the Application is the abstract definition of an application and the Deployment Type represents the concrete instance of the app. To make our app work we need to associate an installer and content with the Application instance. This is done with a DeploymentType.

 //Add DT ObjectId dtId = new ObjectId("Authoring_Scope_Id",     
 "DeploymentType" + Guid.NewGuid().ToString()); 
DeploymentType dt = new DeploymentType(dtId, MsiInstaller.TechnologyId); 
app.DeploymentTypes.Add(dt); 
dt.Title = "My First DT";
dt.Version = 1; 

Again we see the use of ObjectId but we are using the “DeploymentType_” prefix for Name. Once we have the DeploymentType created we add it to Application.DeploymentTypes. The TechnologyId paramter in the DT constructor indicates what type of DT we are creating. We are creating an MSI type DT so we use the static property found on Microsoft.ConfigurationManagement.ApplicationManagement.MsiInstaller that equates to the string “MSI”.

Setup the MsiInstaller

Our new DT has been added to our Application. DeploymentType has a property called “Installer” that exposes the instance of Installer that we need to work with. The DT constructor was nice enough to supply us with an instance of MsiInstaller so we just need to configure it.

 //configure MSI installer to have a product code detection method 
MsiInstaller installer = dt.Installer as MsiInstaller; 
Guid fakeProductCode = Guid.NewGuid(); 
installer.ProductCode = "{" + fakeProductCode.ToString() + "}";

//set commandline 
installer.InstallCommandLine = "MSIExec /i MyApp.Msi";

One critical component to any DT is its Detection Method. The Detection Method indicates how the CM Client is suppose to know if the app is installed or not. This is critical as the App Model is state based. If you get the Detection Method wrong the client will think the app is not installed and will attempt to install it every time Global Evaluation runs (every 7 day I believe) even if the app is installed. The nice part about working with MSIs is that we can just specify a Product Code and the rest is handled for us. The Product Code format is a Guid wrapped with { } such as {27aae8e3-e733-4256-a37f-dc1e0287da9f}.

Add Content

Next we need to associate our DT and MsiInstaller with actual content so the client has something to install.

 //Add content to the DT 
Content installerContent = new Content(); 
installer.Contents.Add(installerContent); 
installerContent.Location = @\\SomeServer\content\MyAppDir; 
//add MSI file to content 
ContentFile msiFile = new ContentFile(); 
msiFile.Name = "MyApp.Msi"; 
installerContent.Files.Add(msiFile);

When setting the Name property on ContentFile use a path that is relative to the path specified by “Location”.

Serializing the Application

We are almost ready to send our app to the site to deploy to clients. Before we do that we need to serialize our instance of Application. We use a special serializer found in Microsoft.ConfigurationManagement.ApplicationManagement.Serialization called SccmSerializer. This serializer will do some addition verification for us to check for invalid properties.

 //serialize app. string appXml = SccmSerializer.SerializeToString(app, true);  

Committing the App to the Site

The last step to creating our app is to use the CM WMI provider to save our serialized data to the site server. To do this we need to create an instance of SMS_Application. I’m not going to go into great detail on working with WMI here as there are a ton of articles that cover that subject. But here is a basic method for getting an instance of SMS_Application from the provider.

 //serialize app. 
string appXml = SccmSerializer.SerializeToString(app, true); 
ManagementObject appInstance = null; 
ManagementPath path = null; 
ObjectGetOptions options = null; 
ManagementClass appClass = null; 
try 
{
 ManagementScope scope = 
 new ManagementScope(@”\\SiteServer\root\Sms\Site_SiteCodeGoesHere”);     
 path = new ManagementPath("SMS_Application");     
 options = new ObjectGetOptions();     
 appClass = new ManagementClass(scope, path, options);
 appInstance = appClass.CreateInstance(); 
} 
finally 
{
 appClass.Dispose(); 
}

The only property we need to set here is SDMPackageXML. We set SDMPackageXML to the serialzed XML string stored in appXml and call Put() to save our app to the site.

 appInstance.Properties["SDMPackageXML"].Value = appXml; appInstance.Put();

You should now have an app that is ready to deploy!

There are a lot of other properties associated with the App Model that we did not explore that can impact how an App behaves on the client. This post covers jus the most basic. In upcoming posts I’ll cover more advanced topics such as DT dependencies and Enhanced Detection Methods.

Comments

  • Anonymous
    July 07, 2011
    Hi, I am working on implementing a client application which publishes and retrieves data from SCCM 2012. Thanks for your post! It was very helpful. Even though this way of creating Application works fine, I would like to know if we can do the entire Application creating process through the .Net interface(Microsoft.ConfigurationManagement.ApplicationManagement). Basically, I would like to know if we can create Application without using WMI. I assume that you will have some WMI wrapper on this .Net library. It would be helpful if you can provide some examples on that. Thanks! Rajeesh

  • Anonymous
    July 11, 2011
    Excellent question! You can in fact do all of this work via the Application Model SDK. The AppManWrapper class should insulate you from the complexities of WMI. I plan on covering the use of this class in a later post that focuses on interaction with the Provider.

  • Anonymous
    July 21, 2011
    Where is the Beta 2 SDK? I can't seem to find it. All searches on MSDN point to the 2007 SDK...

  • Anonymous
    July 22, 2011
    The binaries used in this sample are part of the Admin Console install. You should be able to find them in the bin directory of the install location.

  • Anonymous
    July 26, 2011
    Ya, I get that...so there is no 2012 SDK available? I'm looking for API documentation and additional sample code.

  • Anonymous
    July 31, 2011
    Thanks a lot! Looking forward to AppManWrapper examples...

  • Anonymous
    November 27, 2011
    Hi, Employed the example for adding an Application in sccm2012 works fine. Now how to Distribute Content and then deploy the application using the same Application Model sdk if you provide some sample on this it would be a great help Thanks -Noor

  • Anonymous
    December 14, 2011
    Hi, Adding an application like this works fine. However, something seems to go wrong. When I try to open the application in the console I get the following error message: Value cannot be null. Parameter name: key. When I click on continue. The application opens but it has only the two registers “general” and “catalog”. I tried on B2 and RC1, same effect. Does anyone have an idea, what I’m doing wrong here? Any help is much appreciated! Thanks Silvan

  • Anonymous
    January 12, 2012
    Do you see any additional info in SMSProv.log on the site server when you try to open the App?

  • Anonymous
    January 12, 2012
    The comment has been removed

  • Anonymous
    January 17, 2012
    The comment has been removed

  • Anonymous
    January 26, 2012
    Thanks a lot for your help, much appreciated and sorry for the late reply. I updated to CM 2012 R2 now. I still have the same effect. If I browse through the SMS_Application objects with WBemTest.exe the SMDPackageXML property is always <null> even if I get an instance of an object that I created through the console and which I can open and edit fine. It seems that this property doesn’t get loaded for some reason. However, if I access the object through C# the property is loaded. So here is the SMDPackageXML I created through C# and which I can’t open through the console.


{[SDMPackageXML, <?xml version="1.0" encoding="utf-16"?><AppMgmtDigest xmlns="schemas.microsoft.com/.../AppMgmtDigest" xmlns:xsi="www.w3.org/.../XMLSchema-instance"><Application AuthoringScopeId="Authoring_Scope_Id" LogicalName="Application_4de17288-bc78-4159-bef0-5d0a9bf71781" Version="1"><DisplayInfo><Info Language="en-US"><Description>My first App</Description></Info></DisplayInfo><DeploymentTypes><DeploymentType AuthoringScopeId="Authoring_Scope_Id" LogicalName="DeploymentTypeed67d275-4ab9-4583-bd6f-6eae35672b1f" Version="1"/></DeploymentTypes><Title ResourceId="Res_94412512">App2</Title></Application><DeploymentType AuthoringScopeId="Authoring_Scope_Id" LogicalName="DeploymentTypeed67d275-4ab9-4583-bd6f-6eae35672b1f" Version="1"><Title ResourceId="Res_958853921">My First DT</Title><DeploymentTechnology>GLOBAL/MSIDeploymentTechnology</DeploymentTechnology><Technology>MSI</Technology><Hosting>Native</Hosting><Installer Technology="MSI"><Contents><Content ContentId="Content_0e58e1f0-604d-4f2e-b736-4d97bf87963d" Version="1"><File Name="MyApp.Msi"/><Location>\SomeServercontentMyAppDir</Location><PeerCache>true</PeerCache><OnFastNetwork>Download</OnFastNetwork><OnSlowNetwork>DoNothing</OnSlowNetwork></Content></Contents><DetectAction><Provider>MSI</Provider><Args><Arg Name="ExecutionContext" Type="String">Any</Arg><Arg Name="ProductCode" Type="String">{f7f5a266-a42a-455b-a7d2-e8f152ddae7d}</Arg><Arg Name="PackageCode" Type="String"/><Arg Name="PatchCodes" Type="String[]"/></Args></DetectAction><InstallAction><Provider>MSI</Provider><Args><Arg Name="InstallCommandLine" Type="String">MSIExec /i MyApp.Msi</Arg><Arg Name="WorkingDirectory" Type="String"/><Arg Name="ExecutionContext" Type="String">Any</Arg><Arg Name="RequiresLogOn" Type="String"/><Arg Name="RequiresElevatedRights" Type="Boolean">false</Arg><Arg Name="RequiresUserInteraction" Type="Boolean">false</Arg><Arg Name="RequiresReboot" Type="Boolean">false</Arg><Arg Name="UserInteractionMode" Type="String">Normal</Arg><Arg Name="PostInstallBehavior" Type="String">BasedOnExitCode</Arg><Arg Name="ExecuteTime" Type="Int32">60</Arg><Arg Name="MaxExecuteTime" Type="Int32">120</Arg><Arg Name="RunAs32Bit" Type="Boolean">false</Arg><Arg Name="SuccessExitCodes" Type="Int32[]"><Item>0</Item><Item>1707</Item></Arg><Arg Name="RebootExitCodes" Type="Int32[]"><Item>3010</Item></Arg><Arg Name="HardRebootExitCodes" Type="Int32[]"><Item>1641</Item></Arg><Arg Name="FastRetryExitCodes" Type="Int32[]"><Item>1618</Item></Arg></Args></InstallAction><CustomData><DetectionMethod>ProductCode</DetectionMethod><ProductCode>{f7f5a266-a42a-455b-a7d2-e8f152ddae7d}</ProductCode><InstallCommandLine>MSIExec /i MyApp.Msi</InstallCommandLine><ExecuteTime>60</ExecuteTime><ExitCodes><ExitCode Code="0" Class="Success"/><ExitCode Code="1707" Class="Success"/><ExitCode Code="3010" Class="SoftReboot"/><ExitCode Code="1641" Class="HardReboot"/><ExitCode Code="1618" Class="FastRetry"/></ExitCodes><AllowUninstall>true</AllowUninstall></CustomData></Installer></DeploymentType></AppMgmtDigest>]}

  • Anonymous
    January 30, 2012
    The reason SDMPackageXML is always null is that it is a lazy load property and an additional Get() or "Refresh Object" via wbemtest is needed before that value is fetched. I try to repro that issue with this digest.

  • Anonymous
    February 01, 2012
    I was able to repro the issue and find the cause. Two items:

  • An always requires that the Title value be set. Application.Name is the Logical Name and not the display name.
    - A default language must be specified to keep the console happy. A handy trick is to create the app by hand in the console then get the digest from WMI. Then compare what was produced by the console to what is produced by your code in a diff tool. You can also use SccmSerializer to find digest errors. The following code snippet found the missing Title issue. SccmSerializer.ValidateFromString(Digest_XML); here is the digest I ended up with: <?xml version="1.0" encoding="utf-16"?> <AppMgmtDigest  xmlns="http://schemas.microsoft.com/SystemCenterConfigurationManager/2009/AppMgmtDigest"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">   <Application      AuthoringScopeId="Authoring_Scope_Id"      LogicalName="Application_ede17288-bc78-4159-bef0-5d0a9bf71780" Version="1">     <DisplayInfo DefaultLanguage="en-US">       <Info Language="en-US">         <Title>Foo</Title>         <Description>My first App</Description>       </Info>     </DisplayInfo>     <DeploymentTypes>       <DeploymentType          AuthoringScopeId="Authoring_Scope_Id"          LogicalName="DeploymentType_9d67d275-4ab9-4583-bd6f-6eae35672b10" Version="1"/>     </DeploymentTypes>     <Title ResourceId="Res_94412512">App2</Title>   </Application>   <DeploymentType      AuthoringScopeId="Authoring_Scope_Id"      LogicalName="DeploymentType_9d67d275-4ab9-4583-bd6f-6eae35672b1" Version="1">     <Title ResourceId="Res_958853921">My First DT</Title>     <DeploymentTechnology>GLOBAL/MSIDeploymentTechnology</DeploymentTechnology>     <Technology>MSI</Technology>     <Hosting>Native</Hosting>     <Installer Technology="MSI">       <Contents>         <Content ContentId="Content_0e58e1f0-604d-4f2e-b736-4d97bf87963d" Version="1">           <File Name="MyApp.Msi"/>           <Location>\SomeServercontentMyAppDir</Location>           <PeerCache>true</PeerCache>           <OnFastNetwork>Download</OnFastNetwork>           <OnSlowNetwork>DoNothing</OnSlowNetwork>         </Content>       </Contents>       <DetectAction>         <Provider>MSI</Provider>         <Args>           <Arg Name="ExecutionContext" Type="String">Any</Arg>           <Arg Name="ProductCode" Type="String">{f7f5a266-a42a-455b-a7d2-e8f152ddae7d}</Arg>           <Arg Name="PackageCode" Type="String"/>           <Arg Name="PatchCodes" Type="String[]"/>         </Args>       </DetectAction>       <InstallAction>         <Provider>MSI</Provider>         <Args>           <Arg Name="InstallCommandLine" Type="String">MSIExec /i MyApp.Msi</Arg>           <Arg Name="WorkingDirectory" Type="String"/>           <Arg Name="ExecutionContext" Type="String">Any</Arg>           <Arg Name="RequiresLogOn" Type="String"/>           <Arg Name="RequiresElevatedRights" Type="Boolean">false</Arg>           <Arg Name="RequiresUserInteraction" Type="Boolean">false</Arg>           <Arg Name="RequiresReboot" Type="Boolean">false</Arg>           <Arg Name="UserInteractionMode" Type="String">Normal</Arg>           <Arg Name="PostInstallBehavior" Type="String">BasedOnExitCode</Arg>           <Arg Name="ExecuteTime" Type="Int32">60</Arg>           <Arg Name="MaxExecuteTime" Type="Int32">120</Arg>           <Arg Name="RunAs32Bit" Type="Boolean">false</Arg>           <Arg Name="SuccessExitCodes" Type="Int32[]">             <Item>0</Item>             <Item>1707</Item>           </Arg>           <Arg Name="RebootExitCodes" Type="Int32[]">             <Item>3010</Item>           </Arg>           <Arg Name="HardRebootExitCodes" Type="Int32[]">             <Item>1641</Item>           </Arg>           <Arg Name="FastRetryExitCodes" Type="Int32[]">             <Item>1618</Item>           </Arg>         </Args>       </InstallAction>       <CustomData>         <DetectionMethod>ProductCode</DetectionMethod>         <ProductCode>{f7f5a266-a42a-455b-a7d2-e8f152ddae7d}</ProductCode>         <InstallCommandLine>MSIExec /i MyApp.Msi</InstallCommandLine>         <ExecuteTime>60</ExecuteTime>         <ExitCodes>           <ExitCode Code="0" Class="Success"/>           <ExitCode Code="1707" Class="Success"/>           <ExitCode Code="3010" Class="SoftReboot"/>           <ExitCode Code="1641" Class="HardReboot"/>           <ExitCode Code="1618" Class="FastRetry"/>         </ExitCodes>         <AllowUninstall>true</AllowUninstall>       </CustomData>     </Installer>   </DeploymentType> </AppMgmtDigest>  
  • Anonymous
    February 02, 2012
    Thanks a lot for investigating this and telling me how you did it, much appreciated. After setting the default language everything works just fine. I noticed that you didn’t set the default language in your code but I assume it works anyway? In my case the servers default culture wasn’t “en-US”. However, if I set the default language like this it works “app.DisplayInfo.DefaultLanguage = CultureInfo.CurrentCulture.Name;”. Here is another way to connect to the provider and create an instance of SMS_Application. WqlConnectionManager wqlConnectionManager = new WqlConnectionManager(); wqlConnectionManager.Connect("localhost"); IResultObject appInstance = wqlConnectionManager.CreateInstance("SMS_Application"); appInstance.Properties["SDMPackageXML"].StringValue = SccmSerializer.SerializeToString(app, true); appInstance.Put(); wqlConnectionManager.Close();

  • Anonymous
    April 30, 2012
    Is there an XSD file available for AppMgmtDigest? We're trying to build an application to talk to SCCM remotely without having to have the console installed and I'm guessing licensing precludes us from bundling either Microsoft.ConfigurationManagement.ApplicationManagement.dll or Microsoft.ConfigurationManagement.ApplicationManagement.MsiInstaller.dll

  • Anonymous
    May 07, 2012
    The latest word on the SDK and App Creation Creating apps from XML is not supported. The only supported method to create apps is via the SDK. The SDK will only be bundled with the console and no work is currently planned to create a separate SDK installer. And unfortunately the Software License Terms for Config Mgr does not grant rights for redistribution for SDK DLLs.

  • Anonymous
    June 10, 2012
    All, I was wondering if any of you have been able to deploy an application created using these instructions. I followed it and was able to create the application and the DT but when I try to deploy it, the Appenforce.log file says "unable to locate or validate package source myMSIFile.msi" Any help will be greatly appreciated.

  • Anonymous
    June 15, 2012
    One setup in building the app is defining all of the files that participate in the content. Are you using UNC paths that the site server service account has access to? It is also possible that this sample code needs to be refreshed to work with RTM bits.

  • Anonymous
    June 17, 2012
    Some references to the content location were missing on this code so I was able to (with some help) figure this out. Thanks.

  • Anonymous
    August 26, 2012
    Have anyone an ideea how to deploy an application using powershell or c++ ? Thanks!

  • Anonymous
    January 23, 2013
    Hi, very good nice tut, but I am just strugeling with adding the App Categories. The strange thing is that the User Categories work. In technet I asked for some help but maybe u can help me out: social.technet.microsoft.com/.../456b1245-41ea-4ede-be63-4dd7945bf1b4 Btw. if u wanna add an icon u have to use the Icon Object from the SCCM and then load an icon in it with the Drwaing Icon Object: index.str_ICO = Icon.ExtractAssociatedIcon(textBox_ICOpath.Text.ToString()).ToBitmap();

  • Anonymous
    July 25, 2014
    The comment has been removed

  • Anonymous
    December 22, 2015
    How can we add a requirement to the application and allow it to be installed via a task sequence?