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! RajeeshAnonymous
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 -NoorAnonymous
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 SilvanAnonymous
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 removedAnonymous
January 17, 2012
The comment has been removedAnonymous
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.dllAnonymous
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 removedAnonymous
December 22, 2015
How can we add a requirement to the application and allow it to be installed via a task sequence?