As described in my previous post – Install & Uninstall SharePoint apps by code announced at ESPC 2017– Microsoft announced an Application Lifecycle Management (ALM) API to install, uninstall, deploy, retract, upgrade, and remove your applications from the app catalog of your tenant and/or a specific site collection app catalog (available early 2018).

This API is useful when you want to automate your deployment management of your SPFx solutions and add-ins, which you can do by using, e.g. a console application.
A real world scenario could be that you developed an SPFx solution for a specific set of sites and you want to push that solution onto these sites. Then you can create a console application which retrieves the ID of the solution (or app as it is called by Microsoft in the app catalog), and install it specific on these sites. If there was already an older version installed on the site, the API will upgrade the solution to the newest version that is available in the app catalog.
The PnP team did already a great job by providing an extension for their PnP CSOM project. It includes a new ALM class that contains an AppManager, the AppManager provides you the posibility to execute the specific calls as install/uninstall/upgrade… for a specific site.
How to develop this console application? This blog post will provide you a step by step guide to create the application with ‘real’ REST calls to the API and with the PnP CSOM extension.
By using PnP CSOM extension for the ALM API
Once you created a new console application project in Visual Studio, add the SharePointPnPCoreOnline Nuget Package and include the following references in your program.cs file:
- OfficeDevPnP.Core.ALM
- Microsoft.SharePoint.Client
First thing to do is to create a secure connection to SharePoint Online. With that connection, we can retrieve a valid ClientContext, which is necessary to create an AppManager object.
This can be done by using the SharePointOnlineCredentials implemantation that requires a username (string) and a password (SecureString).
1 2 3 4 5 |
using (var context = new ClientContext(webURL)) { context.Credentials = new SharePointOnlineCredentials(username, securePassword); AppManager appManager = new AppManager(context); } |
Get available apps
From that moment we have the AppManager object, so we can execute several actions. To get more information about a specific app/solution, we can use the GetAvailable() method of the AppManager. This method will execute a call to the ALM API and returns a list of all available apps for the specific site of the ClientContext.
1 2 3 4 5 6 7 8 |
using (var context = new ClientContext(webURL)) { context.Credentials = new SharePointOnlineCredentials(username, securePassword); AppManager appManager = new AppManager(context); // Retrieving all available apps for the current site List<AppMetadata> availableApps = appManager.GetAvailable(); } |
As you can notice, we are retrieving a list of AppMetadata that contains the following information:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
// Summary: // App/solution metadata for apps stored in the corporate catalog public class AppMetadata { public AppMetadata(); // // Summary: // Unique ID of the library list item of the app/solution. [JsonProperty] public Guid Id { get; } // // Summary: // Returns version of the app / solution int the app catalog. [JsonProperty] public Version AppCatalogVersion { get; } // // Summary: // Returns whether an existing instance of an app/solution can be upgraded. True // if there's newer version available in app catalog compared to instance in site. [JsonProperty] public bool CanUpgrade { get; } // // Summary: // Returns whether app/solution has been deployed to the context site. True if particular // app/solution has been installed to the site. [JsonProperty] public bool Deployed { get; } // // Summary: // Returns version of the installed app/solution in the site context. [JsonProperty] public Version InstalledVersion { get; } // // Summary: // Returns wheter app/solution is SharePoint Framework client-side solution. True // for SPFx, False for app/add-in. [JsonProperty] public bool IsClientSideSolution { get; } // // Summary: // Title of the solution [JsonProperty] public string Title { get; } } |
Install a app on a specific site
If you want to install a specific app, you can choose to provide the complete AppMetadata object or the app/solution id to the install method.
1 2 3 4 5 6 7 8 9 10 11 |
using (var context = new ClientContext(webURL)) { context.Credentials = new SharePointOnlineCredentials(username, securePassword); AppManager appManager = new AppManager(context); // Retrieving all available apps for the current site List<AppMetadata> availableApps = appManager.GetAvailable(); AppMetadata mySolution = listApps.Where(x=>x.Title == "MySolution").FirstOrDefault(); appManager.Install(mySolution); // appManager.Install(mySolution.Id); } |
Upgrade an app on a specific site
The property CanUpgrade indicates if their is a newer version available in the app catalog than the version that is installed on the site. If there is a newer version available, the upgrade method can be used to get the app/solution to the latest version.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
using (var context = new ClientContext(webURL)) { context.Credentials = new SharePointOnlineCredentials(username, securePassword); AppManager appManager = new AppManager(context); // Retrieving all available apps for the current site List<AppMetadata> availableApps = appManager.GetAvailable(); AppMetadata mySolution = listApps.Where(x=>x.Title == "MySolution").FirstOrDefault(); if (mySolution.CanUpgrade) appManager.Upgrade(mySolution); } |
Add solution package to tenant app catalog
Adding a solution (app package => .sppkg) can currently only being done to the tenant app catalog and not yet to a site collection app catalog. So this request has to be targeted to the tenant app catalog site:
1 2 3 4 5 6 7 |
using (var context = new ClientContext(tenantAppCatalogURL)) { context.Credentials = new SharePointOnlineCredentials(username, securePassword); AppManager appManager = new AppManager(context); string locationOfPackage = @"C:\temp\MySolution.sppkg"; appManager.Add(locationOfPackage); } |
Deploy solution package in tenant app catalog
Before the solution is avaible for installation on sites, it has to be enabled/deployed into the tenant. This is also only possible for the tenant app catalog, so not on site collection level.
1 2 3 4 5 6 7 8 9 10 11 |
using (var context = new ClientContext(tenantAppCatalogURL)) { context.Credentials = new SharePointOnlineCredentials(username, securePassword); AppManager appManager = new AppManager(context); // Retrieving all available apps for the current site List<AppMetadata> availableApps = appManager.GetAvailable(); AppMetadata mySolution = listApps.Where(x=>x.Title == "MySolution").FirstOrDefault(); appManager.Deploy(mySolution);// appManager.Deploy(mySolution.Id); } |
Retract and remove solution package in the tenant app catalog
If your app is no longer needed on your tenant, you can use the retract and remove action to remove it. This is also only possible for the tenant app catalog, so not on site collection level.
1 2 3 4 5 6 7 8 9 10 11 12 |
using (var context = new ClientContext(tenantAppCatalogURL)) { context.Credentials = new SharePointOnlineCredentials(username, securePassword); AppManager appManager = new AppManager(context); // Retrieving all available apps for the current site List<AppMetadata> availableApps = appManager.GetAvailable(); AppMetadata mySolution = listApps.Where(x=>x.Title == "MySolution").FirstOrDefault(); appManager.Retract(mySolution);// appManager.Retract(mySolution.Id); appManager.Remove(mySolution);// appManager.Remove(mySolution.Id); } |
By using your own REST calls to the ALM API
As with the PnP chapter; once you created a new console application project in Visual Studio, you can add the SharePointPnPCoreOnline Nuget Package and include the following reference in your program.cs file:
- Microsoft.SharePoint.Client
An extra thing here is that you have to take care of the Authorization header, cookies, URL’s, request method, and parsing all by yourself. This is more difficult but possible like shown in the following code example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
Uri siteURL = new Uri("https://ventigrate.sharepoint.com"); using (var context = new ClientContext(siteURL)) { var credentials = new SharePointOnlineCredentials(username, securePassword); context.Credentials = credentials; var accessToken = context.GetAccessToken(); using (var handler = new HttpClientHandler()) { using (var httpClient = new HttpClient(handler)) { context.Load(context.Web); context.ExecuteQuery(); handler.CookieContainer.SetCookies(siteURL, credentials.GetAuthenticationCookie(siteURL)); string requestUrl = $"{siteURL}/_api/web/tenantappcatalog/AvailableApps/"; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl); request.Headers.Add("accept", "application/json;odata=nometadata"); request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); request.Headers.Add("X-RequestDigest", context.GetRequestDigest().Result); HttpResponseMessage response = httpClient.SendAsync(request, new System.Threading.CancellationToken()).Result; } } } |
This response will contain a list of all the available apps for the specific site and further on you can use the ID (and not the whole metadata als with the PnP extension) to install/upgrade… an app.
Installing an app can be done by the url: /_api/web/tenantappcatalog/AvailableApps/GetById(‘GUID’)/Install, and needs to be used in a POST request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
Uri siteURL = new Uri("https://ventigrate.sharepoint.com"); using (var context = new ClientContext(siteURL)) { var credentials = new SharePointOnlineCredentials(username, securePassword); context.Credentials = credentials; var accessToken = context.GetAccessToken(); using (var handler = new HttpClientHandler()) { using (var httpClient = new HttpClient(handler)) { context.Load(context.Web); context.ExecuteQuery(); handler.CookieContainer.SetCookies(siteURL, credentials.GetAuthenticationCookie(siteURL)); string requestUrl = $"{siteURL}/_api/web/tenantappcatalog/AvailableApps/GetById('GUID')/Install"; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUrl); request.Headers.Add("accept", "application/json;odata=nometadata"); request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); request.Headers.Add("X-RequestDigest", context.GetRequestDigest().Result); HttpResponseMessage response = httpClient.SendAsync(request, new System.Threading.CancellationToken()).Result; } } } |
The other calls like adding, deploying, retracting, removing.. can definitly be done by simple REST calls, but are prety much the same as described above.
Recap
As you can see the new ALM API is really powerful and can help a lot of SharePoint teams to create an automatic deploy process for their apps. It can also help these teams to push specific apps to specific sites to increase the usage and installment rate of their apps/solutions.
Creating a console application with ‘custom’ HTTP call to the API is way more difficult than using the PnP extension. So if it is possible and allowed by your internal company policy, PnP is definitely the way to go.