The Microsoft App-V Team Blog

The official blog for Microsoft Application Virtualization

How to create a sample App-V HTTP reporting service

How to create a sample App-V HTTP reporting service

  • Comments 1
  • Likes

GrayAndYellowGearsAlthough you can use the App-V Management Server to gather usage information from clients, many customers have special requirements that could benefit from a customized approach. HTTP Reporting provides a simple way to create a reporting service for App-V that is customized for your business needs.

The following article steps you through the process of creating a simple and scalable system for gathering App-V usage information. This solution will work with any of the supported application publishing methods supported by App-V, including App-V Management Server, Configuration Manager, or even manual deployment via MSIs (Windows Installer).

First, some background. By default, the App-V Client does not record usage information. It is enabled through the policies set up by the Publishing Servers that are configured for the client. Once enabled, the following information is recorded for every application launch: application name, user, and start time. When the application is closed, the end time is also recorded. This information will remain cached on the App-V client until a Publishing Refresh is requested against the reporting server. If you use the App-V Management Server as your reporting server, the usage information is stored in the APPLICATION_USAGE table of the App-V database.

Usage information is sent to the reporting server as a POST request that contains an XML document listing all of the packages loaded on the client system along with the usage information identified above. If the reporting server responds with HTTP 200, the App-V Client will remove the usage information from the client cache. Any other return code will cause the App-V Client to retain the usage information and it will try to send it again the next time a Publishing Refresh is requested against the reporting server.

Note: Currently the App-V Client misinterprets a response of Temporary Redirect as success so you should not use a web server that may return Temporary Redirects.

The App-V Client can be set up with more than one Publishing Server. For example, you could set up one Publishing Server to point to an App-V Management Server for Application Publishing and then setup a second Publishing Server as your reporting server.

For this blog, we are going to set up an HTTP Reporting Service built with ASP.NET. The storing of reporting data will happen in two steps. The App-V Client will connect with our HTTP Reporting Service which will accept the usage information and store it in an XML file. Each night, a scheduled task will load the information from these XML files into a database and then delete the XML files. To avoid filename conflicts, the HTTP Reporting Service will use a GUID as the name for the temporary XML files.

The first step in implementing this solution is to create a web services that performs two simple tasks. If it receives an HTTP GET request, it needs to notify that caller that it is prepared to accept reporting information. If it receives an HTTP POST request, it needs to generate a file name and store the contents of the POST request to the file. Here are the steps for creating your HTTP Reporting Service with Visual Studio 2010.

1. Start Visual Studio and create a new Visual C# ASP.NET Web Application project called “HTTP-Reporting-Service”. Make sure to select “.NET Framework 3.5” unless you already have IIS set up to support “.NET Framework 4.0”.

2. Change “Default.aspx” to contain the following content:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs"
Inherits="HTTP_Reporting_Service._Default" %>

3. Right click on Default.aspx in the Solution Explorer and select “View Code”. Replace the auto generated code with the following:

//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
//
//*********************************************************
using System;
using System.IO;
using System.Collections.Specialized;
using System.Configuration;
using System.Web.Configuration;
namespace HTTP_Reporting_Service
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Load appSettings section of the web.config file
NameValueCollection appSettings = WebConfigurationManager.AppSettings as NameValueCollection;
// Instruct App-V Client to send reporting data to this service
if (Request.HttpMethod.Contains("GET"))
{
String refreshInterval = appSettings["RefreshInterval"];
Response.Output.Write("<DESKTOPCONFIG>");
Response.Output.Write("<POLICY MANAGEDDESKTOP=\"TRUE\" REPORTING=\"TRUE\">");
Response.Output.Write("<REFRESH ONLOGIN=\"TRUE\" PERIOD=\"" + refreshInterval + "\"/></POLICY>");
Response.Output.Write("<APPLIST>");
Response.Output.Write("</APPLIST>");
Response.Output.Write("</DESKTOPCONFIG>");
}
// Save reporting data to temporary cache file
if (Request.HttpMethod.Contains("POST"))
{
// Read web.config for configuration information
String cacheLocation = appSettings["CacheLocation"];
int bufferSize;
try
{
bufferSize = Convert.ToInt32(appSettings["BufferSize"]);
}
catch
{
// If BufferSize is invalid, default to 64K
bufferSize = 64 * 1024;
}
// Save reporting data to a unique filename
String filename = Path.Combine(cacheLocation, System.Guid.NewGuid().ToString() + ".xml");
try
{
Stream inputStream = Request.InputStream;
FileStream outputStream = new FileStream(filename, FileMode.CreateNew);
byte[] buffer = new byte[bufferSize];
int bytesToWrite = Request.TotalBytes - 1;
int bytesWritten = 0;
// Write out everything but the last byte so that the file can be parsed as XML
while (bytesWritten < bytesToWrite)
{
int bytesRead = inputStream.Read(buffer, 0, bufferSize);
if (bytesWritten + bytesRead > bytesToWrite)
bytesRead = bytesToWrite - bytesWritten;
outputStream.Write(buffer, 0, bytesRead);
bytesWritten += bytesRead;
}
outputStream.Close();
Response.StatusCode = 200;
}
catch
{
Response.StatusCode = 500;
}
}
}
}
}

