PowerShell remotely looping through web sites?

I have been tasked to create a PowerShell script that will connect to multiple IIS servers, get all web sites on them and check various statuses (i.e. Started or not, App Pools, etc.).

I have found a huge amount of posts about remotely using get-website which does not work.

Can anyone point me to how to accomplish this? All servers are 2008 R2.

April 26th, 2012 6:33pm

This might help. I just learned this my self:

Function DoIt ($Name,$i){
$objSites = [adsi]"IIS://$Name/W3SVC"
foreach ($objChild in $objSites.Psbase.children)
{ $SiteName = $objChild.servercomment }
$SiteName 
#$ws.Cells.Item($i, 9) = $SiteName
}

I am hoping to get the info into an excel spreadsheet, so you can ignore the $i and the #$ws items.
Free Windows Admin Tool Kit Click here and download it now
April 26th, 2012 10:04pm

Thanks for your reply OldDog1. I could use that if I could get ahold of the web site names as I am not always going to know them. I need to run the PowerShell from serverA to hit servers A through ZZZ and check any web sites on them. I cannot seem to be able to get an object of the remote website that I an use.

Good luck with your project!

April 27th, 2012 4:59pm

did you try that code? it should work, the ADSI connection is saying that you want IIS on Server $name and check the W3SVC (web service) for all sites.. and write out the site name with $SiteName  
Free Windows Admin Tool Kit Click here and download it now
April 29th, 2012 5:21pm

Thank you jrich. I've tested the code and it is good, I can now get each name of each site. How do I then test some settings of that site? I need to get the AppPool associated with it. I need to get the site status. ETC.  Get-WebSite $SiteName still fails.

April 30th, 2012 4:16pm

