Powershell – bonlesave.ru https://bonlesave.ru Записки о виртуализации и о жизни Tue, 05 Jul 2022 04:00:10 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.3 Опрос CDP/LLDP с ESXi через PowerShell/Python https://bonlesave.ru/2022/07/05/get-cdp-lldp-vsphere-esxi-powershell-python/ https://bonlesave.ru/2022/07/05/get-cdp-lldp-vsphere-esxi-powershell-python/#respond Tue, 05 Jul 2022 04:00:10 +0000 https://bonlesave.ru/?p=9603 Continue reading "Опрос CDP/LLDP с ESXi через PowerShell/Python"]]> Однажды у вас может возникнуть желание составить таблицу портов коммутаторов, к которым подключены ваши хосты.

Если у вас “гомогенное” окружение, состоящее из коммутаторов Cisco, то заморачиваться не нужно совсем (CDP настроен в Virtual Switch Standard/Distributed Virtual Switch по умолчанию).

Но если в окружении не только Cisco, то вам необходимо немного сильнее напрячься:

  • использовать только распределенные коммутаторы;
  • включать на них поддержку LLDP;
  • с удивлением обнаружить, что в API опрос CDP и LLDP происходит по-разному.

Так получилось, что я решил для себя задачу опроса CDP/LLDP с помощью Python. Поэтому, когда возникла задача решения ее же через PoSh, я воспользовался уже имеющимися наработками.

Python

Передаем в переменную host результат использования pyvmomi-библиотеки:

Тут список хостов: connection.content.viewManager.CreateContainerView(datacenter, [vim.HostSystem], recursive=True).view

Хост – это один из объектов этого списка.

cdp, host_network_system = {}, {}, host.configManager.networkSystem
    if (host_network_system is not None) and host_network_system.capabilities.supportsNetworkHints:
        for _ in host_network_system.QueryNetworkHint():
            vmnic_name = vmnicConvert[_.device]
            if _.connectedSwitchPort is not None:
                cdp[vmnic_name] = {}
                cdp[vmnic_name]['switchName'] = _.connectedSwitchPort.systemName \
                    if len(_.connectedSwitchPort.systemName) > 6 else _.connectedSwitchPort.devId
                cdp[vmnic_name]['switchPort'] = _.connectedSwitchPort.portId
            elif _.lldpInfo is not None:
                cdp[vmnic_name] = {}
                cdp[vmnic_name]['switchPort'] = _.lldpInfo.portId
                for lldpParam in _.lldpInfo.parameter:
                    if lldpParam.key == "System Name":
                        cdp[vmnic_name]['switchName'] = lldpParam.value.split(".")[0]

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

PowerShell

PoSh-вариант представляет собой более законченное решение

param ($report_path)
if ($report_path -eq $null) {
$report_path = read-host -Prompt "Please set report name"
}

$VMHosts = Get-VMHost | Where-Object {($_.ConnectionState -eq 'Connected') -or ($_.ConnectionState -eq 'Maintenance')} | Sort-Object -Property Name
$count = $VMHosts.count
$i = 0
$Result = @()
foreach ($VMHost in $VMHosts) {
    Write-Progress -PercentComplete (($i/$count*100)) -Activity Get_Discovery -Status "Gathering CDP/LLDP"
    $i++
    $NetworkSystemConfigManager = Get-View -Id $VMHost.ExtensionData.ConfigManager.NetworkSystem
    [System.Object]$pnic = $NetworkSystemConfigManager.NetworkConfig.Pnic.Device | select -Unique
    foreach ($vmhostnic in $pnic){
        $vmnic = $NetworkSystemConfigManager.QueryNetworkHint($vmhostnic)[0]
        if($vmnic.ConnectedSwitchPort -ne $null){
            if(($vmnic.ConnectedSwitchPort.systemName).Length -gt 6){$switchName = $vmnic.ConnectedSwitchPort.systemName}
            else{$switchName = $vmnic.ConnectedSwitchPort.devId}
            $VMHostNetworkInfo = [PSCustomObject]@{
                VMHost = $VMHost.Name
                Vmnic = $vmhostnic
                Switch = $switchName
                Port = $vmnic.ConnectedSwitchPort.PortId
            }
            $Result += $VMHostNetworkInfo
        }
        elseif($vmnic.LldpInfo -ne $null){
            $switchName = ""
            foreach ($item in $vmnic.LldpInfo.parameter){
                if($item.key -eq 'System Name'){$switchName = $item.value}
            }
            $VMHostNetworkInfo = [PSCustomObject]@{
                VMHost = $VMHost.Name
                Vmnic = $vmhostnic
                Switch = $switchName
                Port = $vmnic.LldpInfo.PortId
            }
            $Result += $VMHostNetworkInfo
        }
    }
}
$Result | Export-Csv -NoTypeInformation -Path $report_path

Надеюсь, данные куски кода будут кому-либо полезны.

]]>
https://bonlesave.ru/2022/07/05/get-cdp-lldp-vsphere-esxi-powershell-python/feed/ 0
Заметочка про Custom Attributes https://bonlesave.ru/2022/05/20/zametochka-pro-custom-attributes/ https://bonlesave.ru/2022/05/20/zametochka-pro-custom-attributes/#comments Fri, 20 May 2022 11:48:13 +0000 https://bonlesave.ru/?p=9582 Потребовалось проставить Custom Attribute для виртуальных машин.

На помощь, как обычно, пришел Google и подсказал следующее решение:

$ca=Get-CustomAttribute -Name 'custom_attribute_name'

get-vm -Location vm_folder_name | Get-Annotation -CustomAttribute $ca | %{Set-Annotation -entity $_.AnnotatedEntity -CustomAttribute $ca -Value 'new_value'}

]]>
https://bonlesave.ru/2022/05/20/zametochka-pro-custom-attributes/feed/ 2
PowerShell, SCSILunPath и Datastore Name https://bonlesave.ru/2022/04/18/powershell-scsilunpath-datastore/ https://bonlesave.ru/2022/04/18/powershell-scsilunpath-datastore/#respond Sun, 17 Apr 2022 19:56:10 +0000 https://bonlesave.ru/?p=9536 Continue reading "PowerShell, SCSILunPath и Datastore Name"]]> Обратился ко мне за советом постоянный читатель: помоги, говорит, с моим iSCSI-массивом NetApp. Пытаюсь вывести Get-SCSILunPath, так там ни имени датастора, ни IP-адреса “таргета” в SanId нет 🙁

Посмотрел – и действительно так: в отличие, например, от Huawei, NetApp не выводит IP-адрес IQN-Target в выводе атрибутов Get-SCSILunPath. Да и привязать CanonicalName вида naa.thebeststoragearray к датастору с первого взгляда не удается…

Дальше я погуглил и “вспомнил” про наличие крутого атрибута ExtensionData, в котором смог найти всю интересующую нас информацию и в Get-Datastore, и в Get-ScsiLunPath!
Тут выложен скрипт get-scsilunpath.ps1, позволяющий вывести список путей.

#Usage: get-scsilunpath.ps1 -Esx host1.domain.ru -Storage iqn.1992-08.com.netapp:sn.1234567890
param ($Esx, $Storage)

$dict=@{}
Get-Datastore | %{$dict[$_.extensiondata.info.vmfs.extent.diskname] = $_.name}
Get-ScsiLun -VmHost $Esx | get-scsilunpath | ? {$_.SanID -like $Storage} | select name, sanid, @{N="IP";E={$_.extensiondata.transport.address}}, @{N='Datastore';E={$dict[$_.ScsiCanonicalName]}}, state

Для сопоставления SCSICanonicalName и имени датастора использовалась хэш-таблица, так как она оптимизирована для быстрого поиска значения по ключу (ID LUN’a).

После запуска скрипта:
get-scsilunpath.ps1 -Esx hostname -Storage StorageId
вы получаете список путей к хранилищам с этого массива, с указанием адреса IQN-таргета (если это iSCSI), статуса и названия хранилища.

