public void ConfigureService(AgentSettings settings, CommandSettings command) { ArgUtil.NotNull(command, nameof(command)); Trace.Entering(); if (!_windowsServiceHelper.IsRunningInElevatedMode()) { Trace.Error("Needs Administrator privileges for configure agent as windows service."); throw new SecurityException(StringUtil.Loc("NeedAdminForConfigAgentWinService")); } // TODO: Fix bug that exists in the legacy Windows agent where configuration using mirrored credentials causes an error, but the agent is still functional (after restarting). Mirrored credentials is a supported scenario and shouldn't manifest any errors. // We use NetworkService as default account for build and release agent // We use Local System as default account for deployment agent, deployment pool agent, environment vm agent bool isDeploymentGroupScenario = command.GetDeploymentOrMachineGroup() || command.GetDeploymentPool() || command.GetEnvironmentVMResource(); NTAccount defaultServiceAccount = isDeploymentGroupScenario ? _windowsServiceHelper.GetDefaultAdminServiceAccount() : _windowsServiceHelper.GetDefaultServiceAccount(); string logonAccount = command.GetWindowsLogonAccount(defaultValue: defaultServiceAccount.ToString(), descriptionMsg: StringUtil.Loc("WindowsLogonAccountNameDescription")); string domainName; string userName; GetAccountSegments(logonAccount, out domainName, out userName); if ((string.IsNullOrEmpty(domainName) || domainName.Equals(".", StringComparison.CurrentCultureIgnoreCase)) && !logonAccount.Contains('@')) { logonAccount = String.Format("{0}\\{1}", Environment.MachineName, userName); domainName = Environment.MachineName; } Trace.Info("LogonAccount after transforming: {0}, user: {1}, domain: {2}", logonAccount, userName, domainName); string logonPassword = string.Empty; if (!defaultServiceAccount.Equals(new NTAccount(logonAccount)) && !_windowsServiceHelper.IsWellKnownIdentity(logonAccount) && !_windowsServiceHelper.IsManagedServiceAccount(logonAccount)) { while (true) { try { logonPassword = command.GetWindowsLogonPassword(logonAccount); } catch (ArgumentException e) { Trace.Warning("LogonAccount {0} is not managed service account, although you did not specify WindowsLogonPassword - maybe you wanted to use managed service account? Please see https://aka.ms/gmsa for guidelines to set up sMSA/gMSA account.", logonAccount, userName, domainName); throw; } if (_windowsServiceHelper.IsValidCredential(domainName, userName, logonPassword)) { Trace.Info("Credential validation succeed"); break; } else { if (!command.Unattended()) { Trace.Info("Invalid credential entered"); _term.WriteLine(StringUtil.Loc("InvalidWindowsCredential")); } else { throw new SecurityException(StringUtil.Loc("InvalidWindowsCredential")); } } } } string serviceName; string serviceDisplayName; CalculateServiceName(settings, ServiceNamePattern, ServiceDisplayNamePattern, out serviceName, out serviceDisplayName); if (_windowsServiceHelper.IsServiceExists(serviceName)) { _term.WriteLine(StringUtil.Loc("ServiceAlreadyExists", serviceName)); _windowsServiceHelper.UninstallService(serviceName); } Trace.Info("Verifying if the account has LogonAsService permission"); if (_windowsServiceHelper.IsUserHasLogonAsServicePrivilege(domainName, userName)) { Trace.Info($"Account: {logonAccount} already has Logon As Service Privilege."); } else { if (!_windowsServiceHelper.GrantUserLogonAsServicePrivilage(domainName, userName)) { throw new InvalidOperationException(StringUtil.Loc("CanNotGrantPermission", logonAccount)); } } // grant permission for agent root folder and work folder Trace.Info("Create local group and grant folder permission to service logon account."); string agentRoot = HostContext.GetDirectory(WellKnownDirectory.Root); string workFolder = HostContext.GetDirectory(WellKnownDirectory.Work); Directory.CreateDirectory(workFolder); _windowsServiceHelper.GrantDirectoryPermissionForAccount(logonAccount, new[] { agentRoot, workFolder }); _term.WriteLine(StringUtil.Loc("GrantingFilePermissions", logonAccount)); // install service. _windowsServiceHelper.InstallService(serviceName, serviceDisplayName, logonAccount, logonPassword, settings.EnableServiceSidTypeUnrestricted); // create .service file with service name. SaveServiceSettings(serviceName); // Add registry key after installation _windowsServiceHelper.CreateVstsAgentRegistryKey(); Trace.Info("Configuration was successful, trying to start the service"); if (!command.GetPreventServiceStart()) { _windowsServiceHelper.StartService(serviceName); } }