вторник, 14 октября 2014 г.

Скрипт перемещения нескольких виртуальных машин на основе критерия их местоположения

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

К примеру, у нас есть наименованные по стандарту VM какого-либо проекта (в данном случае имена  содержат *HYD*) и нам известна конечная точка, куда их надо перетащить.



Порядок работы скрипта следующий:

1. Получили массив имён нужных нам VMs;
2. Получили имя хоста виртуализации на который будем двигать VMs в переменную;
3. Берём из массива конкретную VM и находим её местоположение, сравниваем с целевым, если текущее положение VM не равно целевому, то делаем операцию Move.
4. Спим 5 секунд.

Работает для Windows Server 2012 (R2) и SCVMM 2012 R2


#-------------------Version-----------------------------------
Write-Host "Version 1.0" -Foreground Green
Write-Host " "
Write-Host " "
#-------------------Discaimers--------------------------------
Write-host "!WARNING!" -ForegroundColor Yellow
Write-Host "---------------------------------------------------------------------------" -ForegroundColor Yellow
Write-Host "During current procedure of moving VMs to a destination host" -ForegroundColor Yellow
Write-Host "all VMs would be turned off. Migration may take a lot of time" -ForegroundColor Yellow
Write-Host "Make sure that you choose a maintenance window for the migration process" -ForegroundColor Yellow
Write-Host "to avoid an impact with SLA of all services that are under migration" -ForegroundColor Yellow
Write-Host "---------------------------------------------------------------------------" -ForegroundColor Yellow
Write-Host " "
Write-Host " "
Write-Host "INFORMATION" -ForegroundColor Green
Write-Host "---------------------------------------------------------------------------" -ForegroundColor Green
Write-Host "The script will ask you to provide several params for execution." -ForegroundColor Green
Write-Host "Enter all data during input procedure according to template design to avoid different 'shit happens' situations" -ForegroundColor Green
Write-Host "Be carefull during this phase, because there's no protection from badly shaped hands and idiots in this script" -ForegroundColor Yellow
Write-Host "---------------------------------------------------------------------------" -ForegroundColor Green
Write-Host " "
Write-Host " "
Write-Host "Techdiving.pro"
Write-Host "2014"
#-------------------Getting Required User Input---------------
Write-Host " "
Write-Host " "
Write-Host "Prepare to enter required data!" -ForegroundColor Yellow
Write-Host "-------------------------------" -ForegroundColor Yellow
Write-Host " "
#Get VMs-identification info
$VMNameTemplate = Read-Host "Enter some letters or numbers that will allow to identify required VMs (Template:*Identifier*)"
#Get Target location
$TargetLocation = Read-Host "Enter Targeted Location (format should be the following Template: D:\Path)"
#Get a Target host name
$TargetHost = Read-Host "Enter target virtualization host name (FQDN)"
#--------------------MAIN-------------------------------------
#Getting all VMs that are in the scope of seach
$VMsArray = Get-SCVirtualMachine  | where-object {$_.Name -like $VMNameTemplate}
     Write-Host "Following VMs were discovered:" -Foreground YELLOW
     Write-Host "------------------------------" -Foreground YELLOW
FOREACH ($VM in $VMsArray)
    {
     Write-Host $VM.Name -Foreground YELLOW
    }
