how can i write elegant ping with powershell - powershell

I create script for check available resources:
$servers = New-Object System.Collections.Generic.Dictionary"[String,String]"
$servers.Add("Server 1","127.0.0.1")
$servers.Add("Server 2","127.0.0.1")
$servers.Add("Server 3","127.0.0.1")
$virtuals = New-Object System.Collections.Generic.Dictionary"[String,String]"
$virtuals.Add("VM 1","127.0.0.1")
$virtuals.Add("VM 2","127.0.0.1")
$virtuals.Add("VM 3","127.0.0.1")
Write-Output("`n")
Write-Output("Check availiable servers")
foreach ($key in $servers.GetEnumerator()){
if ((Test-Connection $key.Value -Count 3 -Quiet)){
[pscustomobject]#{Server = $key.Key; Status = "Availiable"}
}else{
[pscustomobject]#{Server = $key.Key; Status = "Not availiable"}
}
}
Remove-Variable -Name [pscustomobject]
Write-Output("`n")
Write-Output("Check availiable virtuals")
Write-Output("`n")
foreach ($key in $virtuals.GetEnumerator()){
if ((Test-Connection $key.Value -Count 3 -Quiet)){
[pscustomobject]#{VM = $key.Key; Status = "Availiable"}
}else{
[pscustomobject]#{VM = $key.Key; Status = "Not availiable"}
}
}
If I change the title, then the output is in the following table without the first column. And I can't find how I can print the title in every new cycle? My ultimate goal is to have a header for each new loop and color-coded available and unavailable addresses.

If by 'elegant' you mean outputting a line in the table in some color, depending on availability, use something like this:
$servers = [ordered]#{
"Server 1" = "127.0.0.1"
"Server 2" = "127.0.0.1"
"Server 3" = "127.0.0.1"
}
$virtuals = [ordered]#{
"VM 1" = "127.0.0.1"
"VM 2" = "127.0.0.1"
"VM 3" = "127.0.0.1"
}
Write-Host "Check available servers" -ForegroundColor Cyan
$svr = foreach ($key in $servers.GetEnumerator()){
if ((Test-Connection $key.Value -Count 3 -Quiet)){
[pscustomobject]#{Server = $key.Key; Status = "Available"}
}
else{
[pscustomobject]#{Server = $key.Key; Status = "Not available"}
}
}
# output this as colored table
$svr | Format-Table | Out-String -Stream | ForEach-Object {
$color = if ($_ -match 'Not available') { 'Red' } else { 'Green' }
Write-Host $_ -ForegroundColor $color
}
Write-Host
# now do the same for the VMs
Write-Host "Check available virtuals" -ForegroundColor Cyan
$vms = foreach ($key in $virtuals.GetEnumerator()){
if ((Test-Connection $key.Value -Count 3 -Quiet)){
[pscustomobject]#{VM = $key.Key; Status = "Available"}
}
else{
[pscustomobject]#{VM = $key.Key; Status = "Not available"}
}
}
# output this as colored table
$vms | Format-Table | Out-String -Stream | ForEach-Object {
$color = if ($_ -match 'Not available') { 'Red' } else { 'Green' }
Write-Host $_ -ForegroundColor $color
}
Output:

As PowerShell has already added the titles to the tops of the columns from the previous item, it doesn't add them again. Part of its way to reduce repetition or something probably.
You could maybe do an output using Write-Host and then you can format to your hearts content.
Write-Host "Servers" -ForegroundColor Cyan
Write-Host "Virtuals" -ForegroundColor Cyan
You could also do something like...
if ($status -eq "Available") {
$color = "Green"
} else {
$color = "Red"
}
Then when you come to write your results you could do...
Write-Host "Server Name" -ForegroundColor $color
Hope that makes sense, I can write something out fully if you need.

Related

List SPN's Script - Write results to file issue

Good morning everyone.
I found this script on the InterWeb's which works phenominal. HOWEVER... No matter what I try, and where I put it, I can't seem to get it to do the results to an out-file.
What the hell am I doing wrong, and where does the variable need to go?
# Source / credit:
# https://social.technet.microsoft.com/wiki/contents/articles/18996.active-directory-powershell-script-to-list-all-spns-used.aspx
cls
$search = New-Object DirectoryServices.DirectorySearcher([ADSI]"")
$search.filter = "(servicePrincipalName=*)"
## You can use this to filter for OU's:
## $results = $search.Findall() | ?{ $_.path -like '*OU=whatever,DC=whatever,DC=whatever*' }
$results = $search.Findall()
foreach( $result in $results ) {
$userEntry = $result.GetDirectoryEntry()
Write-host "Object Name = " $userEntry.name -backgroundcolor "yellow" -foregroundcolor "black"
Write-host "DN = " $userEntry.distinguishedName
Write-host "Object Cat. = " $userEntry.objectCategory
Write-host "servicePrincipalNames"
$i=1
foreach( $SPN in $userEntry.servicePrincipalName ) {
Write-host "SPN ${i} =$SPN"
$i+=1
}
Write-host ""
}

Jump in a list of IPs in a foreach

I'm working on a little script to gather some wisdom and options in one script. For later and for myself. It's a simple one but with lot of informations and "reusability".
I'm now trying to achive a jump in an "running" process of a list. All I could think of till now is to achive this via regex. But I can barley use regex properly.
#Custom Ping to identify the scenario
function Custom-Ping {
Param(
[string]$Address
)
$ping = ping $Address /w 1 /n 1
if (![string]::IsNullOrEmpty($ping -Like "*(100% loss)*")) {
$result = "Error"
} elseif(![string]::IsNullOrEmpty($ping -Like "*expired*")) {
$result = "Warning"
} else {
$result = "succeded"
}
return $result
}
$ErrorActionPreference = "SilentlyContinue"
$tstart = Get-Date
$counter = 0
$unreachable = 0
$IPlist = foreach ($Network in 1..29) {
foreach ($Range in 1..254) {
"10.10.$Network.$Range"
}
}
foreach ($IP in $IPlist) {
$counter ++
try {
if ($unreachable -lt 6) {
#SwitchCase
$case = Custom-Ping $IP
switch ($case) {
succeded {
Write-Host "Response from: $IP" -ForegroundColor Green
#$IP | Export-Csv -Path D:\somepath.csv -Append
}
Error {
Write-Host "No Response from: $IP" -ForegroundColor Red
#$IP | Export-Csv -Path D:\somepath.csv -Append
}
Warning {
Write-Host "Time Expired on: $IP" -ForegroundColor Yellow
#$IP | Export-CSV -path D:\somepath.csv -Append
$unreachable ++
}
default {
Write-Warning "An Error Occured while processing"
}
}
} else {
#Hop to the next range as this range isnt accesibble
#$IPswap = $IP
#newIP = $Ipswap "$Network from 2 to 3"
#$IP=$newIP
$unreachable = 0
Write-Host "The Network xxxx cant be reached"
}
} catch {
Write-Warning "Other Error"
}
}
$tend = Get-Date
Write-Host "$counter Completed Ping requests"
New-TimeSpan -Start $tstart -End $tend | select Minutes, Seconds
This is the script so far... I didn't find a way till now to achive this jump of the "network".
For Example it got 5 unreachable in 10.10.2.0 network and then sets to 10.10.3.0 network and starts there again with the process.
I was wondering if this is even possible in this scenario.
Use nested loops and labels instead:
:Network # Now we can jump into the next iteration of this loop from an inner loop
foreach ($Network in 0..29)
{
:Host
foreach($Node in 0..254){
$IP = "10.10.$Network.$Node"
#Counter for "Statistic"
$counter ++
try
{
#Check error sum for Range Hop
if($unreachable -lt 6)
{
#SwitchCase
$case = Custom-Ping $IP
switch ($case)
{
succeded
{
Write-Host "Response from: $IP" -ForegroundColor Green
#$IP | Export-Csv -Path D:\somepath.csv -Append
}
Error
{
Write-Host "No Response from: $IP" -ForegroundColor Red
#$IP | Export-Csv -Path D:\somepath.csv -Append
}
Warning
{
Write-Host "Time Expired on: $IP" -ForegroundColor Yellow
#$IP | Export-CSV -path D:\somepath.csv -Append
$unreachable ++
}
default
{
Write-Warning "An Error Occured while processing"
}
}
}
else
{
$unreachable = 0
Write-Host "The Network 10.10.$Network.0/24 cant be reached, jumping to 10.10.$($Network + 1).0/24"
# jump forward to the next iteration of the outer "Network" loop
continue Network
}
}
catch
{
Write-Warning "Other Error"
}
}
}

Dealing with Arrays in Powershell (testing with Pester)

I have a little trouble understanding the way to implement this process. I want to achieve a total count in the score so that if a test successfully passes or fails it can be added into an array. That array will be counted in the length.
This is my code as an example:
#This stores the array of the number of passed and failed test
$passed = #()
$failed = #()
Describe "Template Syntax" {
It "Has a JSON template" {
$fileLocation = "$here\azuredeploy.json"
$fileCheck = $fileLocation | Test-Path
if ($fileCheck -eq $true) { $passed = $passed + 1
Write-Host "1st file exist " }
if ($fileCheck -eq $false) { $failed = $failed + 1
Write-Host "1st file does exist" }
}
It "Has a parameters file" {
$fileLocation ="$here\azuredeploy.parameters*.json"
$fileCheck = $fileLocation | Test-Path
if ($fileCheck -eq $true) { $passed = $passed + 1;
Write-Host "2nd file exist "}
if ($fileCheck -eq $false) { $failed = $failed + 1
Write-Host "2nd file does exist" }
}
PrintArgs
}
function PrintArgs(){
Write-Host -ForegroundColor yellow "Passed: $($passed.Length) Failed: $($failed.Length)"
}
Is there a different way or another approach that I can do to achieve this? I know that pester does it automatically, however, I want to use a Powershell script to test.
Your Pester tests aren't really Pester tests unless you include a Should assertion. I think you should rewrite your tests as follows:
Describe "Template Syntax" {
$fileLocation = "$here\azuredeploy.json"
$fileCheck = $fileLocation | Test-Path
It "Has a JSON template" {
$fileCheck | Should -Be $true
}
$fileLocation ="$here\azuredeploy.parameters*.json"
$fileCheck = $fileLocation | Test-Path
It "Has a parameters file" {
$fileCheck | Should -Be $true
}
}
If you then run this with Invoke-Pester you get a summary of passed and failed test counts at the end automatically. If you need to access these values, you can use -PassThru to return them to a variable. For example:
$Results = Invoke-Pester .\your.tests.ps1 -PassThru
Then you can get the number of passed and failed tests as follows:
$Results.PassedCount
$Results.FailedCount
If you genuinely want to use your own counters (which would mean maintaining lots of unnecessary logic) you could do as follows:
$Passed = 0
$Failed = 0
Describe "Template Syntax" {
$fileLocation = "$here\azuredeploy.json"
$fileCheck = $fileLocation | Test-Path
It "Has a JSON template" {
$fileCheck | Should -Be $true
}
if ($fileCheck -eq $true) { $Passed++ } else { $Failed++ }
$fileLocation ="$here\azuredeploy.parameters*.json"
$fileCheck = $fileLocation | Test-Path
It "Has a parameters file" {
$fileCheck | Should -Be $true
}
if ($fileCheck -eq $true) { $Passed++ } else { $Failed++ }
Write-Host -ForegroundColor yellow "Passed: $Passed Failed: $Failed"
}
Note that the Describe block acts as a kind of script scope, so you have to print the values of $Passed and $Failed within the Describe to get at the values.
Looking at your code, you do not need arrays to count the scores.
Instead of defining $passed and $failed as arrays, just set them up as integer counters with a starting value of 0
$passed = $failed = 0
Then instead of calling function PrintArgs() you simply do
Write-Host -ForegroundColor yellow "Passed: $passed Failed: $failed"
By the way, to increment a counter you can simply do $passed++ instead of $passed = $passed + 1
If you DO insist on using arrays you can change the $passed = $passed + 1 to something like $passed += $true. By doing that you add a new element to the array with a value of $true (or whatever you feel is more appropriate.

Only prints the last server in the list, I want all servers

This only prints the last server in the list, I'm looking to get all servers and print to screen
$machines = (Get-BrokerMachine -AdminAddress $adminaddress -DesktopGroupName $deliverygroup | Select-Object DNSname).DNSname
foreach($machine in $machines){
$machinelist = Get-BrokerMachine -HostedMachineName $machine
if($machinelist.InMaintenanceMode -eq $true){
$status = "$machine is in maintenance mode"
}else {
$status = "$machine is not in maintenance mode"
}
}
Write-Host $status
Here is a more PowerShell-like approach (not tested):
Get-BrokerMachine -AdminAddress $adminaddress -DesktopGroupName $deliverygroup | ForEach-Object {
$machineName = $_.DNSName
[PSCustomObject] #{
"MachineName" = $machineName
"MaintenanceMode" = (Get-BrokerMachine -HostedMachineName $machine).InMaintenanceMode
}
} | Export-Csv "C:\whatever\results.csv" -NoTypeInformation
$Status is constantly being overwritten by the current machine in your list.
You're looking for:
$Status+=
As opposed to:
$Status=
You'll also want to explicitly state that $Status will be an array at the beginning like so:
$Status=#()
Or when you create the variable and omit the line at the beginning.
[array]$Status +=
Otherwise, you'll get results that run together as it will be treated as a [String]
another funky mode :
function get-BrokerMachineMode
{
param (
[Parameter(Mandatory = $true)]
[string[]]$machines
)
begin
{
$ErrorActionPreference = 'Stop'
Add-Type -Language CSharp #"
public class BrokenBroker {
qpublic System.String MachineName;
public System.String MaintenanceMode;
public BrokenBroker (string MachineName, string MaintenanceMode)
{
this.MachineName = MachineName;
this.MaintenanceMode = IsInMaintenanceMode;
}
}
"#
$status = #()
Write-Verbose "Created objects..."
}
process
{
try
{
$machines = (Get-BrokerMachine -AdminAddress $adminaddress `
-DesktopGroupName $deliverygroup | Select-Object DNSname).DNSname
foreach ($machine in $machines)
{
Write-Verbose "Checking machine: $machine"
$machinelist = Get-BrokerMachine -HostedMachineName $machine
if ($machinelist.InMaintenanceMode -eq $true)
{
$status += New-Object BrokenBroker($machine, $true)
}
else
{
$status += New-Object BrokenBroker($machine, $false)
}
}
}
catch
{
Write-Error $error[0].Exception.Message
}
$status
}
end
{
Write-Verbose "Done"
}
}
this is a function you just must to load then you can launch it just by using this command:
$computers = get-content = {PATH TO TXT FILE}
$list = get-BrokerMachineMode -machines $computers -Verbose

Powershell NSlookup on DC's Forward and Reverse

$topDC1="10.254.90.17"
$topDC2="10.225.224.17"
$topDC3="10.110.33.32"
$topDC4="10.88.100.10"
$DomainName="office.adroot.company.net"
TRY{
$hostname = [System.Net.DNS]::GetHostByName($topDC1).HostName.toupper()
$ipaddress = [System.Net.Dns]::GetHostAddresses($DomainName) | select IPAddressToString -ExpandProperty IPAddressToString
# I want the below to loop foreach ip in the object, ns it against all 4 topDC's, then output each result :(
$NS1 = nslookup $ipaddress[0] $topDC1
Write-host $NS1
}
Catch{
write-host "error"
}
Here is my dirty code so far (just to keep it simple)
I am trying to automate this:
NSLOOKUP office.adroot.company.net
put the results into an object
for each ip in results, do an NSLOOKUP against our top level DC's.
find which DC's haven't been cleaned up after decommission (still in dns)
$DCList="10.254.90.17","10.225.224.17","10.110.33.32","10.88.100.10"
$DomainName="office.adroot.blorg.net","pcd.blorg.ca","blorg.ca","percom.adroot.blorg.net", "blorg.blorg.net","ibg.blorg.net","sacmcm.adroot.blorg.net","sysdev.adroot.blorg.net","adroot.blorg.net"
TRY{
foreach ($DomainNameItem in $DomainName){
Write-Host ""
Write-Host ""
Write-Host "Looking UP result"$DomainNameItem -foreground yellow
Write-Host ""
$hostname = [System.Net.DNS]::GetHostByName($DCListItem).HostName.toupper()
$ipaddress = [System.Net.Dns]::GetHostAddresses($DomainNameItem).IPAddressToString
foreach ($ip in $ipaddress){
Write-Host ""
Write-Host "Looking UP result"$ip -foreground green
foreach ($topdns in $DCList){
$RESULTS = nslookup $ip $topdns
Write-host $RESULTS
}
}
}
}
Catch{
write-host "error"
}
Write-Host ""
Write-Host ""
pause
Got it! This will save me tonnes of work determining if a DNS cleanup is necessary. Thanks guys, I'm learning just how great Powershell can be :)
Try this:
$topDomainControllers = #("10.254.90.17", "10.225.224.17", "10.110.33.32", "10.88.100.10")
$DomainName="office.adroot.company.net"
try {
$hostname = [System.Net.Dns]::GetHostByName($topDC1).HostName.ToUpper()
$ipAddresses = [System.Net.Dns]::GetHostAddresses($DomainName) |
select -ExpandProperty IPAddressToString
foreach ($ipAddress in $ipAddresses) {
$nslookupResult = nslookup $ipAddress
$foundIp = $nslookupResult[1] -match "^\D*(\d+\.\d+\.\d+\.\d+)$"
if ($foundIp -eq $false) {
continue
}
$domainController = $Matches[1]
if ($topDomainControllers.Contains($domainController)) {
Write-Output -Verbose "Found domain controller match for $domainController"
break
} else {
Write-Output -Verbose "No match found for domain controller $domainController"
}
}
} catch {
Write-Output "An error has occured: $_"
}