P.S. Если дойдут руки, я сделаю некий скрипт с аналитикой, который будет анализировать кластер (ы?) на предмет аномалий с этим массивом, а то вручную грустно смотреть на сотни путей…

]]>
https://bonlesave.ru/2022/04/18/powershell-scsilunpath-datastore/feed/ 0
PowerShell, REST и Excel https://bonlesave.ru/2022/04/11/powershell-rest-excel/ https://bonlesave.ru/2022/04/11/powershell-rest-excel/#comments Mon, 11 Apr 2022 04:29:31 +0000 https://bonlesave.ru/?p=9534 Continue reading "PowerShell, REST и Excel"]]> Обнаружили проблему: командлет Get-OrgVdcNetwork почему-то не выводит часть сетей orgVDC. Опытным путем выяснили, что ему не нравятся Shared-сети, то есть сети доступные из нескольких orgVDC.

Для решения проблемы решили воспользоваться REST API от vCloud Director.

А чтобы было не скучно, решили вывод сетей делать сразу в файл Excel, чтобы уменьшить количество ручной работы.

Оригинал PoSH-скрипта я положил на гитхаб, тут его приведу для наглядности (хотя с отступами при копипасте беда).

Скрипту понадобится список vCloud Director’ов в файле vcd_list.txt.

#Script makes an Excel file, which contains oVDC's networks. It takes VCD list from vcd_list.txt file.
function Get-Netmask {
  param ($prefixLength)
  $bitString = ('1' * $prefixLength).PadRight(32, '0')
  $ipString=[String]::Empty

# make 1 string combining a string for each byte and convert to int
  for($i=0;$i -lt 32;$i+=8){
    $byteString=$bitString.Substring($i,8)
    $ipString+="$([Convert]::ToInt32($byteString, 2))."
  }
  $ipString.TrimEnd('.')
}

$ExcelApplication = new-object -comobject excel.application 
$ExcelApplication.Visible = $false
$WorkBook = $ExcelApplication.Workbooks.Add()
$sheets = 1

$apiVersion = '35.0'
$cred = Get-Credential
$password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($cred.Password))
if($password -like "*:*"){Write-Host("Password is containing ':'. Exiting now...");break}
$userPass = $cred.Username
if(!($userPass -like "*@*")){$userPass += "@system"}
$userPass += ":$password"
$bytes= [System.Text.Encoding]::UTF8.GetBytes($userpass)
$encodedlogin=[Convert]::ToBase64String($bytes)

$content = get-content vcd_list.txt | sort -Descending

foreach ($cont in $content) {
  $base_uri = $cont
  $sheets += 1
  $xws1 = $WorkBook.Worksheets.add()
  $xws1.Name = $base_uri.split('/')[2]
  $headers = @{}
  $authheader = "Basic " + $encodedlogin
  $headers.Add("Authorization",$authheader)

  $version_uri = $base_uri + "api/versions"
  try {$get_version = Invoke-RestMethod -Uri $version_uri -Headers $headers -Method GET -ErrorAction Stop}
  catch {$err_mes = $_;$get_version = $false}

  $check_version = $get_version.SupportedVersions.VersionInfo | Where-Object {$_.Version -eq $apiVersion} 

  if ($check_version){
    $headers.Add("Accept","application/*;version=$apiVersion")
    $session_uri = $base_uri + "api/sessions"
    try{$get_session = Invoke-WebRequest -Uri $session_uri -Headers $headers -Method POST -ErrorAction Stop}
    catch{$err_mes = $_;$get_version = $false}
    if ($get_session.StatusCode -eq '200'){
      $base_uri
      Write-Host "Success" -ForegroundColor White
      $bearer_token = 'Bearer '+ $get_session.Headers.Item('X-VMWARE-VCLOUD-ACCESS-TOKEN')
      $headers.Remove("Authorization")
      $headers.Add("Authorization",$bearer_token)
      $net_uri = $base_uri + 'cloudapi/1.0.0/orgVdcNetworks'
      $Data = Invoke-RestMethod -Uri $net_uri -Headers $headers -Method get -ContentType 'application/*+json'
      $total = $Data.resultTotal
      for ($num = 1 ; $num -le [int][Math]::Ceiling($total / 16) ; $num++){
        $tmp_net_uri = $net_uri + '?page=' + [string]$num + '&pageSize=16'
        $Data = Invoke-RestMethod -Uri $tmp_net_uri -Headers $headers -Method get -ContentType 'application/*+json'
        if($num -eq 1){$orgnets = $Data.values}
        else{$orgnets += $Data.values}
        }
      $orgVDCs = @()
      for ($num = 0 ; $num -lt $total ; $num++){
        $row = "" | select Tenant, vDC, OrgVdcNetwork, Gateway, Netmask
        $row.Tenant = $orgnets[$num].orgref.name
        $row.vDC = $orgnets[$num].orgvdc.name
        $row.OrgVdcNetwork = $orgnets[$num].name
        $tmp_net_uri = $net_uri + '/' + $orgnets[$num].id
        $Data = Invoke-RestMethod -Uri $tmp_net_uri -Headers $headers -Method get -ContentType 'application/*+json'
        $row.Gateway = $Data.subnets.values.gateway
        $row.Netmask = Get-Netmask -prefixLength $Data.subnets.values.prefixlength
        $orgVDCs += $row
        }
      $orgVDCs_tmp = $orgVDCs | sort Tenant
      $xws1.Cells.Item(1,1) = 'Tenant'
      $xws1.Cells.Item(1,2) = 'vDC'
      $xws1.Cells.Item(1,3) = 'OrgVdcNetwork'
      $xws1.Cells.Item(1,4) = 'Gateway'
      $xws1.Cells.Item(1,5) = 'Netmask'
      for ($num = 0 ; $num -lt $total ; $num++){
        $xws1.Cells.Item($num+2,1) = $orgVDCs_tmp[$num].Tenant
        $xws1.Cells.Item($num+2,2) = $orgVDCs_tmp[$num].vDC
        $xws1.Cells.Item($num+2,3) = $orgVDCs_tmp[$num].OrgVdcNetwork
        $xws1.Cells.Item($num+2,4) = $orgVDCs_tmp[$num].Gateway
        $xws1.Cells.Item($num+2,5) = $orgVDCs_tmp[$num].Netmask
        }    
      $usedRange = $xws1.UsedRange
      $usedRange.EntireColumn.AutoFit() | Out-Null
      }
    else{Write-Host "Failed" -ForegroundColor Red;Write-Host $err_mes.Exception -ForegroundColor Red}
  }
  else{Write-Host "Failed" -ForegroundColor Red;Write-Host $err_mes.Exception -ForegroundColor Red}
}
$ext=".xlsx"
$path=(Get-Location).Path + "\orgvdcnet$ext"
$ExcelApplication.DisplayAlerts = $false;
$workbook.Worksheets.Item($sheets).Delete()
$WorkBook.Saveas($path)
$WorkBook.Close()
$ExcelApplication.Quit()

  1. скрипт запрашивает учетные данные для подключения к vCD;
  2. скрипт считывает список vCloud Director’ов из текстового файла;
  3. создает Excel-файл, а на нем одноименный лист по каждому vCD;
  4. заполняет список сетей по vCD на листе;
  5. сохраняет результат в файле orgvdcnet.xlsx

Что стоило бы доделать – проверку на наличие установленного Excel (например, тут – $ExcelApplication = new-object -comobject excel.application). Если Excel нет, то делать вывод в CSV-файлы по старинке…

]]>
https://bonlesave.ru/2022/04/11/powershell-rest-excel/feed/ 6
Get-ADUser и Organization Unit https://bonlesave.ru/2021/02/19/get-aduser-ou/ https://bonlesave.ru/2021/02/19/get-aduser-ou/#respond Fri, 19 Feb 2021 04:00:46 +0000 https://bonlesave.ru/?p=9045 Возник вопрос: как экспортировать список пользователей из AD с организационным подразделением (Organization Unit или OU).

Get-ADUser user -Properties * показал, что OU присутствует в двух атрибутах:

  • Distinguishedname: CN=user,ou=ou1,ou=ou2,dc=domain,dc=ru;
  • CanonicalName: domain.ru/ou2/ou1/user

В готовом виде OU нет, хотя для целей сортировки удобнее использовать CanonicalName.

Так родился “однострочник”, позволяющий вытащить OU в качестве атрибута

get-aduser -filter * -SearchBase ou=ou1,ou=ou2,dc=domain,dc=ru" -Properties cn,canonicalname | select name,userprincipalname,@{Name="OU";expression={$_.Canonicalname.substring(0,$_.canonicalname.length-$_.cn.length)}}

]]>
https://bonlesave.ru/2021/02/19/get-aduser-ou/feed/ 0
Zerologon check (проверка) https://bonlesave.ru/2020/09/29/zerologon-check/ https://bonlesave.ru/2020/09/29/zerologon-check/#comments Tue, 29 Sep 2020 14:17:31 +0000 https://bonlesave.ru/?p=8956 Continue reading "Zerologon check (проверка)"]]> Как вы слышали,  в августе 2020 года была обнаружена критическая уязвимость CVE-2020-1472 в протоколе проверки пользователя контроллером домена.

Критическая она потому, что для эксплуатации этой уязвимости достаточно оказаться в одной сети с контроллером домена.

Microsoft конечно же выпустил обновления, но кто ж их ставит, верно?

В сети есть скрипт, позволяющий провести проверку через эксплуатацию уязвимости, но это вряд ли считается нормальным методом проверки.

Поэтому я написал скрипт, который проверяет наличие требуемых обновлений на контроллерах домена Windows Server 2012 R2.

Список обновлений взят из статьи про уязвимость, а также Catalog MS, указывающего на более новые версии обновлений. В моем списке сентябрьское обновление, два августовских и перекрывающий их апдейт безопасности для Windows 2012 R2.

Обратите внимание, что для Windows 2016/2019 номера апдейтов другие!

Для запуска скрипта требуются права администратора домена, так как скрипт проходит по контроллерам и через WMI получает список установленных обновлений.

$updates="KB4577066","KB4571703","KB4571723","KB4578013";

Get-ADDomainController -filter * | sort name | %{
$result="initial";$hf=$null;
$hf=Get-HotFix -ComputerName $_.name | where{$updates -contains $_.hotfixid};
if($hf -eq $null){$result="WARNING"}
else{$result="";foreach($HFs in $HF){$result+=$HFs.HotfixID+";"}};
$_ | select Name, @{Name="Updates";Expression={$result}}}

Результатом работы скрипта является следующий вывод:

Name           Updates
----           -------
BAD-DC02 WARNING
GOOD-DC01 KB4571723;KB4578013;KB4577066;
GOOD-DC02 KB4571723;KB4578013;KB4577066;
MEDI-DC02 KB4571723;KB4571703;
MEDI-DC03 KB4578013;KB4571703;

WARNING означает, что нужно срочно бежать и ставить обновления.

Наличие сентябрьского обновления означает, что вы молодцы. Наличие августовских – ну не безнадежны, по крайней мере :-).

]]>
https://bonlesave.ru/2020/09/29/zerologon-check/feed/ 2
Приборка групп рассылок в Exchange https://bonlesave.ru/2020/03/16/clean-distribution-group-exchange/ https://bonlesave.ru/2020/03/16/clean-distribution-group-exchange/#respond Mon, 16 Mar 2020 10:19:17 +0000 https://bonlesave.ru/?p=8661 Continue reading "Приборка групп рассылок в Exchange"]]> В нашей компании используется MS Exchange Server, в нём создано ~2000 групп рассылок: ~1700 обычных и ~300 динамических, и все это хозяйство держит под собой 2800 e-mail адресов.

