Скрипт массового изменения настроек заданий Veeam Backup

Скрипт берет список задач, которые бекапят сервера на виртуализации VMware и у которых параметр Enable VMware Tools quiescence не включен и меняет эту настройку на «Включено»
Import-Module Veeam.Backup.PowerShell
$jobs = Get-VBRJob | where {($_.Options.ViSourceOptions.VMToolsQuiesce -like 'False') -and ($_.TypeToString -like "VMware Backup")}
Foreach ($job in $jobs)
{
$options = Get-VBRJobOptions $job
$Options.ViSourceOptions.VMToolsQuiesce = $True
Set-VBRJobOptions -job $job -options $options
}

Контролируем доступ по RDP с помощью Telegram и Powershell

Решил я тут на днях  немного упростить жизнь и заодно вспомнить что такое написание скриптов. Не буду хвалиться,  скажу сразу, что за образец взял готовую статью с habr.com. Мне понадобилось с помощью телеграмм и его бота управлять правилом встроенного в windows server брандмауэра. На нём настроено ограничение на вход по RDP с конкретных подсетей (сами знаете что будет, если RDP сервис выставить в интернет без какой-либо защиты). Вот готовый скрипт, осталось только в нем вставить идентификатор бота, аккаунта с которого можно будет отправлять ему сообщения и возможно поменять название правил файервола (у меня они со стандартными именами в русской локализации). После этого добавляйте его в планировщик задач.
Читать далее «Контролируем доступ по RDP с помощью Telegram и Powershell»

Veeam BR: Сбор информации о способах бекапа SQL-серверов

Привет!
Понадобилось выяснить как резервируются сервера SQL в Veeam Backup & Replication. Как известно, их можно бекапить несколькими способами

Вариант бекапа «Try application processing, but ignore failures» у меня не используется, поэтому в скрипте он не обрабатывается. Проверка идет только заданий типа «Windows Agent Backup» и «VMware Backup», то есть Linux, Copy Backup и ленты я не рассматриваю.

Add-PSSnapin VeeamPSSnapIn
$Jobs = Get-VBRJob | where {($_.IsScheduleEnabled -like "True") -and ($_.VssOptions.VssSnapshotOptions.ApplicationProcessingEnabled -like "True") -and ($_.VssOptions.VssSnapshotOptions.Enabled -like "True") -and (($_.TypeToString -like "Windows Agent Backup") -or ($_.TypeToString -like "VMware Backup"))}
foreach ($Job in $Jobs)
{
$Objects = $Job.GetObjectsInJob()
foreach ($Object in $Objects)
{
if (($Object.VssOptions.VssSnapshotOptions.Enabled -like 'True') -and ($Object.VssOptions.VssSnapshotOptions.IgnoreErrors -like 'False'))
{
if ($Object.VssOptions.VssSnapshotOptions.IsCopyOnly)
{
Write-Host "-----------------------------------------------------------------"
Write-Host "Имя задания: " $Job.Name
Write-Host "Имя сервера: " $Object.Name
Write-Host "Тип бекапа SQL: Transaction logs copy only (Lets another application use logs)"
}
else
{
if ($Object.VssOptions.SqlBackupOptions.TransactionLogsProcessing -like 'NeverTruncate')
{
Write-Host "-----------------------------------------------------------------"
Write-Host "Имя задания: " $Job.Name
Write-Host "Имя сервера: " $Object.Name
Write-Host "Тип бекапа SQL: Do not truncate logs (Simple Recovery Model)"
}
if ($Object.VssOptions.SqlBackupOptions.TransactionLogsProcessing -like 'TruncateOnlyOnSuccessJob')
{
Write-Host "-----------------------------------------------------------------"
Write-Host "Имя задания: " $Job.Name
Write-Host "Имя сервера: " $Object.Name
Write-Host "Тип бекапа SQL: Truncate logs (prevents logs from growing forever)"
}
if ($Object.VssOptions.SqlBackupOptions.TransactionLogsProcessing -like 'Backup')
{
$Time_min = $Object.VssOptions.SqlBackupOptions.BackupLogsFrequencyMin
Write-Host "-----------------------------------------------------------------"
Write-Host "Имя задания: " $Job.Name
Write-Host "Имя сервера: " $Object.Name
Write-Host "Тип бекапа SQL: Backup SQL logs every $Time_min minutes"
}
}
}

}
}

