Showing posts with label Active Directory. Show all posts
Showing posts with label Active Directory. Show all posts

Thursday, September 19, 2013

AD Computer scanner with PowerShell for Windows 2003 and up with Gridview

As always; my days get all clogged with things i must do; for instance make an overview of all AD computer objects and sort out all obsolete items.

This can be done in many ways i just opted for the PowerShell way and using the Gridview. this thing is so powerfull out-of-the-box that the script itself remains small.

the output looks like this:

ADComputerScanner1

This grid will find: Name, Description, OperatingSystem, Servicepack,Lastlogontimestamp, Creation date, Logoncount and the time a bad password was put in.

The Gridview itself has some powerful filtering options. With the add criteria; filters items can be added. you just have to experiment with the filters to see what they can do.

This script does NOT use the ActiveDirectory PowerShell module. it just finds the AD domain root itself and connects to it with ADSI so this script can be run just about everywhere as long as it is a Windows Domain with PowerShell enabled on the scanning machine, this script, for example, is ideal to scan a Active Directory that is not Domain level 2008 or higher; it also runs good on older 2003 environments.

Please beware; in large environments this script can pull quite some LDAP traffic from a domain controller; it is not BITS enabled and does not use Qos

have fun with it

#####################################################
# Name: ADComputers Scanner
# Date: 19-9-2013
# Version: 1.0
# Creator: Bas Huygen
# Changetones:
#####################################################

#get ADSI information from root domain
$dc=[ADSI]""
$domain=$dc.distinguishedName
$domainexw="LDAP://"+$domain
$ADDomain=[ADSI]$domainexw
$ADSearch= New-Object System.DirectoryServices.DirectorySearcher
$ADSearch.SearchRoot=$ADDomain
$ADSearch.Filter= "(objectCategory=computer)"
$ADSearch.PropertiesToLoad.AddRange() 2>&1

#get all computers from the root domain
$results=$ADSearch.FindAll()

$array.Clear()

#Loop through all computers of the domain and gather information
foreach($res in $results){
$obj = New-Object PSObject

#$resdescr=$res.Properties.description
$obj | Add-Member NoteProperty Name ([string]$res.properties.name)
$obj | Add-Member NoteProperty Description ([string]$res.Properties.description)
$obj | Add-Member NoteProperty operatingsystem ([string]$res.Properties.operatingsystem)
$obj | Add-Member NoteProperty operatingsystemservicepack ([string]$res.Properties.operatingsystemservicepack)
$rawtime= [string]$res.Properties.lastlogontimestamp
$obj | Add-Member NoteProperty lastlogontimestamp([datetime]::FromFileTime($rawtime))
$obj | Add-Member NoteProperty Created ([string]$res.properties.whencreated)
#$pwlastraw=[string]$res.properties.pwdLastSet
#$obj | Add-Member NoteProperty PasswordLastSet ([datetime]::FromFileTime($pwlastraw))
$obj | Add-Member NoteProperty LogonCount ([string]$res.properties.logoncount)
$badpwdraw=[string]$res.properties.badpasswordtime
$obj | Add-Member NoteProperty badPasswordTime ([datetime]::FromFileTime($badpwdraw))


#Write-Output $obj
[array]$array += $obj
}


$Title = "Machine view of domain $domain"
$array| sort Name|Out-GridView -Title $Title

 


Thursday, July 11, 2013

Help, my virtual DC has got virtual time!

Time services are very important in an Active Directory environment, this is because some required security mechanisms in AD do heavily rely on the correct time of a client; especially Kerberos. this service is responsible for proper authentication in a domain and Kerberos by default does not trust client tickets with a client time delta greater then 5 minutes.

To get an understanding about the Windows Time services please do read http://tigermatt.wordpress.com/2009/08/01/windows-time-for-active-directory/
The key to understanding time services in an Active Directory is: there is only ONE reliable time authority in the directory, this is the Domain Controller which holds the PDC FSMO role (the domain controller that emulates a Windows NT PDC). all other machines do synchronize their time to this PDC emulator. generally as a client you do not have to worry about your local windows time and possible skew to the PDC emulator.

So what's the purpose of this post then?

Look at this picture:
you can imagine, when the PDC emulator has got troubles with its own time; the entire Windows Forest has got time problems.

A computer keeps track of time in the following way: System time is measured by a system clock, which is typically implemented as a simple count of the number of ticks that have transpired since some arbitrary starting date, called the epoch :source: http://en.wikipedia.org/wiki/System_time