Хозяев групп нет, а если и есть, то давно уволились, какого-то реестра тоже нет.

В результате, для одного департамента есть такие группы:

  • DISTRIB-Отдел;
  • Distrib_отдел;
  • Отделъ-Пермь;
  • DISTRIB_IT_Otdel.

И столько же динамических:

  • Рассылка-Отдел (Москва);
  • Рассылка-Отдел (Общая);
  • Рассылка Пермский ф-л (Отдел);
  • DST-PRM_NOT_OTDEL.

Мы с коллегами решили, что пришла пора весенней уборки.

Но как узнать какие из этих групп используются? Отключать по одной и смотреть откуда поднимется вой?

Я решил, что это не наш метод и приступил к анализу Exchange MTL (Message Tracking Logs).

Есть событие EXPAND, которое “развёртывает” группу в список получателей. “Ага”, – сказал я и начал тестировать стандартную и динамическую группы рассылки.

Первый же подвох ждал меня с динамической группой рассылки – она не порождала события EXPAND.

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

Жалкие попытки изучения Python подсказали мне, что для решения задачи “встречаемых” адресов групп в логе лучше использовать структуру вида словарь/dictionary, а не стандартный массив/array, в силу возможности индексировать структуру по уникальному ключу, которым может выступить адрес группы. В итоге, вместо поиска по массиву сопоставления вы “просто” сразу переходите по индексу.

Оказывается, в Powershell тоже есть свои словари – Hashtable. Ну а дальше дело само начало спориться. 🙂

#собираем в список все электронные адреса у стандартных групп рассылки, даже если их несколько

$addresslist=(Get-DistributionGroup -ResultSize unlimited).emailaddresses.smtpaddress

#добавляем туда все электронные адреса у динамических групп рассылки

$addresslist+=(Get-dynamicDistributionGroup -ResultSize unlimited).emailaddresses.smtpaddress

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

$dict=@{}

$addresslist | %{$dict[$_] = 0}

#Создаем список Mailbox-серверов для последующего опроса MessageTrackingLog,
#формируя строку для удаленного подключения WinRM

$Mailboxservers = Get-MailBoxServer | % { "<a href="http://$($_.Name)/PowerShell/">http://$($_.Name)/PowerShell/</a>" }

#Выполняем в параллели опрос всех серверов. Время работы – примерно 30 минут на месяц логов, содержащих 160 тысяч
#событий SUBMIT. Событие Submit лучше тем, что в отличие от Expand оно всегда содержит адрес группы в качестве
#адреса получателя

#По каждому адресу получателя проверяем в нашем словаре наличие такой строки. Если такая строка есть,
#увеличиваем в ней значение счетчика на единицу, показывая общее количество писем на этот адрес

Invoke-Command -ConfigurationName Microsoft.Exchange -ConnectionUri $Mailboxservers -Authentication Kerberos -Script {
Get-MessageTrackingLog -EventId submit -ResultSize Unlimited |Select-Object -ExpandProperty recipients} | %{
if($dict[$_] -ge 0){$dict[$_]++
}
}

#Экспортируем ненулевые элементы в CSV, который затем прекрасно можно преобразовать в Excel, либо использовать дальше

$dict.GetEnumerator() | ?{$_.value -ne 0} | select key,value | export-csv -NoTypeInformation C:\tmp\all2.csv

За месяц письма приходили на 400 из 2800 адресов. Планируем понаблюдать квартал или полгода, после чего начать приборку.

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

До связи ;-).

]]>
https://bonlesave.ru/2020/03/16/clean-distribution-group-exchange/feed/ 0
Развертывание виртуальной машины vSphere через PowerSHell часть 1 https://bonlesave.ru/2019/06/10/vsphere-vm-deployment-powershell-part1/ https://bonlesave.ru/2019/06/10/vsphere-vm-deployment-powershell-part1/#comments Mon, 10 Jun 2019 06:00:24 +0000 https://bonlesave.ru/?p=8330 Continue reading "Развертывание виртуальной машины vSphere через PowerSHell часть 1"]]> Стал задаваться вопросом – как автоматизировать выполнение рутинных операций в vSphere?