Удаленное изменение настроек SNMP в Windows используя скрипт

Привет! Появилась задача: необходимо на серверах с Windows Server 2003 и выше поменять настройки службы SNMP, добавив в настройки еще один IP-адрес, куда будут отсылаться SNMP-трапы и куда их вообще разрешено отсылать. Приведу два скриншота того, о чем я говорю:

Получается, на примере мне надо добавить к IP-адресу 192.168.1.1 еще один, например 192.168.1.2 в обе вкладки.

Проблемы были следующие: компьютеры не в домене; на компьютерах может быть разный локальный администратор и пароль.
Из плюсов: к большинству серверов имеется удаленный доступ к административной шаре admin$.

Был написан скрипт на powershell, который из файла на рабочем столе с именем servers.csv и содержащим список серверов, берет имя сервера и стандартными командами reg и sc очищает ветки реестра, в которых находятся нужные нам данные, и перезагружает службу SNMP. В случае проблем с подключением к серверу, выводится сообщение об ошибке. В процессе работы скрипта лишний вывод сообщений в окно терминала отключен.

Вот содержимое файла servers.csv для примера:

А вот содержимое самого скрипта:

chcp 1251 | Out-Null
cd ~/Desktop
$List = Import-Csv Servers.csv -Header Servers
Foreach ($s in $List) {
    $Serv= $s.Servers
    Write-Host "Подключение к серверу: " -foregroundcolor yellow -NoNewline  
    Write-Host $Serv -foregroundcolor green
    reg delete \\$Serv\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services\SNMP\Parameters\PermittedManagers /va /f   
    reg add \\$Serv\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services\SNMP\Parameters\PermittedManagers /v 1 /t REG_SZ /d 192.168.1.1 2>&1 | Out-Null
    reg add \\$Serv\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services\SNMP\Parameters\PermittedManagers /v 2 /t REG_SZ /d 192.168.1.2 2>&1 | Out-Null
    
    reg delete  \\$Serv\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services\SNMP\Parameters\TrapConfiguration\SPECTRUM  /va /f 2>&1 | Out-Null
    reg add \\$Serv\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services\SNMP\Parameters\TrapConfiguration\SPECTRUM /v 1 /t REG_SZ /d 192.168.1.1 2>&1 | Out-Null
    reg add \\$Serv\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services\SNMP\Parameters\TrapConfiguration\SPECTRUM /v 2 /t REG_SZ /d 192.168.1.2 2>&1 | Out-Null
    sc.exe \\$Serv stop SNMP 2>&1 | Out-Null
    sc.exe \\$Serv start SNMP 2>&1 | Out-Null
}

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

runas /netonly /user:admin "powershell"

подразумевая, что на сервере есть пользователь admin с паролем. Если учетная запись не присутствует на сервере или пароль был введен не правильно, то при выполнении скрипта появится сообщение «Ошибка: Отказано в доступе». Если к серверу невозможно подключиться по причине его сетевой недоступности, то тогда ошибка будет «Ошибка: Не найден сетевой путь». 

В случае успешного подключения появится сообщение «Операция успешно завершена«. Язык вывода сообщений конечно же зависит от установленного языка интерфейса в системе.

Powershell cкрипт для массового отображения/изменения ACL на файловом сервере

Привет! Понадобилось мне недавно провести вручную операцию по удалению из ACL лишней записи. Проблема была в том, что таких папок было около 50 штук. Спасало только то, что это нужно было сделать в папке с одним и тем же именем в разных директориях с одним общим корнем. Я это сделал вручную, так как задачу надо было выполнить быстро, но решил для себя написать скрипт, чтобы можно было быстро править ACL-листы папок в Windows. Необходимо только, чтобы на сервере был Powershell и немного его знания, так как скрипт практически не оттестирован и не проверен в реальной работе на большом количестве данных.
Вот он сам, по комментариям внутри него думаю будет понятно, что надо делать.