4. Build the project. This should create “HTTP-Reporting-Service.dll”

You can now set this service up on IIS7 using the following steps:

1. Log in to the system hosting IIS. Make sure ASP.NET support is enabled.

2. Copy “Default.aspx” into “C:\inetpub\wwwroot\HTTP-Reporting-Service”.

3. Copy “HTTP-Reporting-Service.dll” into “C:\inetpub\wwwroot\HTTP-Reporting-Service\bin”.

4. Create a directory called “C:\Reporting-Cache” to hold the usage data sent by the App-V Client.

5. Create “C:\inetpub\wwwroot\HTTP-Reporting-Service\web.config” with the following content.

<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="CacheLocation" value=" C:\Reporting-Cache "/>
<add key="BufferSize" value="65536"/>
<add key="RefreshInterval" value="720"/>
</appSettings>
</configuration>

6. Open the Internet Information Services Manager and expand the Sites node. Right click on Default Web Site and pick “Add Application”.

7. Enter “HTTP-Reporting-Service” as the Alias. Enter “C:\inetpub\wwwroot\HTTP-Reporting-Service” as the Physical Path. Press Ok.

Next, you need to create program to load the XML files that are created by the HTTP-Reporting-Service created above. Here are the steps to do this using Visual Studio 2010:

1. Start Visual Studio and create a new Visual C# Console Application project called “Report-Data-Loader”. Make sure to select “.NET Framework 3.5” unless you already have IIS set up to support “.NET Framework 4.0”.

2. Replace the contents of Program.cs with the following code:

//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
//
//*********************************************************
using System;
using System.IO;
using System.Data.SqlClient;
using System.Xml;
namespace AppV_Load_Report_Data
{
class Program
{
static int Main(string[] args)
{
// Display usage information of any parameters are missing
if (args.Length < 2)
{
Console.WriteLine("USAGE: AppV-Load-Report-Data <cacheLocation> <dbConnectionString>");
return 1;
}
String cacheLocation = args[0];
String connectionString = args[1];
// Make sure parameters are valid
DirectoryInfo cacheDirectory;
if (!Directory.Exists(cacheLocation))
{
Console.WriteLine("ERROR: The cacheLocation specified does not exist.");
return 2;
}
cacheDirectory = new DirectoryInfo(cacheLocation);
try
{
SqlConnection databaseConnection = new SqlConnection(connectionString);
databaseConnection.Open();
Console.WriteLine("Processing files...");
// Process each XML file in the cache directory
foreach (FileInfo reportFile in cacheDirectory.GetFiles("*.xml"))
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(reportFile.FullName);
XmlNodeList nodeList = xmlDoc.SelectNodes("//CLIENT_DATA/APP_RECORDS/APP_RECORD");
foreach (XmlNode node in nodeList)
{
DateTime shutdown = Convert.ToDateTime(node.Attributes["Shutdown"].Value);
if (shutdown < DateTime.MaxValue)
{
String commandText = "INSERT INTO AppV_Usage_Information " +
"(App_Name, App_Version, User_Account, Start_Time, End_Time) " +
"VALUES (@appName, @appVer, @user, @startTime, @endTime)";
SqlCommand command = new SqlCommand(commandText, databaseConnection);
command.Parameters.AddWithValue("@appName", node.Attributes["Name"].Value);
command.Parameters.AddWithValue("@appVer", node.Attributes["Ver"].Value);
command.Parameters.AddWithValue("@user", node.Attributes["User"].Value);
command.Parameters.AddWithValue("@startTime", Convert.ToDateTime(node.Attributes["Shutdown"].Value));
command.Parameters.AddWithValue("@endTime", shutdown);
command.ExecuteNonQuery();
}
}
reportFile.Delete();
}
databaseConnection.Close();
}
catch (Exception e)
{
Console.WriteLine("ERROR: Unexpected error.");
Console.WriteLine("DETAILS: {0}", e.Message);
return 3;
}
return 0;
}
}
}