#Setting up target host
$vmHost = Get-SCVMHost | where { $_.Name -eq $TargetHost}
#Starting to work with VMsArray
FOREACH ($VM in $VMsArray)
    {
     $VMLocationArray = Get-SCVirtualMachine -Name ($VM).Name
        FOREACH ($VMLocation in $VMLocationArray)
            {
             #Getting Location Path for the VM and trimming it
             $VMLocation = $VM.Location
             $VMLocation = $VMLocation.TrimEnd($VM.Name)
             #Compare results with $TargetLocation
             IF ($VMLocation -eq $TargetLocation)
                    {
                     Write-Host $VM.Name " do not require migration" -Foreground GREEN
                    }
             ELSE
                    {
                     #Migration execution
                     Write-Host " "
                     Write-Host " "
                     Write-Host $VM.Name " is undermigration" -Foreground YELLOW
                     Sleep 2
                     Write-Host " "
                     Write-Host $VM.Name " is under shutdown process" -ForegroundColor RED
                     Sleep 2
                     Stop-SCVirtualMachine -VM $VM -Shutdown | out-null
                     Write-Host " "
                     Write-Host $VM.Name  "Shutdown is Completed" -ForegroundColor Green
                     Sleep 2             
                     Write-Host " "
                     Write-Host $VM.Name  "Starting migration..." -ForegroundColor Green
                     Sleep 2
                     Move-SCVirtualMachine -VM $VM -VMHost $vmHost -Path $TargetLocation -UseLAN -RunAsynchronously -UseDiffDiskOptimization | out-null
                     Write-Host " "
                     Write-Host "Waiting for the end of the job... monitoring of the Migration Job status will be started in 5 sec" -ForegroundColor Yellow
                     sleep 5
                     $Status = Get-SCJob | Where-Object {($_.ResultName -eq $VM) -and ($_.Status -eq "Running")}
                     While ($Status -ne $Null)
                        {
                         #Write-Host "." -ForegroundColor Yellow
                         sleep 5
                         $Status = Get-SCJob | Where-Object {($_.ResultName -eq $VM) -and ($_.Status -eq "Running")}
                         $CounterTime ++
                        }
                    }           
            }
      $CounterTime = $CounterTime * 5 / 60
      Write-Host " "
      Write-Host "Starting VM..." -ForegroundColor Green
      Start-SCVirtualMachine -VM $VM
      Write-Host " "
      Write-Host "Migration of the Current VM took ~ " $CounterTime " minutes" -ForegroundColor Green
      Write-Host " "
      Write-Host "Awaiting 5 sec. to start another process to spread migrations"
      sleep 5
 
    }
Write-Host " "
Write-Host "-------------------------------------------------------------- " -Foreground GREEN
Write-Host "All Ops were being completed" -Foreground GREEN

Как на удалённом ПК установить службу Сервер, File and Printer Sharing for Microsoft Networks

Недавно столкнулся с интересной задачей в проекте по ConfigMgr.

Необходимо было развернуть агентов ConfigMgr на большое кол-во рабочих станций с Windows XP, однако выяснилось, что служба "Сервер", которая отвечает за коммуникации по 135-139 и 445 портам отсутствует в системах. ПК было около 2000 тысяч. Необходимо было централизовано, без походов по ПК, установить и зарегистрировать службу.

ПРИМЕЧАНИЕ! Способ тестировался для русифицированной Windows XP SP3, для поздних операционных систем не проверял.

Пришлось пару дней крепко подумать, в итоге проблему решил с помощью утилиты NetSet из набора Support.CAB, который находится на дистрибутиве Windows XP | Server 2003 (если мне память не изменяет, ещё и в Windows 2000 применялось при OSD для конфигурирование сетевых интерфейсов с правильными параметрами).

Для того, чтобы использовать утилиту, необходимо сформировать файл ответов определённого формата.

ВНИМАНИЕ! При работе с файлом ответов надо помнить, что если компонент в нём не перечислен, но обнаружится в системе, NetSet произведёт его удаление.

Пример файла ответов


[Networking]

[NetAdapters]
Adapter01=Params.Adapter01 //имя сетевого адаптера

[Params.Adapter01]
InfID=* //адаптер может иметь любой GUID

[NetProtocols]
MS_TCPIP=Params.MS_TCPIP

[Params.MS_TCPIP]
AdapterSections=params.TCPIP.Adapter01
DNS=yes
DNSSuffixSearchOrder= mycorp.com, <name>.com
EnableLMHosts=No

[params.TCPIP.Adapter01]
SpecificTo=adapter01
DNSDomain=mycorp.com
DHCP=Yes