Now look at this:


The Host (physical machine) must divide all its processor slices between all VM's (and also reserve some slices for itself). so when your PDC emulator is a virtual machine it cannot rely on its own 'hardware' clock because it is inaccurate.
Windows will indicate problems like these with event error: ID 50, Source W32Time.



the way to tackle this problem is: use the virtualization software integration services. Hyper-V, for instance, installs Hyper-V Time Synchronization Service which will use the Host hardware clock to keep time.

So the bottom line in virtualized environments is:
  • Keep the Virtualization hosts time in sync with a reliable clock.
  • Use the integration services of the virtualization software to couple the Windows time to the hardware clock of the underlying host
  • Keep windows time services running (they are enabled by default), configure the PDC emulator to sync with the same time source the virtualization hosts use
My previous post (the one from July 10, 2013) can help identify what DC's are syncing with whom and the time skew they have.

Till next time

Wednesday, July 10, 2013

script to check synchronization of time service (NTP Offset) of the Domain Controllers in an Active Directory in a GUI

In larger Active Directory environments the synchronization of time on all domain controllers is essential for a healthy AD. AD services will run into problems if the times on domain controllers are ‘out-of-sync’ especially Kerberos (the authentication mechanism used by Windows Active Directory) is sensitive to this.

To check for the domain controllers and their delta (the deviation from their own local time entry and the time on the domain controller the machine synchronizes with) the command W32tm can be used.




The output is a bit hard to read though so i made a little script that extracts the NTP offset and its syncing DC to list it in a small GUI.

the output looks like this:



the GUI is generated with SAPIEN’s great PrimalForms Community Edition. the code is added here so you can use it for yourself. the script does not need any configuration because it uses the ActiveDirectory PowerShell module to get all domain controllers from a Directory.


