3. Build the project. This should produce “Report-Data-Loader.exe”

4. Log in to the IIS Server and copy “Report-Data-Loader.exe” to “C:\ Reporting-Cache”

Before starting Report-Data-Loader you must create the database that will accept the usage information. Here are the steps for setting up the reporting database:

1. Open up the Microsoft SQL Server Management Studio using an account that has permission to create tables.

2. Open up a New Query window and run the following SQL Script to create a sample reporting table:

CREATE TABLE [dbo].[AppV_Usage_Information](
[App_Name] [nvarchar](64) NOT NULL,
[App_Version] [nvarchar](16) NULL,
[User_Account] [nvarchar](256) NOT NULL,
[Start_Time] [datetime] NOT NULL,
[End_Time] [datetime] NULL )

You now want to set up a scheduled task on your IIS Server to run Report-Data-Loader.exe. This can be scheduled to run during an off peak period.

1. Start Task Scheduler from Administrator Tools on your IIS server.

2. Select “Create Task”.

3. On the General tab, enter a name and description for this task and indicate the user account to use for the job.

4. Press New on the Triggers tab. On the New Trigger dialog, select “Daily” and specify a start time that is in your off peak window. Press Ok when you are done.

5. Press New on the Action tab. On the New Action dialog, select “Start a program”, specify “C:\Reporting-Cache\Report-Data-Loader.exe” as the Program, and specify “Server=localhost;Database=AppVReport;Uid=sa;Pwd=password” as the arguments. Note that this is your database connection string. Please make sure to substitute in the values for your database server. Press Ok when done.

6. Press Ok to save the new task.

The last step is to set up your App-V Clients to use the new HTTP Reporting Service.

1. Sign in to your App-V Client machine and open the Application Virtualization Client from the Administrative Tools control.

2. Right Click on Publishing Servers and select “New Server…”

3. Enter “Reporting Service” as the Display Name and select “Standard HTTP Server” as the Type. Press Next.

4. Enter server name as the Host Name (example “MyIISServer”) and “/HTTP-Report-Service/Default.aspx” as the Path and press Finish.

Once you have completed these steps, launch and shut down a couple of applications on your client. Then open the Application Virtualization Client, expand the Publishing Servers node, right click on the Reporting Service” item, and select Refresh Server. This should create an XML file in the “Reporting-Cache” directory of your IIS server. When the Report-Data-Loader.exe task is run it should load the usage information from this file into your database and then delete the XML file.

You will find that this service can support the reporting needs for App-V systems with many thousands of clients.

Happy Reporting!

Michael Bilodeau | Senior SDE

App-V Team blog: http://blogs.technet.com/appv/
AVIcode Team blog: http://blogs.technet.com/b/avicode
ConfigMgr Support Team blog: http://blogs.technet.com/configurationmgr/
DPM Team blog: http://blogs.technet.com/dpm/
MED-V Team blog: http://blogs.technet.com/medv/
OOB Support Team blog: http://blogs.technet.com/oob/
Opalis Team blog: http://blogs.technet.com/opalis
Orchestrator Support Team blog: http://blogs.technet.com/b/orchestrator/
OpsMgr Support Team blog: http://blogs.technet.com/operationsmgr/
SCMDM Support Team blog: http://blogs.technet.com/mdm/
SCVMM Team blog: http://blogs.technet.com/scvmm
Server App-V Team blog: http://blogs.technet.com/b/serverappv
Service Manager Team blog: http://blogs.technet.com/b/servicemanager
System Center Essentials Team blog: http://blogs.technet.com/b/systemcenteressentials
WSUS Support Team blog: http://blogs.technet.com/sus/

clip_image001 clip_image002

Comments
  • Additional info to my previous comment:

    Windows 7 Enterprise x64

    App-V 4.6.1.30091

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment