Рубрика: Скрипты

Здесь собраны разные специфичные и не очень скрипты, не воспринимайте это как портфолио. Это скорее моя публичная записная книжка.

  • «Безопасная» смена пароля пользователя AD.

    Приветствую. После того как я полностью автоматизировал AD в организации, появилась острая необходимость в безопасной передаче пароля конечному пользователю. По регламенту организации сотрудники тех поддержки не должны видеть пароль но при этом должны как то его сбросить и передать. Решение было быстрым и тривиальным. Написал простенький инструмент. конвертировал его в EXE и отдал сотрудникам. Сразу же сообщу. В коде много обращений к API внутренних сервисов организации, у вас же это может сильно отличатся. например функции отправки СМС и получения инфы о пользователе завязаны на веб-серверах, у вас это может иметь другую реализацию. На худой конец, вы всегда можете закомментировать не нужные строки и пользоваться кор-функционалом с отправкой данных только на e-mail.

    Тыкни
    <#
    Итак. Все обращения к базам данных HR у нас осуществляются по API. У вас же это может выполнятся разными способами. 
    #>
    $global:headers = @{
        "content-type"  = "application/json"
        "Authorization" = "Basic SOMEA123123123123123TOKEN"
    }
    # Запрос к API базы HR  
    function Invoke-APIRequest 
    {
        param (
            [Parameter(Mandatory)]
            [string]$Uri,
            [Parameter()]
            [string]$Method = 'POST',
            [Parameter()]
            [string]$Body
        )
        return Invoke-RestMethod -Uri $Uri -Method $Method -Headers $global:headers -Body $Body
    }
    # Получение списка сотрудников
    function Get-Employees 
    {
    $body = @"
    {
            "get_child_departments":"True"
    }
    "@
    return $(Invoke-APIRequest -Uri 'https://hr.domain.site/api/employees' -Body $body)
    }
    # Получение всех Департаментов банка
    function Get-Departments 
    {
    $body = @"
    {
            "get_child_departments":"True"
    }
    "@
        return Invoke-APIRequest -Uri 'https://hr.domain.site/api/gate/department' -Body $body
    }
    # Очевидно
    function get-userinf
    {
    $ipregex = "^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$"
    [string]$date = get-date -Format "yyyy.MM.dd hh-mm"
    $ipaddress = Get-NetIPAddress | ? IPAddress -Match  $ipregex | select IPAddress
    $return = @{
    "Username" = $($env:USERNAME)
    "ComputerName" = $($env:COMPUTERNAME)
    "IPAdress" = $ipaddress.IPAddress
    "ExecutionDate" = $($date)
    }
    
    return ConvertTo-Json $return
    }
    # Очевидно
    function Check-User 
    {
        param (
            [Parameter(Mandatory)]
            [string]$samaccountname = "ttestesteronyan"
        )
    
        try {
            # получаем пользователя
            $user = Get-ADUser -Filter * -Properties GivenName,Surname,Name,Department,Title,Mobile,wWWHomePage,EmployeeID,EmployeeNumber,whenCreated |`
                  ? {if(($_.EmployeeID -eq $samaccountname) -or ($_.Name -eq $samaccountname) -or ($_.employeeNumber -eq $samaccountname) -or ($_.SamaccountName -eq $samaccountname)){$_}}
            $HRuser = (get-Employees).data `
                    | ? {if(($_.employee_id -eq $samaccountname) -or ($_.Name -eq $samaccountname) -or ($_.system_id -eq $samaccountname) -or ($_.name -eq $user.name)){$_}} `
                    | select firstname, lastname, Name, department_id, Employee_ID, email, phone_number ,System_ID 
    
            # собираем данные
            $output = @()
            if($user){
            $output += "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *"
            $output += "Информация о пользователе $Username :"
            $output += "====================================="
            $output += "Логин: $($user.samaccountname)"
            $output += "Статус учетки пользователя: $(if($user.enabled){"включена"}else{"выключена"})"
            $output += "Имя: $($user.GivenName)"
            $output += "Фамилия: $($user.Surname)"
            $output += "ФИО: $($user.Name)"
            $output += "Департамент: $($user.Department)"
            $output += "Должность: $($user.Title)"
            $output += "Мобильный телефон: $($user.Mobile)"
            $output += "Личная почта: $($user.wWWHomePage)"
            $output += "Номер сотрудника: $($user.EmployeeID)"
            $output += "ID сотрудника: $($user.EmployeeNumber)"
            $output += "Дата создания: $($user.whenCreated)"
            $output += "====================================="
            }
            else{
            $output = @()
            }
            if($HRuser){
            $output += "Информация о пользователе $samaccountname в базе HR:"
            $output += "====================================="
            $output += "Имя: $($HRuser.firstname)"
            $output += "Фамилия: $($HRuser.lastname)"
            $output += "ФИО: $($HRuser.Name)"
            $output += "Департамент: $(((Get-Departments).data  | ? id -eq $HRuser.department_id).name)"
            $output += "Должность: $($HRuser.post)"
            $output += "Мобильный телефон: $($HRuser.phone_number)"
            $output += "Личная почта: $($HRuser.email)"
            $output += "Номер сотрудника: $($HRuser.Employee_ID)"
            $output += "ID сотрудника: $($HRuser.System_ID)"
            $output += "====================================="
            }
    
            if(!$user -and !$HRuser){
            return "ПОльзователь не найден ни в АД ни в базе кадров."
            }
            elseif($user -and !$HRuser){
            $output +=  "Пользователь не найден в базе кадров но есть в AD."
            return ($output -join "`r`n")        
            }
            elseif(!$user -and $HRuser){
            $output +=  "Пользователь не найден в AD но найден в базе кадров."
            return ($output -join "`r`n")        
            }
            else{
            return ($output -join "`r`n")        
            }
        }
        catch {
            return "Ошибка при получении информации о пользователе $Username : $_"
        }
    }
    # Очевидно
    function Write-Log 
    {
        param (
            [Parameter(ValueFromPipeline = $true)]
            [string]$Message,
            [Parameter(Mandatory = $true)]
            [string]$LogFilePath
        )
        process {
            $timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
            $logEntry = "$($timestamp): $Message"
            Add-Content -Path $LogFilePath -Value $logEntry
        }
    }
    # Генерация пароля
    function Get-RandomPassword 
    {
        Param (
            [Parameter(Mandatory)]
            [int] $length
        )
        
        $CharSet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.ToCharArray()
        $SpecialCharSet = '!@'.ToCharArray()
        $rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
        $bytes = New-Object byte[]($length)
        $rng.GetBytes($bytes)
        $result = New-Object char[]($length)
        
        # Генерация пароля без спецсимволов
        for ($i = 0 ; $i -lt $length ; $i++) {
            $result[$i] = $CharSet[$bytes[$i] % $CharSet.Length]
        }
        
        # Вставка специального символа в случайную позицию
        $randomIndex = Get-Random -Minimum 0 -Maximum $length
        $specialChar = $SpecialCharSet[$(Get-Random -Minimum 0 -Maximum $SpecialCharSet.Length)]
        $result[$randomIndex] = $specialChar
        
        return -join $result
    }
    # отправка смс по номеру телефона и мыло туда же
    function ConvertTo-Lat 
    {
        param ([string]$russian)
        $alphabet = @{
            "а" = "a"; "б" = "b"; "в" = "v"; "г" = "g"; "д" = "d";
            "е" = "e"; "ё" = "yo"; "ж" = "j"; "з" = "z"; "и" = "i";
            "й" = "i"; "к" = "k"; "л" = "l"; "м" = "m"; "н" = "n";
            "о" = "o"; "п" = "p"; "р" = "r"; "с" = "s"; "т" = "t";
            "у" = "u"; "ф" = "f"; "х" = "h"; "ц" = "c"; "ч" = "ch";
            "ш" = "sh"; "щ" = "sch"; "ь" = ""; "ъ" = ""; "ы" = "y";
            "э" = "e"; "ю" = "yu"; "я" = "ya"; " " = " ";
        }
        $russian_chars = $russian.ToCharArray()
        $russian_in_lat = ""
        foreach ($char in $russian_chars) {
            $stringChar = $char.ToString()
            $lowerChar = $stringChar.ToLower()
            if ($alphabet.ContainsKey($lowerChar)) {
                $translatedChar = $alphabet[$lowerChar]
                if ($stringChar -ne $lowerChar) {
                    $translatedChar = $translatedChar[0].ToUpper() + $translatedChar.Substring(1)
                }
                $russian_in_lat += $translatedChar
            } else {
                $russian_in_lat += $char
            }
        }
        return $russian_in_lat
    }
    # получение токена авторизации для смс-шлюза
    function Get-Token 
    {
    try{
    $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $headers.Add("Content-Type", "application/json")
    $body = @"
    {
        `"username`": `"someUser`",
        `"password`": `"PrettyStronGPa$$w0rd`"
    }
    "@
    $response = Invoke-RestMethod 'https://smsgtw.domain.site/api/auth/token' -Method 'POST' -Headers $headers -Body $body
    return $response
    
    }
    catch{
    return $Error[0].Exception.Message
    }
    }
    # отправка смс по номеру телефона
    function Send-SMS 
    {
    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
    $AuthToken,
    $phone_number,
    $purpose,
    $message
    )
    
    $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $headers.Add("Content-Type", "application/json")
    $headers.Add("Authorization", "Bearer $AuthToken")
    
    $body = @"
    {
        `"purpose`": `"$purpose`",
        `"phone_number`": `"$phone_number`",
        `"message`": `"$message`",
    }
    "@
    
    $response = Invoke-RestMethod 'https://smsgtw.domain.site/api/messages' -Method 'POST' -Headers $headers -Body $body 
    $response.data.when_sent | ConvertTo-Json
    
    }
    # отправка смс по номеру телефона и мыло туда же
    function Send-Notification 
    {
        [CmdletBinding(SupportsShouldProcess = $true)]
        param(
            [string]   $Email,
            [string]   $PhoneNum,
            [string]   $EmailBody,
            [string]   $SmsBody
        )
        $p = ConvertTo-SecureString -String "PrettyStronGPa$$w0rd" -AsPlainText -Force 
        $u = "robot@domain.site"
        $smtpCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $u, $p
    
        $hasEmail = -not [string]::IsNullOrWhiteSpace($Email)
        $hasPhone = -not [string]::IsNullOrWhiteSpace($PhoneNum)
    
        if (-not $hasEmail -and -not $hasPhone) {
            return "Ошибка: не указан ни адрес электронной почты, ни номер телефона."
        }
        $combinedBody = $EmailBody, $SmsBody -join " | "
    
        $sentEmail = $false
        $sentSms   = $false
    
        try {
            if ($hasEmail -and $hasPhone) {
                Send-MailMessage -To $Email `
                                 -From $u `
                                 -Subject "Уведомление" `
                                 -Body $EmailBody `
                                 -SmtpServer "smtp.domain.site" `
                                 -Credential $smtpCredential `
                                 -UseSsl `
                                 -Encoding UTF8
    
                $sentEmail = $true
                Send-SMS         -AuthToken (Get-Token).access_token `
                                 -phone_number $PhoneNum `
                                 -purpose "Send User's data" `
                                 -message "$SmsBody"
                $sentSms = $true
            }
            elseif ($hasEmail) {
                Send-MailMessage -To $Email `
                                 -From $u `
                                 -Subject "Уведомление" `
                                 -Body $combinedBody `
                                 -SmtpServer "smtp.domain.site" `
                                 -Credential $smtpCredential `
                                 -UseSsl `
                                 -Encoding UTF8
                $sentEmail = $true
            }
            else {
                Send-SMS         -AuthToken (Get-Token).access_token `
                                 -phone_number $PhoneNum `
                                 -purpose "Send User's data" `
                                 -message "$((ConvertTo-Lat $combinedBody).replace("_"," "))"
                $sentSms = $true
            }
    
            return [pscustomobject]@{
                DateTime  = Get-Date
                EmailSent = $sentEmail
                SmsSent   = $sentSms
            }
        }
        catch {
            return $_.Exception
        }
    }
    # Смена пароля пльзователя
    function Reset-ADUserPassword
    {
    param($samaccountname)
    try{
        $chekADuser = Get-ADUser -Filter * -Properties GivenName,Surname,Name,Department,Title,MobilePhone,HomePage,EmployeeID,EmployeeNumber,whenCreated |`
                    ? {if(($_.EmployeeID -eq $samaccountname) -or ($_.Name -eq $samaccountname) -or ($_.employeeNumber -eq $samaccountname) -or ($_.SamaccountName -eq $samaccountname)){$_}}
            if(!$chekADuser){
                $result = "$(get-date): Не найден пользователь"
            }
            else{
                if(![string]::IsNullOrWhiteSpace($chekADuser.HomePage) -and ![string]::IsNullOrWhiteSpace($chekADuser.MobilePhone)){
                 $passsword = Get-RandomPassword -length 10
                     try{
                         Set-ADAccountPassword -Identity $chekADuser.DistinguishedName  `
                                               -NewPassword (ConvertTo-SecureString -String $passsword -AsPlainText -Force) `
                                               -Confirm:$false 
                         Set-ADUser $chekADuser.DistinguishedName -ChangePasswordAtLogon $true -Confirm:$false
                         Send-Notification -Email $chekADuser.HomePage `
                                           -PhoneNum $chekADuser.MobilePhone.Replace('+',"") `
                                           -EmailBody "Пароль пользователя $($chekADuser.samaccountname) был сброшен и отправлен на мобильный телефон."`
                                           -SmsBody "One time password: $passsword" 
                     $result = "$(get-date): Cмена пароля у пользователя $($chekADuser.samaccountname) прошла успешно, пароль отправлен и на E-mail и на мобильный телефон."
                        }
                     catch{
                         $result = "$(get-date): Произошла ошибка при сменен пароля у пользователя $($chekADuser.samaccountname): $($Error[0].exception.message)"
                     }
                }
                elseif(![string]::IsNullOrWhiteSpace($chekADuser.HomePage) -and [string]::IsNullOrWhiteSpace($chekADuser.MobilePhone)){
                # Это если нет телефона
                $passsword = Get-RandomPassword -length 10
                     try{
                         Set-ADAccountPassword -Identity $chekADuser.DistinguishedName  `
                                               -NewPassword (ConvertTo-SecureString -String $passsword -AsPlainText -Force) `
                                               -Confirm:$false 
                         Set-ADUser $chekADuser.DistinguishedName -ChangePasswordAtLogon $true -Confirm:$false
                         Send-Notification -Email $chekADuser.HomePage `
                                           -PhoneNum $chekADuser.MobilePhone `
                                           -EmailBody "Пароль пользователя $($chekADuser.samaccountname) был сброшен." `
                                           -SmsBody "One time password: $passsword"
                         $result = "$(get-date): Cмена пароля у пользователя $($chekADuser.samaccountname) прошла успешно, пароль отправлен на E-mail, МОБИЛЬНЫЙ НЕ УКАЗАН!!!."
                        }
                     catch{
                         $result = "$(get-date): Произошла ошибка при сменен пароля у пользователя $($chekADuser.samaccountname): $($Error[0].exception.message)"
                     }
                }
                elseif([string]::IsNullOrWhiteSpace($chekADuser.HomePage) -and ![string]::IsNullOrWhiteSpace($chekADuser.MobilePhone)){
                # Это если нет Email
                $passsword = Get-RandomPassword -length 10
                     try{
                         Set-ADAccountPassword -Identity $chekADuser.DistinguishedName  `
                                               -NewPassword (ConvertTo-SecureString -String $passsword -AsPlainText -Force) `
                                               -Confirm:$false 
                         Set-ADUser $chekADuser.DistinguishedName -ChangePasswordAtLogon $true -Confirm:$false
                         Send-Notification -Email $chekADuser.HomePage `
                                           -PhoneNum $chekADuser.MobilePhone.Replace('+',"") `
                                           -EmailBody "Hi!"`
                                           -SmsBody "$($chekADuser.samaccountname) | one time password: $passsword" 
                        $result = "$(get-date): Cмена пароля у пользователя $($chekADuser.samaccountname) прошла успешно, пароль отправлен на мобильный телефон, ЭЛЕКТРОННАЯ ПОЧТА НЕ УКАЗАНА!!!."
                        }
                     catch{
                        $result = "$(get-date): Произошла ошибка при сменен пароля у пользователя $($chekADuser.samaccountname): $($Error[0].exception.message)"
                     }
                    
                }
                elseif([string]::IsNullOrWhiteSpace($chekADuser.HomePage) -and [string]::IsNullOrWhiteSpace($chekADuser.MobilePhone)){
                # Это если нет Email и телефона
                      $result = "$(get-date): Cмена пароля у пользователя $($chekADuser.samaccountname) отменена, мобильный телефон и электронная почта не указаны в AD и в базе HR!!!."  
                }
                else{
                # Любая другая херня
                    $result = "$(get-date): Произошла ошибка при сменен пароля у пользователя необходимо уточнение данных по этому пользователю."
                }
            }
            }
        catch [System.Management.Automation.CommandNotFoundException]{
        $result = "$(get-date): У вас не установлены средства управления AD! Вы точно привелигированный пользователь?? На всякий случай я отправил ваши данные в ДИБ!!!"
        $whois = get-userinf
        Send-Notification -Email alert@domain.site `
                                      -PhoneNum $null `
                                      -EmailBody "Обнаружена попытка сменить пароль пользователя $samaccountname на компьютере с отсутвующими инструментами администрирования AD!!! Возьмите 'на карандаш'!" `
                                      -SmsBody $whois
        }
    return ($result  + "`r`n")
    }
    
    #
    
    Add-Type -AssemblyName System.Windows.Forms
    Add-Type -AssemblyName System.Drawing
    # Форма
    $form = New-Object System.Windows.Forms.Form
    $form.Text = "Сброс пароля пользователя"
    $form.Size = New-Object System.Drawing.Size(700,400)
    $form.StartPosition = "CenterScreen"
    $form.FormBorderStyle = 'FixedDialog'
    $form.MaximizeBox = $false
    
    # Метка
    $label = New-Object System.Windows.Forms.Label
    $label.Text = "Введите логин, или ФИО, или ID пользователя"
    $label.Location = New-Object System.Drawing.Point(20,20)
    $label.AutoSize = $true
    $form.Controls.Add($label)
    
    # Поле ввода
    $textBox = New-Object System.Windows.Forms.TextBox
    $textBox.Location = New-Object System.Drawing.Point(20,50)
    $textBox.Size = New-Object System.Drawing.Size(300,20)
    $form.Controls.Add($textBox)
    
    # Кнопка «Сменить пароль!»
    $resetButton = New-Object System.Windows.Forms.Button
    $resetButton.Text = "Сменить пароль!"
    $resetButton.Location = New-Object System.Drawing.Point(510,45)
    $resetButton.Size = New-Object System.Drawing.Size(120,30)
    $form.Controls.Add($resetButton)
    
    # Кнопка «Проверить пользователя»
    $checkButton = New-Object System.Windows.Forms.Button
    $checkButton.Text = "Проверить пользователя"
    $checkButton.Location = New-Object System.Drawing.Point(340,45)
    $checkButton.Size = New-Object System.Drawing.Size(160,30)
    $form.Controls.Add($checkButton)
    
    # Текстовое поле для логов
    $logBox = New-Object System.Windows.Forms.TextBox
    $logBox.Location = New-Object System.Drawing.Point(20,100)
    $logBox.Size = New-Object System.Drawing.Size(640,220)
    $logBox.Multiline = $true
    $logBox.ScrollBars = "Vertical"
    $logBox.ReadOnly = $true
    $form.Controls.Add($logBox)
    
    # Прогрессбар ниже logBox, не налезает
    $progressBar = New-Object System.Windows.Forms.ProgressBar
    $progressBar.Location = New-Object System.Drawing.Point(20,340)  # чуть ниже logBox
    $progressBar.Size = New-Object System.Drawing.Size(640,20)       # по ширине logBox
    $progressBar.Style = 'Marquee'
    $progressBar.Visible = $false
    $form.Controls.Add($progressBar)
    
    # Действие кнопки
    $resetButton.Add_Click({
        $username = $textBox.Text
        if ([string]::IsNullOrWhiteSpace($username)) {
            $logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
            $logBox.AppendText("$(get-date):Введите логин пользователя!`r`n")
            return
        }
    
        $logBox.AppendText("$(get-date): Сброс пароля для пользователя $username...`r`n")
        try {
            # Вызов твоей функции
            $logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
            $result = Reset-ADUserPassword -samaccountname $username
            $logBox.AppendText($result + "`r`n")
        }
        catch {
            $logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
            $logBox.AppendText("$(get-date): Ошибка: $_`r`n")
        }
    })
    
    # Действие кнопки проверки пользователя
    $checkButton.Add_Click({
        $progressBar.Visible = $true
        $form.Refresh()
        $username = $textBox.Text
        if ([string]::IsNullOrWhiteSpace($username)) {
            $logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
            $logBox.AppendText("$(get-date): Введите логин пользователя!`r`n")
            $progressBar.Visible = $false
            return
        }
        $logBox.AppendText("$(get-date): Проверка пользователя $username...`r`n")
        try {
            $result = Check-User -samaccountname $username
            $logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
            $logBox.AppendText($result + "`r`n")
        }
        catch {
            $logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
            $logBox.AppendText("Ошибка: $_`r`n")
        }
        $progressBar.Visible = $false
    })
    
    # Показ формы
    $form.Add_Shown({$form.Activate()})
    [void]$form.ShowDialog()
    
    

  • Как же бесят Datastore ISO в дисководе.

    Недавно у меня была работа по массовой миграции виртуальных машин между кластерами. Я столкнулся с проблемой при миграции в виде ошибок и варнингов на тему того что в дисководе виртуальной машины имеется некоторые Datastore ISO файлы. Это конечно же недоработка которая была не учтена когда инфра разворачивалась с помощью Terraform. Однако проблему нужно было решать, и решил я ее радикально. Прочесать все машины на предмет подключенных ISO и «извлечь». Под катом тело скрипта.

    Тыкни
    Connect-VIServer -Server vcenter.site -Credential (Get-Credential administrator@vsphere.local)
    
    $servers = get-vm * | select Folder, Name, VMHost
    #Машины обрабатываются по папке в VCenter но это можно удалить и пройтись по всем машинам в VCenter если закомментить оператора IF
    $foldername = "LinuxInfra"
    
    Foreach($serv in $servers){
    if($serv.folder.name -eq $foldername){
    $vm = Get-VM "$($serv.Name)"
    $cd = Get-CDDrive -VM $vm
    $spec = New-Object VMware.Vim.VirtualMachineConfigSpec
    $cdSpec = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $cdSpec.Operation = [VMware.Vim.VirtualDeviceConfigSpecOperation]::Edit
    $cdSpec.Device = $cd.ExtensionData
    $cdSpec.Device.Backing = New-Object VMware.Vim.VirtualCdromRemoteAtapiBackingInfo
    $cdSpec.Device.Backing.DeviceName = $null
    $cdSpec.Device.Connectable.Connected = $false
    $cdSpec.Device.Connectable.StartConnected = $false
    $cdSpec.Device.Connectable.AllowGuestControl = $true
    $spec.DeviceChange += $cdSpec
    $task = $vm.ExtensionData.ReconfigVM_Task($spec)
    Write-Host "Ожидание завершения операции..."
    while ($task.Info.State -eq "running" -or $task.Info.State -eq "queued") {
        Start-Sleep -Seconds 1
        $task = Get-View -Id $task.MoRef
    }
    Write-Host "ISO диск отключен от $($serv.Name)"
    }
    } 
    
  • Создание задач по репликации Veeam B&R с помощью Powershell

    Мне довелось создать реплику виртуальных машин средствами Veeam B&R. Забавно то что используя GUI это делается за пару кликов, однако задача состояла в том что бы создать реплики на более чем 15 машин при этом каждая машина должна быть отдельной джобой. Что бы не терять большое количество времени, я решил написать небольшой скрипт на Powershell который сделает это за меня. Я постарался максимально унифицировать этот скриптик, но думаю можно будет сделать это еще эффективнее. Тело скрипта под катом.

    Тыкни
    function New-ReplicaJob {
        param (
            [string]$JobName, 
            [string]$TargetServerIP, 
            [string]$VMName, 
            [string]$TargetDatastore,
            [string]$SourceIP, 
            [string]$SourceMask, 
            [string]$ReIpTargetIP, 
            [string]$ReIpTargetMask,
            [string]$ReIpGateway, 
            [string]$ReIpDNS, 
            [string]$SourceNetwork, 
            [string]$TargetNetwork,
            [string]$SourceProxy, 
            [string]$TargetProxy, 
            [string]$Repository = "DefaultRepo"
        )
    
        $Description = "$JobName replication job"
        $RestorePointsToKeep = 7
    
        $TargetServer = Get-VBRServer -Name    $TargetServerIP
        $VMObject = Find-VBRViEntity -Name $VMName
        $Datastore = Find-VBRViDatastore -Name $TargetDatastore -Server $TargetServerIP
        $ReIpRule = New-VBRViReplicaReIpRule -SourceIp $SourceIP -SourceMask $SourceMask -TargetIp $ReIpTargetIP -TargetMask $ReIpTargetMask -TargetGateway $ReIpGateway -DNS $ReIpDNS
    
        $SrcNetwork = Get-VBRViServerNetworkInfo -Server $VMObject.VmHostName | Where-Object { $_.Name -eq $SourceNetwork }
        $TgtNetwork = Get-VBRViServerNetworkInfo -Server $TargetServer.Name | Where-Object { $_.Name -eq $TargetNetwork }
    
        $SrcProxy = Get-VBRViProxy -Name $SourceProxy
        $TgtProxy = Get-VBRViProxy -Name $TargetProxy
    
        Add-VBRViReplicaJob -Name $JobName `
                            -Server $TargetServer `
                            -Entity $VMObject `
                            -Datastore $Datastore `
                            -Suffix "_replica" `
                            -BackupRepository $Repository `
                            -Description $Description `
                            -EnableNetworkMapping `
                            -SourceNetwork $SrcNetwork `
                            -TargetNetwork $TgtNetwork `
                            -SourceProxy $SrcProxy `
                            -TargetProxy $TgtProxy `
                            -RestorePointsToKeep $RestorePointsToKeep `
                            -ReIpRule $ReIpRule `
                            -HighPriority `
                            -Force
    }
    
    Connect-VIServer "$(Read-Host 'Enter vCenter hostname or IP')" -Credential (Get-Credential)
    Connect-VBRServer -Server "$(Read-Host 'Enter Veeam hostname or IP')" -Credential (Get-Credential)
    
    $VMList = @(
    "$(Read-Host 'Enter VM names to replicate, separated by comma (no spaces)')".Split(",") | Where-Object { $_ -ne '' }
    )
    
    foreach ($VM in $VMList) {
        $GuestIP = (Get-VM $VM).Guest.IPAddress[0]
        $GuestNet = (Get-VM $VM).Guest.Nics.NetworkName[0]
        $LastOctet = $GuestIP.Split(".")[-1]
        $NewTargetIP = "192.168.100.$LastOctet"
        $NewGateway = "192.168.100.1"
    
        New-ReplicaJob -JobName $VM `
                       -TargetServerIP "192.168.0.10" `
                       -VMName $VM `
                       -TargetDatastore "TargetDatastore" `
                       -SourceIP $GuestIP `
                       -SourceMask "255.255.255.0" `
                       -ReIpTargetIP $NewTargetIP `
                       -ReIpTargetMask "255.255.255.0" `
                       -ReIpGateway $NewGateway `
                       -ReIpDNS "192.168.100.10" `
                       -SourceNetwork $GuestNet `
                       -TargetNetwork "TargetNetworkName" `
                       -SourceProxy "SourceProxyName" `
                       -TargetProxy "TargetProxyName" `
                       -Repository "DefaultRepo"
    }
    
  • Проблемы с IE?

    Недавно я столкнулся с острой необходимостью прямой работы с IE на Windows 11. Однако, я столкнулся с тем что просто режим совместимости в EDGE совершенно недостаточно. Немного поразмышляв, я пришел к выводу что ничто так не симулирует стабильную работу IE как сам IE. На просторах Интернета я нашел довольно забавный и рабочий метод открытия IE несмотря на все заявления мелкомягких о том что IE выпилен. Ниже пример скрипта на Powershell. В моем решении я скомпилировал нижеследующий скрипт в EXE файл с помощью Invoke-PS2EXE и использую в своих целях.

    Тыкни
    $vbsFilePath = "$env:APPDATA\ie.vbs"
    $vbsContent = @'
    Set objIE = CreateObject("InternetExplorer.Application")
    objIE.Navigate "https://любой.произвольный.сайт"
    objIE.Visible = 1
    '@
    Set-Content -Path $vbsFilePath -Value $vbsContent
    Start-Process "wscript.exe" -ArgumentList $vbsFilePath
    Start-Sleep -Seconds 10
    Remove-Item $vbsFilePath
    
  • Установщик NUPKG

    Установка NUPKG файлов в оффлайн режиме при помощи powershell и GUI на Windows Form.
    1 — Окно для выбора файла установки
    2 — Кнопка установить и кнопка закрыть
    3 — Окно лога для возврата статуса установки или ошибки.
    4 — Окно для выбора CodeSign сертификата если в этом есть необходимость

    Тыкни
    <code>Add-Type -AssemblyName System.Windows.Forms  
    Add-Type -AssemblyName System.Drawing 
    
    Function Install-NUPKG {
    param(
    [parameter(Mandatory=$true)]$nupkgFile,
    $Certificate
    )
    try{
        $RenameFrom = $nupkgfile
        $RenameTo = $($nupkgfile.Replace('.nupkg','.zip'))
        Copy-Item $RenameFrom -Destination $RenameTo -Force
        $DestToExpnd = $RenameTo.replace('.zip','')
        Expand-Archive $RenameTo -DestinationPath $DestToExpnd -Force
        Start-Sleep -Seconds 5
        $name = (Get-ChildItem -Path $DestToExpnd `
        |? {($_.name -Like "*.psm1") -or ($_.name -Like "*.psd1")} `
        | select -First 1).Name.split('.')[0]
        $parentpath = ((Get-Item $DestToExpnd).PSParentPath).replace('Microsoft.PowerShell.Core\FileSystem::','')
        $newfoldname = $($parentpath + "\" + $name)
        Rename-Item $DestToExpnd -NewName $newfoldname
        $psmodulefolders = $ENV:PSModulePath.Split(";")[2]
        Copy-Item $newfoldname -Destination $psmodulefolders -Recurse -Force 
        Start-Sleep -Seconds 2
        Remove-Item $newfoldname -Force -Recurse 
        Remove-Item $RenameTo -Force -Recurse
        
        if($Certificate){
        try{
            Get-ChildItem -Path "$psmodulefolders\$name" -Recurse -Attributes !Directory `
            | % {Set-AuthenticodeSignature -Certificate $Certificate -FilePath $_.FullName -ErrorAction SilentlyContinue `
            | ? Status -eq Valid}
            Return Write-Output "$name is installed and signed with cert $($CheckBox.Text.Split('\') `
            | select -Last 1)"
            }
        catch [System.Management.Automation.ParameterBindingException]{
            $opencert = Get-PfxCertificate $Certificate
            Get-ChildItem -Path "$psmodulefolders\$name" -Recurse -Attributes !Directory `
            | % {Set-AuthenticodeSignature -Certificate $Certificate -FilePath $_.FullName -ErrorAction SilentlyContinue `
            | ? Status -eq Valid}
            Return Write-Output "$name is installed and signed with cert $($CheckBox.Text.Split('\') `
            | select -Last 1)"
            }
    
        }
        
        else{
            Return Write-Output "$name is installed"
        }
    }
    Catch{
        Return Write-Output $Error[0].Exception
    }
    }
    
    Function Get-FileName{
     $initialDirectory =  'C:'
     [System.Reflection.Assembly]::LoadWithPartialName(“System.windows.forms”) `
     | Out-Null
    
     $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
     $OpenFileDialog.initialDirectory = $initialDirectory
     $OpenFileDialog.filter = “All files (*.*)| *.*”
     $OpenFileDialog.ShowDialog() | Out-Null
     $OpenFileDialog.filename
    } 
    
    $window_form = New-Object System.Windows.Forms.Form
    $window_form.Text ='Устновка кастомных модулей NUPKG'
    $window_form.Width = 500
    $window_form.Height = 220
    $window_form.AutoSize = $true
    $window_form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
    ####
    $FormLabel1 = New-Object System.Windows.Forms.Label
    $FormLabel1.Text = "Выберите файл для установки:"
    $FormLabel1.Location = New-Object System.Drawing.Point(0,5)
    $FormLabel1.AutoSize = $true
    $window_form.Controls.Add($FormLabel1)
    $FormLabel3 = New-Object System.Windows.Forms.Label
    
    $FormLabel3.Text = "Результат: "
    $FormLabel3.Location = New-Object System.Drawing.Point(0,100)
    $FormLabel3.AutoSize = $true
    $window_form.Controls.Add($FormLabel3)
    
    $CheckBox = New-Object System.Windows.Forms.CheckBox
    $CheckBox.Text = 'Подписать модуль сертификатом'
    $CheckBox.AutoSize = $true
    $CheckBox.Checked = $False
    $CheckBox.Location  = New-Object System.Drawing.Point(5,55)
    $checkbox.Add_CheckStateChanged({
        If ($Checkbox.Checked) {
            $Certificate = Get-FileName
            $TextBox2.Appendtext("Выбран сертификат: " + $Certificate  + [Environment]::NewLine)
            $CheckBox.Text = $Certificate
        } 
        Else {
            $TextBox2.Appendtext("Выбор отменен" + [Environment]::NewLine)
            $CheckBox.Text = 'Подписать модуль сертификатом'
        }
    })
    $window_form.Controls.Add($CheckBox)
    
    ###
    $TextBox1 = New-Object System.Windows.Forms.TextBox
    $TextBox1.Size = New-Object System.Drawing.Size(390,30)
    $TextBox1.Location  = New-Object System.Drawing.Point(5,25)
    $TextBox1.Text = "Путь к файлу..."
    $window_form.Controls.Add($TextBox1)
    $window_form.Add_Shown({$TextBox1.Select()})
    
    $TextBox2 = New-Object System.Windows.Forms.RichTextBox 
    $TextBox2.ReadOnly = $true 
    $TextBox2.Multiline = $true  
    $TextBox2.Width = 390  
    $TextBox2.Height = 150
    $TextBox2.Multiline = $true
    $TextBox2.Scrollbars = "Vertical"
    $TextBox2.Refresh()
    $TextBox2.ScrollToCaret()
    $TextBox2.ToString()
    $TextBox2.text = ("------------------------------------------------------------" + [Environment]::NewLine)
    $TextBox2.Location = New-Object System.Drawing.Point(5,120)
    $window_form.Controls.Add($TextBox2)
    $window_form.Add_Shown({$TextBox2.Select()})
    ###
    
    $FormButton1 = New-Object System.Windows.Forms.Button
    $FormButton1.Location = New-Object System.Drawing.Size(400,25)
    $FormButton1.Size = New-Object System.Drawing.Size(100,25)
    $FormButton1.Text = "Выбрать"
    $FormButton1.Add_Click({$TextBox1.Text = Get-FileName})
    $window_form.Controls.Add($FormButton1)
    
    $FormButton = New-Object System.Windows.Forms.Button
    $FormButton.Location = New-Object System.Drawing.Size(400,175)
    $FormButton.Size = New-Object System.Drawing.Size(100,25)
    $FormButton.Text = "Поехоли"
    $window_form.Controls.Add($FormButton)
    $FormButton.Add_Click({
    if($CheckBox.Checked -eq $true){
        $info = Install-NUPKG -nupkgFile $TextBox1.Text -Certificate $CheckBox.Text `
        | Out-String
        $TextBox2.Appendtext("$info`r`n")
        $TextBox2.Appendtext("------------------------------------------------------------" + [Environment]::NewLine)
    }
    else{
        $info = Install-NUPKG -nupkgFile $TextBox1.Text `
        | Out-String
        $TextBox2.Appendtext("$info`r`n")
        $TextBox2.Appendtext("------------------------------------------------------------" + [Environment]::NewLine)
    }
    }
    )
    
    $FormButton2 = New-Object System.Windows.Forms.Button
    $FormButton2.Location = New-Object System.Drawing.Size(400,200)
    $FormButton2.Size = New-Object System.Drawing.Size(100,25)
    $FormButton2.Text = "Закрыть"
    $FormButton2.Add_Click({[void]$window_form.Close()})
    $window_form.Controls.Add($FormButton2)
    ###
    [void]$window_form.ShowDialog()
    </code>