public void ConfigureService(AgentSettings settings, CommandSettings command) { Trace.Entering(); // 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. NTAccount defaultServiceAccount = _windowsServiceHelper.GetDefaultServiceAccount(); string logonAccount = command.GetWindowsLogonAccount(defaultValue: defaultServiceAccount.ToString()); 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); } Trace.Info("LogonAccount after transforming: {0}, user: {1}, domain: {2}", logonAccount, userName, domainName); string logonPassword = string.Empty; if (!defaultServiceAccount.Equals(new NTAccount(logonAccount)) && !NativeWindowsServiceHelper.IsWellKnownIdentity(logonAccount)) { while (true) { logonPassword = command.GetWindowsLogonPassword(logonAccount); 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)); } } Trace.Info("Create local group and grant folder permission to service logon account."); GrantDirectoryPermissionForAccount(logonAccount); // install service. _windowsServiceHelper.InstallService(serviceName, serviceDisplayName, logonAccount, logonPassword); // 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"); _windowsServiceHelper.StartService(serviceName); }
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)) && !NativeWindowsServiceHelper.IsWellKnownIdentity(logonAccount)) { while (true) { logonPassword = command.GetWindowsLogonPassword(logonAccount); 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); // 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); } }
public void ConfigureService(RunnerSettings settings, CommandSettings command) { Trace.Entering(); if (!_windowsServiceHelper.IsRunningInElevatedMode()) { Trace.Error("Needs Administrator privileges for configure runner as windows service."); throw new SecurityException("Needs Administrator privileges for configuring runner as windows service."); } // We use NetworkService as default account for actions runner NTAccount defaultServiceAccount = _windowsServiceHelper.GetDefaultServiceAccount(); string logonAccount = command.GetWindowsLogonAccount(defaultValue: defaultServiceAccount.ToString(), descriptionMsg: "User account to use for the service"); 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)) && !NativeWindowsServiceHelper.IsWellKnownIdentity(logonAccount)) { while (true) { logonPassword = command.GetWindowsLogonPassword(logonAccount); if (_windowsServiceHelper.IsValidCredential(domainName, userName, logonPassword)) { Trace.Info("Credential validation succeed"); break; } else { if (!command.Unattended) { Trace.Info("Invalid credential entered"); _term.WriteLine("Invalid windows credentials entered. Try again or ctrl-c to quit"); } else { throw new SecurityException("Invalid windows credentials entered. Try again or ctrl-c to quit"); } } } } string serviceName; string serviceDisplayName; CalculateServiceName(settings, ServiceNamePattern, ServiceDisplayNamePattern, out serviceName, out serviceDisplayName); if (_windowsServiceHelper.IsServiceExists(serviceName)) { _term.WriteLine($"The service already exists: {serviceName}, it will be replaced"); _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.GrantUserLogonAsServicePrivilege(domainName, userName)) { throw new InvalidOperationException($"Cannot grant LogonAsService permission to the user {logonAccount}"); } } // grant permission for runner root folder and work folder Trace.Info("Create local group and grant folder permission to service logon account."); string runnerRoot = HostContext.GetDirectory(WellKnownDirectory.Root); string workFolder = HostContext.GetDirectory(WellKnownDirectory.Work); Directory.CreateDirectory(workFolder); _windowsServiceHelper.GrantDirectoryPermissionForAccount(logonAccount, new[] { runnerRoot, workFolder }); _term.WriteLine($"Granting file permissions to '{logonAccount}'."); // install service. _windowsServiceHelper.InstallService(serviceName, serviceDisplayName, logonAccount, logonPassword); // create .service file with service name. SaveServiceSettings(serviceName); Trace.Info("Configuration was successful, trying to start the service"); _windowsServiceHelper.StartService(serviceName); }