[NetServices]
MS_Server=params.MS_Server
   //собственно кусок, который нам нужен

 
 
 
 
[Params.MS_Server]
optimization=balance
Enable="MS_Server"

[NetClients]
MS_MSClient=Params.MS_MSClient

[Params.MS_MSClient]

Струтура файла ответов достаточно проста:

[Имя  компонента]
Имя компонента = СегменПараметров.компонента

[СегментПараметров.Компонента]
Параметр1 =
Параметр2 =

Далее был написан простенький *.cmd файл, который был распространён GPO (Computer Settings) на все необходимые ПК. Файлик состоял из двух строк:

  • копирование необходимых файлов
 
  • Запуска команды NetSet с файлом ответов.

start "NetSet" /D C:\ Netset "c:\AnswerFile.txt"

Однако нас ждал ещё один подводный камень - все интерфейсы на целевых ПК назывались по разному, имена интерфейсов были нестандартизованны. При тестах выяснил одну забавную особенность: если служба ставится, то одновременно включается для всех интерфейсов.

В Windows XP существует единственный стандартизованный интерфейс с одинаковым названием - это LoopBack iface. Точное название можно посмотреть через netsh:

netsh > interface > ipv4 > show interface

И если указать его в файле ответов, к примеру для русcифицированной Windows XP

Внутренний=Params.Внутренний
Мы получаем искомое.

Собственно, вот и весь рецепт.

Спасибо и буду рад вопросам или другому участию.


http://support.microsoft.com/kb/268781/en-us

четверг, 4 сентября 2014 г.

Forefront Identity Management vNext

Немного инсайда.

Адам Холл и команда по Hybrid Identity готовят релиз Forefront Identity Management vNext, сейчас он проходит альфа тестирование на стабилизацию. CTP1 версия планируется к выходу на ноябрь месяц 2014.

Microsoft в данной версии останавливает развитие FIM Azure Management Agent. Он будет существовать в рамках продукта, но дальнейшего развития и обновления функционала не получит. Теперь, чтобы синхронизировать AD On-Prem с Windows Azure мы, скорее всего, будем использовать только DirSync. Однако, подождём релиза - время покажет :)

четверг, 21 августа 2014 г.

Сложный сценарий выбора рецензента инициатором через портал SCSM 2012 R2

Не всегда функционал "из коробки" пригоден в Заказчике, т.к. могут существовать  определённые отличия бизнес процессов Заказчика от тех, которые видит себе разработчик продукта. К примеру, функционал согласования с подтверждением у линейного руководителя SCSM не всегда применим.

В одном из проектов нам пришлось реализовать сложный сценарий согласования, когда инициатор запроса сам выбирает утверждающего на портале SCSM 2012 R2.
По желанию Заказчика данная реализация должна была включать некий упорядоченный список "ДЕПАРТАМЕНТ->ОТДЕЛ->ФИО согласующего", в котором инициатор выбирает того, кто будет согласовывать запрос.



Для этого нами был создан скрипт ниже:

try


 
  {
   #Import SMlets

   Import-Module SMlets

       
   function WriteToLog($TextOut)

           {
            #This function creates and writes log file to the root partition of the "C Drive"
            #Write-host $TextOut
            $CurrDateTime = Get-date -format 'u'

            $CurrDateTime + " " + $TextOut | Out-file C:\CustomMPs\ScriptLog.log -append

           }
   #Getting srID of the brand new SR to work with

   $SR = Get-SCSMClass System.WorkItem.ServiceRequest$ | Get-SCSMObject -Filter "Displayname -like $srID*"

   $TestOut = "Following SR discovered" + $SR + "with ID" + $srID

   WriteToLog $TestOut

   #Getting User input ReviwerID and looking for a Display name of this reviewer in Active Directory

   $UserDN = $SR.ReviewerID.DisplayName

   $TestOut = "Revewer Identified, reviewer name is:" + $UserDN

   WriteToLog $TestOut

   $User = Get-SCSMClass Microsoft.AD.User$ | Get-SCSMObject -Filter "Displayname -like $UserDN*"

   $TestOut = "User DN is" + $User
 
   WriteToLog $TestOut


   #Setting up relationship

   $wiContainsActivity = Get-SCSMRelationshipClass System.WorkItemContainsActivity

   #Trying to get raID, while it won't become available
   While ($RA -eq $null)
       {
        $RA = Get-SCSMRelatedObject -SMObject $SR -Relationship $wiContainsActivity |? {$_.ClassName -eq 'System.WorkItem.Activity.ReviewActivity'
       }

   $TestOut = "SR contains RA:" + $RA

   WriteToLog $TestOut
  
   #Setting up relationship

   $Reviewer = Get-SCSMRelatedObject -SMObject $RA | ?{$_.ClassName -eq 'System.Reviewer'}

   $reviewerIsUser = Get-SCSMRelationshipClass System.ReviewerIsUser

   #Setting $User variable data as a Reviewer in RA

   New-SCSMRelationshipObject -Relationship $reviewerIsUser -Source $Reviewer -Target $User -Bulk

   Remove-Module SMlets -Force

  }
 


catch
   {
    # save variable to file

    ($_ | ConvertTo-XML).Save("C:\CustomMPs\ScriptLog.xml")
   }


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

1. Пользователь-инициатор, в списке на портале SCSM  2012 R2, выбирает согласующего, именование которого, совпадает с DisplayName в Active Directory;
2. Далее мы получаем ID объекта запрос на обслуживание с помощью переменной srID (тот запрос, который создал пользователь с помощью переменной скрипта в пакете управления);
3. Мы получаем наименование объекта Microsoft.AD.User в службе каталогов;
4. Далее выбираем все RA и пытаемся получить зависимость ReviewActivity до тех пор, пока нам это не удастся.

ПРИМЕЧАНИЕ! Такой подход вызван особенностью функционирования продукта: все компоненты запроса на обслуживания (активности, SLO и другие зависимые компоненты создаются не сразу, а постепенно, друг за другом). Если убрать данный цикл, то назначение рецензента не произойдёт.

5. После того, как мы получили активность согласования (рецензирования) производится назначение пользователя с помощью связи ReviewerIsUser.

Другие особенности скрипта:
Скрипт ведёт журналирование своей активности и ошибок с помощью методов try и catch, данные исключений выгружаются в XML файл, а так же содержит операционный лог успешных действий, заполняемый с помощью функции WriteToLog. Очень удобно для поиска проблем и отслеживания событий. Однако, думаю в продуктиве лучше отключить лог "успешных" действий.
 
Ограничения:
  • Работает при наличии только одной активности рецензирования в запросе  на обслуживание;
  • Имя пользователя в списке на портале самообслуживания должно полностью совпадать с DisplayName пользователя в ActiveDirectory;
HINT
Нашли интересный баг/фитчу при попытке передать значение в $UserDN = $SR.ReviewerID , содержащее уже не DisplayName объекта ReveiwerID из списка enum, а значение строки, которую пользователь заполняет на портале (поле с форматом ввода string, привязанное к расширенному классу SR).

Из коробки не работает, несмотря на то, что вывод типа переменной, с помощью $User.GetType() в обоих случаях: значения списка (enum) $SR.ReviewerID.DisplayName и значения $SR.ReviewerID совпадает!!!

Выражается проблема в том, что не смотря на то, что поле с портала заполняется в SR, значение в срипте $SR.ReviewerID остаётся пустым, при этом формируется xml с ошибкой, что New-SCSMRelationshipObject -Relationship $reviewerIsUser -Source $Reviewer -Target $User -Bulk командлет не может быть выполнен.

Проблема решается явным приведением типа переменной, поступающей из поля с портала:
$SR.ReviewerID.ToString()