#Generated Form Function
function GenerateForm {
########################################################################
# Code Generated By: SAPIEN Technologies PrimalForms (Community Edition) v1.0.10.0
# Generated On: 9-7-2013 14:38
# Generated By: Bas Huygen
########################################################################

#region Import the Assemblies
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
#endregion

#region Generated Form Objects
$form1 = New-Object System.Windows.Forms.Form
$label3 = New-Object System.Windows.Forms.Label
$tbNTskew = New-Object System.Windows.Forms.TextBox
$btnext = New-Object System.Windows.Forms.Button
$btPrevious = New-Object System.Windows.Forms.Button
$tbMaster = New-Object System.Windows.Forms.TextBox
$label2 = New-Object System.Windows.Forms.Label
$label1 = New-Object System.Windows.Forms.Label
$tbDC = New-Object System.Windows.Forms.TextBox
$InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
#endregion Generated Form Objects

#----------------------------------------------
#Generated Event Script Blocks
#----------------------------------------------
#Provide Custom Code for events specified in PrimalForms.

#the ActiveDirectory module is used to list all domain controllers into an array called $ArrayDC
Import-Module ActiveDirectory
$index=0
$ArrayDC=(Get-ADDomainController -Filter *|select name)

#Function Get-DCTime runs w32tm /monitor on the tested DC
#all subsequent sentences extract the values and list them in the textboxes
Function Get-DCTime($index){
$testit=$ArrayDC[$index].name
$tbDC.appendtext($testit)
$substr=(w32tm /monitor /computers:$testit|where{$_ -like "*NTP:*"})
$NTPvalue=$substr.substring(9,20)
$tbNTskew.appendtext($NTPvalue)
$RefID=$substr.substring(36)
$tbMaster.appendtext($RefID)}

#the previous and next buttons invoke Get-DCTime on a previous- or next indexID
$btPrevious_OnClick=
{
$tbDC.clear()
$tbNTskew.clear()
$tbMaster.clear()
$index=$index - 1
Get-DCTime $index
}


$btnext_OnClick=
{
$tbDC.clear()
$tbNTskew.clear()
$tbMaster.clear()
$index=$index + 1
Get-DCTime $index
}

$OnLoadForm_StateCorrection=
{#Correct the initial state of the form to prevent the .Net maximized form issue
    $form1.WindowState = $InitialFormWindowState
}

#----------------------------------------------
#region Generated Form Code
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 232
$System_Drawing_Size.Width = 407
$form1.ClientSize = $System_Drawing_Size
$form1.DataBindings.DefaultDataSourceUpdateMode = 0
$form1.Name = "form1"
$form1.Text = "AD Time services checker"

$label3.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 41
$System_Drawing_Point.Y = 115
$label3.Location = $System_Drawing_Point
$label3.Name = "label3"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 125
$label3.Size = $System_Drawing_Size
$label3.TabIndex = 7
$label3.Text = "NTP Offset relative to"

$form1.Controls.Add($label3)

$tbNTskew.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 190
$System_Drawing_Point.Y = 112
$tbNTskew.Location = $System_Drawing_Point
$tbNTskew.Name = "tbNTskew"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 20
$System_Drawing_Size.Width = 177
$tbNTskew.Size = $System_Drawing_Size
$tbNTskew.TabIndex = 6

$form1.Controls.Add($tbNTskew)


$btnext.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 191
$System_Drawing_Point.Y = 164
$btnext.Location = $System_Drawing_Point
$btnext.Name = "btnext"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 75
$btnext.Size = $System_Drawing_Size
$btnext.TabIndex = 5
$btnext.Text = "Next"
$btnext.UseVisualStyleBackColor = $True
$btnext.add_Click($btnext_OnClick)

$form1.Controls.Add($btnext)


$btPrevious.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 109
$System_Drawing_Point.Y = 164
$btPrevious.Location = $System_Drawing_Point
$btPrevious.Name = "btPrevious"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 75
$btPrevious.Size = $System_Drawing_Size
$btPrevious.TabIndex = 4
$btPrevious.Text = "Previous"
$btPrevious.UseVisualStyleBackColor = $True
$btPrevious.add_Click($btPrevious_OnClick)

$form1.Controls.Add($btPrevious)

$tbMaster.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 190
$System_Drawing_Point.Y = 72
$tbMaster.Location = $System_Drawing_Point
$tbMaster.Name = "tbMaster"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 20
$System_Drawing_Size.Width = 177
$tbMaster.Size = $System_Drawing_Size
$tbMaster.TabIndex = 3

$form1.Controls.Add($tbMaster)

$label2.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 41
$System_Drawing_Point.Y = 75
$label2.Location = $System_Drawing_Point
$label2.Name = "label2"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 100
$label2.Size = $System_Drawing_Size
$label2.TabIndex = 2
$label2.Text = "Synchs with"
$label2.add_Click($handler_label2_Click)

$form1.Controls.Add($label2)

$label1.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 41
$System_Drawing_Point.Y = 37
$label1.Location = $System_Drawing_Point
$label1.Name = "label1"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 143
$label1.Size = $System_Drawing_Size
$label1.TabIndex = 1
$label1.Text = "Domain Controller Name: "

$form1.Controls.Add($label1)

$tbDC.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 190
$System_Drawing_Point.Y = 34
$tbDC.Location = $System_Drawing_Point
$tbDC.Name = "tbDC"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 20
$System_Drawing_Size.Width = 177
$tbDC.Size = $System_Drawing_Size
$tbDC.TabIndex = 0

$form1.Controls.Add($tbDC)

#endregion Generated Form Code

#Save the initial state of the form
$InitialFormWindowState = $form1.WindowState
#Init the OnLoad event to correct the initial state of the form
$form1.add_Load($OnLoadForm_StateCorrection)
#Show the Form
$form1.ShowDialog()| Out-Null

} #End Function

#Call the Function
GenerateForm

Monday, May 27, 2013

The trouble with single label (Active Directory) domain names

Today on a domain of a client of mine (that has a single label Active Directory domain) i experienced a strange phenomenon
a new deployed client computers could not join a domain, the machine got the message An Active Directory domain controller for this domain could not be contacted

I went through all standard troubleshooting steps like:
  • Check local connectivity of the client
    • Hardware
    • Ipconfig
    • ping to the default gateway
  • Check name resolution
    • DNS
    • WINS
Everything looked ok but how could this be? next up were the servers, went through all tests on the domain controllers and services like DNS and DHCP, i found some faults but this were minor. coming to this point I got the impression I have overlooked something; but what?
Thing is: looking at a problem like this one troubleshooting should be started from the source. checking name resolution on the client revealed one interesting thing: the machine could find a machine.domainlabel but as soon as the Windows 7 client wanted to resolve the domain it could not find it...

You see when using a single label domain you normally enter a domain like domain.local but with a single label domain name you enter domain doing this triggers a client to use NETBIOS name resolution! after checking the DHCP I found the fault. the first WINS server stated in DHCP pointed to a server on which a day earlier the WINS was uninstalled. after installing WINS and enabling Use WINS forward lookup everything worked as normal.

this calls for one conclusion: the main problem with single label domain names is that it triggers clients to use WINS name resolution is a number of cases. since all modern AD integrated software uses DNS name resolution a lot of things in these programs probably will falter.

till next time.

Friday, April 12, 2013

Fiddling around with the ActiveDirectory PowerShell module part 1

It has been a few weeks since my last post and i feel obliged to blog something today. so i think the Active Directory module is worth mining. today a simple PowerShell script to list User membership in the AD.

since the AD cmdlets are very extensive it took me a little while to find out how to get group memberships out of the AD. This little script will show the membership of a user from a AD group and it will save it into a Excel workbook.



Import-Module ActiveDirectory
$a = New-Object -ComObject excel.application
$a.visible = $true
$b = $a.workbooks.add()
$C = $b.ActiveSheet

$c.Cells.Item(1,1) = "Login ID"
$c.Cells.Item(1,2) = "Member Off"
$i = 2
$group = "Enterprise Admins","Domain Admins"

foreach ($grp in $group)
{$users = Get-ADGroupMember $grp
foreach($_ in $users){
$c.cells.item($i,1) = $_.SamAccountName;$c.cells.item($i,2) = $grp;$i=$i+1
}
}
$b.SaveAs("C:\Powershell\Adminsreport\Admins.xls")
$a.quit()

Wednesday, February 20, 2013

A handy EventLog Reader with Powershell

Tags van Technorati: ,,,,
So here i am back with another handy script for the IT administrator.

Question: are you, like me, fed up with the tedious way Microsoft event log are to be retrieved and viewed? then i might have a (PowerShell) solution for you!

In this script i have taken some AD PowerShell plugin functions and combined it with a few .Net forms. (thanks to a post on Microsoft TechNet_ furthermore i added Get-Eventlog Cmdlet to derive any eventlog content from any machine you can access in one script. the script fetches only the first n (asked in the script “how many lines”) recent Errors or failures.



# this script reads eventlog from any Computer you can access
# Script build by Bas Huygen February 2013
# the MS forms procedures are from Microsoft: http://technet.microsoft.com/en-us/library/ff730941.aspx
$ErrorActionPreference = "SilentlyContinue"

Do{
# Procedure 1: Get all AD computers and make a selection by filtering it
Clear-Host
$compfilter = Read-Host("please make a selection of computers, this can be one name or a range by the use of * (wildcards)")
If($compfilter -eq ""){$compfilter = "*"}
$allcomps = Get-ADComputer -filter * |Select-Object Name |Sort-Object name |where {$_.name -like "$compfilter"}

# Procedure 2: Select a computer in a form
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

$objForm2 = New-Object System.Windows.Forms.Form
$objForm2.Text = "Select a Computer"
$objForm2.Size = New-Object System.Drawing.Size(300,200)
$objForm2.StartPosition = "CenterScreen"

$objForm2.KeyPreview = $True
$objForm2.Add_KeyDown({if ($_.KeyCode -eq "Enter")
{$x=$objListBoxcmp.SelectedItem;$objForm2.Close()}})
$objForm2.Add_KeyDown({if ($_.KeyCode -eq "Escape")
{$objForm2.Close()}})

$OKButton2 = New-Object System.Windows.Forms.Button
$OKButton2.Location = New-Object System.Drawing.Size(75,120)
$OKButton2.Size = New-Object System.Drawing.Size(75,23)
$OKButton2.Text = "OK"
$OKButton2.Add_Click({$x=$objListBoxcmp.SelectedItem;$objForm2.Close()})
$objForm2.Controls.Add($OKButton2)

$CancelButton2 = New-Object System.Windows.Forms.Button
$CancelButton2.Location = New-Object System.Drawing.Size(150,120)
$CancelButton2.Size = New-Object System.Drawing.Size(75,23)
$CancelButton2.Text = "Cancel"
$CancelButton2.Add_Click({$objForm2.Close()})
$objForm2.Controls.Add($CancelButton2)

$objLabel2 = New-Object System.Windows.Forms.Label
$objLabel2.Location = New-Object System.Drawing.Size(10,20)
$objLabel2.Size = New-Object System.Drawing.Size(280,20)
$objLabel2.Text = "Please select a computer:"
$objForm2.Controls.Add($objLabel2)

$objListBoxcmp = New-Object System.Windows.Forms.ListBox
$objListBoxcmp.Location = New-Object System.Drawing.Size(10,40)
$objListBoxcmp.Size = New-Object System.Drawing.Size(260,20)
$objListBoxcmp.Height = 80

# loop through all compyters filtered out of the AD in procedure 1
ForEach ($c in $allcomps){[void] $objListBoxcmp.Items.Add($c.name)}

$objForm2.Controls.Add($objListBoxcmp)

$objForm2.Topmost = $True

$objForm2.Add_Shown({$objForm2.Activate()})
[void] $objForm2.ShowDialog()

$trgHost = $objListBoxcmp.Text

# Procedure 3: Select an Eventlog Source in a form
#[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
#[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Select an Eventlog Source"
$objForm.Size = New-Object System.Drawing.Size(300,200)
$objForm.StartPosition = "CenterScreen"

$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter")
{$x=$objListBox.SelectedItem;$objForm.Close()}})
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
{$objForm.Close()}})

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click({$x=$objListBox.SelectedItem;$objForm.Close()})
$objForm.Controls.Add($OKButton)

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20)
$objLabel.Size = New-Object System.Drawing.Size(280,20)
$objLabel.Text = "Please select an Eventlog source:"
$objForm.Controls.Add($objLabel)

$objListBox = New-Object System.Windows.Forms.ListBox
$objListBox.Location = New-Object System.Drawing.Size(10,40)
$objListBox.Size = New-Object System.Drawing.Size(260,20)
$objListBox.Height = 80

[void] $objListBox.Items.Add("System")
[void] $objListBox.Items.Add("Application")
[void] $objListBox.Items.Add("Security")

$objForm.Controls.Add($objListBox)

$objForm.Topmost = $True

$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()

$evtLog = $objListBox.Text
$howmany = Read-host ("How many lines should i fetch? (blank fetches 25 lines)")
If ($howmany -eq ""){$howmany = 25}
If($evtLog -eq "Security"){$errtype = "failureaudit"} else{ $errtype = "error"}
# now get the eventlog from the selection
Get-EventLog -ComputerName $trgHost $evtLog -Newest $howmany -EntryType $errtype
Get-EventLog -ComputerName $trgHost $evtLog -Newest $howmany -EntryType $errtype | group-object -property source -noelement |
sort-object -property count -descending

$erID = Read-Host("Zoom in to a specific event? (give IDnumber)")
If($erID -eq ""){}else{Get-EventLog -ComputerName $trgHost $evtLog -Newest $howmany -EntryType $errtype |?{$_.Index -like $erID} |select Message}
$again = Read-Host("Start again? (y)")
}while($again -eq "y" -or $again -eq "Y")





its output will like something like this:






Have fun, comments are welcome and till next time!

Tuesday, February 19, 2013

Simple Connectivity Script

In the last weeks i have spent a lot of time coding Powershell scripts. In the next few days i will share some of them because i think they have added value to the Powershell community.
This script will test AD computers (one or a whole range) on connectivity and return its state in green (when the machine is up) or red (when the machine is down) its core provider is the Active Directory module of powershell and the Test-Connection cmdlet that basicly tests a connection with a ping.

#  This script tests the connectivity of Active Directory computers
# Script built by Bas Huygen februari 2013

# The usefull variable ErrorActionPreference is used to control the feedback Powershell returns to the ‘default-out’ $ErrorActionPreference = "SilentlyContinue"
# we’re clearly using the AD module here…
Import-Module ActiveDirectory
Clear-Host
$inp = Read-Host "give the name of the machine that has to be checked `n
It is also possible to use a wildcard (*) to test a range of computers`n
For example giving SER* will test all AD computers starting with a name like SER01 or SERvertest `n
not providing a value here will input * which means all AD computers will be scanned"
If($inp -eq ""){$inp = "*"} $ir = 0; $is = 0
foreach($comp in (Get-ADComputer -filter *  |where {$_.Name -like $inp})){`
If(Test-Connection -Count 1 $comp.name){$ir ++ ;Write-Host "Machine "  $comp.name  "is up-and-running" -ForegroundColor Green}`
Else{$is ++;Write-Host "Machine " $comp.name "is down" -ForegroundColor Red}}
Write-Host "---------------------`n"
Write-Host "total running machines is" $ir -ForegroundColor Green
Write-Host "total stopped machines " $is -ForegroundColor Red

output looks like this: image
have fun with it and questions, please leave them at this page and i will get back to you.