Remove-Windows Feature throwing error

I have a listing of servers in my environment that I’m trying to disable SMBv1 on. Mixture of both 2008R2 and 2012R2 boxes, trying to automate the process as much as I can.

Here’s my code:

Connect-VIServer -Server v-center.x.x.com
$server = Get-VM -Name Test-NewServer
Enter-PSSession  $server
Remove-WindowsFeature -Name FS-SMB1
Exit-PSSession
Restart-VMGuest $server

When I run the code together, I get this error:

Remove-WindowsFeature : The target of the specified cmdlet cannot be a windows client-based operating system.
At line :4 char:1
+ Remove-WindowsFeature -Name FS-SMB1
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     + Category Info         : DeviceError: (localhost:String) [Uninstall-WindowsFeature], Exception
     + FullyQualifiedErrorId : WindowsClient_NotSupported,Microsoft.Windows.ServerManager.Commands.RemoveWindowsFeatureCommand
WARNING: GuestId property is deprecated.  Please use ConfiguredGuestId and RuntimeGuestId properties instead.

However, when I run the code one line at a time, it executes successfully with no errors. I’m assuming that the code needs some pauses in the process to make it run smoothly. Also, I’d love to schedule the reboots for 8:00 PM the day that I run the code so that everything is happening in off-hours. What’s my most efficient method of achieving this goal? Thank you in advance.

Have you considered using Invoke-Command, instead? Just give it a list of computer names and the Remove- command.

Invoke-Command -Script { Remove-WindowsFeature FS-SMB1 ; Restart-Computer -Force } -ComputerName ONE,TWO,THREE

This is efficiently doing what I need it do, thank you for the heads-up.

So here’s the next iteration of what I’m trying to do. At this point, GPO may just be the way to handle this project, but I’m trying to add an if/else statement to check the OS and act accordingly. When I run this, however, nothing happens. No errors, just displays my code and executes nothing.

<pre>Connect-VIServer -Server v-center.com
$servers = Get-VM (Get-Content -Path C:\SMBv1Disable\TestServers.csv)

foreach($server in $servers){
$OSVersion = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName).ProductName
If($OSVersion -eq "Windows Server 2008 R2 Enterprise"){
    Write-Host "OS is Windows Server 2008 R2"
    Invoke-Command -ScriptBlock {Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" SMB1 -Type DWORD -Value 0 -Force
        }}
ElseIf($OSVersion -eq "Windows Server 2012 R2 Datacenter"){
    Write-Host "OS is Windows Server 2012 R2"
    Invoke-Command -ScriptBlock {Remove-WindowsFeature FS-SMB1 ; Restart-Computer -Force} -ComputerName $servers
    }
}</pre>

Independent of the foreach loop the Invoke-Command is working without any problems. On the local machine, Get-ItemProperty is working fine, but no response whatsoever from my admin console.

Yeah. So… I think you need to walk through your code a bit.

$servers contains a collection of VM objects, not just a server name. Ergo, $server is a VM object, not a server name.

Notice where, in your Invoke-Command, you’re specifying $servers? The -ComputerName property doesn’t know what to do with a bunch of VM objects. I suspect you may want to do something like…

$Servers = $Servers | Select -Expand Name

So that $Servers only contains a computer name. Also, using $servers whilst in the middle of a ForEach loop, the way you’ve done, is going to send this to every server MANY TIMES. If you have 10 servers in $servers, then they’re each going to get hit 10 times, the way you’ve coded this.

Notice, too, that $OSVersion is only querying your local computer. I expect your local computer isn’t either of the operating systems you’ve allowed for, which is why nothing is happening. Your first Invoke-Command also targets your local computer.

Really sit and look and what you’re doing here. I’m not entirely clear what the end goal is, but you’ve got some twisted-up logic, I think. I suspect you may want to put most of this into a SINGLE Invoke-Command -ScriptBlock, right? That way, the If construct and everything are being evaluated by the remote machines?

Also run this in Debug mode in VS Code or the ISE. Stop every line or so, and look and see what your different variables contain. I think you’re making assumptions about what they contain, but I suspect those assumptions aren’t accurate, which is part of the problem.

Understood.

Sort of going through my own crash course in learning this, so apologies for the novice-level stuff in here. I’ll work through what I can. Thank you again.

The end goal is to be able to take a list of server names, determine whether they’re 2008R2 or 2012R2, and depending on the response either change the HKLM DWORD item or uninstall the service on 2012R2, if that makes sense.

OK. Well, you’ve got most of what you need. Let’s say $servers does, in fact, include server names, and not VM objects.

Invoke-Command -Verbose -Computer $servers -Script {
 $VerbosePreference = 'Continue'
 $OSVersion = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName).ProductName
 If ($OSVersion -eq "Windows Server 2008 R2 Enterprise") {
  Write-Verbose "OS is Windows Server 2008 R2"
  Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" SMB1 -Type DWORD -Value 0 -Force
 } ElseIf ($OSVersion -eq "Windows Server 2012 R2 Datacenter") {
  Write-Verbose "OS is Windows Server 2012 R2"
  Remove-WindowsFeature FS-SMB1
  Restart-Computer -Force
 }
}

Something like that. I may have missed a curly bracket. It does feel a bit like you’re hacking things together you’ve found online; that’s why I think it’s worth your time to really walk through that and understand what it’s doing, and why. That’s a great learning experience. Really pay attention to WHERE you’re asking code to run, since Invoke-Command changes that. And really walk through and ask yourself, “what does each variable contain at each point in the script’s execution?” If you don’t know, at some point - how could you find out?

And I’ll obviously recommend “Learn PowerShell in a Month of Lunches” as a way to start thinking through some of these patterns a bit less chaotically ;).

I’ll note, too, that Verbose output on remote machines can be a little tricky. I’m not sure what your intent is, but Write-Host won’t work across a remote connection. Write-Host is a terrible idea generally; forget you learned it and basically don’t use it unless you’re building some kind of text-based user-selection menu. Google “Write-Host Kill Puppy” sometime.

If Write-Verbose isn’t doing it for you, switch to Write-Output. While that’s not really “correct” either, it may get you what you’re looking for. So long as you know it’s really a mis-use of the command, and explore it a bit further when you’ve time.

Understood, and I actually have my copy right here with me of your book already. I’ll be sure to take your advice to heart. Thanks again.