Лучше бы не задавался 🙂

Начал с задачи “развернуть виртуальную машину из шаблона с указанной кастомизацией гостевой операционной системы”.

Основные проблемы, с которыми я столкнулся:

  • мною использовалась настройка кастомизации ОС “Prompt User”, спрашивающая только IP-адрес (подразумевается, что развертывание происходит в одно и то же адресное пространство). Как оказалось, проще использовать передачу “всех” параметров IP в скрипте;
  • перемещение учетной записи сервера после ее развертывания для применения групповых политик в другой контейнер Active Directory;
  • поле “Notes” получает строку. Как передать ей несколько строк, если потребуется перевод строки, я пока не разбирался;
  • за кадром остались процессы дальнейшей настройки сервера – установка обновлений и дополнительного ПО. Это будет рассмотрено в следующих частях статьи.

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

А вот что получил:

Get-OSCustomizationSpec -Name windows | New-OSCustomizationSpec -Name temp1 -Type NonPersistent
Get-OSCustomizationSpec -Name temp1 | Get-OSCustomizationNicMapping | Set-OSCustomizationNicMapping -IpMode UseStaticIP -IpAddress '10.0.0.1' -SubnetMask '255.255.255.0' -DefaultGateway '10.0.0.254' -Dns '10.0.0.254','10.0.0.253'
New-VM -Name 'new-vm' -Template (Get-Template windows_template) -OSCustomizationSpec (Get-OSCustomizationSpec 'temp1') -ResourcePool (Get-ResourcePool -Name 'Resources'-Location 'Cluster_Name') -Datastore (Get-DatastoreCluster -Name 'Datastore_Cluster') -Notes "Zdes mogla by byt vasha reklama"
Start-VM -VM new-vm
Do {Get-ADComputer -identity new-vm -ErrorAction SilentlyContinue;write-host "." -nonewline -f red;sleep 3} until(Get-ADComputer -identity new-vm -ErrorAction SilentlyContinue -warningaction silentlycontinue)
Move-ADObject -identity (Get-ADComputer -identity new-vm).distinguishedname -TargetPath "OU=Servers,DC=DOMAIN,DC=COM"
Do {$tools=(Get-VM -Name new-vm).ExtensionData.Guest.ToolsStatus;write-host "." -nonewline -f red;sleep 3} until(($tools -eq "toolsOk") -or($tools -eq "toolsOld"))
Restart-VMGuest -VM new-vm -Confirm:$false
Start-Sleep -Seconds 10
Do {$tools=(Get-VM -Name new-vm).ExtensionData.Guest.ToolsStatus;write-host "." -nonewline -f red;sleep 3} until(($tools -eq "toolsOk") -or($tools -eq "toolsOld"))
Write-Host("Server is ready!")

Данный скрипт берет гостевую кастомизацию “windows” (Guest Customization OS) и создает на ее основе временную кастомизацию “temp1”.

Затем скрипт задает настройки IP, которые должна будет получить новая ВМ.

Далее мы создаем виртуальную машину из шаблона “windows_template”, используя подготовленную кастомизацию “temp1”. Машина создается в кластере “Cluster_name” с примечанием о том, для чего нужна эта ВМ. Диски шаблона при этом клонируются на кластер хранилищ (Datastore Cluster) с названием “Datastore_Cluster”.

Затем мы включаем виртуальную машину и ждем.

Ждем мы до тех пор, пока в AD не появится учетная запись компьютера. А эта запись должна появиться там потому, что в гостевой кастомизации настроено автодобавление ПК в домен.

Как только мы этого дождались, мы перекладываем эту учетную запись ПК в соответствующую OU.

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

