PROGRAMMATICALLY Change service account’s password for Windows Services, iis app pools and scheduled tasks – C#

Nowadays it’s becoming a very common practice to frequently (half-yearly or quarterly) change password for all AD Service Accounts. It’s done as part of password compliance security guidelines, specially post GDPR implementations.

Earlier for BizTalk environments we use to have “Never Expire Password” for service account and used it to configure BizTalk Services (like Host Instances, ENTSSO & RuleEngineUpdateService), IIS App Pools or SQL Server Services. We also used BizTalk Service Account to configure multiple custom utilities for maintenance purpose as Windows Scheduled Tasks or as Windows Service.

Changing password manually for all these services, scheduled tasks and App Pools is very cumbersome and risky task. If we fail miss or mistype password even at one place, it may cause account lock issue and bring complete environment at halt.

Note – This problem can be addressed in BizTalk 2020 by using Group Managed Service Accounts as they don’t require Passwords.

Refer – What’s New in BizTalk Server 2020

Group Managed Service AccountsExtend windows GMSA support to BizTalk operations and services.

Using Group Managed Service Account

To address this issue in, I have created Windows Application (works for all the version of BizTalk)

It takes three inputs –

  1. Servers (Comma Separated) => List of Servers where change is required. Ex – In a multi-server BizTalk Server, provide name of all the BizTalk App Servers
  2. Service Account => AD Account for which password needs to be changed
  3. New Password

Along with a Checkbox to select features which require change –

  1. Windows Services – Includes BizTalk Host Instances, ENTSSO, RuleEngineUpdateService, IIS and other custom services using this account
  2. IIS App Pools
  3. Scheduled Tasks – Configured with custom utilities

Along with a Checkbox to select features which require change –

  1. Windows Services– Includes BizTalk Host Instances, ENTSSO, RuleEngineUpdateService, IIS and other custom services using this account
  2. IIS App Pools
  3. Scheduled Tasks – Configured with custom utilities

Allowed Operations –

  1. Stop All Services – It acts as a “Pre-requisites/Prepare for change” step to bring down BizTalk Environment. It will STOP all the Host Instances – irrespective of service account, ENTSSO, RuleEngineUpdateService and IIS.
  2. Get List – It retrieves a list of all the services, app pools and scheduled task which are configured with given service account.
  3. Change Password – It changes password for all the services, app pools and scheduled task configured with given service account.
  4. START all Services – It acts as post change activity to bring BizTalk online by starting all the Host Instances, ENTSSO, RuleEngineUpdateService and IIS

You can choose to perform password change activity on all or any – Services, App Pools and Windows Scheduled Tasks.

Code Explanation –

  1. Windows Services – Getting and Updating password for the list of services configured for given service account.

I have used WMI Queries –

SELECT * FROM Win32_Service where StartName like '%<Service Account Name>%'

Sample Code to get the list –