function Print_Dir($list_catalogs)
    {
         $Table = New-Object system.Data.DataTable “$Table_Security”  <#Создание таблицы для отображения ACL#>
         $column1 = New-Object system.Data.DataColumn Директория,([string])
         $column2 = New-Object system.Data.DataColumn ACL,([string])
         $column3 = New-Object system.Data.DataColumn Владелец,([string])
         $table.columns.add($column1)
         $table.columns.add($column2)
         $table.columns.add($column3)
         foreach ($Foundfolder in $list_catalogs) 
            {
                $Access = (Get-Acl $Foundfolder.FullName).AccessToString  
                $row = $table.NewRow()
                $row.Директория = $Foundfolder.FullName
                $row.ACL = “$Access”
                $row.Владелец = $AccessACL.Owner
                $table.Rows.Add($row)
            } 
         $table | format-table -Wrap
        
    }

function Found_Folder()
    {
                $Root_Path = Read-Host 'Введите корневую директорию'   <#Директория в которой производится поиск по-умолчанию#>
                $Target_Folder = Read-Host 'Введите название папки'    <#папка для поиска по-умолчанию #>
                $FFolder = Get-ChildItem $Root_Path -Recurse -Directory -Force -ErrorAction SilentlyContinue -Filter "$Target_Folder" <#Найденные папки#>
                return $FFolder
    }

While ($KEY='1')
{
$ACL_Key = Read-Host 'Что необходимо сделать? (1 - Вывести текущие значения ACL; 2 - Добавление; 3 - Удаление; 4 - Выход из программы)'
switch ($ACL_Key)
{
    1 
        {
                $Found_Folder = Found_Folder
                Print_Dir($Found_Folder)
        }
    2
        {
                $Found_Folder = Found_Folder
                $AccessRuleAdd = New-Object System.Security.AccessControl.FileSystemAccessRule "ONIX\TestAV", "Modify","ContainerInherit,ObjectInherit", "None","Allow" <#ACL, которую требуется добавить в папки#>
                foreach ($Foundfolder in $Found_Folder)
                    {
                        $ACL = $Foundfolder.GetAccessControl("Access")  
                        $ACL.AddAccessRule($AccessRuleAdd)
                        $Foundfolder.SetAccessControl($ACL)
                    } 
                Print_Dir($Found_Folder)
        }
    3
        {
                $Found_Folder = Found_Folder
                $AccessRuleDel = New-Object System.Security.AccessControl.FileSystemAccessRule "ONIX\TestAV", "Modify","ContainerInherit,ObjectInherit", "None","Allow" <#ACL, которую требуется добавить в папки#>
                foreach ($Foundfolder in $Found_Folder)
                    {
                        $ACL = $Foundfolder.GetAccessControl("Access")  
                        $ACL.RemoveAccessRule($AccessRuleDel)
                        $Foundfolder.SetAccessControl($ACL)
                    } 
                Print_Dir($Found_Folder)
        }
    4   
        {
            exit
        }
} 
}

Автоматическое восстановление базы данных SQL из Veeam Backup

Понадобилось автоматически восстанавливать MS SQL базу данных из Veeam Backup & Replication 9.5. Задание должно было запускаться сразу после того, как заканчивалось резервное копирование и восстановление должно было происходить на другой сервер. Вот какой скрипт у меня получился:

Add-PSSnapin VeeamPSSnapIn

$source_host        = "SQL_Original"
$source_db1         = "DB1"
$source_db2         = "DB2"
$source_db3         = "DB3"
$source_db4         = "DB4"
$target_host        = "SQL_Target"
$target_cred        = Get-VBRCredentials -Name "onix\onix_backup"
$target_instance    = "test"

$target_db1    = $source_db1
$target_db2    = $source_db2
$target_db3    = $source_db3
$target_db4    = $source_db4