Still working on my project, here is what i have so far: (The Code function on this site does not handle comments well. Everything between <# and #> is a comment. Most of the WebAdministration Cmdlets do not seem to work on remote sites. This Function 'Get-WebSite' does. Thanks to Boe Prox!)

function Get-WebSite {
<#  
.SYNOPSIS  
    Retrieves information about a website.
.DESCRIPTION
    Retrieves information about a website.
.PARAMETER Url
    URL of the website to test access to.
.PARAMETER UseDefaultCredentials
    Use the currently authenticated user's credentials  
.PARAMETER Proxy
    Used to connect via a proxy
.PARAMETER TimeOut
    Timeout to connect to site, in milliseconds
.PARAMETER Credential
    Provide alternate credentials              
.NOTES  
    Name: Get-WebSite
    Author: Boe Prox
    DateCreated: 08Feb2011        
.EXAMPLE  
    Get-WebSite -url "http://www.bing.com"
    
Description
------------
Returns information about Bing.Com to include StatusCode and type of web server being used to host the site.
#> 
[cmdletbinding(
	DefaultParameterSetName = 'url',
	ConfirmImpact = 'low'
)]
    Param(
        [Parameter(
            Mandatory = $True,
            Position = 0,
            ParameterSetName = '',
            ValueFromPipeline = $True)]
            [string][ValidatePattern("^(http|https)\://*")]$Url,
        [Parameter(
            Position = 1,
            Mandatory = $False,
            ParameterSetName = 'defaultcred')]
            [switch]$UseDefaultCredentials,
        [Parameter(
            Mandatory = $False,
            ParameterSetName = '')]
            [string]$Proxy,
        [Parameter(
            Mandatory = $False,
            ParameterSetName = '')]
            [Int]$Timeout,
        [Parameter(
            Mandatory = $False,
            ParameterSetName = 'altcred')]
            [switch]$Credential            
                        
        )
Begin {     
    $psBoundParameters.GetEnumerator() | % { 
        Write-Verbose "Parameter: $_" 
        }
   
    #Create the initial WebRequest object using the given url
    Write-Verbose "Creating the web request object"        
    $webRequest = [net.WebRequest]::Create($url)
    
    #Use Proxy address if specified
    If ($PSBoundParameters.ContainsKey('Proxy')) {
        #Create Proxy Address for Web Request
        Write-Verbose "Creating proxy address and adding into Web Request"
        $webRequest.Proxy = New-Object -TypeName Net.WebProxy($proxy,$True)
        }
        
    #Set timeout
    If ($PSBoundParameters.ContainsKey('TimeOut')) {
        #Setting the timeout on web request
        Write-Verbose "Setting the timeout on web request"
        $webRequest.Timeout = $timeout
        }        
    
    #Determine if using Default Credentials
    If ($PSBoundParameters.ContainsKey('UseDefaultCredentials')) {
        #Set to True, otherwise remains False
        Write-Verbose "Using Default Credentials"
        $webrequest.UseDefaultCredentials = $True
        }
    #Determine if using Alternate Credentials
    If ($PSBoundParameters.ContainsKey('Credentials')) {
        #Prompt for alternate credentals
        Write-Verbose "Prompt for alternate credentials"
        $wc.Credential = (Get-Credential).GetNetworkCredential()
        }            
        
    #Set TimeStamp prior to attempting connection    
    $then = Get-Date
    }
Process {    
    Try {
        #Make connection to gather response from site
        $response = $webRequest.GetResponse()
        #If successful, get the date for comparison
        $now = Get-Date 
        
        #Generate report
        Write-Verbose "Generating report from website connection and response"  
        $report = @{
            URL = $Url
            StatusCode = $response.Statuscode -as [int]
            StatusDescription = $response.StatusDescription
            ResponseTime = "$(($now - $then).totalseconds)"
            WebServer = $response.Server
            Size = $response.contentlength
            } 
        }
    Catch {
        #Get timestamp of failed attempt
        $now = Get-Date
        #Put the current error into a variable for later use
        $errorstring = "$($error[0])"
        
        #Generate report
        $report = @{
            URL = $Url
            StatusCode = ([regex]::Match($errorstring,"\b\d{3}\b")).value
            StatusDescription = (($errorstring.split('\)')[2]).split('.\')[0]).Trim()
            ResponseTime = "$(($now - $then).totalseconds)" 
            WebServer = $response.Server
            Size = $response.contentlength
            }   
        }
    }
End {        
    #Display Report    
    New-Object PSObject -property $report  
    }    
}  
$ErrorActionPreference = "SilentlyCOntinue"
$Servers = gc servers.txt
$count = $Servers.count
$x = 1
foreach ($server in $Servers) {
Write-Host "$x of $count"
$iis = [ADSI]"IIS://$server/W3SVC"
$site = $iis.psbase.children | where {$_.schemaClassName -eq "IIsWebServer"
foreach ($item in $site) {
$sc = $_.ServerComment.ToString()
	Get-WebSite -url "http://$server/$sc"
	}
	Write-Host $sc
	}
	$x++
} 

  • Edited by OldDog1 Monday, April 30, 2012 8:31 PM
Free Windows Admin Tool Kit Click here and download it now
April 30th, 2012 8:00pm

I have been working on a similar script, and I've gotten this far:

	$names = Invoke-Sqlcmd -Query "SELECT Name FROM Servers" -ServerInstance "Server" -Database "DB"
	

    ForEach ($row in $names) {
		
		$name = $row.ItemArray[0]
		$website = ""
		$state = ""
		$binding = ""
		$path = ""
		
        If (Test-Connection -comp $name -count 1 -Quiet) {
            
			# command for remote box
			cls
			$command = {
			    Import-Module 'WebAdministration'
			    dir "IIS:\Sites"
			}
			
			#connection for remote box
			#$session = New-PSSession -ComputerName $name
			
			$sites = Invoke-Command -ComputerName $name -ScriptBlock $command
			
            #$sites = dir "IIS:\Sites\"
            ## $children = $sites.children
            ForEach ($child in $sites) {
            
				$website = $child.name
				$state = $child.state
	    		ForEach ($b in $child.bindings.Collection) {
					$binding += "[" + $b.protocol + "," + $b.bindingInformation + "]"
				}
				$path = $child.physicalPath
				
				$temp = "" | Select Server, WebSite, State, Bindings, Path
	            $temp.Server = $name
	            $temp.WebSite = $child.name
	            $temp.State = $child.state
	            $temp.Bindings = $binding
	            $temp.Path = $child.physicalPath
	            $report += $temp
				
				$sql_insert = "INSERT INTO [WebApps] ([Server],[WebSite],[State],[Bindings],[Path])
	 			VALUES ('$name', '$webSite', '$state', '$binding', '$path')"
				
				Invoke-Sqlcmd -Query $sql_insert -ServerInstance "Server" -Database "DB"
            }
        }
		else
		{
			$sql_insert = "INSERT INTO [WebApps] ([Server],[WebSite])
 			VALUES ('$name', 'none found')"
			
			Invoke-Sqlcmd -Query $sql_insert -ServerInstance "Server" -Database "DB"
		}

It works great locally but I'm getting one row, then a COM Exception when running against remote web servers. Maybe the code will help you though.


April 30th, 2012 8:25pm

"COM Exception when running against remote web servers"

That is probably something to do with SQL. Powershell uses a com object to connecto SQL (And every other MS product.)

Try it without SQL, just pull the servers from a Text file and write the output to another Text file and see if that works.

function Log {
 param([string]$file,[string]$data)
 Out-File $filename -append -noclobber -inputobject $text -encoding ASCII
}

$Servers = gc servers.txt

Foreach ($item in $servers) {

# your code

$text = $temp.Server, $temp.WebSite, $temp.State, $temp.Bindings, $temp.Path

Log Sitelog.txt $text

}



  • Edited by OldDog1 Monday, April 30, 2012 10:11 PM
Free Windows Admin Tool Kit Click here and download it now
April 30th, 2012 10:10pm

OldDog: Thanks for the suggestion, but the error occurs for me in the remote invoke:

			# command for remote box
			cls
			$command = {
			    Import-Module 'WebAdministration'
			    dir "IIS:\Sites"
			}
						
			$sites = Invoke-Command -ComputerName $name -ScriptBlock $command

JaeEmAre: How does  Get-WebSite fail? If it returns all sites for the server you might try appending an '*' to the site name. I don't think it returns app pool though...

May 1st, 2012 4:05pm

Try this, I don't belive you need to remote with GWMI. Make sure to run it under your Admin credentials.

Get-WmiObject -Namespace root\webadministration `
-computername $computer -class site -authentication 6 |
format-table -property name

Free Windows Admin Tool Kit Click here and download it now
May 1st, 2012 9:10pm

OldDog, there are multiple sites on the iternet that explain Get-WebSite command has a 'bug' and will fail if called remotely. Since we have not been able to find a solution to our specific problem, we have defined a manual workaround and will wait until PowerShell fixes this 'bug'.
May 9th, 2012 12:27pm

I know this is a late post but if your machines are running IIS 7.0 or later, you cabn load the Microsoft.Web.Administration dll and use it to query sites using

[Reflection.Assembly]::LoadFile('C:\Windows\System32\inetsrv\Microsoft.Web.Administration.dll'

  • Proposed as answer by kinyubi 15 hours 3 minutes ago
Free Windows Admin Tool Kit Click here and download it now
January 2nd, 2013 4:41pm

I know this is a late post but if your machines are running IIS 7.0 or later, you cabn load the Microsoft.Web.Administration dll and use it to query sites using

[Reflection.Assembly]::LoadFile('C:\Windows\System32\inetsrv\Microsoft.Web.Administration.dll'

  • Proposed as answer by kinyubi Thursday, March 05, 2015 12:17 AM
January 2nd, 2013 4:41pm

A workaround I successfully use is to do (Get-WebSite).Name to return the names of all the web sites. Then I can use (Get-WebSite <sitename>).State to get the status of each machine. It isn't pretty, but by avoiding the non-serializable ftpserver field which causes the exception, we are able to query the fields that we need one at a time.
Free Windows Admin Tool Kit Click here and download it now
March 4th, 2015 7:23pm

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics