Мониторинг доступности хостов на Powershell

Всем доброго времени суток, хочу поделиться простой инструкцией «Как можно перестать вручную пинговать десяток хостов. Без регистрации и СМС!».

С просторов Интернета

Важно: я не программист и не системный администратор, но мне приходиться работать одновременно с большим набором серверов и сервисов и данный скрипт я написал для собственного удобства.

Всех кому это может быть полезно любопытно, прошу под кат.


Почему Powershell
У меня есть опыт написания простых программ на Python, но он требует либо установленного интерпретатора, либо права на запуск .exe, что, в рамках моей работы не всегда возможно. А вот скрипт на Powershell чаще находит возможность запустить.

Не все любят консоль/терминал


Думаю в этом нет противоречия, в противном случае все бы до сих пользовались консольными почтовыми клиентами и в *nix-системах не появился GUI.

Поскольку в повседневности мне приходится использовать Powershell достаточно примитивно, то для начала я попытался понять, а можно ли к нему прикрутить GUI. Оказывается можно, да еще и достаточно просто:


Add-Type -assembly System.Windows.Forms #Подключим библиотеку
$main_form = New-Object System.Windows.Forms.Form #Создаем объект с нашим "окном"

#Задаем для него параметры
$main_form.Text ='Links up' #Имя в заголовке
$main_form.Width = 300 #Ширина окна
$main_form.Height = 200 #Высота окна
$main_form.AutoSize = $true #Даем ему возможность самостоятельно изменять размер, при необходимости
$main_form.ShowDialog() #Вызываем "окно"

Добавляем к этому элемент вывода и получаем элементарное Windwows-окно тавтология?.


Add-Type -assembly System.Windows.Forms #Подключим библиотеку

$main_form = New-Object System.Windows.Forms.Form #Создаем переменную с нашим "окном"

#Задаем для него параметры через параметры
$main_form.Text ='Links up' #Имя в заголовке
$main_form.Width = 300 #Ширина окна
$main_form.Height = 200 #Высота окна
$main_form.AutoSize = $true #Даем ему возможность самостоятельно изменять размер, при необходимости

$Label = New-Object System.Windows.Forms.Label #Все элементы формы будут заноситься в переменные, но по факту это объекты, как любые другие переменные в Powershell
$Label.Text = "Привет! Я элементарное Windows-окно, а чего добился ты?" #Выводимый текст
$Label.Location  = New-Object System.Drawing.Point(10,65) #Расположение объекта внутри формы по горизонтали и вертикали (x,y)
$Label.AutoSize = $true 

$main_form.Controls.Add($Label) #добавляем созданный объект к форме, чтобы он не потерялся при выполнении скрипта

$main_form.ShowDialog() #Вызываем оскорбительно элементарное окно

Добавим динамики


Так инструмент которым я «пинговал Яндекс» и монтировал *.iso обзавёлся обособленным GUI, но этого мало. Подобным образом можно вывести информацию о доступности, но для мониторинга придется каждый раз перезапускать скрипт.

Для начала нарисуем две иконки и добавим возможность выводить изображения.


$PictureBox = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox.width = 10
$PictureBox.height = 10
$PictureBox.location = New-Object System.Drawing.Point(178,12)

#Вот тут мы переходим к делу привязываем выводимую картинку к результату проверки доступности узла используя командлет
#<b>test-connection</b>, где:
#-Count - передает кол-во запросов
#-computer - IP или доменное имя для запроса
#-quiet - для того чтобы нам вернулось только Boolean значение
if ((test-connection -Count 1 -computer ya.ru -quiet) -eq $True) {
	$PictureBox.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
}
Else {
	$PictureBox.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
}
$PictureBox.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$main_form.Controls.Add($PictureBox)

Опять наши DNS-ники шалят...


И еще добавим кнопку, чтобы была возможность обновлять показания.


$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(100,150) 
$Button.Size = New-Object System.Drawing.Size(80,30)
$Button.Text = "Reload"

# Вешаем на событие нажатия кнопки обращение каждого PictureBox согласно ранее описанной логике
$Button.Add_Click({ 
	if ((Test-Connection -Count 1 -computer ya.ru -quiet) -eq $True)
		{$PictureBox.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox.imageLocation	= "C:\Test\no.png"}
	  
	if ((Test-Connection -Count 1 -computer 8.8.8.8 -quiet) -eq $True)
		{$PictureBox1.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox1.imageLocation	= "C:\Test\no.png"}
 	})

$main_form.Controls.Add($Button)


Чудесно, теперь в продакшн!?


Вручную прописав около полудюжины хостов я вдруг понял одну вещь — а ведь этот проект закончиться, хосты поменяют и мне придется каждый раз править скрипт. С одной стороны ничего страшного, это же решение для себя любимого. С другой стороны это решение для себя любимого вы взгляните на то что получается:

Зрелище не для слабонервных
Add-Type -assembly System.Windows.Forms #Подключим библиотеку

$main_form = New-Object System.Windows.Forms.Form #Создаем переменную с нашим "окном"

#Задаем для него параметры через параметры
$main_form.Text ='Links up' #Имя в заголовке
$main_form.Width = 300 #Ширина окна
$main_form.Height = 200 #Высота окна
$main_form.AutoSize = $true #Даем ему возможность самостоятельно изменять размер, при необходимости

