Automating Managed Computer Account Removal in System Center Essentials 2007

One of the partners asked recently about the possibility of automating the removal of managed computer account from System Center Essentials 2007 (SCE). In their scenario the administrator is removing manually the computer account from the Active Directory (also that he verifies that this computer account has been removed from SCE Manged Computers security group ) and would like that the corresponding managed computer account in System Center Essentials be removed automatically.  The idea is to have a scheduled task that executes periodically, queries System Center database for managed computer accounts and checks if this account has been removed from Active Directory. If so it would be removed form SCE too.

It turns out that powershell scripting option is not available with System Center Essentials 2007 ( it is with Operations Manager see https://technet.microsoft.com/en-us/magazine/cc671178.aspx ). However, we can use the SDK API (https://msdn.microsoft.com/en-us/library/bb437506.aspx) and in particular Microsoft.EnterpriseManagement.Administration namespace to automate administrative tasks in System Center. We can obtain a list of SCE managed computer accounts by calling a function ManagementGroupAdministration.GetAgentManagedComputers() which accepts as an argument a filter criteria expression ( see https://msdn.microsoft.com/en-us/library/bb437603.aspx for syntax). This function will return a read only collection of AgentManagedComputer objects representing managed computer accounts. Now that we know how to build a list of SCE accounts it would be tempting to build the list of Active Directory computer accounts storing them in the List<AgentMangagedComputer> structure because we could then use the Except() function (see https://msdn.microsoft.com/en-us/library/bb300779.aspx) of the List to get a list of computer accounts that should be removed. However, it turns out that AgentManagedComputer object does not offer a public constructor. This forces us to perform checks as the SCE managed computer accounts are being enumerated, constructing a list of candidates for deletion.

AgentMangagedComputer.PrincipalName will yield FQDN name of the machine – but what should be the equivalent Active Directory attribute we could query? Well we use csvde command line utility not only to test the syntax of our Active Direcotry query but also to get a list of all the attributes for the queried object. So running following command:

csvde.exe –f out.txt –s localhost –p subtree –r “(objectCategory=Computer)”  we can see that the dnsHostName attribute contains the FQDN name of the machine in Active Directory.

To compile the sample below you will need to add reference to System.ServiceProcess (standard .Net 2.0 assembly) and

Microsoft.EnterpriseManagement.OperationsManager.dll

Microsoft.EnterpriseManagement.OperationsManager.Common.dll

that can be found in the %ProgramFiles%\System Center Operations Manager 2007\SDK Binaries directory of the System Center machine.

    1: using System;
    2: using System.Collections.Generic;
    3: using System.Linq;
    4: using System.Text;
    5: using System.DirectoryServices;
    6: using Microsoft.EnterpriseManagement;
    7: using Microsoft.EnterpriseManagement.Administration;
    8: using Microsoft.EnterpriseManagement.Common;
    9: using System.ServiceProcess;
   10:  
   11: namespace MomSynchTask
   12: {
   13:   
   14:     class ComputerAccount{
   15:         private string _name= "";
   16:         public string Name { 
   17:                 get {return _name;}
   18:                 set{ _name=value;}
   19:         }
   20:         public ComputerAccount(string name){
   21:             Name = name;
   22:         }
   23:         public override string ToString()
   24:         {
   25:             return Name;
   26:         }
   27:     }
   28:  
   29:     class Task
   30:     {
   31:         
   32:         List<ComputerAccount> AdComputerAccounts = new List<ComputerAccount>();
   33:         List<AgentManagedComputer> MomAccountsToDelete = new List<AgentManagedComputer>();
   34:  
   35:         // uses SDK to connect to Sce using current credentials
   36:         protected ManagementGroup ConnectToSce(string hostname)
   37:         {
   38:             Console.WriteLine("Attempting to connect to the SDK Service by using the current user's credentials.");
   39:             ManagementGroup mg = null;
   40:             try
   41:             {
   42:                  mg = ManagementGroup.Connect(hostname);
   43:                 if (mg.IsConnected)
   44:                     Console.WriteLine("Connection succeeded.");
   45:                 else
   46:                     throw new InvalidOperationException("Not connected to an SDK Service.");
   47:             }
   48:             catch (ServerDisconnectedException sde)
   49:             {
   50:                 Console.WriteLine("\nConnection failed. " + sde.Message);
   51:                 if (sde.InnerException != null)
   52:                     Console.WriteLine(sde.InnerException.Message);
   53:  
   54:                 // Call custom method to prompt the user for credentials if needed.
   55:  
   56:             }
   57:             catch (ServiceNotRunningException snr)
   58:             {
   59:                 Console.WriteLine(snr.Message);
   60:  
   61:                 // Make one attempt to start the service.
   62:                 System.ServiceProcess.ServiceController sc = new
   63:                     ServiceController("MOMSDK", hostname);
   64:  
   65:                 Console.WriteLine("Attempting to start the SDK Service on " + hostname + ".");
   66:                 sc.Start();
   67:  
   68:                 // Wait 20 seconds for it to enter the Running state.
   69:                 sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 20));
   70:  
   71:                 if (sc.Status == ServiceControllerStatus.Running)
   72:                 {
   73:                     mg = new ManagementGroup(hostname);
   74:                     Console.WriteLine("Connection succeeded.");
   75:                 }
   76:                 else
   77:                 {
   78:                     throw new InvalidOperationException("Unable to restart and connect to the SDK Service.");
   79:                 }
   80:             }
   81:             return mg;
   82:         }
   83:  
   84:         public void GetAdComputerAccounts()
   85:             {
   86:                 try
   87:                 {
   88:                     DirectoryEntry root = new DirectoryEntry();
   89:                     DirectorySearcher searcher = new DirectorySearcher();
   90:                     searcher.Filter = "(objectCategory=Computer)";
   91:                     searcher.PropertiesToLoad.Add("Name");
   92:                     searcher.PropertiesToLoad.Add("dnsHostName");
   93:                     searcher.PropertiesToLoad.Add("sAMAccountName");
   94:                     searcher.PageSize = 1000;
   95:                     searcher.SearchScope = SearchScope.Subtree;
   96:                     Console.WriteLine("Enumerating computer accounts in Active Directory...");
   97:                     foreach (SearchResult result in searcher.FindAll())
   98:                     {
   99:                         DirectoryEntry entry = result.GetDirectoryEntry();
  100:  
  101:                         ComputerAccount computer = new ComputerAccount((string)entry.Properties["dnsHostName"].Value);
  102:                         AdComputerAccounts.Add(computer);
  103:  
  104:                         Console.WriteLine("Name=" + (string)entry.Properties["Name"].Value + 
  105:                                 " dnsHostName=" + (string)entry.Properties["dnsHostName"].Value +
  106:                                 " sAMAccountName=" + (string)entry.Properties["sAMAccountName"].Value);
  107:                     }
  108:                 }
  109:                 catch (Exception ex)
  110:                 {
  111:                     Console.WriteLine(ex.Message);
  112:                 }
  113:         }
  114:         public ManagementGroupAdministration GetMomComputerAccouts(string hostname)
  115:         {
  116:             ManagementGroupAdministration mga=null;
  117:             ManagementGroup mg = ConnectToSce(hostname);
  118:             if (mg !=null){
  119:                mga = mg.GetAdministration();
  120:               //filter managed computer accounts see https://msdn.microsoft.com/en-us/library/bb437603.aspx
  121:               AgentManagedComputerCriteria criteria = new AgentManagedComputerCriteria("Name LIKE '%'");
  122:               foreach (AgentManagedComputer momAccount in mga.GetAgentManagedComputers(criteria))
  123:               {
  124:                 //BIOS name of computer
  125:                 Console.WriteLine("BIOS Name=" + momAccount.ComputerName + " FQDN Name=" + momAccount.PrincipalName + " Name=" + momAccount.Name);                
  126:                 //check if this computer Exists in Active Directory- we compate  PrincipalName with dnsHostName 
  127:                 if (AdComputerAccounts.Exists(p => p.Name.Equals(momAccount.PrincipalName, System.StringComparison.InvariantCultureIgnoreCase)) == false)
  128:                 {
  129:                     Console.WriteLine("Added Mom Computer Account:" + momAccount.PrincipalName + " to deletion list");
  130:                     MomAccountsToDelete.Add(momAccount);
  131:                 }
  132:                 else
  133:                 {
  134:                     Console.WriteLine("Computer Account:" + momAccount.PrincipalName + " appears to exist in Active Directory.");
  135:  
  136:                 }
  137:  
  138:               }
  139:             }
  140:             return mga;
  141:         }
  142:         public void RemoveMomComputerAccounts(string[] args, ManagementGroupAdministration mga)
  143:         {
  144:  
  145:          mga.DeleteAgentManagedComputers(MomAccountsToDelete);
  146:          Console.WriteLine("Removed Mom Computer accounts");
  147:          return;
  148:         }
  149:     }
  150:  
  151:     class Program
  152:     {
  153:         static void Main(string[] args)
  154:         {
  155:             Task t = new Task();
  156:             //Get list of computer accounts from Active Directory
  157:             t.GetAdComputerAccounts();
  158:             //Get list of Ops Manager managed computer accounts to be deleted
  159:             ManagementGroupAdministration mga = t.GetMomComputerAccouts("localhost");
  160:             //Remove managed computer accounts that have no corresponding Active Directory account
  161:             // if /d switch has been specified on command line
  162:             if (args.Length > 0)
  163:             {
  164:                 if ((mga != null) && (args[0].Equals("/d")))
  165:                     t.RemoveMomComputerAccounts(args, mga);
  166:             }
  167:         }
  168:         
  169:     }
  170: }