$(
$restorepoint = Get-VBRApplicationRestorePoint -SQL -Name $source_host| Sort-Object creationtime -Descending | Select-Object -First 1
$database1 = Get-VBRSQLDatabase -ApplicationRestorePoint $restorepoint -Name $source_db1
$database2 = Get-VBRSQLDatabase -ApplicationRestorePoint $restorepoint -Name $source_db2
$database3 = Get-VBRSQLDatabase -ApplicationRestorePoint $restorepoint -Name $source_db3
$database4 = Get-VBRSQLDatabase -ApplicationRestorePoint $restorepoint -Name $source_db4
$restore_session = Start-VBRSQLDatabaseRestore -Database $database1 -ServerName $target_host -InstanceName $target_instance -DatabaseName $target_db1 -GuestCredentials $target_cred -SqlCredentials $target_cred -Force -Wait
$restore_session = Start-VBRSQLDatabaseRestore -Database $database2 -ServerName $target_host -InstanceName $target_instance -DatabaseName $target_db2 -GuestCredentials $target_cred -SqlCredentials $target_cred -Force -Wait
$restore_session = Start-VBRSQLDatabaseRestore -Database $database3 -ServerName $target_host -InstanceName $target_instance -DatabaseName $target_db3 -GuestCredentials $target_cred -SqlCredentials $target_cred -Force -Wait
$restore_session = Start-VBRSQLDatabaseRestore -Database $database4 -ServerName $target_host -InstanceName $target_instance -DatabaseName $target_db4 -GuestCredentials $target_cred -SqlCredentials $target_cred -Force -Wait
) *>&1 >> "C:\SQLRestore_errors.log"

Все достаточно просто: берется оригинальный хост с базами данных, список баз для восстановления, хост на который надо восстановить, учетная запись от имени которой будет восстановление (должна быть сохранена в «Manage Credentials» и целевой инстанс SQL если требуется). Находится последняя точка резервного копирования базы (на точку восстановления с проигрыванием логов восстановиться можно, но не получится это сделать автоматически) и восстанавливается. Если происходят ошибки, то они выводятся в текстовый файл (в консоли Veeam ошибки работы скрипта показаны не будут).

Остается в задаче, которая бекапит SQL-сервер, настроить запуск скрипта, делаем это в «Advanced Settings». Задаем так строку C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noninteractive -file «C:\Scripts\SQLRestore.ps1»

Удаление ярлыка с рабочего стола через GPO средствами Powershell

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

Перед тем, как настраивать GPO для запуска powershell-скрипта на компьютерах необходимо средствами групповых политик разрешить запуск PS-скриптов. По-умолчанию, на компьютере запрещен их запуск. Поэтому заходим в дефолтную GPO для домена в User Configuration — Policies — Administrative Templates — Windows Components — Windows PowerShell. Находим там политику «Turn on Script Execution«, включаем её и переводим в режим работы «Allow local scripts and remote signed scripts«.

Теперь можно настраивать политику, которая будет запускать PS-скрипт при входе пользователя в систему. Для этого заходим в созданную для этого политику в раздел User Configuration — Policies  — Windows Settings — Scripts — Logon. Выбираем вкладку «PowerShell Scripts» и нажимаем кнопку «Add». Далее нажимаем кнопку «Browse» и в открывшееся окно проводника копируем файл с powershell-скриптом. После этого выбираем этот файл и нажимаем «Open» в этом же окне и «OK» в предыдущем. Таким образом политика настроена.

А вот текст самого скрипта. Сохраняем его под расширением PS1 у себя на компе.

$DeleteItem = "Кодекс-клиент.lnk"
$Date = Get-Date
$ComputerName = $Env:Computername
$UserName = $Env:UserName
$DesktopPath = [Environment]::GetFolderPath("Desktop")
$ShortcutFiles = Get-ChildItem -Path $DesktopPath -Recurse -Include *.lnk
Write-Host "Всего найдено ярлыков:" $ShortcutFiles.Count -ForegroundColor Green
$Changed = 0
ForEach ($Shortcut in $ShortcutFiles) {
IF ($Shortcut -match $DeleteItem ) {
Remove-Item $Shortcut
$Changed ++
}
}
Write-Host "Удалено ярлыков:" $Changed -ForegroundColor Green
echo $Date $UserName "Всего найдено ярлыков:" $ShortcutFiles.Count "Удалено ярлыков:" $Changed >> \\DC-01\C$\Logs_remove_shortcut\$ComputerName.log

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

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

Поиск по реестру Windows через командную строку или powershell

Иногда очень нужно в скриптах выполнить поиск определенного ключа и например поменять его. Для того чтобы осуществить поиск в Windows есть команда reg query. Ее нужно запускать с определенными ключами. У меня эта команда обычно выглядит следующим образом:

reg query HKCU\Software\Microsoft /f "test" /s /d /e

HKCU\Software\Microsoft — ветка реестра, по которой осуществляется поиск; после ключа /f вписываем в кавычках шаблон для поиска; ключ /s осуществляет поиск по вложенным подразделам и их параметрам; ключ /d указываем на поиск только по данным, а не по названию ключей и наконец ключ /e, говорит, что надо возвращать только точные совпадения.

Создание ярлыка и символической ссылки через powershell

Недавно мне понадобилось создать через Deployment Workbench в Task Sequense ярлыки на рабочем столе пользователей при установке Windows 10. Казалось что может быть проще? На самом деле пришлось повозиться.

Я всегда думал, что символическая ссылка и обыкновенный ярлык в проводнике Windows — это одно и тоже. Чем же отличаются ярлык и символическая ссылка?

  1. для создания символической ссылки требуются права администратора
  2. если скопировать символическую ссылку в другую директорию, то вместо символической ссылки окажется файл, на который ссылается ссылка, но без расширения. Например сделаем так:
    cp "Internet Explorer" .\test2\
    и вот что можно увидеть в папке test2
  3. В проводнике Windows отображает эти ресурсы по-разному:
    Поэтому для сценариев развертывания Windows лучше всего подойдет возможность создания ярлыков: их можно и скопировать и прав администратора они не будут запрашивать. Создавать только их сложнее: необходимо вписать следующие строчки в ваш скрипт:
    $Install_Path = "C:\Users\AVIvanov\Desktop\test"
    $WSShell = New-Object -com WScript.Shell
    $ShortcutPath = Join-Path -Path $Install_Path -ChildPath "Internet Explorer.lnk"
    $NewShortcut = $WSShell.CreateShortcut($ShortcutPath)
    $NewShortcut.TargetPath = "C:\Program Files\Internet Explorer\iexplore.exe"
    $NewShortcut.Save()
    Так мы создадим ярлык на Internet Explorer в папке «test» на моем рабочем столе.

    Если нужно создать ярлык на URL-адрес, то это будет выглядеть немного по другому:
    $Install_Path = "C:\Users\AVIvanov\Desktop\test"
    $WSShell = New-Object -com WScript.Shell
    $ShortcutPath = Join-Path -Path $Install_Path -ChildPath "Яндекс.url"
    $NewShortcut = $WSShell.CreateShortcut($ShortcutPath)
    $NewShortcut.TargetPath = "https://yandex.ru"
    $NewShortcut.Save()

    Ещё пример: скрипт, который будет открывать в Google Chrome сайт mail.ru:
    $Install_Path = "C:\Users\onix\Desktop"
    $WSShell = New-Object -com WScript.Shell
    $ShortcutPath = Join-Path -Path $Install_Path -ChildPath "MAILRU.lnk"
    $NewShortcut = $WSShell.CreateShortcut($ShortcutPath)
    $NewShortcut.TargetPath = "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
    $NewShortcut.Arguments = "https://mail.ru"
    $NewShortcut.Save()

    Если кому-то все же понадобится создать именно символическую ссылку через powershell, то такая возможность появилась совсем недавно в Windows 10 1703:
    New-Item -Path "C:\Users\AVIvanov\Desktop\test\Internet Explorer" -value "C:\Program Files\Internet Explorer\iexplore.exe" -ItemType symboliclink -в той же папка «test» на рабочем столе

Сброс пароля пользователя Active Directory

Пароль можно сбросить и потребовать установить новый 4 способами

1) через командную строку с помощью dsmod
dsmod user DN_пользователя -pwd Новый_пароль -mustchpwd yes

2) через PowerShell
$objUser=[ADSI]"LDAP://DN_пользователя"
$objUser.SetPassword("Новый_пароль")
$objUser.Put ("pwdLastSet",0) //требование сменить пароль
$objUser.SetInfo()

3) через VBScript
Set objUser=GetObject("LDAP://DN_пользователя")
objUser.SetPassword "Новый_пароль"
objUser.Put "pwdLastSet",0
objUser.SetInfo

4) импортирование пароля с помощью команды LDIFDE. Подробности по ссылке http://support.microsoft.com/kb/263991