ShS's Blog

Just another sysadmin's weblog

Scripting Games 2010. Advanced Event 4 (Remotely creating environmental variable)

Posted by shs на 2010/05/19

Четвертое задание:

 Event Scenario

Your company has just purchased a rather expensive enterprise-wide networked application. The application consists of a server back end and a client front end. After the server back end application has been installed, updated, and configured to listen on the appropriate ports, the client application is deployed using your network deployment application. Unfortunately, the client application installer failed to create a specific system environmental variable that is used to check for client licensing before allowing the client to launch. This was not discovered until the package had been fully deployed. In a panic, you boss calls you and asks if you can create a script that will read a text file of computer names that have been licensed for the application, and create the new environmental variable. The environmental variable is shown here:

Name: ScriptingGuys

Value: ScriptingGuys.Com

 

Design points

· Your script should allow for any environmental variable, system, or user. It should also allow the user to specify a specific name and value.

· Your script should check to see if the environmental variable already exists. If it does, it should prompt to override by displaying the current value.

· Your script should also provide a silent mode—just create the variable and assign a new value, with no questions asked. But it should create a log of computers that already had the variable, with the previous setting.

· Your script should create a log that lists all the computers that were successfully modified, and include the date and time when the modification was made.

 

 

И так, сначала задание показалось  элементарным, пока я не вчитался и не понял, что требуется изменять переменные окружения не только на локальном, но и на удаленных компьютерах. Погуглив, я нашел, каким образом можно изменять переменные окружения на удаленных компьютерах: http://powershell.com/cs/forums/p/3059/4126.aspx

Как ни странно, но этот метод работает, хотя, как я не выполнял команды

PS > [wmiclass]"Win32_Environment" | Get-Member -memberType *method

TypeName: System.Management.ManagementClass#ROOT\cimv2\Win32_Environment

Name MemberType Definition
---- ---------- ----------
ConvertFromDateTime ScriptMethod System.Object ConvertFromDateTime();
ConvertToDateTime ScriptMethod System.Object ConvertToDateTime();

или

PS > $i = ([wmiclass]"Win32_Environment").CreateInstance()
PS > $i|gm *
TypeName: System.Management.ManagementObject#\Win32_Environment
Name MemberType Definition
---- ---------- ----------
Caption Property System.String Caption {get;set;}
Description Property System.String Description {get;set;}
InstallDate Property System.String InstallDate {get;set;}
Name Property System.String Name {get;set;}
Status Property System.String Status {get;set;}
SystemVariable Property System.Boolean SystemVariable {get;set;}
UserName Property System.String UserName {get;set;}
VariableValue Property System.String VariableValue {get;set;}
__CLASS Property System.String __CLASS {get;set;}
__DERIVATION Property System.String[] __DERIVATION {get;set;}
__DYNASTY Property System.String __DYNASTY {get;set;}
__GENUS Property System.Int32 __GENUS {get;set;}
__NAMESPACE Property System.String __NAMESPACE {get;set;}
__PATH Property System.String __PATH {get;set;}
__PROPERTY_COUNT Property System.Int32 __PROPERTY_COUNT {get;set;}
__RELPATH Property System.String __RELPATH {get;set;}
__SERVER Property System.String __SERVER {get;set;}
__SUPERCLASS Property System.String __SUPERCLASS {get;set;}
ConvertFromDateTime ScriptMethod System.Object ConvertFromDateTime();
ConvertToDateTime ScriptMethod System.Object ConvertToDateTime();

увидеть  в результатах их работы метод Put мне так и не удалось, но он все-таки есть и работает ;).

