Thursday, September 10, 2015

Turn off those Firewalls–Remotely–With PowerShell

Yet again I was given a task at work. This task was to turn off the Firewalls on hundreds of servers. So instead of logging into each one manually and changing the profiles on all the Firewall Profiles; Domain, Private and Public. I decided let us let PowerShell shine again.Now, they didn’t want the Firewall Services stopped, just the Profile states to be off. So after a little research and some help from some co-workers I put this script together.

Let’s talk about something thing through before I go into the script. The easiest way to turn off these Profile states is to run.

netsh advfirewall set allprofiles state off

This of course needs to be run locally on the machine. So I figured why not just use psexec to run the script. So I made a loop for the servers, looped it on the psexec and away it ran.. It ran VERY slowly. I had hundreds of these to run through. This would not work. So I decided to try and use PowerShell Invoke-Command. This required to have a session started using Enter-PSSession. Which of course gave this error.

 

Enter-PSSession : Connecting to remote server Server01 failed with the following error message : WinRM cannot process the request

 

Well that’s not going to work because I need to have the WinRM service installed. I don’t have the much time to get approvals to install the WinRM service on all these machines. So I remembered one of my coworkers had run scripts against a remote machine the other week using PowerShell. So I asked for his secret. The secret was  Invoke-WmiMethod. Here is the code simply put.

 

Invoke-WmiMethod -class Win32_process -name Create -ArgumentList (“CMD.EXE /C netsh advfirewall set allprofiles state off”) –ComputerName Server01  

 

This actually runs the script against the server with no Invoke-Command or other service to be installed. So I set off to write the full script and it is FAST. Sooo much faster than I was hoping for.

 

$command = "netsh advfirewall set allprofiles state off"

 

$cmd = "CMD.EXE /C " +$command

 

ForEach ($server in Get-Content "c:\scripts\computers.txt")

{

 

$theProc = Invoke-WmiMethod -class Win32_process -name Create -ArgumentList ($cmd) -ComputerName $server

If($theProc.ReturnValue -eq "0"){write-host  "$server - Completed successfully"}else{write-host  "$server - Completed UNsuccessfully"}

}

 

Now one of the downfalls of this is, you don’t know if the script worked. Of course you can go see on the server if the script did what it was supposed to do, but that is not what I am talking about. Basically you don’t get the output of the cmd. All you get is ReturnValue of 0 if the command went through correctly. Not that your script ran successfully. Just that your little cmd soldier has been sent into the field with the operations it was told to do successfully.

Wednesday, September 2, 2015

PowerShell – Hanging Get-WmiObject

 

I ran into something that I didn’t know was an issue with Get-WmiObject. I have been using Get-WmiObject for quite sometime so this was a shock to me. I had written a script to grab the description from servers and dump them to a gridview.

I wanted to run this on 1400 servers. Meaning know it was going to take awhile. When I ended up running it, it took too long, actually waaaay to long. The reason? Some of the Get-WmiObject calls were hanging for what ever reason. No problem I thought lets assign the timeout argument to the Get-WmiObject? Where is the timeout argument? ??? After a bit of searching and reading I learned there is no way to set a timeout to the Get-WmiObject commandlet.

After some more reading I learned that the best way to handing this is to setup the Get-WmiObject as a Job. The job can handle the Get-WmiObject process and if it takes longer than you think it should, kill the job and move on.

I don’t have the old way I was running the script but this is the new way.. I tried to comment it as much as I could.

 

    # try this and catch it if it fails

    try {

        # Set the try to stop if it error out

        $ErrorActionPreference = 'Stop'

       

              

        # start the Job and assign it to a variable.. 

 

        # -scriptBlock -- assign the -scriptblock argument to show what to run.. this is where the

        #     get-wmiobject is run. This is encapsualted in {}

 

        # param  -- set the param to pass along the server name from the -ArgumentList to the

        #    Get-WmiObject commandlet

 

        # -ArgumentList pass in the server information.

 

        # | Wait-Job  -Timeout 2 - This tells the job to wait for 2 seconds.

 

        $job = start-job -scriptBlock {param ([string]$server) Get-WmiObject -Class Win32_OperatingSystem -ComputerName $server } -ArgumentList $server | Wait-Job  -Timeout 2

 

        # This dumps out the job information to a variable to use later

        $results = $job | Receive-Job | select Description

       

        # If the Job state is not completed then it was and error, so write out an error for the

        #   try/catch to catch.

        if ($job.state -ne "Completed") {Write-Error "Error"}

       

        # This is the where we put the variables into an array.

        $List += [pscustomobject]@{

        'ComputerName' = $server

        'Description'  = $results.Description

        }

    }

 

    catch {

        #catch if there is an error and if there is write that there was one in the lsit array..

       $List += [pscustomobject]@{

       'ComputerName'         = $server

       'Description' = "Error"

 

       }

    }

 

 

Here is the full script.

 

 

 

#get the list of servers to scan from serverlist.txt

$serverlist = "C:\scripts\computers.txt"

$List = @()

$i = 0

$fileCount = 0

 

#get the count of servers for updates to the user.

 

$fileCount = (Get-Content $serverlist | Measure-Object).Count

 

 

#Loop the server list.

foreach ($server in Get-Content $serverlist) {

 

#increment the number of records proccessed

$i++

 

#Write to the screen whats happening.

Write-Progress -activity "Connecting to server $server" -status "Scanning: $i of $($fileCount)" -percentComplete (($i / $fileCount)  * 100)

 

 

 

 

 

 

 

    # try this and catch it if it fails

    try {

        # Set the try to stop if it error out

        $ErrorActionPreference = 'Stop'

       

              

        # start the Job and assign it to a variable.. 

        # -scriptBlock -- assign the -scriptblock argument to show what to run.. this is where the get-wmiobject is run. This is encapsualted in {}

        # param  -- set the param to pass along the server name from the -ArgumentList to the Get-WmiObject commandlet

        # -ArgumentList pass in the server information.

        # | Wait-Job  -Timeout 2 - This tells the job to wait for 2 seconds.

 

        $job = start-job -scriptBlock {param ([string]$server) Get-WmiObject -Class Win32_OperatingSystem -ComputerName $server } -ArgumentList $server | Wait-Job  -Timeout 2

 

        # This dumps out the job information to a variable to use later

        $results = $job | Receive-Job | select Description

       

        # If the Job state is not completed then it was and error, so write out an error for the try/catch to catch.

        if ($job.state -ne "Completed") {Write-Error "Error"}

       

        # This is the where we put the variables into an array.

        $List += [pscustomobject]@{

        'ComputerName' = $server

        'Description'  = $results.Description

        }

    }

 

    catch {

        #catch if there is an error and if there is write that there was one in the lsit array..

       $List += [pscustomobject]@{

       'ComputerName'         = $server

       'Description' = "Error"

 

       }

    }

 

 

}

 

$List  | Out-GridView