This may be a philosophical question but I would like to know how the following 2 items differ, from a speed and efficiency perspective. In PowerShell I have 2 objects that look like this:
$ObjectA = #()
1..10 | foreach-object{
$obj = New-Object System.Object
$obj | Add-Member -Type NoteProperty -Name index -Value $_
$ObjectA += $obj
}
$ObjectB = #()
5..15 | foreach-0bject{
$obj = New-Object System.Object
$obj | Add-Member -Type NoteProperty -Name index -Value $_
$ObjectB += $obj
}
Now, I want to get the objects that exist in both. I can do it 1 of 2 ways.
Solution 1:
$ObjectA | foreach-object{
$ind = $_
$matching = $ObjectB | where {$_ -eq $ind}
if (![string]::IsNullOrEmpty($matching)){
##do stuff with the match
}
}
Solution 2:
$matches = Compare-Object $ObjectA $ObjectB -Property index | where {$_.SideIndicator -eq '=='} -PassThru
$matches | foreach-object {
##do stuff with the matches.
}
My question is, when my array of objects gets very large (30K+) which one is going to be a better solution from a performance perspective? I don't know how the Compare-Object cmdlet works internally so I really don't know. Or does it not matter?
Thanks in advance.
As #Knows Not Much has pointed out, Compare-Object usually offers better performance than iterating the collection and comparing objects yourself. But the other answer fails to use the -ExcludeDifferent parameter and instead iterates over the Compare-Object output. This means doing many useless string comparisons for the SideIndicator property. For optimal performance, and simpler code, just use -IncludeEqual and -ExcludeDifferent:
$ObjectA = #()
1..10000 | %{
$obj = New-Object System.Object
$obj | Add-Member -Type NoteProperty -Name index -Value $_
$ObjectA += $obj
}
$ObjectB = #()
1000..7000 | %{
$obj = New-Object System.Object
$obj | Add-Member -Type NoteProperty -Name index -Value $_
$ObjectB += $obj
}
# Iterating over the result of Compare-Object takes 2.6 seconds.
Measure-Command { $matches_where_eq = Compare-Object $ObjectA $ObjectB -Property index -IncludeEqual | where {$_.SideIndicator -eq '=='} ; echo $matches_where_eq.count }
# Using -IncludeEqual and -ExcludeDifferent takes 2.1 seconds (80% of previous).
Measure-Command { $matches_ed_ie = Compare-Object $ObjectA $ObjectB -Property index -ExcludeDifferent -IncludeEqual; echo $matches_ed_ie.Count }
Even if you take a dataset of size 10000 you can easily see that compare object is way way faster.
I modified your code to make it work on powershell 3.0
cls
$ObjectA = #()
1..10000 | %{
$obj = New-Object System.Object
$obj | Add-Member -Type NoteProperty -Name index -Value $_
$ObjectA += $obj
}
$ObjectB = #()
1000..7000 | %{
$obj = New-Object System.Object
$obj | Add-Member -Type NoteProperty -Name index -Value $_
$ObjectB += $obj
}
Measure-Command {
$count = 0
$matches = Compare-Object $ObjectA $ObjectB -Property index -IncludeEqual | where {$_.SideIndicator -eq '=='}
}
echo $matches.length
echo $matches.Count
Measure-Command {
$count = 0
$ObjectA | %{
$ind = $_
$matching = $ObjectB | where {$_.Index -eq $ind.Index}
if (![string]::IsNullOrEmpty($matching)){
$count = $count + 1
}
}
echo $count
}
The compare-object returns in less than 5 seconds .... but the other approach just gets stuck forever.
This will build a regex search out of one array and then perform a regex match against the other array.
Solution 3:
[regex]$RegMatch = '(' + (($ObjectA |foreach {[regex]::escape($_)}) –join "|") + ')'
$ObjectB -match $RegMatch
Might want to throw some logic at that to build the regex out of the smaller set of data and then run the larger set against it to speed things up, but I'm pretty sure this would be fastest.
Related
Need help with speeding up the script as it is going to be run against 10-20K servers. Currently tested on 4K servers and took almost 6 hours. Tried running it asjob (One parent job and 4000 childjobs, it runs fine and a lot faster but the parent job gets stuck in "running" state forever. It is because one of the childjobs stays in "Notstarted" state. Not sure how to fix that.
######################################################################################
$today = Get-Date
$path = (Get-Location).Path
$path += "\"
$date = Get-Date -uformat "%Y%m%d%H%M"
$Inputfile = $path + "Computers.txt"
$outfile = $path + "Report\" + "Certificate_Report_$date.csv"
$transcript = $path + "Logs\" + "Transcript_$date.log"
Start-Transcript $transcript
$computers = gc $Inputfile
Foreach ($c in $computers){
$cert = Invoke-Command -ComputerName $c -ScriptBlock{Get-ChildItem Cert:\localmachine -Recurse} -ErrorVariable issue -ErrorAction Continue
If ($issue){
$Connection = $Error[0].FullyQualifiedErrorId
$obj1 = New-Object Psobject
$Obj1 | Add-Member -MemberType NoteProperty -Name Server -Value $c
$Obj1 | Add-Member -MemberType NoteProperty -Name Serverconnection -Value $Connection
#$report += $obj1
$obj1 | Export-Csv $outfile -NoTypeInformation -Append -force
}
Else{$Connection = "Success"}
Foreach ($cer in $cert){
if($cer.Thumbprint -ne $null){
$obj = New-Object Psobject
$Obj | Add-Member -MemberType NoteProperty -Name Server -Value $c
$Obj | Add-Member -MemberType NoteProperty -Name Serverconnection -Value $Connection
$Obj | Add-Member -MemberType NoteProperty -Name PsParentpath -Value $Cer.PsParentpath
$Obj | Add-Member -MemberType NoteProperty -Name Subject -Value $Cer.Subject
$Obj | Add-Member -MemberType NoteProperty -Name Thumbprint -Value $Cer.Thumbprint
$Obj | Add-Member -MemberType NoteProperty -Name DnsNamelist -Value $Cer.DNSNamelist
$Obj | Add-Member -MemberType NoteProperty -Name FriendlyName -Value $Cer.FriendlyName
$Obj | Add-Member -MemberType NoteProperty -Name Issuer -Value $Cer.Issuer
$Obj | Add-Member -MemberType NoteProperty -Name Valid_From -Value $Cer.NotBefore
$Obj | Add-Member -MemberType NoteProperty -Name Expiration_Date -Value $Cer.NotAfter
if ($cer.NotAfter -lt $today){
$status = "Expired"
}
Else{$status = "Valid"}
$Obj | Add-Member -MemberType NoteProperty -Name Cert_Status -Value $status
$obj | Export-Csv $outfile -NoTypeInformation -Append
}
}
}
Stop-Transcript
The only obvious optimization that comes to mind (without parallelizing the remote queries) is to avoid | Add-Member and use [pscustomobject] syntax for the result objects:
$today = Get-Date
$date = Get-Date -uformat "%Y%m%d%H%M"
$Inputfile = (Resolve-Path "Computers.txt").Path
$outfile = (Resolve-Path "Report\Certificate_Report_$date.csv").Path
$transcript = (Resolve-Path "Logs\Transcript_$date.log").Path
Start-Transcript $transcript
$computers = gc $Inputfile
Foreach ($c in $computers) {
$cert = Invoke-Command -ComputerName $c -ScriptBlock { Get-ChildItem Cert:\localmachine -Recurse } -ErrorVariable issue -ErrorAction Continue
If ($issue) {
$Connection = $Error[0].FullyQualifiedErrorId
$obj1 = [pscustomobject]#{
Server = $c
Serverconnection = $Connection
} | Export-Csv $outfile -NoTypeInformation -Append -force
}
Else {
$Connection = "Success"
}
Foreach ($cer in $cert) {
if ($null -ne $cer.Thumbprint) {
[pscustomobject]#{
Server = $c
Serverconnection = $Connection
PsParentpath = $Cer.PsParentpath
Subject = $Cer.Subject
Thumbprint = $Cer.Thumbprint
DnsNamelist = $Cer.DNSNamelist
FriendlyName = $Cer.FriendlyName
Issuer = $Cer.Issuer
Valid_From = $Cer.NotBefore
Expiration_Date = $Cer.NotAfter
Cert_Status = if ($cer.NotAfter -lt $today) { "Expired" } else { "Valid" }
} | Export-Csv $outfile -NoTypeInformation -Append
}
}
}
Stop-Transcript
As Lee_Dailey mentions, you might also want to try offloading parallel execution of the remoting commands to Invoke-Command completely, by passing it all the computer names up front:
Invoke-Command -ComputerName $computers -ScriptBlock {Get-ChildItem Cert:\localmachine -Recurse} -ErrorAction Continue |For-EachObject {
# Process the results here
}
If you want help troubleshooting using background jobs, please post the code with which you have problems :)
Script posted in my question took almost 6 hours to do the same thing this modified version does in under 30 mins. Grateful for the help on this post. Final script below:
$today = Get-Date
$date = Get-Date -uformat "%Y%m%d%H%M"
$Inputfile = gc (Resolve-Path "Computers.txt").Path
$outfile = (Resolve-Path "Report\").Path + "Certificate_Report_$date.csv"
$transcript = (Resolve-Path "Logs\").Path + "Transcript_$date.log"
$failed = "Couldn't retrieve Data"
$IC_ScriptBlock = {Get-ChildItem Cert:\localmachine -Recurse}
$IC_Params = #{
ComputerName = $Inputfile
ScriptBlock = $IC_ScriptBlock
ErrorAction = 'SilentlyContinue'
}
$responding = Invoke-Command #IC_Params|ForEach-Object {
if ($null -ne $_.Thumbprint) {
[pscustomobject]#{
Server = $_.pscomputername
PsParentpath = $_.PsParentpath
Subject = $_.Subject
Thumbprint = $_.Thumbprint
DnsNamelist = $_.DNSNamelist
FriendlyName = $_.FriendlyName
Issuer = $_.Issuer
Valid_From = $_.NotBefore
Expiration_Date = $_.NotAfter
Cert_Status = if ($_.NotAfter -lt $today) { "Expired" } else { "Valid" }
} | Export-Csv $outfile -NoTypeInformation -Append
}
}
$not_responding = $Inputfile.Where({
$_ -notin $responding.Pscomputername -and "[$_]" -notin $responding.pscomputername
}).
foreach({
[pscustomobject]#{
Server = $_
PsParentpath = $failed
Subject = $failed
Thumbprint = $failed
DnsNamelist = $failed
FriendlyName = $failed
Issuer = $failed
Valid_From = $failed
Expiration_Date = $failed
Cert_Status = "NA"
} | Export-Csv $outfile -Append -NoTypeInformation
})
I am very new to using powershell and trying to execute a script that scans the entire network for all .mdb and .accdb files, for example, and generates a spreadsheet containing the data on them that I process elsewhere.
I put the sensistive data that I didnt want to provide in ()s
Here is my code:
#single threaded
import-module activedirectory
$arr = #()
$computers = Get-ADComputer -filter 'name -like "(employee computers)*"' | Select -Exp Name
foreach ($computer in $computers) {
Write-Host "Scanning" $computer "..."
gci \\$computer\c$\* -Include *.mdb, *.accdb -Recurse | ? {$_.PSIsContainer -eq $False} | % {
$obj = New-Object PSObject
$obj | Add-Member NoteProperty Directory $_.DirectoryName
$obj | Add-Member NoteProperty Name $_.Name
$obj | Add-Member NoteProperty FullName $_.FullName
$obj | Add-Member NoteProperty Size $_.Length
$obj | Add-Member NoteProperty CreationTime $_.CreationTime
$obj | Add-Member NoteProperty LastWriteTime $_.LastWriteTime
$arr += $obj
Write-Host "Scanning..."
}}
$arr | Export-CSV -notypeinformation '(path)\EmployeeDBs.csv'
This has been working pretty well so far, but for certain machines and/or directories on some machines I am receiving the following error messages:
Get-ChildItem : The specified network name is no longer available
[Get-ChildItem], IOException
+ FullyQualifiedErrorId : DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand
and
Get-ChildItem : An object at the specified path \\(employee computer)\c$ does not exist.
[Get-ChildItem], IOException
+ FullyQualifiedErrorId : ItemDoesNotExist,Microsoft.PowerShell.Commands.GetChildItemCommand
I have been googling around but havent had much luck in understanding these error messages. Would somebody be able to explain what the issues are?
I am thinking (hoping) that they are permissions problems because I am testing the scripts on my personal machine before I run them from the admin machine
Any insight is greatly appreciated!
EDIT: below is my edited code for asynchronous execution:
import-module activedirectory
$computers = Get-ADComputer -filter 'name -like "wa-150*"' | Select -Exp Name
Get-job | Remove-Job -Force
Remove-Item -path (path)\EmployeeDBs.txt
foreach ($computer in $computers) {
$scriptBlock = {gci \\$($args[0])\c$\Users\z*\Desktop\* -Include *.mdb, *.accdb -Recurse | ? {$_.PSIsContainer -eq $False} | % {
$obj = New-Object PSObject
$obj | Add-Member NoteProperty Directory $_.DirectoryName
$obj | Add-Member NoteProperty Name $_.Name
$obj | Add-Member NoteProperty Size $_.Length
$obj | Add-Member NoteProperty CreationTime $_.CreationTime
$obj | Add-Member NoteProperty LastWriteTime $_.LastWriteTime
Write-Output -InputObject $obj
}
}
while ((Get-Job -State Running).Count -ge 20) {
Write-Host "Full - Waiting ... "
Start-Sleep -Seconds 5;
}
Start-Job -name $computer -ScriptBlock $scriptBlock -ArgumentList $computer
#Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $computer
}
Get-Job | Wait-Job | Receive-Job | Out-File -Append -FilePath '(path)\EmployeeDBs.txt'
Write-Host "Done"
I am having issues finding the correct syntax I need to filter my results on only listing directories with a file count of above a specified amount (600 in my case).
This is my code so far;
$server_dir= "D:\backup"
$export_dir= "C:\support\spcount.txt"
if($server_dir)
{
$folders = Get-ChildItem $server_dir
$output = #()
foreach($folder in $folders)
{
$fname = $folder.Name
$fpath = $folder.FullName
$fcount = Get-ChildItem $fpath | Measure-Object | Select-Object -Expand Count
$obj = New-Object psobject -Property #{FolderName = $fname; FileCount = $fcount} | Format-List;
$output += $obj
}
#Output
$output | Tee-Object -FilePath $export_dir | Format-list FileCount
}
And I am getting positive results with this, it is listing all Child Items within the backup dir however I need to filter this to only display and out too text format IF the directory contains 600 or more files.
Can anybody help me please?
I am fairly new too powershell so please pull me up if this code is not the greatest, I am forever wanting too learn.
Thanks!
I think I found the issue. It's that Format-List statement at the end of your object creation statement. It pipes the newly created object through Format-List, and thus transforms it into something else.
$obj = New-Object psobject -Property #{FolderName = $fname; FileCount = $fcount} | Format-List
So if you remove that last bit, you'll get the object you expect
$obj = New-Object psobject -Property #{FolderName = $fname; FileCount = $fcount}
So when you use the where statement to filter, you'll actually have a FileCount property to filter on.
I detected it by running the $output through Get-Member which showed me it wasn't the object with the expected properties.
So basically, here's your code, including fixes:
if($server_dir)
{
# *** Added the -directory flag, cause we don't need those pesky files ***
$folders = Get-ChildItem $server_dir -directory
$output = #()
foreach($folder in $folders)
{
$fname = $folder.Name
$fpath = $folder.FullName
$fcount = Get-ChildItem $fpath | Measure-Object | Select-Object -Expand Count
# *** Format-List was dropped here to avoid losing the objects ***
$obj = New-Object psobject -Property #{FolderName = $fname; FileCount = $fcount}
$output += $obj
}
# *** And now the filter and we're done ***
$output | where -Property FileCount -ge 600 | Tee-Object -FilePath $export_dir | Format-list FileCount
}
Note also the -directory to get only folders with get-childitem, and the -ge 600 (greater than or equal) instead of -gt 599 which is just a bit more obvious.
Remember that the Format-* statements actually transform the data passed through them. So you should only use those at the end of the pipeline to show data on screen or dump it to a file.
Don't use it to transform the data you still want to work with later on.
So in short you could do something like this to get that information.
Get-ChildItem C:\temp -Directory |
Select Name,#{Label="Count";Expression={(Get-Childitem $_ -file -Recurse).Count}} |
Where-Object{$_.Count -lt 10}
Let see if we can incorporate that in your code. Your if statement is also kind of pointless. Your variable contains a non-null \ non-zerolength string so it will always be True. You want it to work if the directory exists I imagine.
$server_dir= "D:\backup"
$export_dir= "C:\support\spcount.txt"
if(Test-Path $server_dir){
Get-ChildItem C:\temp -Directory |
Select Name,#{Label="Count";Expression={(Get-Childitem $_ -file -Recurse).Count}} |
Where-Object{$_.Count -lt 10} |
ConvertTo-Csv | Tee -File $export_dir | ConvertFrom-Csv
} Else {
Write-Warning "$server_dir does not exist."
}
Just working on getting this to file and screen with Tee just a moment.
I see 2 ways to do this.
Filter it in your output like this:
$output | where -property FileCount -gt 599 | # ... your code to write to the output
Or not store it in the output array if it doesn't match the condition:
if ($fcount -gt 599) {
$obj = New-Object psobject -Property #{FolderName = $fname; FileCount = $fcount} | Format-List;
$output += obj
}
I have some directories structure like below, where the last folder name will be the current date changing everyday, as below:
D:\data\Backup\WINDOWSDATA\18-03-2015
D:\data\Backup\LINUXDATA\18-03-2015
D:\data\Backup\UBUNTUDATA\18-03-2015
Under each date folder (18-03-2015) there will be maximum four .dat files having different time stamps in their names, as given below:
BKP_DS_FETCHER_6AM.dat
BKP_DS_FETCHER_10AM.dat
BKP_DS_FETCHER_2PM.dat
BKP_DS_FETCHER_6PM.dat
I am trying to generate following results in an output.txt file on the basis of simple logic that is if .dat file is there for a particular time, there should come Success otherwise Failed in output.txt for example as below:
output.txt:
FOLDER_NAME 6AM 10AM 2PM 6PM
WINDOWSDATA Success Failed Success Success
LINUXDATA Success Success Failed Success
UBUNTUDATA Failed Success Success Success
Please can somebody help me show the way to achieve it (in Batch or Powershell) ?
Push-Location D:\data\Backup
$todayDir = Get-Item (get-date -Format 'dd-MM-yyyy') -ErrorAction SilentlyContinue
if($todayDir)
{
Push-Location $todayDir
$logResult = #{}
dir -Directory | foreach {
$logResult.($_.Name) = $_.Name | Get-ChildItem -Filter '*.dat' |
foreach {
$isMatch = $_.Name -match 'BKP_DS_FETCHER_(?<hour>.*).dat$'
if($isMatch) {
$Matches.hour
}
}
}
$hourProperties = ($logResult.Values | foreach {$_}) | Sort-Object | Get-Unique
$logResult.Keys | foreach {
$obj = New-Object pscustomobject
$obj | Add-Member -MemberType NoteProperty -Name 'FOLDER_NAME' -Value $_
foreach($p in $hourProperties) {
$v=$null
if($logResult[$_] -contains $p) {
$v = 'Success'
}
else {
$v = 'Failed'
}
$obj | Add-Member -MemberType NoteProperty -Name $p -Value $v
}
$obj
} | Format-Table
Pop-Location
}
Pop-Location
Here is a way to do it in PowerShell:
$date = Get-Date -Format 'yyyy-MM-dd'
$basePath = 'D:\data\Backup\'
$outputPath = 'D:\data\Backup\output_' + $date + '.txt'
$baseFileName = 'BKP_DS_FETCHER_[HOUR].dat'
$hours = #( '6AM', '10AM', '2PM', '6PM' )
function Check-Folder( $folderName )
{
$resultLine = New-Object -TypeName System.Object
$resultLine | Add-Member -MemberType NoteProperty -Name 'FOLDER_NAME' -Value $folderName
foreach( $h in $script:hours )
{
$path = $script:basePath + $folderName + '\' + $script:date + '\' + $script:baseFileName.Replace('[HOUR]',$h)
#Write-Host $path
if( Test-Path -Path $path )
{
$resultLine | Add-Member -MemberType NoteProperty -Name $h -Value 'Success'
}
else
{
$resultLine | Add-Member -MemberType NoteProperty -Name $h -Value 'Failed'
}
}
return $resultLine
}
$results = #()
$results += Check-Folder 'WINDOWSDATA'
$results += Check-Folder 'LINUXDATA'
$results += Check-Folder 'UBUNTUDATA'
$results | Format-Table -AutoSize | Out-File -FilePath $outputPath
Output will look like this:
FOLDER_NAME 6AM 10AM 2PM 6PM
----------- --- ---- --- ---
WINDOWSDATA Success Failed Success Success
LINUXDATA Failed Failed Failed Failed
UBUNTUDATA Failed Success Failed Failed
I'm looking for a way to go through all binding settings already configured in my IIS.
Im using this to work with the IIS in Powershell:
Import-Module WebAdministration
So far I was able to get the main required information i want:
$Websites = Get-ChildItem IIS:\Sites
My array $Websites is filled correctly and with the following command...
$Websites[2]
..I recieve this result:
Name ID State Physical Path Bindings
---- -- ----- ------------- --------------
WebPage3 5 D:\Web\Page3 http *:80:WebPage3
https *:443:WebPage3
Now here's the part I having a hard time with:
I want to check if the binding is correct. In order to do that I only need the binding. I tried:
foreach ($site in $Websites)
{
$site = $Websites[0]
$site | select-string "http"
}
Debugging that code shows me that $Site doesn't contain what I expected: "Microsoft.IIs.PowerShell.Framework.ConfigurationElement". I currently have no clue how to explicitly get to the binding information in order to to something like this (inside the foreach loop):
if ($site.name -eq "WebPage3" -and $site.Port -eq "80") {
#website is ok
}
else {
#remove all current binding
#add correct binding
}
Thank you for your help!
Solution:
Import-Module WebAdministration
$Websites = Get-ChildItem IIS:\Sites
foreach ($Site in $Websites) {
$Binding = $Site.bindings
[string]$BindingInfo = $Binding.Collection
[string]$IP = $BindingInfo.SubString($BindingInfo.IndexOf(" "),$BindingInfo.IndexOf(":")-$BindingInfo.IndexOf(" "))
[string]$Port = $BindingInfo.SubString($BindingInfo.IndexOf(":")+1,$BindingInfo.LastIndexOf(":")-$BindingInfo.IndexOf(":")-1)
Write-Host "Binding info for" $Site.name " - IP:"$IP", Port:"$Port
if ($Site.enabledProtocols -eq "http") {
#DO CHECKS HERE
}
elseif($site.enabledProtocols -eq "https") {
#DO CHECKS HERE
}
}
I don't know exactly what you are trying to do, but I will try. I see that you reference $Websites[2] which is webPage3.
You can do it like this:
$site = $websites | Where-object { $_.Name -eq 'WebPage3' }
Then when you look at $site.Bindings, you will realize that you need the Collection member:
$site.bindings.Collection
On my machine this returns this:
protocol bindingInformation
-------- ------------------
http *:80:
net.tcp 808:*
net.pipe *
net.msmq localhost
msmq.formatname localhost
https *:443:
And the test might then look like this:
$is80 = [bool]($site.bindings.Collection | ? { $_.bindingInformation -eq '*:80:' })
if ($is80) {
#website is ok
} else {
#remove all current binding
#add correct binding
}
I sent content of Collection to pipeline and filtere only objects where property bindingInformation is equal to desired value (change it). Then I cast it to [bool]. This will return $true if there is desired item, $false otherwise.
I found that if there were multiple bindings on a site then if I needed to script access to individual parts of the bindings otherwise I only got the first binding. To get them all I needed the script to be extended as below:
Import-Module WebAdministration
$Websites = Get-ChildItem IIS:\Sites
foreach ($Site in $Websites) {
$Binding = $Site.bindings
[string]$BindingInfo = $Binding.Collection
[string[]]$Bindings = $BindingInfo.Split(" ")
$i = 0
$header = ""
Do{
Write-Output ("Site :- " + $Site.name + " <" + $Site.id +">")
Write-Output ("Protocol:- " + $Bindings[($i)])
[string[]]$Bindings2 = $Bindings[($i+1)].Split(":")
Write-Output ("IP :- " + $Bindings2[0])
Write-Output ("Port :- " + $Bindings2[1])
Write-Output ("Header :- " + $Bindings2[2])
$i=$i+2
} while ($i -lt ($bindings.count))
}
I had something similar to the last answer, but this corrects to HTTPS sites and adds a bit more information that is useful.
Import-Module WebAdministration
$hostname = hostname
$Websites = Get-ChildItem IIS:\Sites
$date = (Get-Date).ToString('MMddyyyy')
foreach ($Site in $Websites) {
$Binding = $Site.bindings
[string]$BindingInfo = $Binding.Collection
[string[]]$Bindings = $BindingInfo.Split(" ")#[0]
$i = 0
$status = $site.state
$path = $site.PhysicalPath
$fullName = $site.name
$state = ($site.name -split "-")[0]
$Collection = ($site.name -split "-")[1]
$status = $site.State
$anon = get-WebConfigurationProperty -Filter /system.webServer/security/authentication/AnonymousAuthentication -Name Enabled -PSPath IIS:\sites -Location $site.name | select-object Value
$basic = get-WebConfigurationProperty -Filter /system.webServer/security/authentication/BasicAuthentication -Name Enabled -PSPath IIS:\ -location $site.name | select-object Value
Do{
if( $Bindings[($i)] -notlike "sslFlags=*"){
[string[]]$Bindings2 = $Bindings[($i+1)].Split(":")
$obj = New-Object PSObject
$obj | Add-Member Date $Date
$obj | Add-Member Host $hostname
$obj | Add-Member State $state
$obj | Add-Member Collection $Collection
$obj | Add-Member SiteName $Site.name
$obj | Add-Member SiteID $site.id
$obj | Add-member Path $site.physicalPath
$obj | Add-Member Protocol $Bindings[($i)]
$obj | Add-Member Port $Bindings2[1]
$obj | Add-Member Header $Bindings2[2]
$obj | Add-member AuthAnon $Anon.value
$obj | Add-member AuthBasic $basic.value
$obj | Add-member Status $status
$obj #take this out if you want to save to csv| export-csv "c:\temp\$date-$hostname.csv" -Append -notypeinformation
$i=$i+2
}
else{$i=$i+1}
} while ($i -lt ($bindings.count))
}