Затем ждем 10 секунд пока VMware Tools 100% остановятся в ходе перезагрузки, ждем пока они будут запущены и выводим на экран сообщение о том, что сервер готов.

]]>
https://bonlesave.ru/2019/06/10/vsphere-vm-deployment-powershell-part1/feed/ 1
Exchange 2013 CU22 и информация об Exchange https://bonlesave.ru/2019/05/20/exchange-2013-cu22-i-informaciya-ob-exchange/ https://bonlesave.ru/2019/05/20/exchange-2013-cu22-i-informaciya-ob-exchange/#respond Mon, 20 May 2019 07:58:55 +0000 https://bonlesave.ru/?p=8308 Continue reading "Exchange 2013 CU22 и информация об Exchange"]]> Я уже писал о том, что после разворачивания CU22 на Exchange 2013 вы получаете запись в реестре о том, что, оказывается, на сервере-то установлен Exchange 2013 CU20. Microsoft проблему признало и выпустило совет, как можно вручную исправить ключ реестра.

Я же по привычке делюсь скриптом, который исправит этот ключ реестра по всем вашим серверам Exchange, подразумевая что все они – Exhchange 2013 CU22.

Get-ExchangeServer | %{$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$_.name); $regKey= $reg.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Microsoft Exchange v15",$true); $regKey.SetValue("DisplayName","Microsoft Exchange Server 2013 Cumulative Update 22",[Microsoft.Win32.RegistryValueKind]::String);$_.name}

Скрипт пройдется по всем серверам Exchange в организации, поменяет параметр реестра HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Exchange v15\Displayname и выведет имя сервера в качестве интерактива с вами.

Если вам нужно, чтобы обрабатывались не все сервера, а только какой-то список, то:

  • список серверов нужно будет подать на вход вместо командлета Get-ExchangeServer;
  • вместо $_.name использовать более подходящий $_

P.S. История с этим апдейтом напомнила

“Здравствуйте, я бедный албанский вирус. Я пока что ничего не умею, и к сожалению, не могу причинить вред вашему компьютеру. Пожалуйста, будьте так любезны, сотрите один из важных файлов с жесткого диска вашего компьютера самостоятельно и перешлите меня 30 друзьям. И будет вам счастье. Если вы этого не сделаете я обижусь и не будет вам счастья и конфет весь следующий год! Заранее благодарен за понимание и сотрудничество”.

]]>
https://bonlesave.ru/2019/05/20/exchange-2013-cu22-i-informaciya-ob-exchange/feed/ 0
Проблемы с бэкапом из-за нецелого размера vmdk-диска https://bonlesave.ru/2019/05/15/vmdk-float-size-fails-backup/ https://bonlesave.ru/2019/05/15/vmdk-float-size-fails-backup/#comments Wed, 15 May 2019 03:43:02 +0000 https://bonlesave.ru/?p=8297 Continue reading "Проблемы с бэкапом из-за нецелого размера vmdk-диска"]]> Обратил внимание на то, что некоторые виртуальные машины не бэкапятся через штатный VADP API. Техническая поддержка NetBackup нашла в логах, что причиной этого является дробный (нецелый) размер VMDK-диска.

Создать такой диск очень просто – указать дробный размер для терабайт в “толстом” клиенте:

Если изменить единицы измерения с терабайт на гигабайты, вы увидите, что диск занимает нецелое количество гигабайт (и соответственно, мегабайт).

Веб-клиент в этом плане получше – он сразу показывает наличие проблемы:

Лечится проблема достаточно легко:

  1. Меняете единицы измерения на мегабайты и увеличиваете размер диска в сторону ближайшего целого значения. В приведенном случае вместо 2’086’666,23925781 МБ у вас должно стать 2’086’667 МБ.
  2. Растягиваете размер файловой системы на вновь появившееся место.

Для экспресс-проверки вашей инфраструктуры вы можете воспользоваться следующим PowerCLI-скриптом:

Get-VM | Get-HardDisk | where {($_.capacityGB-[math]::Truncate($_.capacityGB)) -gt 0} | ft capacityGB, parent -AutoSize

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

]]>
https://bonlesave.ru/2019/05/15/vmdk-float-size-fails-backup/feed/ 1