BAM and Alerts

Topics: General Questions, Getting Started/Beginner, Tips and Tricks
Oct 8, 2012 at 3:28 PM

Does anyone know of a good way to store the BAM alerts and reimport then in automatically?  Does BTDF do this?  Are there other tools that can do this?

We are running into issues, with having our alerts disappear when we deploy our views. 

I'm fairly certian it is because of this:

Note:  BAM definitions created using the BAM Add-in for Excel cannot contain alerts.




Oct 10, 2012 at 3:37 AM

Hi Cyrus,

I do not know of a BizTalk tool to export and import alerts.  It may be possible using the SQL Notification Services tools.  Seems like the BizTalk tools let you view, enable and disable them, but not create them.  BTDF does not support this today.


Oct 11, 2012 at 7:38 PM


Thanks Tom.  That was my reading of the documents as well.  I want to throw out some ideas and see what you think.

According to this link (, the BAM definition allows Alerts to be placed into the Bam definition file (the file created by the BAM excel add-in).

The get-defxml command ( creates a file that conforms the the BAM definition and it contains Alerts and subscribers (if there are any).  (I've tested this)

So in theory, all one would need to do, is place the Alerts (from the file get-defxml created) into the file that was created by the BAM excel addin prior to the Bam definition being imported\updated.  That would make it possible to store off any alerts to keep them from being wiped, if the View needed updating.  (I'm in the process of testing this to see if will work.)

I am trying to do this process through the MSBUIlD process that is already established.  I'm running into problems calling my custom Task (written in C# and located in the "DeploymentFramework.BuildTasks" you created).

I'm getting the error below

error MSB4036: The "CopyAlerts" task was not found. Check the following: 1.) The name of the task in the project file is the same as the name of the task class. 2.) The task class is "public" and implements the Microsoft.Build.Framework.ITask interface. 3.) The task is correctly declared with <UsingTask> in the project file, or in the *.tasks files located in the "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727" directory.

What is the overal all process of creating a Task?  I think i missed an item.  I implemented the "Task" interface (like GetOsVersion did) and have tested and built the project.  When i call the task inside the build project, i get error MSB4036.  I currently trying to get up to speed on MSBUILD, but thought I would ask here to see if there was something special I need to do, to implement a new task for BTDF.

Oct 12, 2012 at 5:14 AM

Sounds like it could work then.  You might consider adding your custom task as a separate assembly vs. modifying the BTDF assembly, but either way works.

The task assembly must be copied to <ProgramFiles>\MSBuild\DeploymentFrameworkForBizTalk\5.0, and you need to add a UsingTask element.  The BTDF UsingTask elements are in the BizTalkDeploymentFramework.targets, but you should be able to add one in your .btdfproj file instead.

This is one of the BTDF lines:
<UsingTask AssemblyFile="DeploymentFramework.BuildTasks.dll" TaskName="GetOsVersion" />

Sounds like you're missing one of those two steps.


Oct 12, 2012 at 3:46 PM

Yep that was it, I was putting the Dll in the wrong place (also I updated the wrong, "targets" file.  Thanks for the help.  So far testing is going well, but I've yet to take the process through adding an additional field to the view and activity (that is where I ran into problems).  I update this area once I know more.



Dec 19, 2012 at 9:47 PM

Wanted to lay out some code that I used to solve this issue.  It seems to work well for my needs, at this time.


The overridden MSBuild task

  <!--This overrides the default DeployBam Target-->
  <Target Name="DeployBam" DependsOnTargets="ExportBAMXMLFromXLS;UndeployBam" Condition="'$(IncludeBAM)' == 'true'">

    <Message Text="Cyrus Freaking Override of the DeployBAM task, hold your hats boys." />
    <!--cleaning up previous file-->
    <Delete Files="@(BamDefinitionsQualified->'%(RootDir)%(Directory)CompleteBAMdefinitions.xml')"/>
    <!--Export the current BAM defincation for the current view that we are working with-->

    <Message Text="Exporting the current bam definitions..." />
       Command=""$(BtsDir)Tracking\bm.exe" get-defxml -FileName:"@(BamDefinitionsQualified->'%(RootDir)%(Directory)CompleteBAMdefinitions.xml')"" />

    <!-- View names and associated accounts must be defined in a property called BAMViewsAndAccounts -->
    <!-- The format of the value of the BAMViewsAndAccounts property must be: -->
    <!-- ViewName1:DOMAIN\GroupName1,DOMAIN\UserName1;ViewName2:BUILTIN\Administrators,COMPUTERNAME\UserName2;<etc. etc.> -->
    <!-- This is easily pulled from the settings spreadsheet by including BAMViewsAndAccounts in the PropsFromEnvSettings ItemGroup. -->

    <ItemGroupFromSeparatedList SeparatedList="$(BAMViewsAndAccounts)" FormatString="{0}" Separator=";" ReverseList="false" ListItemRegex="(?'viewName'\w+)\s*:\s*(?'groupNames'[\w\s\\,]+)$"
                                Condition="'$(BAMViewsAndAccounts)' != ''">
      <Output TaskParameter="ItemGroup" ItemName="BAMViewsAndAccountsGroup" />

    <CopyAlerts BAMDefinition="@(BamDefinitionsQualified->'%(RootDir)%(Directory)%(Filename).xml')"

    <!-- Deploy BAM definitions. -->
    <!-- First, deploy the BAM definitions -->
    <Message Text="Deploying BAM definition..." />
    <Exec ContinueOnError="True" Command=""$(BtsDir)Tracking\bm.exe" update-all -DefinitionFile:"@(BamDefinitionsQualified->'%(RootDir)%(Directory)%(Filename).xml')"">
      <Output TaskParameter="ExitCode" PropertyName="UpdateAllErrorCode"/>

    <Exec   ContinueOnError="False"
            Command=""$(BtsDir)Tracking\bm.exe" remove-view -Name:"%(BAMViewsAndAccountsGroup.viewName)"">

    <Exec ContinueOnError="False"
          Command=""$(BtsDir)Tracking\bm.exe" update-all -DefinitionFile:"@(BamDefinitionsQualified->'%(RootDir)%(Directory)%(Filename).xml')"">

      Make a copy of the BAM definition XML as [filename]_LastAutoDeploy.xml. Next time we undeploy, we'll look
      for that file first in case the definition file no longer matches what is deployed. If there is a mismatch,
      bm.exe won't auto-undeploy the model.

      Condition="Exists(@(BamDefinitionsQualified->'%(RootDir)%(Directory)%(Filename)_LastAutoDeploy.xml'))" />
      DestinationFiles="@(BamDefinitionsQualified->'%(RootDir)%(Directory)%(Filename)_LastAutoDeploy.xml')" />
    <Message Text="Finished deploying BAM definition." />

    <Message Text="Deploying BAM security..." />

    <!-- Next, apply permissions to the BAM views -->
     Command=""$(BtsDir)Tracking\bm.exe" add-account -View:"%(BAMViewsAndAccountsGroup.viewName)" -AccountName:"%(BAMViewsAndAccountsGroup.groupNames)""
     Condition="'$(BAMViewsAndAccounts)' != ''"/>
    <Message Text="Finished deploying BAM security." />

    <Message Text="Deploying BAM tracking profiles..." Condition="'@(BamTrackingProfilesQualified)' != ''" />
      Command=""$(BtsDir)Tracking\BttDeploy.exe" "@(BamTrackingProfilesQualified)""
      Condition="'%(Identity)' == '%(Identity)' and '@(BamTrackingProfilesQualified)' != ''" />
    <Message Text="Finished deploying BAM tracking profiles." Condition="'@(BamTrackingProfilesQualified)' != ''" />
The custom redist code that copies my dll to my MSI.
  <Target Name="CustomRedist">
    <MakeDir Directories="$(RedistDir)\INT08DeploymentTasks\INT08DeploymentTasks\bin\Release" />

    <!-- Force MSBuild to expand the item spec into physical file specs -->
    <CreateItem Include="..\INT08DeploymentTasks\INT08DeploymentTasks\bin\Release\**\*.*">
      <Output TaskParameter="Include" ItemName="INT08DeploymentTasksSourceGroup" />
    <!-- Copy all of the files and subfolders from ..\TestFiles to $(RedistDir)\TestFiles -->
    <Copy DestinationFolder="$(RedistDir)\INT08DeploymentTasks\INT08DeploymentTasks\bin\Release\%(RecursiveDir)" SourceFiles="@(INT08DeploymentTasksSourceGroup)"/>
Class that implements the CopyAlerts task
	public class CopyAlerts: Task
		private string _BAMDefinition;
		private string _completeBAMDefinition;
		private string _viewName;

		public string BAMDefinition
			get { return _BAMDefinition; }
			set { _BAMDefinition = value; }

		public string CompleteBAMDefinition
			get { return _completeBAMDefinition; }
			set { _completeBAMDefinition = value; }

		public string ViewName
			get { return _viewName; }
			set { _viewName = value; }

		public override bool Execute( )
			string ViewID = "";

			if( !File.Exists( BAMDefinition ) )
				this.Log.LogError( "Unable to find file '{0}'", BAMDefinition );
				return false;

			if( !File.Exists( CompleteBAMDefinition ) )
				this.Log.LogError( "Unable to find file '{0}'.", CompleteBAMDefinition );
				return false;

			XDocument BAMDefinitionXML = XDocument.Load( BAMDefinition );
			XDocument CompleteBAMDefinitionXML = XDocument.Load( CompleteBAMDefinition );

			//searching the complete BAM defination (has all views in it) for my specific view that 
			//i'm intested in.
			var views = from view in CompleteBAMDefinitionXML.Descendants( @"{}View" )
						let attributes = view.Attributes()
						where view.Attribute( "Name" ).Value == ViewName
						select view;

			if( views.Count() == 1 )
				//getting the ID of the view from the BAM defination
				ViewID = views.ElementAt( 0 ).Attribute( "ID" ).Value;

				//now go to the complete bam defination and my view alerts that have been set up.
				var viewAlerts = new XElement( @"{}Alerts",
														from alerts in CompleteBAMDefinitionXML
																		.Descendants( @"{}Alerts" )
																		.Descendants( @"{}ViewAlert" )
														where alerts.Element( @"{}ViewRef" ).Value == ViewID
														select alerts );

				//Getting the numbers to output to the user.
				int ViewAlertCount = viewAlerts.Descendants( "{}ViewAlert" ).Count();
				int AlertInstances = viewAlerts.Descendants( "{}AlertInstance" ).Count();

				if( ViewAlertCount > 0 && AlertInstances > 0 )

					BAMDefinitionXML.Root.LastNode.AddBeforeSelf( viewAlerts );

					BAMDefinitionXML.Save( BAMDefinition );

					this.Log.LogMessage( "\tAlerts Copied for View {0} to {1}...", ViewName, BAMDefinition );
					this.Log.LogMessage( "\tStats for CopyAlerts for View {0}..." + System.Environment.NewLine +
										"\tView Alerts Count: {1}..." + System.Environment.NewLine +
										"\tAlert Instances Count: {2}...", ViewName, ViewAlertCount, AlertInstances );
					this.Log.LogMessage( "There were no alerts to copy for view {0} ", ViewName );
				this.Log.LogMessage( "There were no alerts to copy for view {0} ", ViewName );

			return true;


Dec 21, 2012 at 4:29 AM

Hi Cyrus,

Thanks for closing the loop and posting your solution!  I'll link this over to the Issue Tracker for consideration to include in a future release.


Dec 21, 2012 at 4:31 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.