From fb733123ffd05cd25231aa41b9ad6fc4f510b06b Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Mon, 18 Sep 2017 17:44:04 +0200 Subject: [PATCH 01/16] [Feature]Remove-Service added to Management module --- .../commands/management/Service.cs | 214 ++++++++++++++++++ .../resources/ServiceResources.resx | 3 + .../utils/PInvokeDllNames.cs | 1 + .../Set-Service.Tests.ps1 | 21 +- 4 files changed, 238 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs index 5a3a8b6e8b3..046e558bac4 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs @@ -2167,6 +2167,212 @@ protected override void BeginProcessing() } // class NewServiceCommand #endregion NewServiceCommand + #region RemoveServiceCommand + /// + /// This class implements the set-service command + /// + [Cmdlet(VerbsCommon.Remove, "Service", SupportsShouldProcess = true, DefaultParameterSetName = "Name")] + [OutputType(typeof(ServiceController))] + public class RemoveServiceCommand : ServiceBaseCommand + { + #region Parameters + + /// + /// Name of the service to create + /// + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Name")] + [Alias("ServiceName", "SN")] + public string Name { get; set; } + + /// + /// The following is the definition of the input parameter "InputObject". + /// Specifies ServiceController object representing the services to be stopped. + /// Enter a variable that contains the objects or type a command or expression + /// that gets the objects. + /// + [Parameter(ValueFromPipeline = true, ParameterSetName = "InputObject")] + public ServiceController InputObject { get; set; } + + /// + /// The following is the definition of the input parameter "ComputerName". + /// Set the properties of service running on the list of computer names + /// specified. The default is the local computer. + /// Type the NETBIOS name, an IP address, or a fully-qualified domain name of + /// one or more remote computers. To indicate the local computer, use the + /// computer name, "localhost" or a dot (.). When the computer is in a different + /// domain than the user, the fully-qualified domain name is required. + /// + [Parameter(ValueFromPipelineByPropertyName = true)] + [ValidateNotNullOrEmpty] + [Alias("cn")] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + public String[] ComputerName { get; set; } = new string[] { "." }; + + #endregion Parameters + + #region Overrides + /// + /// Create the service + /// + [ArchitectureSensitive] + protected override void ProcessRecord() + { + ServiceController service = null; + string ServiceComputerName = null; + foreach (string computer in ComputerName) + { + bool objServiceShouldBeDisposed = false; + try + { + if (_ParameterSetName.Equals("InputObject", StringComparison.OrdinalIgnoreCase) && InputObject != null) + { + service = InputObject; + Name = service.ServiceName; + ServiceComputerName = service.MachineName; + objServiceShouldBeDisposed = false; + } + else + { + ServiceComputerName = computer; + service = new ServiceController(Name, ServiceComputerName); + objServiceShouldBeDisposed = true; + } + Diagnostics.Assert(!String.IsNullOrEmpty(Name), "null ServiceName"); + // "new ServiceController" will succeed even if + // there is no such service. This checks whether + // the service actually exists. + + string unusedByDesign = service.DisplayName; + } + catch (ArgumentException ex) + { + //cannot use WriteNonterminatingError as service is null + ErrorRecord er = new ErrorRecord(ex, "ArgumentException", ErrorCategory.ObjectNotFound, computer); + WriteError(er); + continue; + } + catch (InvalidOperationException ex) + { + //cannot use WriteNonterminatingError as service is null + ErrorRecord er = new ErrorRecord(ex, "InvalidOperationException", ErrorCategory.ObjectNotFound, computer); + WriteError(er); + continue; + } + + try // In finally we ensure dispose, if object not pipelined. + { + // confirm the operation first + // this is always false if WhatIf is set + if (!ShouldProcessServiceOperation(service)) + { + continue; + } + + NakedWin32Handle hScManager = IntPtr.Zero; + NakedWin32Handle hService = IntPtr.Zero; + try + { + hScManager = NativeMethods.OpenSCManagerW( + ServiceComputerName, + null, + NativeMethods.SC_MANAGER_ALL_ACCESS + ); + if (IntPtr.Zero == hScManager) + { + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new Win32Exception(lastError); + WriteObject(exception); + WriteNonTerminatingError( + service, + ServiceComputerName, + exception, + "ComputerAccessDenied", + ServiceResources.ComputerAccessDenied, + ErrorCategory.PermissionDenied); + continue; + } + hService = NativeMethods.OpenServiceW( + hScManager, + Name, + NativeMethods.SERVICE_DELETE + ); + if (IntPtr.Zero == hService) + { + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new Win32Exception(lastError); + WriteNonTerminatingError( + service, + exception, + "CouldNotRemoveService", + ServiceResources.CouldNotSetService, + ErrorCategory.PermissionDenied); + continue; + } + + + bool status = NativeMethods.DeleteService(hService); + + if (!status) + { + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new Win32Exception(lastError); + WriteNonTerminatingError( + service, + exception, + "CouldNotRemoveService", + ServiceResources.CouldNotRemoveService, + ErrorCategory.PermissionDenied); + } + } + finally + { + if (IntPtr.Zero != hService) + { + bool succeeded = NativeMethods.CloseServiceHandle(hService); + if (!succeeded) + { + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new Win32Exception(lastError); + WriteNonTerminatingError( + service, + exception, + "CouldNotRemoveService", + ServiceResources.CouldNotRemoveService, + ErrorCategory.PermissionDenied); + } + } + + if (IntPtr.Zero != hScManager) + { + bool succeeded = NativeMethods.CloseServiceHandle(hScManager); + if (!succeeded) + { + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new Win32Exception(lastError); + WriteNonTerminatingError( + service, + exception, + "CouldNotRemoveService", + ServiceResources.CouldNotRemoveService, + ErrorCategory.PermissionDenied); + } + } + } // finally + } //End try + finally + { + if (objServiceShouldBeDisposed) + { + service.Dispose(); + } + } + }//end for + } + #endregion Overrides + } // class RemoveServiceCommand + #endregion RemoveServiceCommand + #region ServiceCommandException /// /// Non-terminating errors occurring in the service noun commands @@ -2264,8 +2470,10 @@ internal static class NativeMethods internal const int ERROR_SERVICE_NOT_ACTIVE = 1062; internal const DWORD SC_MANAGER_CONNECT = 1; internal const DWORD SC_MANAGER_CREATE_SERVICE = 2; + internal const DWORD SC_MANAGER_ALL_ACCESS = 0xf003f; internal const DWORD SERVICE_QUERY_CONFIG = 1; internal const DWORD SERVICE_CHANGE_CONFIG = 2; + internal const DWORD SERVICE_DELETE = 0x10000; internal const DWORD SERVICE_NO_CHANGE = 0xffffffff; internal const DWORD SERVICE_AUTO_START = 0x2; internal const DWORD SERVICE_DEMAND_START = 0x3; @@ -2297,6 +2505,12 @@ bool CloseServiceHandle( NakedWin32Handle hSCManagerOrService ); + [DllImport(PinvokeDllNames.DeleteServiceDllName, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern + bool DeleteService( + NakedWin32Handle hService + ); + [DllImport(PinvokeDllNames.ChangeServiceConfigWDllName, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool ChangeServiceConfigW( diff --git a/src/Microsoft.PowerShell.Commands.Management/resources/ServiceResources.resx b/src/Microsoft.PowerShell.Commands.Management/resources/ServiceResources.resx index 60ecdd041f0..8c94d5bb35a 100644 --- a/src/Microsoft.PowerShell.Commands.Management/resources/ServiceResources.resx +++ b/src/Microsoft.PowerShell.Commands.Management/resources/ServiceResources.resx @@ -171,6 +171,9 @@ Service '{1} ({0})' was created, but its description cannot be configured due to the following error: {2} + + Service '{1} ({0})' cannot be removed due to the following error: {2} + 'Cannot access dependent services of '{1} ({0})' diff --git a/src/System.Management.Automation/utils/PInvokeDllNames.cs b/src/System.Management.Automation/utils/PInvokeDllNames.cs index 33d21c45983..619349b888f 100644 --- a/src/System.Management.Automation/utils/PInvokeDllNames.cs +++ b/src/System.Management.Automation/utils/PInvokeDllNames.cs @@ -135,5 +135,6 @@ internal static class PinvokeDllNames internal const string Process32FirstDllName = "api-ms-win-core-toolhelp-l1-1-0"; /*121*/ 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*/ } } 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 354daff452a..17b83f3b457 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 @@ -1,4 +1,5 @@ -Describe "Set/New-Service cmdlet tests" -Tags "Feature", "RequireAdminOnWindows" { +Import-Module ".\Microsoft.PowerShell.Commands.Management.dll" +Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnWindows" { BeforeAll { $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() if ( -not $IsWindows ) { @@ -195,6 +196,24 @@ Describe "Set/New-Service cmdlet tests" -Tags "Feature", "RequireAdminOnWindows" } } + It "Remove-Service can remove a service" { + try { + $servicename = "testremoveservice" + $parameters = @{ + Name = $servicename; + BinaryPathName = "$PSHOME\powershell.exe" + } + $service = New-Service @parameters + $service | Should Not BeNullOrEmpty + Remove-Service -Name $servicename + $service = Get-Service -Name $servicename -ErrorAction SilentlyContinue + $service | Should BeNullOrEmpty + } + finally { + Get-CimInstance Win32_Service -Filter "name='$servicename'" | Remove-CimInstance -ErrorAction SilentlyContinue + } + } + It "Using bad parameters will fail for '' where '' = ''" -TestCases @( @{cmdlet="New-Service"; name = 'credtest' ; parameter = "Credential" ; value = ( [System.Management.Automation.PSCredential]::new("username", From 32759f4df0b69ae3e0547fad9a01d5a0f186fb9c Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Mon, 18 Sep 2017 17:52:20 +0200 Subject: [PATCH 02/16] [Feature]Capitalization --- .../commands/management/Service.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs index 046e558bac4..2f5a6ca89af 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs @@ -2247,14 +2247,14 @@ protected override void ProcessRecord() } catch (ArgumentException ex) { - //cannot use WriteNonterminatingError as service is null + //Cannot use WriteNonterminatingError as service is null ErrorRecord er = new ErrorRecord(ex, "ArgumentException", ErrorCategory.ObjectNotFound, computer); WriteError(er); continue; } catch (InvalidOperationException ex) { - //cannot use WriteNonterminatingError as service is null + //Cannot use WriteNonterminatingError as service is null ErrorRecord er = new ErrorRecord(ex, "InvalidOperationException", ErrorCategory.ObjectNotFound, computer); WriteError(er); continue; @@ -2262,8 +2262,8 @@ protected override void ProcessRecord() try // In finally we ensure dispose, if object not pipelined. { - // confirm the operation first - // this is always false if WhatIf is set + // Confirm the operation first. + // This is always false if WhatIf is set. if (!ShouldProcessServiceOperation(service)) { continue; @@ -2358,7 +2358,7 @@ protected override void ProcessRecord() ErrorCategory.PermissionDenied); } } - } // finally + } // Finally } //End try finally { @@ -2367,7 +2367,7 @@ protected override void ProcessRecord() service.Dispose(); } } - }//end for + }//End for } #endregion Overrides } // class RemoveServiceCommand From 072582f42f92fdd22df85ae588bc9071e2ba4cf7 Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Mon, 18 Sep 2017 21:06:31 +0200 Subject: [PATCH 03/16] [Feature]Added test cases for Remove-Service, Removed return value --- .../commands/management/Service.cs | 1 - .../Set-Service.Tests.ps1 | 24 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs index 2f5a6ca89af..b2c260f99c5 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs @@ -2172,7 +2172,6 @@ protected override void BeginProcessing() /// This class implements the set-service command /// [Cmdlet(VerbsCommon.Remove, "Service", SupportsShouldProcess = true, DefaultParameterSetName = "Name")] - [OutputType(typeof(ServiceController))] public class RemoveServiceCommand : ServiceBaseCommand { #region Parameters 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 17b83f3b457..54bdf4eaa86 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 @@ -1,4 +1,3 @@ -Import-Module ".\Microsoft.PowerShell.Commands.Management.dll" Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnWindows" { BeforeAll { $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() @@ -214,6 +213,29 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW } } + It "Remove-Service can accept pipeline input of a ServiceController" { + try { + $servicename = "testremoveservice" + $parameters = @{ + Name = $servicename; + BinaryPathName = "$PSHOME\powershell.exe" + } + $service = New-Service @parameters + $service | Should Not BeNullOrEmpty + Get-Service -Name $servicename | Remove-Service -ErrorAction SilentlyContinue + $service = Get-Service -Name $servicename -ErrorAction SilentlyContinue + $service | Should BeNullOrEmpty + } + finally { + Get-CimInstance Win32_Service -Filter "name='$servicename'" | Remove-CimInstance -ErrorAction SilentlyContinue + } + } + + It "Remove-Service cannot accept a service that does not exist" { + { Remove-Service -Name "testremoveservice" } | Should Throw + } + + It "Using bad parameters will fail for '' where '' = ''" -TestCases @( @{cmdlet="New-Service"; name = 'credtest' ; parameter = "Credential" ; value = ( [System.Management.Automation.PSCredential]::new("username", From 1349f60ac27193fbbaabc544c51fc4eb1cba17a0 Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Mon, 18 Sep 2017 21:14:37 +0200 Subject: [PATCH 04/16] [Feature]Documentation from copy-paste code corrected to reflect the new function --- .../commands/management/Service.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs index b2c260f99c5..3764fda3020 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs @@ -2177,7 +2177,7 @@ public class RemoveServiceCommand : ServiceBaseCommand #region Parameters /// - /// Name of the service to create + /// Name of the service to remove /// /// [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Name")] @@ -2186,7 +2186,7 @@ public class RemoveServiceCommand : ServiceBaseCommand /// /// The following is the definition of the input parameter "InputObject". - /// Specifies ServiceController object representing the services to be stopped. + /// Specifies ServiceController object representing the services to be removed. /// Enter a variable that contains the objects or type a command or expression /// that gets the objects. /// @@ -2212,7 +2212,7 @@ public class RemoveServiceCommand : ServiceBaseCommand #region Overrides /// - /// Create the service + /// Remove the service /// [ArchitectureSensitive] protected override void ProcessRecord() @@ -2309,7 +2309,6 @@ protected override void ProcessRecord() continue; } - bool status = NativeMethods.DeleteService(hService); if (!status) From b1c7e4da7cb107771ca7907d9aee53144780b6c6 Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Mon, 18 Sep 2017 21:26:53 +0200 Subject: [PATCH 05/16] [Feature]Erroraction to be sure that an error is thrown in testcase --- .../commands/management/Service.cs | 2 +- .../Microsoft.PowerShell.Management/Set-Service.Tests.ps1 | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs index 3764fda3020..b1b372760b3 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs @@ -2169,7 +2169,7 @@ protected override void BeginProcessing() #region RemoveServiceCommand /// - /// This class implements the set-service command + /// This class implements the remove-service command /// [Cmdlet(VerbsCommon.Remove, "Service", SupportsShouldProcess = true, DefaultParameterSetName = "Name")] public class RemoveServiceCommand : ServiceBaseCommand 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 54bdf4eaa86..941a8d3fb0a 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 @@ -1,3 +1,4 @@ +Import-Module "C:\Users\jsa\code\PowerShell\src\powershell-win-core\bin\Debug\netcoreapp2.0\win7-x64\Microsoft.PowerShell.Commands.Management.dll" Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnWindows" { BeforeAll { $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() @@ -232,7 +233,7 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW } It "Remove-Service cannot accept a service that does not exist" { - { Remove-Service -Name "testremoveservice" } | Should Throw + { Remove-Service -Name "testremoveservice" -ErrorAction 'Stop' } | Should Throw } From 17c4d4ae52e139422abcd71fa484c7bc61940086 Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Mon, 18 Sep 2017 21:31:45 +0200 Subject: [PATCH 06/16] [Feature]Removed direct reference to module in Pester test --- .../Microsoft.PowerShell.Management/Set-Service.Tests.ps1 | 1 - 1 file changed, 1 deletion(-) 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 941a8d3fb0a..f99abd05bbc 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 @@ -1,4 +1,3 @@ -Import-Module "C:\Users\jsa\code\PowerShell\src\powershell-win-core\bin\Debug\netcoreapp2.0\win7-x64\Microsoft.PowerShell.Commands.Management.dll" Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnWindows" { BeforeAll { $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() From 2585dbe0fc5cd71a47c79315e6478dafee2e640b Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Mon, 18 Sep 2017 21:40:56 +0200 Subject: [PATCH 07/16] [Feature]Removed extra line in test --- .../Microsoft.PowerShell.Management/Set-Service.Tests.ps1 | 1 - 1 file changed, 1 deletion(-) 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 f99abd05bbc..f68911163c1 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 @@ -235,7 +235,6 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW { Remove-Service -Name "testremoveservice" -ErrorAction 'Stop' } | Should Throw } - It "Using bad parameters will fail for '' where '' = ''" -TestCases @( @{cmdlet="New-Service"; name = 'credtest' ; parameter = "Credential" ; value = ( [System.Management.Automation.PSCredential]::new("username", From b56dba9f933ded212ba0d0f4f32debad3acfc30c Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Mon, 18 Sep 2017 22:43:48 +0200 Subject: [PATCH 08/16] [Feature]Use FullyQualifiedErrorId in Remove-Service test without a valid servicename --- .../Microsoft.PowerShell.Management/Set-Service.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f68911163c1..39b555c972d 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 @@ -232,7 +232,7 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW } It "Remove-Service cannot accept a service that does not exist" { - { Remove-Service -Name "testremoveservice" -ErrorAction 'Stop' } | Should Throw + { Remove-Service -Name "testremoveservice" -ErrorAction 'Stop' } | ShouldBeErrorId "InvalidOperationException,Microsoft.PowerShell.Commands.RemoveServiceCommand" } It "Using bad parameters will fail for '' where '' = ''" -TestCases @( From c519a9a67b9db702ceca8562d3ea5899e45d94ad Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Tue, 19 Sep 2017 00:05:40 +0200 Subject: [PATCH 09/16] [Feature]Exposed Remove-Service --- .../Microsoft.PowerShell.Management.psd1 | 1 + .../Microsoft.PowerShell.Management.psd1 | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Modules/Windows-Core/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 b/src/Modules/Windows-Core/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 index 9a8bdff1565..1807ae22e70 100644 --- a/src/Modules/Windows-Core/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 +++ b/src/Modules/Windows-Core/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 @@ -56,6 +56,7 @@ CmdletsToExport=@("Add-Content", "Restart-Service", "Set-Service", "New-Service", + "Remove-Service", "Set-Content", "Set-ItemProperty", "Test-Connection", diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 b/src/Modules/Windows-Full/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 index 6325b6fe97d..b2024a3651b 100644 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 +++ b/src/Modules/Windows-Full/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 @@ -68,6 +68,7 @@ CmdletsToExport=@("Add-Content", "Restart-Service", "Set-Service", "New-Service", + "Remove-Service", "Set-Content", "Set-ItemProperty", "Set-WmiInstance", From cb2ee47748344d97b0d81339368a4d2417d09f6a Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Tue, 19 Sep 2017 00:33:38 +0200 Subject: [PATCH 10/16] [Feature]Consistent casing, Named Arguments & Remove incorrect exception on cleanup --- .../commands/management/Service.cs | 33 ++++++------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs index b1b372760b3..a791da2fbbf 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs @@ -2179,7 +2179,6 @@ public class RemoveServiceCommand : ServiceBaseCommand /// /// Name of the service to remove /// - /// [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Name")] [Alias("ServiceName", "SN")] public string Name { get; set; } @@ -2218,7 +2217,7 @@ public class RemoveServiceCommand : ServiceBaseCommand protected override void ProcessRecord() { ServiceController service = null; - string ServiceComputerName = null; + string serviceComputerName = null; foreach (string computer in ComputerName) { bool objServiceShouldBeDisposed = false; @@ -2228,13 +2227,13 @@ protected override void ProcessRecord() { service = InputObject; Name = service.ServiceName; - ServiceComputerName = service.MachineName; + serviceComputerName = service.MachineName; objServiceShouldBeDisposed = false; } else { - ServiceComputerName = computer; - service = new ServiceController(Name, ServiceComputerName); + serviceComputerName = computer; + service = new ServiceController(Name, serviceComputerName); objServiceShouldBeDisposed = true; } Diagnostics.Assert(!String.IsNullOrEmpty(Name), "null ServiceName"); @@ -2273,9 +2272,9 @@ protected override void ProcessRecord() try { hScManager = NativeMethods.OpenSCManagerW( - ServiceComputerName, - null, - NativeMethods.SC_MANAGER_ALL_ACCESS + lpMachineName: serviceComputerName, + lpDatabaseName: null, + dwDesiredAccess: NativeMethods.SC_MANAGER_ALL_ACCESS ); if (IntPtr.Zero == hScManager) { @@ -2284,7 +2283,7 @@ protected override void ProcessRecord() WriteObject(exception); WriteNonTerminatingError( service, - ServiceComputerName, + serviceComputerName, exception, "ComputerAccessDenied", ServiceResources.ComputerAccessDenied, @@ -2331,13 +2330,7 @@ protected override void ProcessRecord() if (!succeeded) { int lastError = Marshal.GetLastWin32Error(); - Win32Exception exception = new Win32Exception(lastError); - WriteNonTerminatingError( - service, - exception, - "CouldNotRemoveService", - ServiceResources.CouldNotRemoveService, - ErrorCategory.PermissionDenied); + Diagnostics.Assert(lastError != 0, "ErrorCode not success"); } } @@ -2347,13 +2340,7 @@ protected override void ProcessRecord() if (!succeeded) { int lastError = Marshal.GetLastWin32Error(); - Win32Exception exception = new Win32Exception(lastError); - WriteNonTerminatingError( - service, - exception, - "CouldNotRemoveService", - ServiceResources.CouldNotRemoveService, - ErrorCategory.PermissionDenied); + Diagnostics.Assert(lastError != 0, "ErrorCode not success"); } } } // Finally From 2d7e0000a727e4551acfe39ce42d6ee47d4e5ebb Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Tue, 19 Sep 2017 07:29:14 +0200 Subject: [PATCH 11/16] [Feature]Remove-Service test should fail if the service was not removed --- .../Microsoft.PowerShell.Management/Set-Service.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 39b555c972d..a360936e377 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 @@ -222,7 +222,7 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW } $service = New-Service @parameters $service | Should Not BeNullOrEmpty - Get-Service -Name $servicename | Remove-Service -ErrorAction SilentlyContinue + Get-Service -Name $servicename | Remove-Service $service = Get-Service -Name $servicename -ErrorAction SilentlyContinue $service | Should BeNullOrEmpty } From 4ffd2c0de8632c1b0d8bc55fa657075f20d9eab7 Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Tue, 19 Sep 2017 18:00:16 +0200 Subject: [PATCH 12/16] [Feature]Cleanup comments & add Remove-Service for CI --- .../commands/management/Service.cs | 16 +++++++--------- .../engine/Basic/DefaultCommands.Tests.ps1 | 1 + 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs index a791da2fbbf..b1b52e317c5 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs @@ -2169,7 +2169,7 @@ protected override void BeginProcessing() #region RemoveServiceCommand /// - /// This class implements the remove-service command + /// This class implements the Remove-Service command /// [Cmdlet(VerbsCommon.Remove, "Service", SupportsShouldProcess = true, DefaultParameterSetName = "Name")] public class RemoveServiceCommand : ServiceBaseCommand @@ -2233,26 +2233,24 @@ protected override void ProcessRecord() else { serviceComputerName = computer; + // "new ServiceController" will succeed even if there is no such service. + // This checks whether the service actually exists. service = new ServiceController(Name, serviceComputerName); objServiceShouldBeDisposed = true; } Diagnostics.Assert(!String.IsNullOrEmpty(Name), "null ServiceName"); - // "new ServiceController" will succeed even if - // there is no such service. This checks whether - // the service actually exists. - string unusedByDesign = service.DisplayName; } catch (ArgumentException ex) { - //Cannot use WriteNonterminatingError as service is null + // Cannot use WriteNonterminatingError as service is null ErrorRecord er = new ErrorRecord(ex, "ArgumentException", ErrorCategory.ObjectNotFound, computer); WriteError(er); continue; } catch (InvalidOperationException ex) { - //Cannot use WriteNonterminatingError as service is null + // Cannot use WriteNonterminatingError as service is null ErrorRecord er = new ErrorRecord(ex, "InvalidOperationException", ErrorCategory.ObjectNotFound, computer); WriteError(er); continue; @@ -2344,7 +2342,7 @@ protected override void ProcessRecord() } } } // Finally - } //End try + } // End try finally { if (objServiceShouldBeDisposed) @@ -2352,7 +2350,7 @@ protected override void ProcessRecord() service.Dispose(); } } - }//End for + }// End for } #endregion Overrides } // class RemoveServiceCommand diff --git a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 index 50ddb81b4d6..519243982a7 100644 --- a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 +++ b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 @@ -407,6 +407,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Set-PSDebug", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-PSSessionConfiguration", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-Service", , $($FullCLR -or $CoreWindows ) +"Cmdlet", "Remove-Service", , $($FullCLR -or $CoreWindows ) "Cmdlet", "Set-StrictMode", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-TimeZone", , $($FullCLR -or $CoreWindows ) "Cmdlet", "Set-TraceSource", , $($FullCLR -or $CoreWindows -or $CoreUnix) From 2fcf55eaaf70d6508f92071245cac16041b69135 Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Tue, 19 Sep 2017 18:09:41 +0200 Subject: [PATCH 13/16] [Feature]Remove-Service in CI set in alphabetic order --- test/powershell/engine/Basic/DefaultCommands.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 index 519243982a7..7c5d70f3fce 100644 --- a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 +++ b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 @@ -374,6 +374,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Remove-PSDrive", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Remove-PSSession", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Remove-PSSnapin", , $($FullCLR ) +"Cmdlet", "Remove-Service", , $($FullCLR -or $CoreWindows ) "Cmdlet", "Remove-TypeData", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Remove-Variable", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Remove-WmiObject", , $($FullCLR ) @@ -407,7 +408,6 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Set-PSDebug", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-PSSessionConfiguration", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-Service", , $($FullCLR -or $CoreWindows ) -"Cmdlet", "Remove-Service", , $($FullCLR -or $CoreWindows ) "Cmdlet", "Set-StrictMode", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Set-TimeZone", , $($FullCLR -or $CoreWindows ) "Cmdlet", "Set-TraceSource", , $($FullCLR -or $CoreWindows -or $CoreUnix) From da41f7deb811f32538e0dc7f0d811646ceb8acf8 Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Tue, 19 Sep 2017 21:36:46 +0200 Subject: [PATCH 14/16] [Feature]Use ParameterSetName instead of _ParameterSetName and rewrite test which used the function class directly --- .../commands/management/Service.cs | 6 +- .../Set-Service.Tests.ps1 | 109 +++++++++++++----- 2 files changed, 85 insertions(+), 30 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs index b1b52e317c5..2e0464e5e36 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs @@ -24,7 +24,7 @@ namespace Microsoft.PowerShell.Commands /// /// This class implements the base for service commands /// - public abstract class ServiceBaseCommand : Cmdlet + public abstract class ServiceBaseCommand : PSCmdlet { #region Internal @@ -1601,7 +1601,7 @@ protected override void ProcessRecord() bool objServiceShouldBeDisposed = false; try { - if (_ParameterSetName.Equals("InputObject", StringComparison.OrdinalIgnoreCase) && InputObject != null) + if (this.ParameterSetName.Equals("InputObject", StringComparison.OrdinalIgnoreCase) && InputObject != null) { service = InputObject; Name = service.ServiceName; @@ -2223,7 +2223,7 @@ protected override void ProcessRecord() bool objServiceShouldBeDisposed = false; try { - if (_ParameterSetName.Equals("InputObject", StringComparison.OrdinalIgnoreCase) && InputObject != null) + if (this.ParameterSetName.Equals("InputObject", StringComparison.OrdinalIgnoreCase) && InputObject != null) { service = InputObject; Name = service.ServiceName; 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 a360936e377..01d52028b47 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 @@ -53,41 +53,96 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW { & $script } | ShouldBeErrorId $errorid } - It "Set-Service can change '' to ''" -TestCases @( - @{parameter = "Description"; value = "hello"}, - @{parameter = "DisplayName"; value = "test spooler"}, - @{parameter = "StartupType"; value = "Disabled"}, - @{parameter = "Status" ; value = "running" ; expected = "OK"} - ) { - param($parameter, $value, $expected) - $currentService = Get-CimInstance -ClassName Win32_Service -Filter "Name='spooler'" - $originalStartupType = (Get-Service -Name spooler).StartType + It "Set-Service can change 'Description' to 'hello'" { + $serviceName = "spooler" + $parameter = "Description" + $value = "hello" + $service = Get-Service $serviceName + $originalServiceDescription = (Get-CimInstance -ClassName Win32_Service -Filter "Name='$serviceName'").$parameter try { - $setServiceCommand = [Microsoft.PowerShell.Commands.SetServiceCommand]::new() - $setServiceCommand.Name = "Spooler" - $setServiceCommand.$parameter = $value - $setServiceCommand.Invoke() - $updatedService = Get-CimInstance -ClassName Win32_Service -Filter "Name='spooler'" - if ($expected -eq $null) { - $expected = $value + $arguments = @{ + 'Name' = $serviceName + $parameter = $value } - if ($parameter -eq "StartupType") { - $updatedService.StartMode | Should Be $expected + Set-Service @arguments + (Get-CimInstance -ClassName Win32_Service -Filter "Name='$serviceName'").$parameter | Should BeExactly $value + } + finally { + $arguments = @{ + 'Name' = $serviceName + $parameter = $originalServiceDescription } - else { - $updatedService.$parameter | Should Be $expected + Set-Service @arguments + (Get-CimInstance -ClassName Win32_Service -Filter "Name='$serviceName'").$parameter | Should BeExactly $originalServiceDescription + } + } + + It "Set-Service can change 'DisplayName' to 'test spooler'" { + $serviceName = "spooler" + $parameter = "DisplayName" + $value = "test spooler" + $originalServiceDisplayName = (Get-Service $serviceName).$parameter + try { + $arguments = @{ + 'Name' = $serviceName + $parameter = $value } + Set-Service @arguments + (Get-Service -Name $serviceName).$parameter | Should BeExactly $value } finally { - if ($parameter -eq "StartupType") { - $setServiceCommand.StartupType = $originalStartupType + $arguments = @{ + 'Name' = $serviceName + $parameter = $originalServiceDisplayName } - else { - $setServiceCommand.$parameter = $currentService.$parameter + Set-Service @arguments + (Get-Service -Name $serviceName).$parameter | Should BeExactly $originalServiceDisplayName + } + } + + It "Set-Service can change 'StartupType' to 'Disabled'" { + $serviceName = "spooler" + $parameter = "StartupType" + $value = "Disabled" + $originalServiceStartupType = (Get-Service $serviceName).StartType + try { + $arguments = @{ + 'Name' = $serviceName + $parameter = $value + } + Set-Service @arguments + (Get-Service -Name $serviceName).StartType | Should BeExactly $value + } + finally { + $arguments = @{ + 'Name' = $serviceName + $parameter = $originalServiceStartupType + } + Set-Service @arguments + (Get-Service -Name $serviceName).StartType | Should BeExactly $originalServiceStartupType + } + } + + It "Set-Service can change 'Status' to 'Running'" { + $serviceName = "spooler" + $parameter = "Status" + $value = "Running" + $originalServiceStatus = (Get-Service $serviceName).$parameter + try { + $arguments = @{ + 'Name' = $serviceName + $parameter = $value + } + Set-Service @arguments + (Get-Service -Name $serviceName).$parameter | Should BeExactly $value + } + finally { + $arguments = @{ + 'Name' = $serviceName + $parameter = $originalServiceStatus } - $setServiceCommand.Invoke() - $updatedService = Get-CimInstance -ClassName Win32_Service -Filter "Name='spooler'" - $updatedService.$parameter | Should Be $currentService.$parameter + Set-Service @arguments + (Get-Service -Name $serviceName).$parameter | Should BeExactly $originalServiceStatus } } From 675384059f7eeeed72d01b2004ec0f6cd07937d6 Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Wed, 20 Sep 2017 08:23:48 +0200 Subject: [PATCH 15/16] Revert "[Feature]Use ParameterSetName instead of _ParameterSetName and rewrite test which used the function class directly" This reverts commit da41f7deb811f32538e0dc7f0d811646ceb8acf8. --- .../commands/management/Service.cs | 6 +- .../Set-Service.Tests.ps1 | 109 +++++------------- 2 files changed, 30 insertions(+), 85 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs index 2e0464e5e36..b1b52e317c5 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs @@ -24,7 +24,7 @@ namespace Microsoft.PowerShell.Commands /// /// This class implements the base for service commands /// - public abstract class ServiceBaseCommand : PSCmdlet + public abstract class ServiceBaseCommand : Cmdlet { #region Internal @@ -1601,7 +1601,7 @@ protected override void ProcessRecord() bool objServiceShouldBeDisposed = false; try { - if (this.ParameterSetName.Equals("InputObject", StringComparison.OrdinalIgnoreCase) && InputObject != null) + if (_ParameterSetName.Equals("InputObject", StringComparison.OrdinalIgnoreCase) && InputObject != null) { service = InputObject; Name = service.ServiceName; @@ -2223,7 +2223,7 @@ protected override void ProcessRecord() bool objServiceShouldBeDisposed = false; try { - if (this.ParameterSetName.Equals("InputObject", StringComparison.OrdinalIgnoreCase) && InputObject != null) + if (_ParameterSetName.Equals("InputObject", StringComparison.OrdinalIgnoreCase) && InputObject != null) { service = InputObject; Name = service.ServiceName; 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 01d52028b47..a360936e377 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 @@ -53,96 +53,41 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW { & $script } | ShouldBeErrorId $errorid } - It "Set-Service can change 'Description' to 'hello'" { - $serviceName = "spooler" - $parameter = "Description" - $value = "hello" - $service = Get-Service $serviceName - $originalServiceDescription = (Get-CimInstance -ClassName Win32_Service -Filter "Name='$serviceName'").$parameter - try { - $arguments = @{ - 'Name' = $serviceName - $parameter = $value - } - Set-Service @arguments - (Get-CimInstance -ClassName Win32_Service -Filter "Name='$serviceName'").$parameter | Should BeExactly $value - } - finally { - $arguments = @{ - 'Name' = $serviceName - $parameter = $originalServiceDescription - } - Set-Service @arguments - (Get-CimInstance -ClassName Win32_Service -Filter "Name='$serviceName'").$parameter | Should BeExactly $originalServiceDescription - } - } - - It "Set-Service can change 'DisplayName' to 'test spooler'" { - $serviceName = "spooler" - $parameter = "DisplayName" - $value = "test spooler" - $originalServiceDisplayName = (Get-Service $serviceName).$parameter + It "Set-Service can change '' to ''" -TestCases @( + @{parameter = "Description"; value = "hello"}, + @{parameter = "DisplayName"; value = "test spooler"}, + @{parameter = "StartupType"; value = "Disabled"}, + @{parameter = "Status" ; value = "running" ; expected = "OK"} + ) { + param($parameter, $value, $expected) + $currentService = Get-CimInstance -ClassName Win32_Service -Filter "Name='spooler'" + $originalStartupType = (Get-Service -Name spooler).StartType try { - $arguments = @{ - 'Name' = $serviceName - $parameter = $value + $setServiceCommand = [Microsoft.PowerShell.Commands.SetServiceCommand]::new() + $setServiceCommand.Name = "Spooler" + $setServiceCommand.$parameter = $value + $setServiceCommand.Invoke() + $updatedService = Get-CimInstance -ClassName Win32_Service -Filter "Name='spooler'" + if ($expected -eq $null) { + $expected = $value } - Set-Service @arguments - (Get-Service -Name $serviceName).$parameter | Should BeExactly $value - } - finally { - $arguments = @{ - 'Name' = $serviceName - $parameter = $originalServiceDisplayName + if ($parameter -eq "StartupType") { + $updatedService.StartMode | Should Be $expected } - Set-Service @arguments - (Get-Service -Name $serviceName).$parameter | Should BeExactly $originalServiceDisplayName - } - } - - It "Set-Service can change 'StartupType' to 'Disabled'" { - $serviceName = "spooler" - $parameter = "StartupType" - $value = "Disabled" - $originalServiceStartupType = (Get-Service $serviceName).StartType - try { - $arguments = @{ - 'Name' = $serviceName - $parameter = $value + else { + $updatedService.$parameter | Should Be $expected } - Set-Service @arguments - (Get-Service -Name $serviceName).StartType | Should BeExactly $value } finally { - $arguments = @{ - 'Name' = $serviceName - $parameter = $originalServiceStartupType - } - Set-Service @arguments - (Get-Service -Name $serviceName).StartType | Should BeExactly $originalServiceStartupType - } - } - - It "Set-Service can change 'Status' to 'Running'" { - $serviceName = "spooler" - $parameter = "Status" - $value = "Running" - $originalServiceStatus = (Get-Service $serviceName).$parameter - try { - $arguments = @{ - 'Name' = $serviceName - $parameter = $value + if ($parameter -eq "StartupType") { + $setServiceCommand.StartupType = $originalStartupType } - Set-Service @arguments - (Get-Service -Name $serviceName).$parameter | Should BeExactly $value - } - finally { - $arguments = @{ - 'Name' = $serviceName - $parameter = $originalServiceStatus + else { + $setServiceCommand.$parameter = $currentService.$parameter } - Set-Service @arguments - (Get-Service -Name $serviceName).$parameter | Should BeExactly $originalServiceStatus + $setServiceCommand.Invoke() + $updatedService = Get-CimInstance -ClassName Win32_Service -Filter "Name='spooler'" + $updatedService.$parameter | Should Be $currentService.$parameter } } From 427bc7d6c8e56108943f9b44ed19341cca33a3ba Mon Sep 17 00:00:00 2001 From: Jonas Andersen Date: Wed, 20 Sep 2017 09:39:26 +0200 Subject: [PATCH 16/16] [Feature]Remove _ParameterSetName check & added test for pipeline input in Set-Service --- .../commands/management/Service.cs | 4 ++-- .../Set-Service.Tests.ps1 | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs index b1b52e317c5..568d1fe44ad 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs @@ -1601,7 +1601,7 @@ protected override void ProcessRecord() bool objServiceShouldBeDisposed = false; try { - if (_ParameterSetName.Equals("InputObject", StringComparison.OrdinalIgnoreCase) && InputObject != null) + if (InputObject != null) { service = InputObject; Name = service.ServiceName; @@ -2223,7 +2223,7 @@ protected override void ProcessRecord() bool objServiceShouldBeDisposed = false; try { - if (_ParameterSetName.Equals("InputObject", StringComparison.OrdinalIgnoreCase) && InputObject != null) + if (InputObject != null) { service = InputObject; Name = service.ServiceName; 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 a360936e377..cf78e8ba69a 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 @@ -235,6 +235,25 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW { Remove-Service -Name "testremoveservice" -ErrorAction 'Stop' } | ShouldBeErrorId "InvalidOperationException,Microsoft.PowerShell.Commands.RemoveServiceCommand" } + It "Set-Service can accept pipeline input of a ServiceController" { + try { + $servicename = "testsetservice" + $newdisplayname = "newdisplayname" + $parameters = @{ + Name = $servicename; + BinaryPathName = "$PSHOME\powershell.exe" + } + $service = New-Service @parameters + $service | Should Not BeNullOrEmpty + Get-Service -Name $servicename | Set-Service -DisplayName $newdisplayname + $service = Get-Service -Name $servicename + $service.DisplayName | Should BeExactly $newdisplayname + } + finally { + Get-CimInstance Win32_Service -Filter "name='$servicename'" | Remove-CimInstance -ErrorAction SilentlyContinue + } + } + It "Using bad parameters will fail for '' where '' = ''" -TestCases @( @{cmdlet="New-Service"; name = 'credtest' ; parameter = "Credential" ; value = ( [System.Management.Automation.PSCredential]::new("username",