$Label = New-Object System.Windows.Forms.Label #Все элементы формы будут заноситься в переменные, но по факту это объекты, как любые другие переменные в Powershell
$Label.Text = "ya.ru		............................" #Выводимый текст 
$Label.Location  = New-Object System.Drawing.Point(15,10) #Расположение объекта внутри формы по горизонтали и вертикали (x,y)
$Label.AutoSize = $true 

$Label1 = New-Object System.Windows.Forms.Label #Все элементы формы будут заноситься в переменные, но по факту это объекты, как любые другие переменные в Powershell
$Label1.Text = "8.8.8.8		............................" #Выводимый текст googl-овский DNS-ник
$Label1.Location  = New-Object System.Drawing.Point(15,30) #Расположение объекта внутри формы по горизонтали и вертикали (x,y)
$Label1.AutoSize = $true 

$Label2 = New-Object System.Windows.Forms.Label
$Label2.Text = "192.168.x.x	............................"
$Label2.Location  = New-Object System.Drawing.Point(15,50)
$Label2.AutoSize = $true

$Label3 = New-Object System.Windows.Forms.Label
$Label3.Text = "192.168.x.x	............................"
$Label3.Location  = New-Object System.Drawing.Point(15,70)
$Label3.AutoSize = $true

$Label4 = New-Object System.Windows.Forms.Label
$Label4.Text = "10.0.x.x	 ............................"
$Label4.Location  = New-Object System.Drawing.Point(15,90)
$Label4.AutoSize = $true

$Label5 = New-Object System.Windows.Forms.Label
$Label5.Text = "162.102.x.x	 ............................"
$Label5.Location  = New-Object System.Drawing.Point(15,110)
$Label5.AutoSize = $true

$PictureBox = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox.width = 10
$PictureBox.height = 10

#Вот тут мы переходим к делу привязываем выводимую картинку к результату проверки доступности узла используя командлет
#<b>test-connection</b>, где:
#-Count - передает кол-во запросов
#-computer - IP или доменное имя для запроса
#-quiet - для того чтобы нам вернулось только Boolean значение
$PictureBox.location = New-Object System.Drawing.Point(235,12)
if ((test-connection -Count 1 -computer ya.ru -quiet) -eq $True) {
	$PictureBox.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
}
Else {
	$PictureBox.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
}
$PictureBox.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$PictureBox1 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox1.width = 10
$PictureBox1.height = 10
$PictureBox1.location = New-Object System.Drawing.Point(235,32)
if ((test-connection -Count 1 -computer 8.8.8.8 -quiet) -eq $True) {
	$PictureBox1.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
}
Else {
	$PictureBox1.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
}
$PictureBox1.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$PictureBox2 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox2.width = 10
$PictureBox2.height = 10
$PictureBox2.location = New-Object System.Drawing.Point(235,52)
if ((test-connection -Count 1 -computer 192.168.x.x -quiet) -eq $True) {
	$PictureBox2.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
}
Else {
	$PictureBox2.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
}
$PictureBox2.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$PictureBox3 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox3.width = 10
$PictureBox3.height = 10
$PictureBox3.location = New-Object System.Drawing.Point(235,72)
if ((test-connection -Count 1 -computer 192.168.x.x -quiet) -eq $True) {
	$PictureBox3.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
}
Else {
	$PictureBox3.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
}
$PictureBox3.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$PictureBox4 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox4.width = 10
$PictureBox4.height = 10
$PictureBox4.location = New-Object System.Drawing.Point(235,92)
if ((test-connection -Count 1 -computer 10.0.x.x -quiet) -eq $True) {
	$PictureBox4.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
}
Else {
	$PictureBox4.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
}
$PictureBox1.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$PictureBox5 = New-Object system.Windows.Forms.PictureBox #Объект для вывода изображений называется вполне логично
$PictureBox5.width = 10
$PictureBox5.height = 10
$PictureBox5.location = New-Object System.Drawing.Point(235,112)
if ((test-connection -Count 1 -computer 162.102.x.x -quiet) -eq $True) {
	$PictureBox5.imageLocation = "C:\Test\yes.png" #Объект обращается к соответствующей картинке 
	}
Else {
	$PictureBox5.imageLocation = "C:\Test\no.png" #Объект обращается к соответствующей картинке 
	}
$PictureBox5.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::zoom

$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(100,150) 
$Button.Size = New-Object System.Drawing.Size(80,30)
$Button.Text = "Reload"

# Вешаем на событие нажатия кнопки обращение каждого PictureBox согласно ранее описанной логике
$Button.Add_Click({ 
	if ((Test-Connection -Count 1 -computer ya.ru -quiet) -eq $True)
		{$PictureBox.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox.imageLocation	= "C:\Test\no.png"}
	  
	if ((Test-Connection -Count 1 -computer 8.8.8.8 -quiet) -eq $True)
		{$PictureBox1.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox1.imageLocation	= "C:\Test\no.png"}

	if ((Test-Connection -Count 1 -computer 192.168.x.x -quiet) -eq $True)
		{$PictureBox2.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox2.imageLocation	= "C:\Test\no.png"}

	if ((Test-Connection -Count 1 -computer 192.168.x.x -quiet) -eq $True)
		{$PictureBox3.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox3.imageLocation	= "C:\Test\no.png"}

	if ((Test-Connection -Count 1 -computer 10.0.x.x -quiet) -eq $True)
		{$PictureBox4.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox4.imageLocation	= "C:\Test\no.png"}

	if ((Test-Connection -Count 1 -computer 162.102.x.x -quiet) -eq $True)
		{$PictureBox5.imageLocation	= "C:\Test\yes.png"}
	Else {$PictureBox5.imageLocation	= "C:\Test\no.png"}
	})

