System Center Orchestrator: Шаблон ранбука

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Всем привет!

Хочу поделиться PowerShell шаблоном для создания ранбуков. Хотя правильнее, наверное, сказать Activity.

Входящие данные и различные параметры будем хранить в хэш таблице $rb_input объявленной в начале скрипта. В последующем эту переменную будет удобно передавать во все функции и вызовы Invoke-Command через весь скрипт в качестве параметра. А также с помощью такой организации входящих данных скрипт становится легко переносимым на другие системы и конфигурации: меняем всё в $rb_input и переиспользуем.

Многие общие параметры для ранбуков я храню во внешнем файле и ссылаюсь на них в $rb_input. Изменение скриптов во внешнем редакторе становится удобнее, чем при использовании переменных Оркестратора. В переменных Оркестратора я храню только пароли.

На выходе ранбук возвращает обязательные параметры: количество возникших ошибок в переменной $errors, количество предупреждений $warnings и текст этих самых ошибок и предупреждений в $message. И не обязательные - результат работы ранбука или Activity.

Чтобы иметь возможность выстраивать цепочки из Activity или вызовов других ранбуков (Invoke Runbook) без лишних ветвлений, на входе принимаем возвращенные данные от предыдущего Activity: $errors, $warnings, $message. В процессе работы скрипта эти переменные инкрементируются и дополняются. На выходе из последнего Activity получим полную картину ошибок и предупреждений. Достаточно будет забрать эти переменные из последнего Activity.

Если предыдущие Activity или вызовы других ранбуков основанных на этом шаблоне завершились с ошибками, то скрипт не будет выполняться. Для этого проверяем равна ли нулю переменная $errors.

# Runbook template

# Файл с общими часто используемыми параметрами
. c:\Orchestrator\settings\config.ps1

# Все входящие и не публичные параметры указываем в $rb_input

$rb_input = @{
	param1 = '{Пользовательский параметр1 из Initialize Data}'
	param2 = '{Пользовательский параметр2 из Initialize Data}'
	mail_to = ''
	mail_to_admin = 'admin@example.org'
	ps_server = 'localhost'
	ps_user = ''
	ps_passwd = ''
	who_start_runbook = '{Параметр одного из предыдущих Activity}'
	smtp_server = $global:smtp_server    # Переменная из файла config.ps1 с адресом SMTP сервера
	smtp_from = $global:smtp_from        # Переменная из файла config.ps1 с адресом отправителя
}

# Если ранбуки или Activity запускаются цепочкой, то результат возвращенный предыдущим
# Activity указываем здесь

$result = @{
	errors = [int] '{errors из предыдущего Activity либо оставляем 0}'
	warnings = [int] '{warnings из предыдущего Activity либо оставляем 0}'
	messages = @(@'
{message из предыдущего Activity либо оставляем пустым}
'@)
}

# Основной блок ранбука

$DebugPreference = 'SilentlyContinue'  # Изменить на Continue для отображения отладочных сообщений
$ErrorActionPreference = 'Stop'

function main($rb_input)
{
	trap
	{
		return @{ errors = 0; warnings = 0; messages = @("Critical error[{0},{1}]: {2}`r`n`r`nProcess interrupted!`r`n" -f $_.InvocationInfo.ScriptLineNumber, $_.InvocationInfo.OffsetInLine, $_.Exception.Message); }
	}

	try
	{
		$result = @{ errors = 0; warnings = 0; messages = @() }

		# Проверка корректности заполнения полей

		if([string]::IsNullOrWhiteSpace($rb_input.param1))
		{
			$result.errors++; $result.messages += 'Ошибка: Не заполнено поле param1';
		}

		if([string]::IsNullOrWhiteSpace($rb_input.param2))
		{
			$result.errors++; $result.messages += 'Ошибка: Не заполнено поле param2';
		}

		if($result.errors -gt 0)
		{
			return $result
		}

		# *** PUT YOU CODE HERE ***

		Write-Debug 'PUT YOU CODE HERE'

		$result['other_param1'] = 'Какая-то произвольная переменная с результатами работы скрипта'
		
		return $result
	}
	catch
	{
		$result.errors++; messages += ('ERROR[{0},{1}]: {2}' -f $_.InvocationInfo.ScriptLineNumber, $_.InvocationInfo.OffsetInLine, $_.Exception.Message);
		return $result
	}
}


# Выполняем ранбук, только если предыдущие завершились без ошибок

if($result.errors -eq 0)
{
	$output = main -rb_input $rb_input

	# Объединяем результаты с предыдущим ранбуком

	$result.error += $output.error
	$result.warnings += $output.warnings
	$result.messages += $output.messages

	# Если скрипт завершился без ошибок, то возвращаем и результат

	if($output.errors -eq 0)
	{
		$other_param1 = $output.other_param1	# Та самая произвольная переменная
	}
}

# Возврат значений

$exit_code = 0
if($result.errors -gt 0 -or $result.warnings -gt 0)
{
	$exit_code = 1
}

$errors = $result.errors
$warnings = $result.warnings
$message = $result.messages -join "`r`n"

Write-Debug ('Errors: {0}, Warnings: {1}, Messages: {2}' -f $errors, $warnings, $message)

Если ранбук предназначен для запуска конечным пользователем, а не для вызова из других ранбуков, в последнем Activity я использую скрипт ThrowException для визуализации того, что ранбук завершился с ошибками или предупреждениями. В этом случае в консоли меняется иконка на восклицательный знак.

[int] $errors = '{errors из последнего Activity}'
[int] $warnings = '{warnings из последнего Activity}'
$message = @'
{message из последнего Activity}
'@

if(($errors + $warnings) -ne 0)
{
	throw New-Object System.Exception('Message: {0}' -f $message)
}

Скрипт на GitHub

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

И ещё мучает вопрос, на который не смог найти ответ. Как экранировать пользовательский ввод? Orchestrator в голом виде вставляет введенные пользователем параметры и легко можно сделать инъекцию в скрипт.

Источник: https://habr.com/ru/post/573790/


Интересные статьи

Интересные статьи

В этой статье мы расскажем, как оптимизировать крупный проект в «Битрикс24» и увеличить его производительность в 3 раза, изменяя настройки MySQL и режим питания CPU. Дано Корпоративн...
Всем привет. Если вы когда-либо работали с универсальными списками в Битрикс24, то, наверное, в курсе, что страница детального просмотра элемента полностью идентична странице редак...
Задача При разработке нашей игры The Unliving, мы поставили перед собой задачу по отображению различных сообщений, таких, как нанесенный урон, нехватка здоровья или энергии, величина награды...
Схема компьютера CS-1 показывает, что большая часть отведена для питания и охлаждения гигантского «процессора-на-пластине» Wafer Scale Engine (WSE). Фото: Cerebras Systems В августе 2019 го...
Однажды, в понедельник, мне пришла в голову мысль — "а покопаюсь ка я в новом ядре" (новым относительно, но об этом позже). Мысль не появилась на ровном месте, а предпосылками для нее стали: ...