Friday, October 16, 2015

Download MP3s with Powershell

So I found a site with some MP3s that I wanted but didn't want to download but didn't want to right click.. Save as.. blah blah.. so after some searching and modifing some scripts i made this.


Change the 2 variables $theurl ; the website URL, and $storagedir ; the place you want to store them and watch it go. It has some thing built into it that takes care of url variables, direct url, and some url encoding issues. I tried to comment it as much as I could.


# =================================================================================
# =                                                                               =
# =                       Variables to change. Begin                              =
# =                                                                               =
# =================================================================================


$theurl = "http://www.sky-animes.com/music/LQ/7185"
$storagedir = "C:\source\mp3\FullMetal"


# =================================================================================
# =                                                                               =
# =                      Variables to change. End                                 =
# =                                                                               =
# =================================================================================



$response = Invoke-WebRequest -Uri $theurl  -UseBasicParsing -Verbose
$links = $response.Links
$imgurlinks = @()
$webclient = New-Object System.Net.WebClient

Write-Output "-------------------------------------------------------------------------"
Write-Output "-------------------------------------------------------------------------"

$response.Links.href


Write-Output "-------------------------------------------------------------------------"
Write-Output "-------------------------------------------------------------------------"



md $storagedir -ErrorAction SilentlyContinue

ForEach ($link in $links){ #loop through all the hrefs on the page with a ForEach

    $href = $link.href #put the href to Variable.


    if ($href -like "*.mp3" ) { #Look for only mp3's hrefs
    
        $filename = $href.Split("/")[-1]
        #Write-Output "Href: " $href
        #Write-Output "FileName: " $filename
       
        if ($filename -like "*?*"){ #does the filename have a query?
            $filename2 = $filename.Split("?")[0] #make the filename2 the filename without any queries
        } #End If
        else{
            $filename2 = $filename #since no queries were found just make the filename2 the name of the orginal filename
        } # End Else
       


        if($href -contains "http://"){ #does the href have a direct path to the file? if not lets try and add in the orignating url to it so it can find it.
            $newhref = $href #No need to mess with the href.. just pass it to the newhref and move along.
        } #End http:// if
       
        else{
            $newhref = $theurl + "\" + $href #put together theUrl and the Href to make a path to try.
        } #End http:// else

       
        $newFile = $filename2.Replace("%20","-") # Replace %20 url encoded spaces with an Underscore for the new mp3 file.
        $newFile = $newFile.Replace("&","-and-") # Replace & url encoded spaces with an "-and-" for the new mp3 file.
        $newFile = $newFile.Replace("---","-") # Replace & url encoded spaces with an "-and-" for the new mp3 file.
      
     

        $newFile = "$storagedir\$newFile" #put together the path and the file of where to put the file when downloaded.

        $newhref = $newhref.Replace("&","&")


        Write-Output "Href Location: " $newhref
        Write-Output "Download Location: " $newFile

        $webclient.DownloadFile($newhref,$newFile) #download the file using the href and the file we just put together above.

    } #end MP3 If.
} #end ForEach


       
explorer $storagedir #open the output folder








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