$main_form.Controls.Add($Label) #добавляем созданный объект к форме, чтобы он не потерялся при выполнении скрипта
$main_form.Controls.Add($Label1)
$main_form.Controls.Add($Label2)
$main_form.Controls.Add($Label3)
$main_form.Controls.Add($Label4)
$main_form.Controls.Add($Label5)
$main_form.Controls.Add($PictureBox)
$main_form.Controls.Add($PictureBox1)
$main_form.Controls.Add($PictureBox2)
$main_form.Controls.Add($PictureBox3)
$main_form.Controls.Add($PictureBox4)
$main_form.Controls.Add($PictureBox5)
$main_form.Controls.Add($Button)

$main_form.ShowDialog() #Вызываем оскорбительно элементарное окно

Даже если соблюдать чистоту и придерживаться структуры то периодическое редактирование будет вызывать не самые приятные ощущения.

Для начала выносим проверку доступности в отдельную функцию, а путь к каталогу в отдельную переменную:

$script_path	= "C:\Test\PS ping"

#Переезжаем в финкцию уже созданный объект PictureBox и IP-адресс который нужно проверить.
#На выходе оно меняет подгружаемую картинку.
function get_status($PictureBox,$path_ip) {
	if ((Test-Connection -Count 1 -computer $path_ip -quiet) -eq $True)
		{$PictureBox.imageLocation	= $script_path + "\yes.png"}
	Else {$PictureBox.imageLocation	= $script_path + "\no.png"} 
	}

Вынесем данные о хосте во внешний файл path.txt, записывая в виде «ip/host-functional-name/», а в документе будем принимать их в массивы. Так же сделаем расчет положения по Y более автоматизированным. Все это дает нам возможность создавать Label`ы и checkbox`ы вызовом функции:


#создаем описательную линию, на вход идут: объект Label, IP-адресс, описание и позиция по оси Y
Function Create_line($label,$path_ip,$caption, $top){ 
	$label.Location = New-Object System.Drawing.Point(1, $top)
	$label.text = $path_ip+$caption
	$label.font = $font
	$Label.AutoSize = $true
	}
#создаем индикатор для описательной линию, на вход идут: объект PictureBox, IP-адресс и позиция по оси Y
Function Create_link($PictureBox,$path_ip, $top){ 
	$PictureBox.width	= 10
	$PictureBox.height	= 10
	$PictureBox.location	= New-Object System.Drawing.Point(210,$top)
	get_status -PictureBox $PictureBox -path_ip $path_ip
	$PictureBox.SizeMode	= [System.Windows.Forms.PictureBoxSizeMode]::zoom
	}

$line = Get-content -LiteralPath $script_path"\path.txt" #вытаскиваем данные о необходимых хостах
$len = $line.Length	#вычисляем длину, а точнее массив длин каждой строки

$i = 0

#далее объявляем массивы которые понадобятся дальше
$ip = @()	#массив ip
$capt = @()	#массив описания
$Labels =@()	#массив Label
$PictureBoxs =@()	#массив PictureBox

while($i -lt $len){
	$f = $line[$i].IndexOf("/") #вычисляем позицию начала описания
	$l = $line[$i].LastIndexOf("/")	#вычисляем позицию конца описания
	$ip += $line[$i].Substring(0,$f)	#вытаскиваем ip
	$capt += $line[$i].Substring($f+1,$l-$f-1)	#вытаскиваем описание

	#создаем поле Label
	Create_line -label ($label_obj = New-Object System.Windows.Forms.Label) -path_ip $ip[$i] -caption $capt[$i] -top $label_from_top
	#создаем поле PictureBox
	Create_link -PictureBox ($PictureBox_obj = New-Object system.Windows.Forms.PictureBox) -path_ip $ip[$i] -top $label_from_top

	$label_from_top += 15	#делаем "шаг" по оси Y

	$Labels	+= $label_obj	#вносим созданный Label в общий массив
	$PictureBoxs	+= $PictureBox_obj	#вносим созданный PictureBox в общий массив
	$main_form.Controls.Add($Labels[$i])
	$main_form.Controls.Add($PictureBoxs[$i])
	$i	+=1
	}

Добавим место для вывода времени последней проверки, потому что это удобно, и немого дополним действие при нажатии на кнопку.


$Label0	= New-Object System.Windows.Forms.Label
$Label0.Text	= Get-Date
$Label0.Location	= New-Object System.Drawing.Point(80,180)
$Label0.AutoSize	= $true
$main_form.Controls.Add($Label0)
$Button.Add_Click({ 
	while($i -lt $len){
		get_status -PictureBox $PictureBoxs[$i] -path_ip $ip[$i]
		$i +=1
		}
	$Label0.Text	= Get-Date 
	})

А так же я присоединил небольшую фитчуне баг, которая при обнаружении недоступного хоста сообщало не только изменением индикатора, но и вопило из динамиков.

Add-Type -AssemblyName System.Speech
$voice	= New-Object System.Speech.Synthesis.SpeechSynthesizer
$voice.Rate	= 5
$voice.Speak("Ворнинг! Ворнинг! Байз из андер аттак!")

Так что после нескольких украшательств и приведения к читабельномуудобному мне виду, получилось вот так:

Полный код скрипта
Add-Type -AssemblyName System.Speech
Add-Type -assembly System.Windows.Forms

$script_path	= "C:\PS ping"
$label_from_top	= 10
$voice	= New-Object System.Speech.Synthesis.SpeechSynthesizer
$voice.Rate	= 5

#Переезжаем в финкцию уже созданный объект PictureBox и IP-адресс который нужно проверить.
#На выходе оно меняет подгружаемую картинку.
function get_status($PictureBox,$path_ip) {
	if ((Test-Connection -Count 1 -computer $path_ip -quiet) -eq $True) {
		$PictureBox.imageLocation	= $script_path + "\yes.png"
		}
	Else {
		$PictureBox.imageLocation	= $script_path + "\no.png"
		$voice.Speak("Ошибка! Хост " + $path_ip + ", недоступен!")
		}
	}

#создаем описательную линию, на вход идут: объект Label, IP-адресс, описание и позиция по оси Y
Function Create_line($label,$path_ip,$caption, $top){ 
	$label.Location	= New-Object System.Drawing.Point(1, $top)
	$label.text	= $path_ip+$caption
	$label.font	= $font
	$Label.AutoSize	= $true
	}

#создаем индикатор для описательной линию, на вход идут: объект PictureBox, IP-адресс и позиция по оси Y
Function Create_link($PictureBox,$path_ip, $top){ 
	$PictureBox.width	= 10
	$PictureBox.height	= 10
	$PictureBox.location	= New-Object System.Drawing.Point(210,$top)
	get_status -PictureBox $PictureBox -path_ip $path_ip
	$PictureBox.SizeMode	= [System.Windows.Forms.PictureBoxSizeMode]::zoom
	}

$main_form	= New-Object System.Windows.Forms.Form
$main_form.Text ='Links up' #Имя в заголовке
$main_form.Width = 300 #Ширина окна
$main_form.Height = 200 #Высота окна
$main_form.AutoSize = $true #Даем ему возможность самостоятельно изменять размер, при необходимости
$main_form.TopMost	= $true #Это очень важная штука, она дает возможность закрепить окно поверх других

$line = Get-content -LiteralPath $script_path"\path.txt" #вытаскиваем данные о необходимых хостах
$len = $line.Length	#вычисляем длину, а точнее массив длин каждой строки

$i = 0

#далее объявляем массивы которые понадобятся дальше
$ip	= @()	#массив ip
$capt	= @()	#массив описания
$Labels	=@()	#массив Label
$PictureBoxs	=@()	#массив PictureBox

while($i -lt $len){
	$f	= $line[$i].IndexOf("/") #вычисляем позицию начала описания
	$l	= $line[$i].LastIndexOf("/")	#вычисляем позицию конца описания
	$ip	+= $line[$i].Substring(0,$f)	#вытаскиваем ip
	$capt	+= $line[$i].Substring($f+1,$l-$f-1)	#вытаскиваем описание

	#создаем поле Label
	Create_line -label ($label_obj = New-Object System.Windows.Forms.Label) -path_ip $ip[$i] -caption $capt[$i] -top $label_from_top
	#создаем поле PictureBox
	Create_link -PictureBox ($PictureBox_obj = New-Object system.Windows.Forms.PictureBox) -path_ip $ip[$i] -top $label_from_top

	$label_from_top	+= 15	#делаем "шаг" по оси Y

	$Labels	+= $label_obj	#вносим созданный Label в общий массив
	$PictureBoxs	+= $PictureBox_obj	#вносим созданный PictureBox в общий массив
	$main_form.Controls.Add($Labels[$i])
	$main_form.Controls.Add($PictureBoxs[$i])
	$i	+=1
}

#это поле с последним временем проверки
$Label0	= New-Object System.Windows.Forms.Label
$Label0.Text	= Get-Date
$Label0.Location	= New-Object System.Drawing.Point(80,180)
$Label0.AutoSize	= $true


# Вешаем на событие нажатия кнопки обращение каждого PictureBox согласно ранее описанной логике
$Button	= New-Object System.Windows.Forms.Button
$Button.Location	= New-Object System.Drawing.Size(100,200)
$Button.Size	= New-Object System.Drawing.Size(80,30)
$Button.Text	= "Reload"

$Button.Add_Click({ 
	while($i -lt $len){
		get_status -PictureBox $PictureBoxs[$i] -path_ip $ip[$i]
		$i	+=1
		}
	$i	= 0
	$Label0.Text	= Get-Date 
})

$main_form.Controls.Add($Button)
$main_form.Controls.Add($Label0)

$main_form.ShowDialog()


Послесловие


Надеюсь кому-то данный туториал будет полезен. Если же вы такое чуть-ли не с пеленок делали, то поздравляю, я — не делал. Но я готов выслушать ваши комментарии и предложения.

Для меня самого было приятно покопаться и собрать вот такую небольшую, но полезную в быту утилиту. В планах еще стоит реализовать добавления и удаления узлов из GUI, а так же сделать таки то что должно было быть реализованно в первую очередь автоматическое обновление статусов без необходимости «тыкать» кнопку и чтобы оно не вешало «powershell.exe».

Последняя версия скрипта на GitHub

Средняя зарплата в IT

