diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs
index 29c407c50d0..24d574b0edd 100644
--- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs
+++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs
@@ -571,7 +571,7 @@ protected override void ProcessRecord()
{
if (!DependentServices.IsPresent && !RequiredServices.IsPresent)
{
- WriteObject(service);
+ WriteObject(AddProperties(service));
}
else
{
@@ -594,6 +594,107 @@ protected override void ProcessRecord()
}
#endregion Overrides
+
+ ///
+ /// Adds UserName, Description, BinaryPathName, DelayedAutoStart and StartupType to a ServiceController object.
+ ///
+ ///
+ /// ServiceController as PSObject with UserName, Description and StartupType added
+ private PSObject AddProperties(ServiceController service) {
+ NakedWin32Handle hScManager = IntPtr.Zero;
+ NakedWin32Handle hService = IntPtr.Zero;
+ int lastError = 0;
+ PSObject serviceAsPSObj = PSObject.AsPSObject(service);
+ try
+ {
+ hScManager = NativeMethods.OpenSCManagerW(
+ lpMachineName: service.MachineName,
+ lpDatabaseName: null,
+ dwDesiredAccess: NativeMethods.SC_MANAGER_CONNECT
+ );
+ if (IntPtr.Zero == hScManager) {
+ lastError = Marshal.GetLastWin32Error();
+ Win32Exception exception = new Win32Exception(lastError);
+ WriteNonTerminatingError(
+ service,
+ exception,
+ "FailToOpenServiceControlManager",
+ ServiceResources.FailToOpenServiceControlManager,
+ ErrorCategory.PermissionDenied);
+ }
+ hService = NativeMethods.OpenServiceW(
+ hScManager,
+ service.ServiceName,
+ NativeMethods.SERVICE_QUERY_CONFIG
+ );
+ if (IntPtr.Zero == hService) {
+ lastError = Marshal.GetLastWin32Error();
+ Win32Exception exception = new Win32Exception(lastError);
+ WriteNonTerminatingError(
+ service,
+ exception,
+ "CouldNotGetServiceInfo",
+ ServiceResources.CouldNotGetServiceInfo,
+ ErrorCategory.PermissionDenied);
+ }
+
+ NativeMethods.SERVICE_DESCRIPTIONW description = new NativeMethods.SERVICE_DESCRIPTIONW();
+ bool querySuccessful = NativeMethods.QueryServiceConfig2(hService, NativeMethods.SERVICE_CONFIG_DESCRIPTION, out description);
+
+ NativeMethods.SERVICE_DELAYED_AUTO_START_INFO autostartInfo = new NativeMethods.SERVICE_DELAYED_AUTO_START_INFO();
+ querySuccessful = querySuccessful && NativeMethods.QueryServiceConfig2(hService, NativeMethods.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, out autostartInfo);
+
+ NativeMethods.QUERY_SERVICE_CONFIG serviceInfo = new NativeMethods.QUERY_SERVICE_CONFIG();
+ querySuccessful = querySuccessful && NativeMethods.QueryServiceConfig(hService, out serviceInfo);
+
+ if(!querySuccessful) {
+ WriteNonTerminatingError(
+ service: service,
+ innerException: null,
+ errorId: "CouldNotGetServiceInfo",
+ errorMessage: ServiceResources.CouldNotGetServiceInfo,
+ category: ErrorCategory.PermissionDenied
+ );
+ }
+
+ PSProperty noteProperty = new PSProperty("UserName", serviceInfo.lpServiceStartName);
+ serviceAsPSObj.Properties.Add(noteProperty, true);
+ serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#UserName");
+
+ noteProperty = new PSProperty("Description", description.lpDescription);
+ serviceAsPSObj.Properties.Add(noteProperty, true);
+ serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#Description");
+
+ noteProperty = new PSProperty("DelayedAutoStart", autostartInfo.fDelayedAutostart);
+ serviceAsPSObj.Properties.Add(noteProperty, true);
+ serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#DelayedAutoStart");
+
+ noteProperty = new PSProperty("BinaryPathName", serviceInfo.lpBinaryPathName);
+ serviceAsPSObj.Properties.Add(noteProperty, true);
+ serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#BinaryPathName");
+
+ noteProperty = new PSProperty("StartupType", NativeMethods.GetServiceStartupType(service.StartType, autostartInfo.fDelayedAutostart));
+ serviceAsPSObj.Properties.Add(noteProperty, true);
+ serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#StartupType");
+ }
+ finally
+ {
+ if (IntPtr.Zero != hService) {
+ bool succeeded = NativeMethods.CloseServiceHandle(hService);
+ if (!succeeded) {
+ Diagnostics.Assert(lastError != 0, "ErrorCode not success");
+ }
+ }
+
+ if (IntPtr.Zero != hScManager) {
+ bool succeeded = NativeMethods.CloseServiceHandle(hScManager);
+ if (!succeeded) {
+ Diagnostics.Assert(lastError != 0, "ErrorCode not success");
+ }
+ }
+ } // finally
+ return serviceAsPSObj;
+ }
}
#endregion GetServiceCommand
@@ -1403,7 +1504,7 @@ public string Description
[Parameter]
[Alias("StartMode", "SM", "ST")]
[ValidateNotNullOrEmpty]
- public ServiceStartMode StartupType
+ public ServiceStartupType StartupType
{
get { return startupType; }
set
@@ -1413,7 +1514,7 @@ public ServiceStartMode StartupType
}
// We set the initial value to an invalid value so that we can
// distinguish when this is and is not set.
- internal ServiceStartMode startupType = (ServiceStartMode)(-1);
+ internal ServiceStartupType startupType = ServiceStartupType.InvalidValue;
///
@@ -1530,6 +1631,7 @@ protected override void ProcessRecord()
NakedWin32Handle hScManager = IntPtr.Zero;
NakedWin32Handle hService = IntPtr.Zero;
+ IntPtr delayedAutoStartInfoBuffer = IntPtr.Zero;
try
{
hScManager = NativeMethods.OpenSCManagerW(
@@ -1567,10 +1669,9 @@ protected override void ProcessRecord()
ErrorCategory.PermissionDenied);
return;
}
-
// Modify startup type or display name or credential
if (!String.IsNullOrEmpty(DisplayName)
- || (ServiceStartMode)(-1) != StartupType || null != Credential)
+ || ServiceStartupType.InvalidValue != StartupType || null != Credential)
{
DWORD dwStartType = NativeMethods.SERVICE_NO_CHANGE;
if (!NativeMethods.TryGetNativeStartupType(StartupType, out dwStartType))
@@ -1638,6 +1739,32 @@ protected override void ProcessRecord()
ErrorCategory.PermissionDenied);
}
+ // Set the delayed auto start
+ NativeMethods.SERVICE_DELAYED_AUTO_START_INFO ds = new NativeMethods.SERVICE_DELAYED_AUTO_START_INFO();
+ ds.fDelayedAutostart = StartupType == ServiceStartupType.AutomaticDelayedStart;
+ size = Marshal.SizeOf(ds);
+ delayedAutoStartInfoBuffer = Marshal.AllocCoTaskMem(size);
+ Marshal.StructureToPtr(ds, delayedAutoStartInfoBuffer, false);
+
+ status = NativeMethods.ChangeServiceConfig2W(
+ hService,
+ NativeMethods.SERVICE_CONFIG_DELAYED_AUTO_START_INFO,
+ delayedAutoStartInfoBuffer);
+
+ if (!status)
+ {
+ int lastError = Marshal.GetLastWin32Error();
+ Win32Exception exception = new Win32Exception(lastError);
+ WriteNonTerminatingError(
+ Name,
+ DisplayName,
+ Name,
+ exception,
+ "CouldNotSetServiceDelayedAutoStart",
+ ServiceResources.CouldNotSetServiceDelayedAutoStart,
+ ErrorCategory.PermissionDenied);
+ }
+
// Handle the '-Status' parameter
if (!string.IsNullOrEmpty(Status))
{
@@ -1694,6 +1821,10 @@ protected override void ProcessRecord()
}
finally
{
+ if (IntPtr.Zero != delayedAutoStartInfoBuffer)
+ {
+ Marshal.FreeCoTaskMem(delayedAutoStartInfoBuffer);
+ }
if (IntPtr.Zero != hService)
{
bool succeeded = NativeMethods.CloseServiceHandle(hService);
@@ -1809,12 +1940,12 @@ public string Description
///
///
[Parameter]
- public ServiceStartMode StartupType
+ public ServiceStartupType StartupType
{
get { return startupType; }
set { startupType = value; }
}
- internal ServiceStartMode startupType = ServiceStartMode.Automatic;
+ internal ServiceStartupType startupType = ServiceStartupType.Automatic;
///
/// Account under which the service should run
@@ -1865,6 +1996,7 @@ protected override void BeginProcessing()
NakedWin32Handle hScManager = IntPtr.Zero;
NakedWin32Handle hService = IntPtr.Zero;
IntPtr password = IntPtr.Zero;
+ IntPtr delayedAutoStartInfoBuffer = IntPtr.Zero;
try
{
hScManager = NativeMethods.OpenSCManagerW(
@@ -1987,6 +2119,34 @@ protected override void BeginProcessing()
ErrorCategory.PermissionDenied);
}
+ // Set the delayed auto start
+ if(StartupType == ServiceStartupType.AutomaticDelayedStart) {
+ NativeMethods.SERVICE_DELAYED_AUTO_START_INFO ds = new NativeMethods.SERVICE_DELAYED_AUTO_START_INFO();
+ ds.fDelayedAutostart = true;
+ size = Marshal.SizeOf(ds);
+ delayedAutoStartInfoBuffer = Marshal.AllocCoTaskMem(size);
+ Marshal.StructureToPtr(ds, delayedAutoStartInfoBuffer, false);
+
+ succeeded = NativeMethods.ChangeServiceConfig2W(
+ hService,
+ NativeMethods.SERVICE_CONFIG_DELAYED_AUTO_START_INFO,
+ delayedAutoStartInfoBuffer);
+
+ if (!succeeded)
+ {
+ int lastError = Marshal.GetLastWin32Error();
+ Win32Exception exception = new Win32Exception(lastError);
+ WriteNonTerminatingError(
+ Name,
+ DisplayName,
+ Name,
+ exception,
+ "CouldNotNewServiceDelayedAutoStart",
+ ServiceResources.CouldNotNewServiceDelayedAutoStart,
+ ErrorCategory.PermissionDenied);
+ }
+ }
+
// write the ServiceController for the new service
using (ServiceController service =
new ServiceController(Name)) // ensure dispose
@@ -1996,6 +2156,11 @@ protected override void BeginProcessing()
}
finally
{
+ if (IntPtr.Zero != delayedAutoStartInfoBuffer)
+ {
+ Marshal.FreeCoTaskMem(delayedAutoStartInfoBuffer);
+ }
+
if (IntPtr.Zero != password)
{
Marshal.ZeroFreeCoTaskMemUnicode(password);
@@ -2305,6 +2470,7 @@ internal static class NativeMethods
// from winuser.h
internal const int ERROR_SERVICE_ALREADY_RUNNING = 1056;
internal const int ERROR_SERVICE_NOT_ACTIVE = 1062;
+ internal const int ERROR_INSUFFICIENT_BUFFER = 122;
internal const DWORD SC_MANAGER_CONNECT = 1;
internal const DWORD SC_MANAGER_CREATE_SERVICE = 2;
internal const DWORD SC_MANAGER_ALL_ACCESS = 0xf003f;
@@ -2316,6 +2482,9 @@ internal static class NativeMethods
internal const DWORD SERVICE_DEMAND_START = 0x3;
internal const DWORD SERVICE_DISABLED = 0x4;
internal const DWORD SERVICE_CONFIG_DESCRIPTION = 1;
+ internal const DWORD SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3;
+ internal const DWORD SERVICE_CONFIG_SERVICE_SID_INFO = 5;
+
internal const DWORD SERVICE_WIN32_OWN_PROCESS = 0x10;
internal const DWORD SERVICE_ERROR_NORMAL = 1;
@@ -2336,6 +2505,25 @@ NakedWin32Handle OpenServiceW(
DWORD dwDesiredAccess
);
+ [DllImport(PinvokeDllNames.QueryServiceConfigDllName, CharSet = CharSet.Unicode, SetLastError = true)]
+ internal static extern
+ bool QueryServiceConfigW(
+ NakedWin32Handle hSCManager,
+ IntPtr lpServiceConfig,
+ DWORD cbBufSize,
+ out DWORD pcbBytesNeeded
+ );
+
+ [DllImport(PinvokeDllNames.QueryServiceConfig2DllName, CharSet = CharSet.Unicode, SetLastError = true)]
+ internal static extern
+ bool QueryServiceConfig2W(
+ NakedWin32Handle hService,
+ DWORD dwInfoLevel,
+ IntPtr lpBuffer,
+ DWORD cbBufSize,
+ out DWORD pcbBytesNeeded
+ );
+
[DllImport(PinvokeDllNames.CloseServiceHandleDllName, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern
bool CloseServiceHandle(
@@ -2379,6 +2567,26 @@ internal struct SERVICE_DESCRIPTIONW
internal string lpDescription;
};
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct QUERY_SERVICE_CONFIG
+ {
+ internal uint dwServiceType;
+ internal uint dwStartType;
+ internal uint dwErrorControl;
+ [MarshalAs(UnmanagedType.LPWStr)] internal string lpBinaryPathName;
+ [MarshalAs(UnmanagedType.LPWStr)] internal string lpLoadOrderGroup;
+ internal uint dwTagId;
+ [MarshalAs(UnmanagedType.LPWStr)] internal string lpDependencies;
+ [MarshalAs(UnmanagedType.LPWStr)] internal string lpServiceStartName;
+ [MarshalAs(UnmanagedType.LPWStr)] internal string lpDisplayName;
+ };
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct SERVICE_DELAYED_AUTO_START_INFO
+ {
+ internal bool fDelayedAutostart;
+ };
+
[DllImport(PinvokeDllNames.CreateServiceWDllName, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern
NakedWin32Handle CreateServiceW(
@@ -2460,6 +2668,77 @@ public static extern bool QueryInformationJobObject(SafeHandle hJob, int JobObje
ref JOBOBJECT_BASIC_PROCESS_ID_LIST lpJobObjectInfo,
int cbJobObjectLength, IntPtr lpReturnLength);
+ internal static bool QueryServiceConfig(NakedWin32Handle hService, out NativeMethods.QUERY_SERVICE_CONFIG configStructure)
+ {
+ IntPtr lpBuffer = IntPtr.Zero;
+ configStructure = default(NativeMethods.QUERY_SERVICE_CONFIG);
+ DWORD bufferSize, bufferSizeNeeded = 0;
+ bool status = NativeMethods.QueryServiceConfigW(
+ hSCManager: hService,
+ lpServiceConfig: lpBuffer,
+ cbBufSize: 0,
+ pcbBytesNeeded: out bufferSizeNeeded);
+
+ if (status != true && Marshal.GetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER) {
+ return status;
+ }
+
+ try
+ {
+ lpBuffer = Marshal.AllocCoTaskMem((int)bufferSizeNeeded);
+ bufferSize = bufferSizeNeeded;
+
+ status = NativeMethods.QueryServiceConfigW(
+ hService,
+ lpBuffer,
+ bufferSize,
+ out bufferSizeNeeded);
+ configStructure = (NativeMethods.QUERY_SERVICE_CONFIG)Marshal.PtrToStructure(lpBuffer, typeof(NativeMethods.QUERY_SERVICE_CONFIG));
+ }
+ finally
+ {
+ Marshal.FreeCoTaskMem(lpBuffer);
+ }
+ return status;
+ }
+
+ internal static bool QueryServiceConfig2(NakedWin32Handle hService, DWORD infolevel, out T configStructure)
+ {
+ IntPtr lpBuffer = IntPtr.Zero;
+ configStructure = default(T);
+ DWORD bufferSize, bufferSizeNeeded = 0;
+
+ bool status = NativeMethods.QueryServiceConfig2W(
+ hService: hService,
+ dwInfoLevel: infolevel,
+ lpBuffer: lpBuffer,
+ cbBufSize: 0,
+ pcbBytesNeeded: out bufferSizeNeeded);
+
+ if (status != true && Marshal.GetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER) {
+ return status;
+ }
+
+ try
+ {
+ lpBuffer = Marshal.AllocCoTaskMem((int)bufferSizeNeeded);
+ bufferSize = bufferSizeNeeded;
+
+ status = NativeMethods.QueryServiceConfig2W(
+ hService,
+ infolevel,
+ lpBuffer,
+ bufferSize,
+ out bufferSizeNeeded);
+ configStructure = (T)Marshal.PtrToStructure(lpBuffer, typeof(T));
+ }
+ finally
+ {
+ Marshal.FreeCoTaskMem(lpBuffer);
+ }
+ return status;
+ }
+
///
/// Get appropriate win32 StartupType
///
@@ -2472,22 +2751,23 @@ public static extern bool QueryInformationJobObject(SafeHandle hJob, int JobObje
///
/// If a supported StartupType is provided, funciton returns true, otherwise false.
///
- internal static bool TryGetNativeStartupType(ServiceStartMode StartupType, out DWORD dwStartType)
+ internal static bool TryGetNativeStartupType(ServiceStartupType StartupType, out DWORD dwStartType)
{
bool success = true;
dwStartType = NativeMethods.SERVICE_NO_CHANGE;
switch (StartupType)
{
- case ServiceStartMode.Automatic:
+ case ServiceStartupType.Automatic:
+ case ServiceStartupType.AutomaticDelayedStart:
dwStartType = NativeMethods.SERVICE_AUTO_START;
break;
- case ServiceStartMode.Manual:
+ case ServiceStartupType.Manual:
dwStartType = NativeMethods.SERVICE_DEMAND_START;
break;
- case ServiceStartMode.Disabled:
+ case ServiceStartupType.Disabled:
dwStartType = NativeMethods.SERVICE_DISABLED;
break;
- case (ServiceStartMode)(-1):
+ case ServiceStartupType.InvalidValue:
dwStartType = NativeMethods.SERVICE_NO_CHANGE;
break;
default:
@@ -2496,8 +2776,44 @@ internal static bool TryGetNativeStartupType(ServiceStartMode StartupType, out D
}
return success;
}
+
+ internal static ServiceStartupType GetServiceStartupType(ServiceStartMode startMode, bool delayedAutoStart)
+ {
+ ServiceStartupType result = ServiceStartupType.Disabled;
+ switch(startMode)
+ {
+ case ServiceStartMode.Automatic:
+ result = delayedAutoStart ? ServiceStartupType.AutomaticDelayedStart : ServiceStartupType.Automatic;
+ break;
+ case ServiceStartMode.Manual:
+ result = ServiceStartupType.Manual;
+ break;
+ case ServiceStartMode.Disabled:
+ result = ServiceStartupType.Disabled;
+ break;
+ }
+ return result;
+ }
}
#endregion NativeMethods
+
+ #region ServiceStartupType
+ ///
+ ///Enum for usage with StartupType. Automatic, Manual and Disabled index matched from System.ServiceProcess.ServiceStartMode
+ ///
+ public enum ServiceStartupType {
+ ///Invalid service
+ InvalidValue = -1,
+ ///Automatic service
+ Automatic = 2,
+ ///Manual service
+ Manual = 3,
+ ///Disabled service
+ Disabled = 4,
+ ///Automatic (Delayed Start) service
+ AutomaticDelayedStart = 10
+ }
+ #endregion ServiceStartupType
}
#endif // Not built on Unix
diff --git a/src/Microsoft.PowerShell.Commands.Management/resources/ServiceResources.resx b/src/Microsoft.PowerShell.Commands.Management/resources/ServiceResources.resx
index 4a1aa45ee7d..6218714a18c 100644
--- a/src/Microsoft.PowerShell.Commands.Management/resources/ServiceResources.resx
+++ b/src/Microsoft.PowerShell.Commands.Management/resources/ServiceResources.resx
@@ -162,15 +162,24 @@
Service '{1} ({0})' cannot be configured due to the following error: {2}
+
+ Service '{1} ({0})' cannot be queried due to the following error: {2}
+
Service '{1} ({0})' description cannot be configured due to the following error: {2}
+
+ Service '{1} ({0})' automatic (delayed start) cannot be configured due to the following error: {2}
+
Service '{1} ({0})' cannot be created due to the following error: {2}
Service '{1} ({0})' was created, but its description cannot be configured due to the following error: {2}
+
+ Service '{1} ({0})' was created, but its StartupType 'Automatic (Delayed Start)' could not be configured due to the following error: {2}
+
Service '{1} ({0})' cannot be removed due to the following error: {2}
diff --git a/src/System.Management.Automation/utils/PInvokeDllNames.cs b/src/System.Management.Automation/utils/PInvokeDllNames.cs
index 619349b888f..1841115f560 100644
--- a/src/System.Management.Automation/utils/PInvokeDllNames.cs
+++ b/src/System.Management.Automation/utils/PInvokeDllNames.cs
@@ -136,5 +136,7 @@ internal static class PinvokeDllNames
internal const string Process32NextDllName = "api-ms-win-core-toolhelp-l1-1-0"; /*122*/
internal const string GetACPDllName = "api-ms-win-core-localization-l1-2-0.dll"; /*123*/
internal const string DeleteServiceDllName = "api-ms-win-service-management-l1-1-0.dll"; /*124*/
+ internal const string QueryServiceConfigDllName = "api-ms-win-service-management-l2-1-0.dll"; /*125*/
+ internal const string QueryServiceConfig2DllName = "api-ms-win-service-management-l2-1-0.dll"; /*126*/
}
}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1
index 77f3fc62667..e3f189a65d3 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1
@@ -4,9 +4,19 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW
if ( -not $IsWindows ) {
$PSDefaultParameterValues["it:skip"] = $true
}
+ if ($IsWindows) {
+ $userName = "testuserservices"
+ $testPass = "Secret123!"
+ net user $userName $testPass /add > $null
+ $password = ConvertTo-SecureString $testPass -AsPlainText -Force
+ $creds = [pscredential]::new(".\$userName", $password)
+ }
}
AfterAll {
$global:PSDefaultParameterValues = $originalDefaultParameterValues
+ if ($IsWindows) {
+ net user $userName /delete > $null
+ }
}
It "SetServiceCommand can be used as API for '' with ''" -TestCases @(
@@ -14,10 +24,8 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW
@{parameter = "DisplayName" ; value = "hello"},
@{parameter = "Description" ; value = "hello world"},
@{parameter = "StartupType" ; value = "Automatic"},
- @{parameter = "StartupType" ; value = "Boot"},
@{parameter = "StartupType" ; value = "Disabled"},
@{parameter = "StartupType" ; value = "Manual"},
- @{parameter = "StartupType" ; value = "System"},
@{parameter = "Status" ; value = "Running"},
@{parameter = "Status" ; value = "Stopped"},
@{parameter = "Status" ; value = "Paused"},
@@ -96,10 +104,8 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW
@{parameter = "DisplayName" ; value = "hello world"},
@{parameter = "Description" ; value = "this is a test"},
@{parameter = "StartupType" ; value = "Automatic"},
- @{parameter = "StartupType" ; value = "Boot"},
@{parameter = "StartupType" ; value = "Disabled"},
@{parameter = "StartupType" ; value = "Manual"},
- @{parameter = "StartupType" ; value = "System"},
@{parameter = "Credential" ; value = (
[System.Management.Automation.PSCredential]::new("username",
#[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Demo/doc/test secret.")]
@@ -235,6 +241,33 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW
{ Remove-Service -Name "testremoveservice" -ErrorAction 'Stop' } | ShouldBeErrorId "InvalidOperationException,Microsoft.PowerShell.Commands.RemoveServiceCommand"
}
+ It "Get-Service can get the '' of a service" -TestCases @(
+ @{property = "Description"; value = "This is a test description"}
+ @{property = "BinaryPathName"; value = "$PSHOME\powershell.exe";},
+ @{property = "UserName"; value = $creds.UserName; parameters = @{ Credential = $creds }},
+ @{property = "StartupType"; value = "AutomaticDelayedStart";}
+ ) {
+ param($property, $value, $parameters)
+ try {
+ $servicename = "testgetservice"
+ $startparameters = @{Name = $servicename; BinaryPathName = "$PSHOME\powershell.exe"}
+ if($parameters -ne $null) {
+ foreach($key in $parameters.Keys) {
+ $startparameters.$key = $parameters.$key
+ }
+ } else {
+ $startparameters.$property = $value
+ }
+ $service = New-Service @startparameters
+ $service | Should Not BeNullOrEmpty
+ $service = Get-Service -Name $servicename
+ $service.$property | Should BeExactly $value
+ }
+ finally {
+ Get-CimInstance Win32_Service -Filter "name='$servicename'" | Remove-CimInstance -ErrorAction SilentlyContinue
+ }
+ }
+
It "Set-Service can accept a ServiceController as pipeline input" {
try {
$servicename = "testsetservice"
@@ -281,11 +314,11 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW
(ConvertTo-SecureString "PlainTextPassword" -AsPlainText -Force)));
errorid = "CouldNotNewService,Microsoft.PowerShell.Commands.NewServiceCommand"},
@{cmdlet="New-Service"; name = 'badstarttype'; parameter = "StartupType"; value = "System";
- errorid = "CouldNotNewService,Microsoft.PowerShell.Commands.NewServiceCommand"},
+ errorid = "CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.NewServiceCommand"},
@{cmdlet="New-Service"; name = 'winmgmt' ; parameter = "DisplayName"; value = "foo";
errorid = "CouldNotNewService,Microsoft.PowerShell.Commands.NewServiceCommand"},
@{cmdlet="Set-Service"; name = 'winmgmt' ; parameter = "StartupType"; value = "Boot";
- errorid = "CouldNotSetService,Microsoft.PowerShell.Commands.SetServiceCommand"}
+ errorid = "CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.SetServiceCommand"}
) {
param($cmdlet, $name, $parameter, $value, $errorid)
$parameters = @{$parameter = $value; Name = $name; ErrorAction = "Stop"}