string strWMINamespace = @"\\" + strServer + @"\root\cimv2";
                if (strServiceAccount.Contains('\\'))
                {
                    //If service account contains backslash it needs to be replaced by double backslash
                    strServiceAccount = strServiceAccount.Replace(@"\", @"\\");
                }
                //SELECT * FROM Win32_Service where StartName like '%<Service Account Name>%'
                //Yields BizTalk Host Instances and ENTSSO
                string strWMIQuery = @"SELECT * FROM Win32_Service where StartName like '%" + strServiceAccount + "%'";
                //Create EnumerationOptions and run wql query  
                EnumerationOptions enumOptions = new EnumerationOptions();
                enumOptions.ReturnImmediately = false;
                using (ManagementObjectSearcher searchObject = new ManagementObjectSearcher(strWMINamespace, strWMIQuery, enumOptions))
                {
                    //Enumerate through the result set and stop if not stopped and Change Password  
                    foreach (ManagementObject service in searchObject.Get())
                    {
                     //Iterate over list of services and perform operation
                     //Updating the Password, refer - https://morgantechspace.com/2015/03/csharp-change-service-account-username-and-password.html#wmi
                     //Creating account object
                     object[] accountParams = new object[11];
                     //Updating Username for testing
                     //accountParams[6] = testUserName;
                     accountParams[7] = strNewPassword;
                     uint returnCode = (uint)service.InvokeMethod("Change", accountParams);
                     if (returnCode == 0)
                       {
                         updateLogs("Password changed successfully for service - " + service["Name"] + "");
                       }
                    }
                }

2. IIS App Pools => Getting and updating password for list of App Pools configured using given service account.

 using (ServerManager IISServerManager = ServerManager.OpenRemote(strServer))
 {
   foreach (ApplicationPool appPool in IISServerManager.ApplicationPools)
    {
      if (appPool.ProcessModel.UserName.ToLower().Contains(strServiceAccount))
      {
         updateLogs("Attempting to change password for App Pool - " + appPool.Name);
         appPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser;
         //Updating Username for testing
         //appPool.ProcessModel.UserName = testUserName;
         appPool.ProcessModel.Password = strNewPassword;
         boolRequiresCommit = true;
       }
    } 
    if (boolRequiresCommit)
     {
         IISServerManager.CommitChanges();
         updateLogs("Password changed successfully for App Pools");
     }
 }

3. Windows Scheduled Tasks => Getting and updating password for list of Windows Scheduled Tasks configured using given service account.

Microsoft.Win32.TaskScheduler.TaskService taskService = new TaskService(strServer);
foreach (Microsoft.Win32.TaskScheduler.Task task 
in taskService.RootFolder.GetTasks().Where
                    (t => t.Definition.Principal.UserId.ToString().ToLower().Contains
                          (strServiceAccount.ToLower())))
{
     //Disabling each task if it's enabled
     if (task.State != TaskState.Disabled)
       {
         task.Stop();
         task.Enabled = false;
       }
string strRunAsCMD = @"/C schtasks.exe /CHANGE /S " + strServer + " /TN \"" + task.Name + "\" /RU " + strServiceAccount + " /RP " + strNewPassword + "";
    updateLogs("Attempting to change password for task - " + task.Name + "");
    executeCommand(strRunAsCMD);
    updateLogs("Password changed successfully for task - " + task.Name + "");
}
 
//Code to Execute command in Windows Command Prompt                
private void executeCommand(string strRunAsCMD)
{
    var processInfo = new ProcessStartInfo("cmd.exe", strRunAsCMD);
    processInfo.CreateNoWindow = true;
    processInfo.UseShellExecute = false;
    processInfo.RedirectStandardError = true;
    processInfo.RedirectStandardOutput = true;
    processInfo.Verb = "runas";

    var process = Process.Start(processInfo);

    string strProcessOutput = process.StandardOutput.ReadToEnd();
    string strProcessError = process.StandardError.ReadToEnd();

    string logMsg = "executeCommand - Command Execution Completed with Exit Code : " +               process.ExitCode.ToString() + Environment.NewLine;
   if (String.IsNullOrEmpty(strProcessError))
    {
      logMsg += "Process Output - " + strProcessOutput;
    }
    else
    {
       logMsg += "Process Error - " + strProcessError;
    }
    updateLogs(logMsg);

    process.WaitForExit();
    //Checking if the process execution failed
    if (process.ExitCode != 0)
    {
       updateLogs("executeCommand - Error occurred in execution." + Environment.NewLine + "Exit Code - " + process.ExitCode + Environment.NewLine +
                           "Process Error Output - " + strProcessError);
     }

 process.Close();
}

4. Starting/Stopping Host Instances

private async System.Threading.Tasks.Task stopOrStartAllHostInstances(HostAction hostAction)
        {
            try
            {
                //Create EnumerationOptions and run wql query  
                System.Management.EnumerationOptions enumOptions = new System.Management.EnumerationOptions();
                enumOptions.ReturnImmediately = false;

                //Search for all HostInstances of 'InProcess' type in the Biztalk namespace scope  
                System.Management.ManagementObjectSearcher searchObject =
                    new System.Management.ManagementObjectSearcher("root\\MicrosoftBizTalkServer",
                    "Select * from MSBTS_HostInstance Where HostType=1 And IsDisabled=False", enumOptions);

                //Enumerate through the result set and stop each HostInstance if it is running  
                foreach (ManagementObject inst in searchObject.Get())
                {
                    Task<string> task = new Task<string>(() => stopOrStartHostInstance(inst, hostAction));
                    task.Start();
                    updateLogs("Attempting to perform " + hostAction + " on HostInstance of Host: " + inst["HostName"] + " on Server: " + inst["RunningServer"] + "." + Environment.NewLine);
                    string status = await task;
                    updateLogs("Successfully performed " + hostAction + " action on " + inst["HostName"] + " on Server: " + inst["RunningServer"] + "." + Environment.NewLine + Environment.NewLine);
                }
            }
            catch (Exception excep)
            {
                string strExceptionMessage = "Exception occurred in function-stopOrStartAllHostInstances. Message - " + excep.Message + Environment.NewLine + "StackTrace - " + excep.StackTrace;
                throw new Exception(strExceptionMessage);
            }
        }

private string stopOrStartHostInstance(ManagementObject inst, HostAction hostAction)
        {
            try
            {
                if (hostAction == HostAction.STOP)
                {
                    //Check if ServiceState is 'Started'  
                    if (inst["ServiceState"].ToString() == "4")
                    { inst.InvokeMethod("Stop", null); }
                }
                if (hostAction == HostAction.START)
                {
                    if (inst["ServiceState"].ToString() == "1")//Status Stopped
                    {
                        inst.InvokeMethod("Start", null);
                    }
                }
                return "Success";
            }
            catch (Exception excep)
            {
                string strExceptionMessage = "Exception occurred in Function - stopOrStartHostInstance. Message - " + excep.Message + Environment.NewLine + "StackTrace - " + excep.StackTrace;
                throw new Exception(strExceptionMessage);
            }
        }

5. Starting/Stopping Services

private async System.Threading.Tasks.Task stopORStartAdditionalServices(string strServerNames, ServiceAction serviceAction)
        {
            try
            {
                foreach (string strServerName in strServerNames.Split(','))
                {
                    ServiceController[] scServices = ServiceController.GetServices(strServerName);
                    foreach (ServiceController service in scServices)
                    {
                        //Stopping/Starting IIS, RULEEngineUpdateService and ENTSSO services
                        if (service.ServiceName.ToUpper().Contains("W3SVC") || service.ServiceName.ToUpper().Contains("RULEENGINEUPDATESERVICE") || service.ServiceName.ToUpper().Contains("ENTSSO"))
                        {
                            Task<string> task = new Task<string>(() => stopOrStartService(service, serviceAction));
                            task.Start();
                            updateLogs("Attempting to " + serviceAction + " - " + service.DisplayName + " On Server - " + strServerName + Environment.NewLine);
                            string result = await task;
                            updateLogs("Successfully performed Action - " + serviceAction + " on Service - " + service.DisplayName + " On Server - " + strServerName + Environment.NewLine);
                        }
                    }
                }
            }
            catch (Exception excep)
            {
                string strException = "Exception Occurred in function - stopORStartAdditionalServices. Message - " + excep.Message + Environment.NewLine + "Stack Trace - " + excep.StackTrace + Environment.NewLine;
                throw new Exception(strException);
            }
        }

private string stopOrStartService(ServiceController service, ServiceAction serviceAction)
        {
            try
            {
                if ((serviceAction == ServiceAction.STOP) && (service.Status != ServiceControllerStatus.Stopped))
                {
                    //stopping the service 
                    service.Stop();
                }
                else if ((serviceAction == ServiceAction.START) && (service.Status == ServiceControllerStatus.Stopped))
                {
                    //Starting the services
                    service.Start();
                }
                return "Success";
            }
            catch (Exception excep)
            {
                string strException = "Exception Occurred in function - stopOrStartService. Message - " + excep.Message + Environment.NewLine + "Stack Trace - " + excep.StackTrace + Environment.NewLine;
                throw new Exception(strException);
            }
        }

Hope it’s helpful.

Download the complete code from here

Contact Me:- 

@Gmail@Facebook , @Twitter, @LinkedIn @MSDNTechnet, @My Personal Blog 

2 thoughts on “PROGRAMMATICALLY Change service account’s password for Windows Services, iis app pools and scheduled tasks – C#”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s