Что касается самих соревнований, то я заметил, что количество балов, которые начисляют за скрипт, сильно зависит от его оформления и соблюдения формальных признаков выполнения задания. Так, например, видел несколько скриптов, которые, IMHO,  не выполняли задание полностью или выполняли, но не то или не совсем то, что требовалось, и, не смотря на это, получали высшие оценки. Например, вместо определения uptime’а пользователя некоторые соревнующиеся определяли uptime компьютера и т.п. В то же самое время  они имели красиво оформленную шапку, со всякими «синописами» и выводили справочную информацию при запуске. Лепить первый блок-коммент с подробным описанием скрипта, а затем делать функцию, выводящую в качестве справки фактически ту же самую информацию, мне показалось излишеством. Поэтому я написал и начал включать в каждый скрипт функцию, выводящую в качестве справочной информации первый блок-коммент, запущенного на исполнение скрипта. Вот, эта функция:

###############################################################################
#
# Function Show-Help. Write to the host first blok comment of this script
#
Function Show-Help ($ScriptFullName){
   if ($ScriptFullName) {
       $IsHelpLine=$false
       switch -file $ScriptFullName {
           {$_ -match "<#"}     {Write-Host $_;$IsHelpLine=$true;continue}
           {$_ -match "#>"}     {Write-Host $_;$IsHelpLine=$false;break}
           {$IsHelpLine}        {Write-Host $_;}
       }
   }
}

А, вот, пример ее использования:

   #Write top block comment as help
   Write-Host "Error: Parameter CompList required! `n`n"
   Show-Help $MyInvocation.MyCommand.Path

Для тех, кто так же, как и я, любит пользовать PowerGUI ScriptEditor для создания и отладки скриптов: обратите внимание, что текущая версия PowerGUI ScriptEditor 2.0.0.1082 содержит баги, которые не позволяют использовать блок-коммент (<#….#>) в качестве первого комментария в скрипте, если скрипт содержит ключевое слово param(…)

Этот баг разработчики обещали исправить в версии 2.1, которая скоро будет доступна для скачивания. Другой баг заключается в том, что $MyInvocation.MyCommand.Path не возвращает полное имя скрипта, если оный скрипт запущен в PowerGUI ScriptEditor. Эту багу разработчики планируют исправить в версии 2.2. К счастью, имеется workaround, который позволяет обойти обе  эти ошибки: нужно создать скрипт, который будет запускать ваш «проблемный» скрипт. В этом случае обе эти багги не проявляют себя. Например, для запуска скрипта, полное имя которого – c:\scripts\4.ps1, можно использовать скрипт, состоящий из единственной строчки &”c:\scripts\4.ps1”, открытый в соседнем окне PowerGUI ScriptEditor’а.

Ну, и наконец мой скрипт, для решения 4го задания:

<#
   EnvironmetVars.ps1 shs 20100503
   .Synopsis
   Create|Update Environment variables on the remote computers
      EnvironmetVars.ps1 <CompList> [UserName] [VarName] [VarValue] [Log] [-Silent]
   .Parameter CompList
   List of computers for further processing
   .Parameter UserName
   Name of User for wich environment variable will be created
   .Parameter VarName
   Name of environmet variable that will be created
   .Parameter VarValue
   Value of environmet variable that will be set
   .Parameter Log
   Full name of the log-file
   .Parameter -Silent
   Switch output to the host or to the log
   .Example
   .\EnvironmetVars.ps1 C:\Scripts\PoSh\SG2010\4\4.txt
#>
param ([string]$CompList, [string]$UserName="<SYSTEM>",[string]$VarName="ScriptingGuys", $VarValue="ScriptingGuys.Com",[string]$Log=".\EnvVar.log",[switch]$Silent)
#
###############################################################################
#
# Function Show-Help. Write to the host first blok comment of this script
#
Function Show-Help ($ScriptFullName){
   if ($ScriptFullName) {
       $IsHelpLine=$false
       switch -file $ScriptFullName {
           {$_ -match "<#"}     {Write-Host $_;$IsHelpLine=$true;continue}
           {$_ -match "#>"}     {Write-Host $_;$IsHelpLine=$false;break}
           {$IsHelpLine}        {Write-Host $_;}
       }
   }
}
###############################################################################
#
# Filter Where-Online. Return pingable computers only
#
Filter Where-Online
{
   $CompName=$_
   $ping = new-object System.Net.NetworkInformation.Ping
   try {if ($ping.send($_).Status -eq "Success" ) { $_ }}
   catch {Write-Verbose "$CompName `t-`t ping error"}
}
###############################################################################
#
# Function Write-Report. Make report and send it to screen or file
#
Function Write-Report ($ReportString,[boolean]$MakeLog=$Silent,$LogName=$Log) {
   if ($MakeLog) {
       Out-File -FilePath $LogName -InputObject $ReportString -Append
   }
   else {
       Write-Host $ReportString
   }
}
###############################################################################
#
# Function Set-RemEnvVar. Set Environmet Varibles using WMI
#
Function Set-RemEnvVar ($ComputerName, $UserName, $VarName, $VarValue) {
   #Return 0 if variable was modified succesfully
   $Result=0
   try {
       $EnvRem = ([wmiclass]"//$ComputerName/root/cimv2:Win32_Environment").CreateInstance()
        $EnvRem.Name = $VarName
        $EnvRem.VariableValue = $VarValue
        $EnvRem.UserName = $UserName
        $EnvRem.Put()|Out-Null
   }
   #Return 1 if we got error
   catch {
       $Result=1
   }
   $Result
}
###############################################################################
#
# Function Get-RemEnvVar. Set Environmet Varibles using WMI
#
Function Get-RemEnvVar ($ComputerName, $UserName, $VarName) {
   try {
       $Filter="Name='$VarName' and UserName='$UserName'"
       Write-Verbose "Filter = $Filter"
       (gwmi -Class Win32_Environment -ComputerName $ComputerName -Filter "Name='$VarName' and UserName='$UserName'" -ErrorAction Stop).VariableValue
   }
   #if error ocured...
   catch {
       Write-Verbose "Error getting environment variables from computer '$ComputerName'"
       #... return "***Error***" as result of function
       "***Error***"
   }
}
#================================== Script Entry point ========================
cls
#$VerbosePreference=Continue
#if parameter $CompList was passed to the script...
if ($Complist) {
   #if list of computers exist...
   if (Test-Path $CompList) {
       #for each computer in the list which is online...
       gc $CompList| Where-Online | foreach {
       #gc $CompList| foreach {
           #Set Initial values
           $ComputerName=$_
           $ReportString=$null
           #Prepare UserNmae to use with WMI Win32_Environment class
           #Escape "\" with "\" (replace "\" with "\\")
           $UserName= $UserName -replace "\\","\\"
           #Try get environment variable remotly
           switch ($OldValue=Get-RemEnvVar $ComputerName $UserName $VarName) {
               #varible does not exist. Create varible and set value
               {$OldValue -eq $null} {
                   if (Set-RemEnvVar $ComputerName $UserName $VarName $VarValue) {
                       $ReportString="`t`tError setting value"
                   }
                   Break
               }
                              #Error getting varible
               {$OldValue -eq "***Error***"} {
                   $ReportString="`tError getting value"
                   Break
               }
                              #Variable exist already and have right value.
               {$OldValue -eq $VarValue} {
                   #Nothing to do. Do not set $ReportString.
                   Break
               }
                              #Variable exist but have wrong value
               Default {
                   $ReportString="Old value='$OldValue'"
                   if (Set-RemEnvVar $ComputerName $UserName $VarName $VarValue) {
                   $ReportString+="`t`tError setting value"
                   }
               }
           }
           #If $ReportString exist add date and computername and
           #write it to Report
           if ($ReportString) {
               $ReportString = "$(Get-Date)`t$ComputerName`t$ReportString"
               Write-Report -ReportString $ReportString
           }
       }
   }
   else {
       Write-Host "Error: List of comuters '$CompList' doesn't exist"
   }
}
else {
   #Write top block comment as help
   Write-Host "Error: Parameter CompList required! `n`n"
   Show-Help $MyInvocation.MyCommand.Path
}
Реклама

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

 
%d такие блоггеры, как: