This article describes how to use the FIM CM Notification API to send an e-mail message to a distribution list when a certificate enrolled through the FIM CM portal enters its renewal period.
Back in 2005, Microsoft acquired Canadian firm Alacris, primarily for its idNexus smart card management software. After rebranding the product as Microsoft Certificate Lifecycle Manager during its beta period, it was merged with Microsoft’s meta-directory product, MIIS 2003 and released under the banner of ILM 2007; the certificate management component was known as Certificate Lifecycle Manager 2007. The focus of Certificate Lifecycle Manager 2007 (CLM 2007) remained the management of smart cards, but a session at TechEd North America in 2008 by Brian Komar with the tongue-in-cheek title of Using Microsoft Identity Lifecycle Manager 2007 Certificate Management for Something Other than Smart Cards highlighted other possibilities for the product. One of the scenarios discussed in the session, which can still be viewed here http://channel9.msdn.com/Events/TechEd/NorthAmerica/2008/IDA355, was using CLM 2007 to enrol web server certificates. The main benefit of using CLM 2007 to enrol the certificates was that it allowed an e-mail notification to be sent when the certificate entered its renewal period. Brian’s session didn’t go in to too much detail about how the e-mail was generated or how the body of the e-mail could be customised, but I thought there would be enough information out on the Internet and maybe even some example code, that would allow me to get the scenario working. Rather surprisingly I found very little information other than the Notification API documentation on MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/bb468066(v=vs.85).aspx), so I set myself a challenge. The objective was to send an e-mail to a distribution group whenever a web server certificate enrolled through CLM 2007’s successor, FIM Certificate Management 2010 (FIM CM), entered its renewal period. The body of the e-mail would provide the subject name of the certificate, the name of the host where the certificate was installed and any useful information about the certificate due to expire. In addition, only the published FIM CM APIs would be used. This latter requirement introduced some inefficiency into the solution and I’ll describe how these may be overcome later in this article.
In order to keep the length of this article to a minimum, I don’t want to go into too much detail about how to set up FIM CM or how to configure the delivery of e-mail notifications. To gain a better understanding of what installation and configuration steps are required, I recommend reviewing the tutorials on Dmitrii Lezine’s blog (http://cloudidentityblog.com/2011/02/01/implementing-fim-2010-certificate-management-part-1/). I also recommend watching Brian Komar’s TechEd session mentioned earlier, as this goes into the basics of configuration for enrolling web server certificates. The main idea behind Brian’s approach is to use a dedicated user account as an enrolment agent and configure the account with the e-mail address of a distribution list that contains administrators who can respond to the renewal e-mail notification. IMPORTANT: Before I started the development work, I made sure I could get FIM CM to deliver a standard e-mail message, which may be configured using the Renewal Policy section of a management policy. Once that was successful, the distribution method configured in the Renewal Policy was changed to ‘Do not distribute’ and responsibility for delivering the e-mail message was then down to the add-in. In doing this, it was easier to determine whether any problems were down to general FIM CM configuration or down to the code and its configuration. I highly recommend adopting the same approach if you use this add-in, as it will save a lot of troubleshooting time!
For a bit of background I will quickly cover what happens when a web server certificate is enrolled through FIM CM in relation to the underlying SQL Server tables. An understanding of what happens under the covers is necessary to appreciate how the solution works. Activities in FIM CM, such as certificate enrolment, are policy-based. The policy is expressed within a profile template which holds details of the workflows to be used at each stage in a certificate’s life, the templates to be used when enrolling certificates and an indication as to whether the profile template is for a smart card or a software certificate. When creating a new request for a certificate, a row is added to the Requests table. Several columns are populated including a unique identifier for the request (request_uuid) and a column containing information gathered from data collection items (req_reg_data). When the certificate request is submitted, rows are added to the Profiles, ProfileCertificates and Certificates tables and the row recently added to the Requests table is updated. An abbreviated view of the database tables and the relationship between them is illustrated below:
A row is added to the Profiles table to indicate which profile template is to be used when the request is processed. The row gets a unique identifier (profile_uuid) and references the unique identifier of the profile template to be used in the request (pr_profile_template_uuid); several other columns are also populated. After the row is added to the Profiles table, the Requests table is updated, with the req_profile_uuid column of the new request being updated with the value of profile_uuid from the Profiles table. In doing this, FIM CM knows which profile template to use when enrolling certificates for the request. When a certificate or certificates are issued (remember that a profile template can contain one or more certificate templates), a row is added to the Certificates table for each certificate and includes a unique identifier (cert_id), the certificate’s common name (cert_issued_common_name) and the certificate’s renewal date (cert_renew); several other columns are also populated. A row is also added to the ProfileCertificates table for each certificate and records the certificate’s unique identifier (pc_cert_id) and the profile from which it was derived (pc_profile_uuid); this latter value matches the value held in the req_profile_uuid column of the Requests table and the profile_uuid column of the Profiles table. This activity is illustrated in the following diagrams. Note that some data is held as XML and as a result is not easily extracted unless using the FIM CM APIs. To begin with, a new enrolment request is created in the FIM CM portal and as a result a new row is added to the Requests table:
dbo.REQUESTS
request_uuid
req_data (XML)
req_reg_data (XML)
req_profile_uuid
req_type
req_target_user_uuid
abc-123-989
<XML>
Host Name= APPSRV1
Common Name= www.contoso.com
1 (Enroll)
bfe-789-456
After the request is successfully submitted and executed within the FIM CM portal, rows are added to the Profiles, ProfileCertificates and Certificates tables:
dbo.PROFILES
profile_uuid
pr_profile_template_uuid
aaa-444-121
ptu-333-901
dbo.PROFILECERTIFICATES
pc_profile_uuid
pc_cert_id
21
dbo.CERTIFICATES
cert_id
cert_issued_common_name
cert_renew
www.contoso.com
2014-02-17 18:45:07.000
The Requests table is also updated to include a reference to the Profile used to enrol certificates:
When a renewal request is generated by FIM CM due to a certificate entering its renewal period, a new request row is added to the Requests table. The req_data column contains the unique identifier of the profile from which the certificate was derived. This information may be used to tie the renewal request to the original enrolment request that contains some important data collection items. These are added to the body of the e-mail message along with other information to help identify the certificate that needs to be renewed.
5df-686-849
Profile UUID= aaa-444-121
3 (Renew)
So now seems like a good time to explain where all the information for the body of the e-mail message will come from.
In the e-mail body, I wanted to include enough information for the expiring certificate to be identified and to indicate on which server the certificate was installed, so I decided on the following:
Some of this information has already been identified as being stored in one of the SQL Server tables, such as cert_issued_common_name, but other information, such as the host name of the server where the certificate is installed, is not maintained by FIM CM at all. In fact, after reviewing the FIM CM APIs, only the last three items in the list are accessible; the common name, while stored in the Certificates table is not presented through the APIs. In order to address the issue of certificate common name and host name, I decided to add them as data collection items in the profile template definition and require them to be entered by the administrator creating the enrolment request. The values are stored in the req_reg_data column in the Requests table and are held in XML format. Now that I’ve covered the basics of how FIM CM handles requests and where the data for the notification e-mail will be sourced, it is time to look at the code. I will discuss how the renewal request is detected and how to go about collecting all the information to send the reminder e-mail to the distribution list.
I’ll now start to discuss the code that accompanies this post. The MSDN article, How to: Register for an Event Using a Notification Handler (http://msdn.microsoft.com/en-us/library/windows/desktop/bb468051.aspx), describes how to amend web.config to register for particular FIM CM events. In a default installation of FIM CM, web.config is located in C:\Program Files\Microsoft Forefront Identity Manager\2010\Certificate Management\web. The section to be amended is called ClmNotifications. The extract below shows a single registration for the DistributeSecrets event, which is the event that is triggered when a certificate enters its renewal period. <ClmNotifications> <add event="DistributeSecrets" class="Technet.Clm.RenewalNotification.OnDistributeSecrets,Technet.Clm.RenewalNotification" /> </ClmNotifications>
In order to handle the event, the code must implement a notification handler class. This must implement two methods: Initialize and Notify. It is not necessary for the Initialize method to perform any processing, but it provides an opportunity to pass in a configuration string via the initializationData attribute of the notification registration. Note that this attribute is not used in the example above.
The Notify method must also be implemented. It is passed a Notification object that contains several properties. Of interest to us are NotificationType and Request. The notification type property indicates what type of event has been triggered. If the event is not a DistributeSecrets event, it can be ignored. The Request property is a reference to the FIM CM request object that triggered the event. This is passed as a parameter to the ProcessRequest method of the ClmRenewal class, which contains all the code necessary to find the original enrolment request, extract the data collection items for Host Name and certificate Common Name, and send an e-mail for each certificate in the associated profile template.
After enabling .NET Remoting in the Initialise method, which takes its configuration from the application’s .config file, the next step is to find the original enrolment request, as this will contain details of the two data collection items. This is where the code becomes inefficient. The GetEnrolRequest method is called, passing in the profile UUID from the Request object provided in the notification. The profile UUID identifies the profile used in the original enrolment request. If you recall the earlier discussion of the records that are written to the various FIM CM database tables, you will remember that a new record is written to the Profiles table in response to a new enrolment request being written to the Requests table. A column in the Requests table (req_profile_uuid) is then updated with the profile UUID of the record in the Profiles table. In doing this, the request and the profile are linked together. In order to find the original enrolment request, which contains the data collection items, using only the FIM CM APIs, the following steps need to be performed:
In scoping the search operation in step 1, additional criteria are specified, in order to minimise the number of records returned and which must subsequently be searched through:
Having found the original enrolment request, the next step is to obtain the profile record using the profile UUID from the Request object provided in the notification. This is easily obtainable using the FindOperations.GetProfile method of the Provisioning API. The original enrolment request and the profile are passed to the SendNotification method. SendNotification obtains all the certificates associated with the profile using the Provisioning API’s FindOperations.FindCertificates method, calling the SendEmail method for each certificate returned. The enrolment request object is also passed to SendEmail so that the data collection items can be extracted and added to the body of the e-mail message. The data collection items are taken from the DataCollection property of the request. An e-mail message is sent for each certificate in the profile.
The code that retrieves the original enrolment request is inefficient as there is no way using the FIM CM APIs to retrieve a record from the Requests table based on a value in the req_profile_uuid column. The obvious way around this is to go direct to the tables using SQL statements, e.g.
SELECT request_uuid FROM dbo.Requests WHERE req_profile_uuid = request.OldProfileUuid
Having obtained the request UUID, the corresponding request object can then be obtained via the FindOperations.GetRequest method of the Provisioning API; the data collection items may then be extracted in the same manner as described previously. I mentioned earlier that the common name of the certificate is not exposed by any of the FIM CM APIs, so I included it as a data collection item. A further optimisation that negates the need to capture the subject name of the certificate is to read it directly from the Certificates table, again using SQL, e.g. SELECT c.cert_issued_common_name FROM dbo.Certificates AS c, dbo.ProfileCertificates AS pc WHERE pc.pc_profile_uuid = request.OldProfileUuid AND pc.pc_cert_id = c.cert_id Retrieving the certificate detail in this way has another advantage. I originally envisaged that the profile used to enrol a certificate through the FIM CM portal would contain only one certificate template, but this need not be the case. If a profile contained two or more certificate templates whose validity period was different, an e-mail message would be generated for all certificates in the profile. By retrieving the certificate’s subject name in this way, access is also gained to the cert_renew column of the Certificates table which could be checked to see if it was this particular certificate that caused the renewal notification in the first place. If it was, the e-mail message would be sent, if it wasn’t, sending an e-mail message for this certificate could be skipped. I have yet to implement either of the optimisations outlined above and it is left as an exercise for the enthusiastic reader! However, when I do find the time to complete this work, I’ll update this article with the revised code.
The source code for the add-in can be found at the end of this article. To create a compiled DLL, perform the following tasks:
The add-in’s configuration file can also be found at the end of this article. This will need a certain amount of customisation, as described below.
<client> <wellknown type="Microsoft.Clm.Provision.FindOperationsByCulture, Microsoft.Clm.Provision" url="http://fimcm1/certificatemanagement/remoterequests3.rem" /> <wellknown type="Microsoft.Clm.Provision.RequestOperationsByCulture, Microsoft.Clm.Provision" url="http://fimcm1/certificatemanagement/remoterequests2.rem" /> <wellknown type="Microsoft.Clm.Provision.PermissionOperationsByCulture, Microsoft.Clm.Provision" url="http://fimcm1/certificatemanagement/remoterequests4.rem" /> <wellknown type="Microsoft.Clm.Provision.ExecuteOperationsByCulture, Microsoft.Clm.Provision" url="http://fimcm1/certificatemanagement/remoterequests5.rem" /> </client> The DLL and the DLL.config files need to be copied to two separate locations:
It may be necessary to stop the Forefront Identity Manager CM Update Service before copying the files. Finally, the web.config file needs amending to register for the DistributeSecrets event and to configure .NET remoting. The changes required are shown below. <ClmNotifications> <add event="DistributeSecrets" class="Technet.Clm.RenewalNotification.OnDistributeSecrets,Technet.Clm.RenewalNotification" /> </ClmNotifications> <system.runtime.remoting> <application> <service> <wellknown mode="Singleton" type="Microsoft.Clm.BusinessLayer.RemoteRequests, Microsoft.Clm.BusinessLayer" objectUri="remoterequests.rem" /> <wellknown mode="SingleCall" type="Microsoft.Clm.Provision.RequestOperationsByCulture, Microsoft.Clm.Provision" objectUri="remoterequests2.rem" /> <wellknown mode="SingleCall" type="Microsoft.Clm.Provision.FindOperationsByCulture, Microsoft.Clm.Provision" objectUri="remoterequests3.rem" /> <wellknown mode="SingleCall" type="Microsoft.Clm.Provision.PermissionOperationsByCulture, Microsoft.Clm.Provision" objectUri="remoterequests4.rem" /> <wellknown mode="SingleCall" type="Microsoft.Clm.Provision.ExecuteOperationsByCulture, Microsoft.Clm.Provision" objectUri="remoterequests5.rem" /> </service> ...
In order to test the add-in, enrol a certificate for the enrolment agent account using the FIM CM portal. After the certificate has been successfully enrolled, use SQL Server Management Studio to open the Certificates table. Find the certificate that was just enrolled (it is likely to be the last one in the list) and amend the cert_renew date to a time that ensures the certificate is in its renewal period. During development of the add-in, I used a certificate template with a 4 hour validity period and a 3 hour renewal period, so I adjusted the cert_renew date back by 2 hours to ensure that the certificate would be within its renewal period. Restart the Forefront Identity Manager CM Update Service. This will make the service look for expiring certificates and run the add-in to deliver an e-mail message. Event messages are written to the FIM Certificate Management event log, which can be found under the Applications and Services Logs node in Event Viewer. Once the add-in has delivered an e-mail message successfully through manual amendment of the cert_renewal date, it needs to be tested using the normal run cycle of the Forefront Identity Manager CM Update Service. By default, this runs every 5 hours, but its frequency can be reduced to a minimum of 1 hour by amending the Microsoft.Clm.Service.exe.config file that can be found in C:\Program Files\Microsoft Forefront Identity Manager\2010\Certificate Management\Bin. The setting to amend is called Microsoft.Clm.Service.Interval. The value is expressed in milliseconds, so an interval of 1 hour needs a value of 3600000. Additional information about configuring the FIM CM Service can be found here: http://technet.microsoft.com/en-us/library/ee534907(v=ws.10).aspx. If everything works successfully, the result will be an e-mail message similar to the one shown below:
During the introduction, I mentioned that I was unable to find any examples of using the Notification API in this manner out on the Internet. As a consequence, the approach I’ve taken may not be the most optimal and I may be playing fast-and-loose with FIM CM! However, by sticking to the published APIs, I hope the risk of this has been minimised. It is several years since I plied my trade as a developer, so I’m sure the code could be improved significantly and I’ll be happy to take feedback from anyone with suggestions for change. C# Source Code, TechNet.Clm.RenewalNotification.cs
using
System;
System.Collections.Generic;
System.Collections.ObjectModel;
System.Configuration;
System.Diagnostics;
System.Linq;
System.Net;
System.Net.Mail;
System.Reflection;
System.Runtime.Remoting;
System.Text;
Microsoft.Clm.Shared;
Microsoft.Clm.Shared.Requests;
Microsoft.Clm.Shared.Profiles;
Microsoft.Clm.Shared.Notifications;
Microsoft.Clm.Provision;
namespace
TechNet.Clm.RenewalNotification
{
/// Notification handler class
class
OnDistributeSecrets : INotificationSink
void
INotificationSink.Initialize(
string
data)
/// Initialize must be implemented, even if it does nothing
}
INotificationSink.Notify(Notification notification)
if
(notification.NotificationType != NotificationType.DistributeSecrets)
ClmRenewal.LogMessage(
.Format(
"Unexpected notification received: {0}."
, notification.NotificationType), EventLogEntryType.Information, 2);
throw
new
ApplicationException(
, notification.NotificationType));
/// Process the notification
"Notification received: {0}"
ClmRenewal.ProcessRequest(notification.Request);
/// Renewal handler class
internal
static
ClmRenewal
public
ProcessRequest(Request request)
LogMessage(
"Started custom renewal notification processing."
, EventLogEntryType.Information, 1);
"Renewal Uuid is {0}, OldProfileUuid is {1}."
, request.Uuid, request.OldProfileUuid), EventLogEntryType.Information, 0);
/// Initialise remoting
try
Initialise();
catch
(Exception ex)
"A problem occurred initialising remoting: {1}"
, ex.Message), EventLogEntryType.Error, 0);
return
;
/// Get the enrolment request for which the renewal was triggered
Request ClmEnrolRequest =
null
bool
IsFound = GetEnrolRequest(request.OldProfileUuid,
ref
ClmEnrolRequest);
(!IsFound)
"No enrolment requests were found."
, EventLogEntryType.Error, 0);
"A problem occurred trying to read the enrolment request: {1}"
/// Get the profile
Profile ClmProfile =
ClmProfile = FindOperations.GetProfile(request.OldProfileUuid);
LogMessage(String.Format(
"Found Profile for OldProfileUUID {0}"
, request.OldProfileUuid),EventLogEntryType.Information, 0);
"There was a problem obtaining the profile identified by '{0}'.{1}{2}"
, request.OldProfileUuid, Environment.NewLine, ex.Message), EventLogEntryType.Error, 0);
/// Send notification email
SendNotification(ClmEnrolRequest, ClmProfile);
"Finished custom renewal notification processing."
/// Set the .NET Remoting configuration
Initialise()
ExeConfigurationFileMap fileMap =
ExeConfigurationFileMap();
fileMap.ExeConfigFilename = Assembly.GetExecutingAssembly().Location +
".config"
"Config file: {0}"
, fileMap.ExeConfigFilename), EventLogEntryType.Information, 0);
System.Configuration.Configuration config;
config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
"Failed to open config file. {0}"
, ex.Message), EventLogEntryType.Information, 0);
ex;
RemotingConfiguration.Configure(config.FilePath,
false
);
"Failed to configure remoting. {0}"
/// Find the original enrolment request
GetEnrolRequest(Guid profileUuid,
Request clmRequest)
/// Set filters for rerieveing requests
RequestType[] RequestTypes =
RequestType[1] { RequestType.Enroll };
RequestStatus[] RequestStatuses =
RequestStatus[1] { RequestStatus.Completed };
Guid[] OriginatorUsersUuid =
Guid[0];
Guid[] TargetUsersUuid =
Guid[1] {
Guid(GetEnrolmentAgentUuid()) };
DateTime dt1 =
DateTime(2011, 1, 1);
DateTime dt2 = DateTime.Now;
"Retrieved enrolment agent account UUID: {0}"
, TargetUsersUuid[0]), EventLogEntryType.Information, 0);
ReadOnlyCollection<Request> ClmEnrolRequests =
ClmEnrolRequests = FindOperations.FindRequests(RequestTypes, RequestStatuses, OriginatorUsersUuid, TargetUsersUuid, dt1, dt2);
"A problem was encountered retrieving enrolment requests.{0}{1}"
, Environment.NewLine, ex.Message), EventLogEntryType.Error, 0);
"Enrolment request count: {0}"
, ClmEnrolRequests.Count), EventLogEntryType.Information, 0);
/// Look at each request object to find the one with the correct profile UUID
foreach
(Request request
in
ClmEnrolRequests)
(request.NewProfileUuid.Equals(profileUuid))
clmRequest = request;
true
/// Gather the certificate(s) affected by the renewal request and send a notification for each
SendNotification(Request request, Profile profile)
ReadOnlyCollection<Microsoft.Clm.Shared.Certificates.X509ClmCertificate> ProfileCertificates =
ProfileCertificates = FindOperations.FindCertificates(profile);
"Failure when retrieving profile certificates.{0}{1}"
"Number of profile certificates found is {0}."
, ProfileCertificates.Count), EventLogEntryType.Information, 0);
(Microsoft.Clm.Shared.Certificates.X509ClmCertificate X509Certificate
ProfileCertificates)
"Processing profile certificate with serial number {0}."
, X509Certificate.SerialNumber), EventLogEntryType.Information, 0);
SendEmail(request, X509Certificate);
/// Send an e-mail message about each certificate needing renewal
SendEmail(Request request, Microsoft.Clm.Shared.Certificates.X509ClmCertificate certificate)
/// SMTP options
Host = GetConfigItem(
"MailHost"
Int16 Port = Convert.ToInt16(GetConfigItem(
"MailHostPort"
));
/// Mail options
To = GetConfigItem(
"MailTo"
From = GetConfigItem(
"MailFrom"
Subject = GetConfigItem(
"MailSubject"
Body = GetConfigItem(
"MailBody"
/// Replace placeholders with values
DataCollectionItems =
DataCollection dc = request.DataCollection;
(DataCollectionItem dci
dc)
DataCollectionItems += String.Format(
"{0}: {1}{2}"
, dci.Name, dci.Value, Environment.NewLine);
Body = Body.Replace(
"%Data_Collection_Items"
, DataCollectionItems);
"%Serial_Number"
, certificate.SerialNumber);
"%Thumbprint"
, certificate.Thumbprint);
"%Certificate_Template"
, certificate.TemplateCommonName);
/// Create and send mail message
MailMessage mm =
MailMessage(From, To, Subject, Body);
SmtpClient sc =
SmtpClient(Host, Port);
sc.Send(mm);
"An error occurred sending the e-mail message: {0}"
"Message sent for certificate with serial number {0}"
, certificate.SerialNumber), EventLogEntryType.Information, 0);
/// Get the UUID of the enrolment agent account from the configuration file
GetEnrolmentAgentUuid()
GetConfigItem(
"EnrolmentAgentUuid"
/// Read a setting from the configuration file
key)
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
config.AppSettings.Settings[key].Value;
/// Write an entry to the event log
message, EventLogEntryType type,
int
id)
Debug.Print(message);
(!EventLog.SourceExists(
"TechNet.Clm.RenewalNotification"
))
EventLog.CreateEventSource(
,
"FIM Certificate Management"
// Set DebugLevel to 4 in the .config file to get all messages
(Convert.ToInt16(type) >= Convert.ToInt16(GetConfigItem(
"DebugLevel"
)))
EventLog.WriteEntry(
, message, type, id);
Application configuration file, TechNet.Clm.RenewalNotification.dll.config
<?
xml
version
=
"1.0"
encoding
"utf-8"
?>
<
configuration
>
system.runtime.remoting
application
channels
channel
"http"
useDefaultCredentials
"true"
port
"0"
clientProviders
formatter
"binary"
/>
</
client
wellknown
type
"Microsoft.Clm.Provision.FindOperationsByCulture, Microsoft.Clm.Provision"
url
"http://fimcm1.corp.contoso.com/certificatemanagement/remoterequests3.rem"
"Microsoft.Clm.Provision.RequestOperationsByCulture, Microsoft.Clm.Provision"
"http://fimcm1.corp.contoso.com/certificatemanagement/remoterequests2.rem"
"Microsoft.Clm.Provision.PermissionOperationsByCulture, Microsoft.Clm.Provision"
"http://fimcm1.corp.contoso.com/certificatemanagement/remoterequests4.rem"
"Microsoft.Clm.Provision.ExecuteOperationsByCulture, Microsoft.Clm.Provision"
"http://fimcm1.corp.contoso.com/certificatemanagement/remoterequests5.rem"
appSettings
add
key
value
"4"
"fa04f13e-2482-48fb-9a1f-c9e518c605f0"
"smtp.corp.contoso.com"
"25"
"webadmin@corp.contoso.com"
"webteam@corp.contoso.com"
"Certificate Expiry Notification"
value="This is an automated message generated by the Forefront Identity Manager 2010 Certificate Management system.
A certificate identified by the details below has entered its renewal period:
%Data_Collection_Items
Serial Number: %Serial_Number
Thumbprint: %Thumbprint
Certificate Template: %Certificate_Template
Further details about the certificate can be obtained through the FIM CM portal by selecting 'Find a certificate' from
the 'Manage Users And Certificates' section of the portal home page (http://clm.contoso.com/certificatemanagement).
Renew the certificate promptly to ensure that there is no interruption of service.
PLEASE NOTE: No further notifications regarding the status of this certificate will be issued." />