111 000 ₽/мес.
Средняя зарплата по всем IT-специализациям на основании 7 268 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 19

    0
    «Я не программист», говорите? По-моему, именно такие утилиты выдают программиста )
      0
      Программисты пишут сами для себя утилиты? =)
      Мне казалось это удел всех, кто в какой-то момент понимает что нужна автоматизация.
      0
      Не работает на Windows 7 @ Powershell 2.0
        0
        Код не идеален и проверялся только на PSversion 4.0 и 5.1, под Win10, WinServ 2012 и WinServ 2008.

        Можете поделиться, на что жалуется?
          +1
          Заголовок спойлера
          PS C:\PS ping> C:\PS ping\script.ps1
          Произошла ошибка при вызове метода, так как [System.Object[]] не содержит метод с именем "Substring".
          C:\PS ping\script.ps1:42 знак:27
          + $line = $data.Substring <<<< (0,47)
          + CategoryInfo : InvalidOperation: (Substring:String) [], RuntimeException
          + FullyQualifiedErrorId : MethodNotFound

          Не удается индексировать в массив NULL.
          C:\PS ping\script.ps1:51 знак:16
          + $f = $line[ <<<< $i].IndexOf("/")
          + CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
          + FullyQualifiedErrorId : NullArray

          Не удается индексировать в массив NULL.
          C:\PS ping\script.ps1:52 знак:16
          + $l = $line[ <<<< $i].LastIndexOf("/")
          + CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
          + FullyQualifiedErrorId : NullArray

          Не удается индексировать в массив NULL.
          C:\PS ping\script.ps1:53 знак:18
          + $ip += $line[ <<<< $i].Substring(0,$f)
          + CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
          + FullyQualifiedErrorId : NullArray

          Не удается индексировать в массив NULL.
          C:\PS ping\script.ps1:54 знак:19
          + $capt += $line[ <<<< $i].Substring($f+1,$l-$f-1)
          + CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
          + FullyQualifiedErrorId : NullArray

          Test-Connection : Не удается проверить аргумент для параметра "ComputerName". Аргумент пустой или имеет значение NULL. Укажите не пустой аргумент, не имеющий значение NULL, после чего повторите выполнение команды.
          C:\PS ping\script.ps1:10 знак:41
          + if ((Test-Connection -Count 1 -computer <<<< $path_ip -quiet) -eq $True) {
          + CategoryInfo : InvalidData: (:) [Test-Connection], ParameterBindingValidationException
          + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand

          Не удается индексировать в массив NULL.
          C:\PS ping\script.ps1:51 знак:16
          + $f = $line[ <<<< $i].IndexOf("/")
          + CategoryInfo : InvalidOperation: (1:Int32) [], RuntimeException
          + FullyQualifiedErrorId : NullArray

          Не удается индексировать в массив NULL.
          C:\PS ping\script.ps1:52 знак:16
          + $l = $line[ <<<< $i].LastIndexOf("/")
          + CategoryInfo : InvalidOperation: (1:Int32) [], RuntimeException
          + FullyQualifiedErrorId : NullArray

          Не удается индексировать в массив NULL.
          C:\PS ping\script.ps1:53 знак:18
          + $ip += $line[ <<<< $i].Substring(0,$f)
          + CategoryInfo : InvalidOperation: (1:Int32) [], RuntimeException
          + FullyQualifiedErrorId : NullArray

          Не удается индексировать в массив NULL.
          C:\PS ping\script.ps1:54 знак:19
          + $capt += $line[ <<<< $i].Substring($f+1,$l-$f-1)
          + CategoryInfo : InvalidOperation: (1:Int32) [], RuntimeException
          + FullyQualifiedErrorId : NullArray

          Test-Connection : Не удается проверить аргумент для параметра "ComputerName". Аргумент пустой или имеет значение NULL. Укажите не пустой аргумент, не имеющий значение NULL, после чего повторите выполнение команды.
          C:\PS ping\script.ps1:10 знак:41
          + if ((Test-Connection -Count 1 -computer <<<< $path_ip -quiet) -eq $True) {
          + CategoryInfo : InvalidData: (:) [Test-Connection], ParameterBindingValidationException
          + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand

          Не удается индексировать в массив NULL.
          C:\PS ping\script.ps1:51 знак:16
          + $f = $line[ <<<< $i].IndexOf("/")
          + CategoryInfo : InvalidOperation: (2:Int32) [], RuntimeException
          + FullyQualifiedErrorId : NullArray

          Не удается индексировать в массив NULL.
          C:\PS ping\script.ps1:52 знак:16
          + $l = $line[ <<<< $i].LastIndexOf("/")
          + CategoryInfo : InvalidOperation: (2:Int32) [], RuntimeException
          + FullyQualifiedErrorId : NullArray

          Не удается индексировать в массив NULL.
          C:\PS ping\script.ps1:53 знак:18
          + $ip += $line[ <<<< $i].Substring(0,$f)
          + CategoryInfo : InvalidOperation: (2:Int32) [], RuntimeException
          + FullyQualifiedErrorId : NullArray

          Не удается индексировать в массив NULL.
          C:\PS ping\script.ps1:54 знак:19
          + $capt += $line[ <<<< $i].Substring($f+1,$l-$f-1)
          + CategoryInfo : InvalidOperation: (2:Int32) [], RuntimeException
          + FullyQualifiedErrorId : NullArray

          Test-Connection : Не удается проверить аргумент для параметра "ComputerName". Аргумент пустой или имеет значение NULL. Укажите не пустой аргумент, не имеющий значение NULL, после чего повторите выполнение команды.
          C:\PS ping\script.ps1:10 знак:41
          + if ((Test-Connection -Count 1 -computer <<<< $path_ip -quiet) -eq $True) {
          + CategoryInfo : InvalidData: (:) [Test-Connection], ParameterBindingValidationException
          + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand



            0
            Вижу что не находит метод «Substring», но с чем это связано: мой код, версия ОС или версия Powershell — не могу сказать. Может кто-то с большим опытом сможет указать на причину.

            К сожалению, сейчас нет возможности протестировать в похожих условиях, но как только появится — обязательно проверю и отпишусь.
            +1
            И еще вдогонку:
            Windows server 2016 @ Powershell 5
            Запускается, выдавая первую ошибку. После нажатия Reload выдает 2 и 3.

            Windows PowerShell
            (C) Корпорация Майкрософт (Microsoft Corporation), 2016. Все права защищены.

            PS > $PSVersionTable

            Name Value
            ---- -----
            PSVersion 5.1.14393.3053
            PSEdition Desktop
            PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
            BuildVersion 10.0.14393.3053
            CLRVersion 4.0.30319.42000
            WSManStackVersion 3.0
            PSRemotingProtocolVersion 2.3
            SerializationVersion 1.1.0.1

            PS C:\PS ping> .\script.ps1
            Исключение при вызове "Speak" с "1" аргументами: "Обнаружена ошибка звукового устройства. - Error Code: 0x2"
            C:\PS ping\script.ps1:15 знак:3
            + $voice.Speak("Ошибка! Хост " + $path_ip + ", недоступен!")
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
            + FullyQualifiedErrorId : AudioException

            Не удается найти свойство "imageLocation" для данного объекта. Убедитесь, что оно существует и его можно задать.
            C:\PS ping\script.ps1:11 знак:3
            + $PictureBox.imageLocation = $script_path + "\yes.png"
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : PropertyNotFound

            Исключение при вызове "Speak" с "1" аргументами: "Операция является недопустимой из-за текущего состояния объекта."
            C:\PS ping\script.ps1:15 знак:3
            + $voice.Speak("Ошибка! Хост " + $path_ip + ", недоступен!")
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
            + FullyQualifiedErrorId : InvalidOperationException

            Cancel


            Windows server 2016 @ Powershell 6
            Не запускается совсем.

            PS > $PSVersionTable

            Name Value
            ---- -----
            PSVersion 6.2.1
            PSEdition Core
            GitCommitId 6.2.1
            OS Microsoft Windows 10.0.14393
            Platform Win32NT
            PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
            PSRemotingProtocolVersion 2.3
            SerializationVersion 1.1.0.1
            WSManStackVersion 3.0

            PS > .\script.ps1
            Add-Type : Cannot find path 'C:\PS ping\System.Windows.Forms.dll' because it does not exist.
            At C:\PS ping\script.ps1:2 char:1
            + Add-Type -assembly System.Windows.Forms
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : ObjectNotFound: (C:\PS ping\System.Windows.Forms.dll:String) [Add-Type], ItemNotFoundException
            + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.AddTypeCommand

            Exception setting "Rate": "Object reference not set to an instance of an object."
            At C:\PS ping\script.ps1:7 char:1
            + $voice.Rate = 5
            + ~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
            + FullyQualifiedErrorId : ExceptionWhenSetting

            New-Object : Cannot find type [System.Windows.Forms.Form]: verify that the assembly containing this type is loaded.
            At C:\PS ping\script.ps1:34 char:16
            + $main_form = New-Object System.Windows.Forms.Form
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
            + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

            The property 'Text' cannot be found on this object. Verify that the property exists and can be set.
            At C:\PS ping\script.ps1:35 char:1
            + $main_form.Text = 'Links up'
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : PropertyNotFound

            The property 'Width' cannot be found on this object. Verify that the property exists and can be set.
            At C:\PS ping\script.ps1:36 char:1
            + $main_form.Width = 250
            + ~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : PropertyNotFound

            The property 'Height' cannot be found on this object. Verify that the property exists and can be set.
            At C:\PS ping\script.ps1:37 char:1
            + $main_form.Height = 250
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : PropertyNotFound

            The property 'AutoSize' cannot be found on this object. Verify that the property exists and can be set.
            At C:\PS ping\script.ps1:38 char:1
            + $main_form.AutoSize = $true
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : PropertyNotFound

            The property 'TopMost' cannot be found on this object. Verify that the property exists and can be set.
            At C:\PS ping\script.ps1:39 char:1
            + $main_form.TopMost = $true
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : PropertyNotFound

            New-Object : Cannot find type [System.Windows.Forms.Label]: verify that the assembly containing this type is loaded.
            At C:\PS ping\script.ps1:59 char:35
            + ... ne -label ($label_obj = New-Object System.Windows.Forms.Label) -path_ ...
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
            + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

            New-Object : Cannot find type [system.Windows.Forms.PictureBox]: verify that the assembly containing this type is loaded.
            At C:\PS ping\script.ps1:60 char:45
            + ... ($PictureBox_obj = New-Object system.Windows.Forms.PictureBox) -path_ ...
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
            + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

            You cannot call a method on a null-valued expression.
            At C:\PS ping\script.ps1:62 char:2
            + $main_form.Controls.Add($labels[$i])
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : InvokeMethodOnNull

            You cannot call a method on a null-valued expression.
            At C:\PS ping\script.ps1:63 char:2
            + $main_form.Controls.Add($PictureBoxs[$i])
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : InvokeMethodOnNull

            New-Object : Cannot find type [System.Windows.Forms.Label]: verify that the assembly containing this type is loaded.
            At C:\PS ping\script.ps1:59 char:35
            + ... ne -label ($label_obj = New-Object System.Windows.Forms.Label) -path_ ...
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
            + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

            New-Object : Cannot find type [system.Windows.Forms.PictureBox]: verify that the assembly containing this type is loaded.
            At C:\PS ping\script.ps1:60 char:45
            + ... ($PictureBox_obj = New-Object system.Windows.Forms.PictureBox) -path_ ...
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
            + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

            You cannot call a method on a null-valued expression.
            At C:\PS ping\script.ps1:62 char:2
            + $main_form.Controls.Add($labels[$i])
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : InvokeMethodOnNull

            You cannot call a method on a null-valued expression.
            At C:\PS ping\script.ps1:63 char:2
            + $main_form.Controls.Add($PictureBoxs[$i])
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : InvokeMethodOnNull

            New-Object : Cannot find type [System.Windows.Forms.Label]: verify that the assembly containing this type is loaded.
            At C:\PS ping\script.ps1:59 char:35
            + ... ne -label ($label_obj = New-Object System.Windows.Forms.Label) -path_ ...
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
            + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

            New-Object : Cannot find type [system.Windows.Forms.PictureBox]: verify that the assembly containing this type is loaded.
            At C:\PS ping\script.ps1:60 char:45
            + ... ($PictureBox_obj = New-Object system.Windows.Forms.PictureBox) -path_ ...
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
            + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

            You cannot call a method on a null-valued expression.
            At C:\PS ping\script.ps1:62 char:2
            + $main_form.Controls.Add($labels[$i])
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : InvokeMethodOnNull

            You cannot call a method on a null-valued expression.
            At C:\PS ping\script.ps1:63 char:2
            + $main_form.Controls.Add($PictureBoxs[$i])
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : InvokeMethodOnNull

            New-Object : Cannot find type [System.Windows.Forms.Label]: verify that the assembly containing this type is loaded.
            At C:\PS ping\script.ps1:69 char:14
            + $Label0 = New-Object System.Windows.Forms.Label
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
            + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

            The property 'Text' cannot be found on this object. Verify that the property exists and can be set.
            At C:\PS ping\script.ps1:70 char:1
            + $Label0.Text = Get-Date
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : PropertyNotFound

            The property 'Location' cannot be found on this object. Verify that the property exists and can be set.
            At C:\PS ping\script.ps1:71 char:1
            + $Label0.Location = New-Object System.Drawing.Point(80,180)
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : PropertyNotFound

            The property 'AutoSize' cannot be found on this object. Verify that the property exists and can be set.
            At C:\PS ping\script.ps1:72 char:1
            + $Label0.AutoSize = $true
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : PropertyNotFound

            New-Object : Cannot find type [System.Windows.Forms.Button]: verify that the assembly containing this type is loaded.
            At C:\PS ping\script.ps1:74 char:14
            + $Button = New-Object System.Windows.Forms.Button
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
            + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

            The property 'Location' cannot be found on this object. Verify that the property exists and can be set.
            At C:\PS ping\script.ps1:75 char:1
            + $Button.Location = New-Object System.Drawing.Size(100,200)
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : PropertyNotFound

            The property 'Size' cannot be found on this object. Verify that the property exists and can be set.
            At C:\PS ping\script.ps1:76 char:1
            + $Button.Size = New-Object System.Drawing.Size(80,30)
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : PropertyNotFound

            The property 'Text' cannot be found on this object. Verify that the property exists and can be set.
            At C:\PS ping\script.ps1:77 char:1
            + $Button.Text = "Reload"
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : PropertyNotFound

            You cannot call a method on a null-valued expression.
            At C:\PS ping\script.ps1:79 char:1
            + $Button.Add_Click({
            + ~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : InvokeMethodOnNull

            You cannot call a method on a null-valued expression.
            At C:\PS ping\script.ps1:89 char:1
            + $main_form.Controls.Add($Button)
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : InvokeMethodOnNull

            You cannot call a method on a null-valued expression.
            At C:\PS ping\script.ps1:90 char:1
            + $main_form.Controls.Add($Label0)
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : InvokeMethodOnNull

            You cannot call a method on a null-valued expression.
            At C:\PS ping\script.ps1:92 char:1
            + $main_form.ShowDialog()
            + ~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (:) [], RuntimeException
            + FullyQualifiedErrorId : InvokeMethodOnNull


              0
              Исключение при вызове «Speak» с «1» аргументами: «Обнаружена ошибка звукового устройства. — Error Code: 0x2»
              C:\PS ping\script.ps1:15 знак:3
              + $voice.Speak(«Ошибка! Хост » + $path_ip + ", недоступен!")
              + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
              + CategoryInfo: NotSpecified: (:) [], MethodInvocationException
              + FullyQualifiedErrorId: AudioException
              Первое логично, на серверах редко подразумевается наличие динамика.

              Add-Type: Cannot find path 'C:\PS ping\System.Windows.Forms.dll' because it does not exist.
              At C:\PS ping\script.ps1:2 char:1
              + Add-Type -assembly System.Windows.Forms
              + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
              + CategoryInfo: ObjectNotFound: (C:\PS ping\System.Windows.Forms.dll:String) [Add-Type], ItemNotFoundException
              + FullyQualifiedErrorId: PathNotFound,Microsoft.PowerShell.Commands.AddTypeCommand
              Странно.

              Давайте так, хоть где-то удалось запустить? =)

        +1
        Что только не придумают чтобы ставить zabbix… (можно вписать любую систему мониторинга)
        Если много свободного времени, то лучше его потратить на настройку системы, а не ее написание.
          +1
          $script_path = "C:\Test\PS ping"

          Есть же $PSScriptRoot

            0
            Не не зналдумал об этом.
            Действительно, можно. Внесу в изменения.
            0
            1. Вы уже рисуете точки. Что мешает вам нарисовать зеленый и красный квадратик system.drawing.graphics.fillrectangle? Тогда вам ненужны эти картинки-лампочки.
            2. Зачем вы парсите тектовый файл? Скрипт сам по себе текстовый файл. Объявите хэш таблицу с ip и описнаиями в начале скрипта и пробегайте её в цикле. Надо добавить что-то новое, убрать старое — открыли блокнотом и поправили одну переменную.
              +1
              Как вставить свою иконку в форму
              Ресурс храниться внутри скрипта.
              #каринка в base64
              base64IconString = "iVB...mCC"
              $bitmap = New-Object System.Windows.Media.Imaging.BitmapImage
              $bitmap.BeginInit()
              $bitmap.StreamSource = [System.IO.MemoryStream][System.Convert]::FromBase64String($base64IconString)
              $bitmap.EndInit()
              $bitmap.Freeze()
              
              $form.Icon = $bitmap
              

              почти таким же способом можно подгрузить и другие ресурсы

              Смена цвета элемента в заместо подгрузки картинки
              Фрагмент вырезан из другого скрипта и не адаптирован под текущий…
              В данном примере элемент $Ping кнопка и она меняет цвет и текст в зависимости от результата…
                      if (Test-Connection $ComboBox.Text -Count 1 -Quiet) {
                          $Ping.content = "Ping ok"
                          $Ping.Background = "#FF3BE62A"
                      }
                      else {
                          $Ping.content = "Ping ERROR!"
                          $Ping.Background = "#FFE62A2A"
                      }
              

              можно сделать что бы пинговать только отдельный ресурс…

              Хранение настроек в XML
              Пример хранения настроек в XML. Взято из другого скрипта
              <?xml version="1.0" encoding="utf-8"?>
              <Settings>
                <Windows>
                  <WindowsTitle>Выберите компьютер</WindowsTitle>
                  <WindowsToolTip>Программа передает имя компьютера как параметр на запуск</WindowsToolTip>
                  <Lable>Выберите компьютер из списк или внесите свой:</Lable>
                  <ToolTip>Выберите компьютер из списк или внесите свой, для запуска остнастки</ToolTip>
                  <Button>Запустить остнаску</Button>
                </Windows>
                <Programm>
                  <RunnungString>c:\windows\system32\compmgmt.msc</RunnungString>
                  <ParametrString>/s /computer:$parametr</ParametrString>
                </Programm>
                <list>
                  <name>Server.Domain.name</name>
                  <name>OtherServer.Domain.name</name>
                </list>
              </Settings>
              

              $myDir = [System.AppDomain]::CurrentDomain.BaseDirectory.TrimEnd('\')
              if ($myDir -eq $PSHOME.TrimEnd('\')) { $myDir = $PSScriptRoot}
              If (!(Test-Path $MyDir\Settings.xml)) {write-host 'Setting file ' $MyDir\Settings.xml ' not found..' -ForegroundColor Red ; exit}
              [xml]$ConfigFile = Get-Content $ConfigXML
              
              
              $form.Title = $ConfigFile.Settings.Windows.WindowsTitle
              $form.ToolTip = $ConfigFile.Settings.Windows.WindowsToolTip
              
              $ComboBox.ToolTip = $ConfigFile.Settings.windows.ToolTip
              $button1.Content = $ConfigFile.Settings.windows.Button
              
              $RunnungProgrammString = $ConfigFile.Settings.Programm.RunnungString
              $RunnungParametrString = $ConfigFile.Settings.Programm.ParametrString
              
              #Добвление в цикле множества элементов
              [array]$ComboBoxItems = $ConfigFile.Settings.list.name
              $ComboBoxItems| % {
                  $ComboBox.Items.Add($_)| out-null
              }
              

              Сохранение настроек в XML. Добавление новых записей в секцию Settings\list
              $ConfigFile.Settings.list.AppendChild($newSettings)
              $ConfigFile.Save($ConfigXML)
              



              Формы проще рисовать через XAML (есть графические редкаторы).
              Для уведомления можно сделать не только звук но и Baloon Popup в трее.
                0
                Формы проще рисовать через XAML (есть графические редкаторы).
                Возможно, но я в тех туториолах что я находил для этой утилиты, была такая примитивная реализация.
                Для уведомления можно сделать не только звук но и Baloon Popup в трее.
                Любопытно, особенно то, что в спойлерах, возьму на заметку. =)
                0

                Есть уже готовый софт для таких целей PingInfoView

                  +1
                  Можно без релоада периодически выполнять скрипт, например в цикле со слипом на нужное кол-во секунд.
                  Не знаю — можно тут сторонние ссылки? Вот первое, что нагуглилось — ephos.github.io/posts/2018-8-20-Timers
                  Там несколько разных вариантов работы с таймерами в powershell.
                    0
                    Пробовал запихивать в цикл while с засыпанием на небольшое время, до 3600s, в результате чего окно повисало на период между обновлениями или не запускалось вовсе. Возможно что-то дела не правильно, но спасибо за источник, ознакомлюсь. =)
                      0
                      Лучше вот так:
                      кусок кода
                      $timer=New-Object System.Windows.Forms.Timer
                      $timer.Interval=1000
                      $timer.add_Tick({Write-Host(«TODO»)})
                      $timer.Start();
                      $main_form.add_Closing({$timer.Stop()});
                      $main_form.ShowDialog() #Вызываем «окно»

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое