Слава сиськамПротоколу Сноверу!

>> PowerShell V2 RTM <<

Tuesday, October 27, 2009 8:34:36 PM (FLE Standard Time, UTC+02:00)   Comments [0]    

 

Примечание: данный материал публикуется на правах ТЗ (ТЗТайное Знание) и обязателен для знания администраторам PKI.

В предыдущей части (Certificate chaining engine в PowerShell) мы рассмотрели реализацию certificate chaining engine в .NET в виде класса X509Chain. Но этот рассказ был бы неполным, если мы не поговорили про CAPICOM.Chain. Хоть Microsoft рекомендует использовать .NET, а не CAPICOM для решения данной задачи, я считаю необходимым осветить этот вопрос. Для начала начнём с реализации CAPICOM в PowerShell. По непонятным мне пока причинам мне не удалось завести его в PowerShell на системах Windows Vista и выше, поэтому примеры буду приводить на Windows Server 2003.

PS G:\> $chain = New-Object -ComObject "CAPICOM.Chain" PS G:\> $chain Certificates ------------ PS G:\> $chain | gm -MemberType methods TypeName: System.__ComObject#{ca65d842-2110-4073-aee3-d0aa5f56c421} Name MemberType Definition ---- ---------- ---------- ApplicationPolicies Method IOIDs ApplicationPolicies () Build Method bool Build (ICertificate) CertificatePolicies Method IOIDs CertificatePolicies () ExtendedErrorInfo Method string ExtendedErrorInfo (int) PS G:\>

Так же, как и в .NET у нас есть метод Build(), который запускает certificate chaining engine для указанного сертификата. Но это COM объект и X509Certificate2 объекты не принимает. Поэтому мы средствами этого же CAPICOM получим нужный объект сертификата:

PS G:\> $store = New-Object -ComObject "CAPICOM.Store" PS G:\> $store.Open(2,"my",128) PS G:\> $cert = @() PS G:\> $store.Certificates | %{$cert += $_} PS G:\> $cert = $cert[1] PS G:\> $cert Version : 3 SerialNumber : 167D6D32000000000011 SubjectName : CN=Administrator, CN=Users, DC=sysadmins, DC=lv IssuerName : CN=sysadmins-LV-CA, DC=sysadmins, DC=lv ValidFromDate : 2009.08.07. 16:09:56 ValidToDate : 2010.08.07. 16:09:56 Thumbprint : 1FA71B51BCE461BEE24B11AA9EF855ED00DFDFD4 Archived : False PrivateKey : PS G:\>

Вот такой объект сертификата мы получили. Вот его и пропустим через метод Build():

PS G:\> $chain.Build($cert) False PS G:\> $chain.Status() 32 PS G:\> $chain.Certificates Version : 3 SerialNumber : 167D6D32000000000011 SubjectName : CN=Administrator, CN=Users, DC=sysadmins, DC=lv IssuerName : CN=sysadmins-LV-CA, DC=sysadmins, DC=lv ValidFromDate : 2009.08.07. 16:09:56 ValidToDate : 2010.08.07. 16:09:56 Thumbprint : 1FA71B51BCE461BEE24B11AA9EF855ED00DFDFD4 Archived : False PrivateKey : Version : 3 SerialNumber : 3DFFAF914D613282427BD591C1FB586D SubjectName : CN=sysadmins-LV-CA, DC=sysadmins, DC=lv IssuerName : CN=sysadmins-LV-CA, DC=sysadmins, DC=lv ValidFromDate : 2009.08.06. 10:21:01 ValidToDate : 2014.08.06. 10:30:54 Thumbprint : E82ACC45841280DDEAB9F7847418FA26354457A7 Archived : False PrivateKey : PS G:\> $chain.ApplicationPolicies() Name FriendlyName Value ---- ------------ ----- 123 Smart Card Logon 1.3.6.1.4.1.311.20.2.2 101 Client Authentication 1.3.6.1.5.5.7.3.2 PS G:\>

Метод вернул False, т.е. наш сертификат не прошёл проверку. Свойство Certificates содержит все сертификаты в текущей цепочке. А метод ApplicationPolicies() показал Application Policies (в прошлом EKU). Ну и метод Status() вернул код ошибки. Все коды ошибок расписаны в описании самого метода. Т.е. ошибка 32 по табличке означает:

CAPICOM_TRUST_IS_UNTRUSTED_ROOT (&H00000020) — The certificate or certificate chain is based on an untrusted root.

Особо углубляться дальше я смысла не вижу, поскольку используя эти примеры можно поупражняться в других задачах с использованием CAPICOM. А вместо этого, мы разберём кое-какие неочевидные, но очень важные вещи.

Когда вышел Remote Desktop Connection 7.0 (который поставляется вместе с Windows 7), Александр Станкевич (MVP: Enterprise Security) заметил очень плохую особенность с ним, а именно — рандомно пропал доступ к терминальным серверам, которые используют SSL для терминальных подключений. В ходе множества проверок было констатировано:

  • цепочка завершалась на доверенном корневом сертификате (через просмотр сертификата);
  • CDP/AIA пути были валидные и CRL не были просрочены;
  • остальные параметры были сконфигурированы верно.

RDP клиент мотивировал отказ в подключении следюущим: «RDP клиент не смог проверить сертификат на отзыв». Причём подключение с предыдущих версий RDC, а так же подключение к TS Web Access через IE было успешным. Как выяснилось на днях, причина была в том, что в новом RDC был изменён алгоритм certificate chaining engine и новая версия RDC больше не доверяла корневым сертификатам в пользовательском хранилище. Переустановка корневого сертификата в компьютерное хранилище решала проблему RDC 7. Но, как я говорил, в Windows 7/2008 R2 было изменено поведение certificate chaining engine в реализации X509Chain. Продемонстрирую небольшую табличку, которая покажет доверие пользовательским корневым сертификатам в CAPICOM и .NET реализациях certificate chaining engine для различных операционных систем:

  X509Chain (.NET) CAPICOM.Chain (CryptoAPI)
Windows XP Yes, of course! No!
Windows Server 2003 Yes, of course! No!
Windows Vista Yes, of course! No!
Windows Server 2008 Yes, of course! No!
Windows 7 No! No!
Windows Server 2008 R2 No! No!

Таблица доверия пользовательскким корневым сертификатам различными движками построения цепочки сертификатов

Следует учитывать, что различные приложения могут использовать свой движок для построения цепочек сертификатов, как RDC 7.0 и PKIView.msc использует CAPICOM, а PowerShellX509Chain. Однако, у меня есть уверенность, что существует ещё одна реализация этого движка, которую использует Internet Explorer. Это только мои догадки, но они основаны на том, что Internet Explorer (кроме Windows 7 и выше) использует пользовательский контейнер для проверки SSL веб-сайтов, т.е. по симптомам похоже на X509Chain. Однако, этот класс появился только в .Net Framework 2.0, которого, к примеру в оригинальной инсталляции Windows XP/2003 не было. Либо, как вариант, есть возможность настраивать этот момент в каждой реализации движка построения цепочек, но я пока не нашёл как. Это пока всё, что у меня есть по этому поводу.

Monday, October 19, 2009 9:52:44 PM (FLE Daylight Time, UTC+03:00)   Comments [5]    

 

Я предлагаю снова поговорить о certificate chaining engine, о котором мы уже говорили в посте Certificate Chaining Engine — как это работает но в рамках его реализации в .NET и PowerShell. В Windows есть несколько реализаций этого chaining engine. Самые популярные:

CAPICOM — вещь несколько стрёмная и её лучше избегать. Тем более мне не удалось завести его в Vista/Windows 7. Реализация в .NET в виде X509Chain ничуть не хуже, но, в то же время, проще. Итак, предлагаю начать с создания этого объекта с помощью New-Object:

[↓] [vPodans] $chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain [↓] [vPodans] $chain ChainContext ChainPolicy ChainStatus ChainElements ------------ ----------- ----------- ------------- 0 System.Security.Cryptograp... {} {} [↓] [vPodans] $chain | gm -membertype methods TypeName: System.Security.Cryptography.X509Certificates.X509Chain Name MemberType Definition ---- ---------- ---------- Build Method bool Build(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() Reset Method System.Void Reset() ToString Method string ToString() [↓] [vPodans]

Здесь видно, что метод, который будет строить и проверять цепочку будет называться Build() и в качестве аргумента этот метод принимает только объекты x509Certificate2, а возвращать объект типа Boolean — True/False. Объекты такого типа можно найти в провайдере Certififcates (просто выполнить dir cert:\store\container) или импортировать из файла — Импорт сертификатов в PowerShell. Но сначала мы посмотрим, что здесь можнол настроить. А настроить можно ChainPolicy, представляющий собой объект X509ChainPolicy. Эта политика позволяет задавать порядок проверки отзыва сертификатов, соответствие сертификата каким-то certificate policy, наличие определённых application policy (в прошлом известен нам как EKUExtended Key Usage) и некритичность каких-то ошибок. Мы всё разбирать не будем, а только основное:

[↓] [vPodans] $chain.ChainPolicy ApplicationPolicy : {} CertificatePolicy : {} RevocationMode : Online RevocationFlag : ExcludeRoot VerificationFlags : NoFlag VerificationTime : 17.10.2009 20:35:17 UrlRetrievalTimeout : 00:00:00 ExtraStore : {} [↓] [vPodans]

Первое, что может быть интересным — Revocation Mode, который задаёт режим проверки отзыва и может иметь 3 вполне понятных значения, которые перечислены в X509RevocationMode:

[↓] [vPodans] [System.Enum]::GetNames([system.security.cryptography.x509certificates.x509revocationmode]) NoCheck Online Offline

Тут очень просто:

  • NoCheck — проверяет цепочку сертификатов без проверки на отзыв любых сертификатов в цепочке;
  • Online — проверяет сертификаты в цепочке скачивая новые списки CRL из свойства CDP сертификата, игнорируя локальный кеш CRL (используется по умолчанию, как это видно в просмотре свойств ChainPolicy)
  • Offline — проверяет сертификаты на отзыв с использованием кешированного CRL (если есть).

Если проверяем сертификаты на отзыв, то мы можем выбрать что именно будем проверять и этот выбор перечислен в X509RevocationFlag:

[↓] [vPodans] [System.Enum]::GetNames([system.security.cryptography.x509certificates.x509revocationflag]) EndCertificateOnly EntireChain ExcludeRoot

и здесь тоже всё достаточно понятно:

  • EndCertificateOnly — проверяет на отзыв только сам проверяемый сертификат. Остальные сертификаты в цепочке не проверяются;
  • EntireChain — проверяет на отзыв абсолютно все сертификаты в цепочке включая корневой сертификат. Правила хорошего тона диктуют нам не включать в корневой сертификат CA поля CDP и AIA, поскольку это лишено смысла и в большинстве случаев с этим флагом вы будете получать невалидную цепочку;
  • ExcludeRoot — проверяет на отзыв все сертификаты в цепочке, кроме корневого сертификата, что есть религионзно правильно и этот флаг установлен по умолчанию.

Следующий момент определяет флаги проверки, т.е. по каким именно критериям будет проверяться цепочка сертификатов:

[↓] [vPodans] [System.Enum]::GetNames([system.security.cryptography.x509certificates.x509verificationflags]) NoFlag IgnoreNotTimeValid IgnoreCtlNotTimeValid IgnoreNotTimeNested IgnoreInvalidBasicConstraints AllowUnknownCertificateAuthority IgnoreWrongUsage IgnoreInvalidName IgnoreInvalidPolicy IgnoreEndRevocationUnknown IgnoreCtlSignerRevocationUnknown IgnoreCertificateAuthorityRevocationUnknown IgnoreRootRevocationUnknown AllFlags

Эти флаги так же достаточно понятны и подробно разжёваны в перечислении X509VerificationFlags.

Примечание: эти флаги показывают что будет исключено из проверки, а не наоборот. По умолчанию проверяется всё (установлен NoFlag).

Как можно задавать эти флаги и режимы, если есть такая потребность? Можно пойти двумя способами — правильным и неправильным, хотя оба рабочие:

[↓] [vPodans] # правильный метод: [↓] [vPodans] $revflag = [System.Security.Cryptography.X509Certificates.X509RevocationFlag]::EndCertificateOnly [↓] [vPodans] $chain.ChainPolicy.RevocationFlag = $revflag [↓] [vPodans] $chain.ChainPolicy ApplicationPolicy : {} CertificatePolicy : {} RevocationMode : Online RevocationFlag : EndCertificateOnly VerificationFlags : NoFlag VerificationTime : 17.10.2009 20:35:17 UrlRetrievalTimeout : 00:00:00 ExtraStore : {} [↓] [vPodans] # неправильный метод, но мне он нравится [↓] [vPodans] $chain.ChainPolicy.RevocationFlag = "excluderoot" [↓] [vPodans] $chain.ChainPolicy ApplicationPolicy : {} CertificatePolicy : {} RevocationMode : Online RevocationFlag : ExcludeRoot VerificationFlags : NoFlag VerificationTime : 17.10.2009 20:35:17 UrlRetrievalTimeout : 00:00:00 ExtraStore : {} [↓] [vPodans]

В принципе, все эти enumeration можно просто указывать в виде строки, а PowerShell уже сам подобъёт его под нужный тип.

Примечание: флаги проверки на отзыв можно указывать только по одному, а флаги исключений можно перечислять через запятую. Это видно по названию класса: если класс указан в единственном числе (X509RevocationFlag), то и значение можно указать только одно, а если во множественном числе (X509VerificationFlags), то их можно указать несколько через запятую.

В принципе, можно делать проверку:

[↓] [vPodans] $valid = (dir cert:\currentuser\my)[1] [↓] [vPodans] $invalid = (dir cert:\currentuser\my)[0] [↓] [vPodans] $chain.Build($valid) True [↓] [vPodans] $chain.ChainElements | select -expand certificate Thumbprint Subject ---------- ------- 986D375362652FE9E39BA4D042A6B8BA75745998 CN=Administrator, CN=Users, DC=sysadmins, DC=lv E82ACC45841280DDEAB9F7847418FA26354457A7 CN=sysadmins-LV-CA, DC=sysadmins, DC=lv [↓] [vPodans]

Я из локального хранилища взял заведомо хороший сертификат и плохой (разумеется, это сертификат с сайта БиЛайн :)). Метод Build() вернул True, что означает успешную проверку всей цепочки моего сертификата до доверенного корня. А вот что случилось с сертификатом билайна:

[↓] [vPodans] $chain.reset() [↓] [vPodans] $chain.Build($invalid) False [↓] [vPodans] $chain.ChainElements | select -expand certificate Thumbprint Subject ---------- ------- EB74DA32E865C78FCB853DDA5FE45962098E1B3B CN=trust.beeline.ru, OU=DIT, O=Vimpelcom, L=Moscow, S=Moscow, C=RU [↓] [vPodans] $chain.ChainStatus Status StatusInformation ------ ----------------- PartialChain Sertificesanas kedi nevareja veidot pie uzticamas saknes... RevocationStatusUnknown Atsauksanas funkcija nevareja parbaudit sertifikata atsa... OfflineRevocation Atsauksanas funkcija nevareja parbaudit atsauksanu, jo a... [↓] [vPodans] $chain.ChainStatus | %{$_.statusinformation.trim()} Sertificesanas kedi nevareja veidot pie uzticamas saknes iestades. Atsauksanas funkcija nevareja parbaudit sertifikata atsauksanu. Atsauksanas funkcija nevareja parbaudit atsauksanu, jo atsauksanas serveris bija bezsaiste. [↓] [vPodans]

Поскольку при каждой проверке сертификаты в ChainElements накапливаются, то после каждой проверки следует очищать объект методом Reset(). Как и следовало ожидать, сертификат билайна вернул False, показывая, что цепочка у этого сертификата имеет проблемы. В случае неуспешной проверки, будет заполняться свойство ChainStatus. Данное свойство содержит все ошибки, которые были костатированы при проверке цепочки. Для меня пока непонятным стал факт, что ошибки он написал на латышском языке, хотя я его об этом не просил. Откуда он это взял — непонятно, но он сказал, что не смог построить цепочку до доверенного корня и функция проверки отзыва провалилась по всем статьям, поскольку пути в CDP сертификата нерабочие.

Примечание: обязательно следует учитывать тот факт, что при построении цепочки сертификатов и проверке доверия класс X509Chain в Windows 7 и Windows Server 2008 R2 работает в контексте LocalSystem и всегда игнорирует пользовательские контейнеры Trusted Root Certification Authorities. Поэтому если корень цепочки не заканчивается на одном из сертификатов Trusted Root CAs хранилища LocalMachine, то цепочка будет считаться недоверенной. Для предыдущих ОС цепочка может заканчиваться на пользовательском хранилище Current User.

В принципе это всё, что вам следует знать про реализацию certificate chaining engine в .NET и его использование в PowerShell. В качестве бонуса прилагаю скрипт для проверки цепочки сертификатов:

#####################################################################
# Test-Certificate.ps1
# Version 1.0
#
# Passes certificate through certificate chaining engine
#
# Vadims Podans (c) 2009
# http://www.sysadmins.lv/
#####################################################################
#requires –Version 2.0

function Test-Certificate {
[CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        $Certificate,
        [System.Security.SecureString]$Password,
        [ValidateSet("NoCheck", "Online", "Offline")]
        [string]$CRLMode = "Online",
        [ValidateSet("EndCertificateOnly", "EntireChain", "ExcludeRoot")]
        [string]$CRLFlag = "ExcludeRoot",
        [ValidateSet("AllFlags", "AllowUnknownCertificateAuthority", "NoFlag", "IgnoreNotTimeValid",
            "IgnoreCtlNotTimeValid", "IgnoreNotTimeNested", "IgnoreInvalidBasicConstraints",
            "IgnoreWrongUsage", "IgnoreInvalidName", "IgnoreInvalidPolicy", "IgnoreEndRevocationUnknown",
            "IgnoreCtlSignerRevocationUnknown", "IgnoreCertificateAuthorityRevocationUnknown",
            "IgnoreRootRevocationUnknown")]
        [string[]]$VerificationFlags = "NoFlag"
    )
    
    begin {
        # создаём объекты X509Certificate2 и X509chain ещё до поступлениея первого объекта сертификата
        $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
        $chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
        # в X509Chain записываем параметры проверки сертификатов. Как критерии
        # отзыва, так и исключения из проверки
        $chain.ChainPolicy.RevocationFlag = $CRLFlag
        $chain.ChainPolicy.RevocationMode = $CRLMode
        $chain.ChainPolicy.VerificationFlags = $VerificationFlags
        # мини-функция для предоставления понятного вывода на экран о статусе проверки
        # и ошибках в цепочке, если они есть.
        function _getstatus_ ($status, $chain, $cert) {
            if ($status) {
                Write-Host Current certificate $cert.SerialNumber chain and revocation status is valid -ForegroundColor Green
            } else {
                Write-Warning "Current certificate $($cert.SerialNumber) chain is invalid due of the following errors:"
                $chain.ChainStatus | %{Write-Host $_.StatusInformation.trim() -ForegroundColor Red}
            }
        }
    }
    
    process {
        # если аргумент сертификата уже является объектом X509Certificate2, то
        # сразу строим цепочку и получаем статус цепочки вызывая мини-функцию
        if ($_ -is [System.Security.Cryptography.X509Certificates.X509Certificate2]) {
            $status = $chain.Build($_)
            _getstatus_ $status $chain $_
        } else {
        # если аргумент не является объектом X509Certificate2, то это будет путь к файлу
        # если указанный путь не существует, то ругаемся и выходим
            if (!(Test-Path $Certificate)) {Write-Warning "Specified path is invalid"; return}
            else {
            # если путь существует, то проверяем, что это путь файловой системы.
            # если это не так, то ругаемся и выходим
                if ((Resolve-Path $Certificate).Provider.Name -ne "FileSystem") {
                    Write-Warning "Spicifed path is not recognized as filesystem path. Try again"; return
                } else {
                # если предварительные проверки прошли успешно, то выбираем объект файла
                    $Certificate = gi $(Resolve-Path $Certificate)
                # и на основании расширения файла через Switch преобразуем файл в
                # X509Certificate2 объект или объекты
                    switch -regex ($Certificate.Extension) {
                    "\.CER|\.DER|\.CRT" {$cert.Import($Certificate.FullName)}
                    "\.PFX" {
                        if (!$Password) {$Password = Read-Host "Enter password for PFX file $certificate" -AsSecureString}
                        # тут пришлось указать контекст хранилища, куда предполагается сохранить сертификат.
                        # на самом деле мы никуда не будем сохранять объект сертификата, это было сделано
                        # по причине отсутствия подходящего конструктора метода Import(), чтобы
                        # можно было указать только путь к файлу и пароль от PFX
                            $cert.Import($Certificate.FullName, $password, "UserKeySet")
                        }
                    "\.P7B|\.SST" {
                        $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
                        $cert.Import([system.IO.File]::ReadAllBytes($file.FullName))
                        }
                    default {Write-Warning "Looks like your specified file is not a certificate file"; return}
                    }
                    # полученный объект или объекты сертификатов пропускаем через метод Build() и вызываем
                    # мини-функцию расшифровки статуса проверки
                    $cert | %{
                        $status = $chain.Build($_)
                        _getstatus_ $status $chain $_
                    }
                    # после каждой итерации очищаем объекты сертификата и цепочки
                    $cert.Reset()
                    $chain.Reset()
                }
            }
        }
    }
}

Данный скрипт может принимать аргументы в виде уже готовых объектов X509Certificate2 или с указанием пути к файлу сертификата, например:

dir cert:\currentuser\my | Test-Certificate
Test-Certificate .\mycert.cer

Ну и при желании можно поуказывать там разные параметры и флаги проверки.

Saturday, October 17, 2009 11:51:59 PM (FLE Daylight Time, UTC+03:00)   Comments [18]    

 

Знаете ли вы как подключать IntelliSense к новым командлетам, которые появляются после подключения оснастки? Например, в консоли PowerShell вы подключаете оснастку Windows.ServerBackup и сразу получаете доступ к новым командлетам. В PowerGUI, к сожалению, после подключения оснастки через Add-PSSnapin или Import-Module новые командлеты начинают работать при отладке, но без подсветки и прочих красивостей блондинка-стайл. Мне потребовалось несколько часов, чтобы узнать, в чём тут дело. Из них минут 10 заняла переписка с командой разработчиков PowerGUI (угу, я иногда злоупотребляю своим положением).

Итак, если вы хотите получать IntelliSense для новых командлетов, которые появляются после подключения оснастки или импорта модуля (Import-Module), то нужно сделать: File –> PowerShell Libraries:

Powershell Snapins and Modules in PowerGUI

В окне натыкать галочек и/или кнопкой Add Module добавить модуль/модули и тогда появится IntelliSense и прочие побочные принадлежности в самом редакторе.

Have fun with a script writing with PowerGUI!

Friday, October 16, 2009 8:18:19 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Ещё небольшое дополнение к Certificate Management Pack, который я сейчас пишу. Данный код позволяет запускать и останавливать службу Certification Authority на сервере. Поскольку я для решения этой задачи использую WMI, то ремотинг будет обеспечен. Следует учесть, что все команды по управлению службой CA будут расположены за конвейером после команды Get-CertificationAuthority. Это было сделано в рамках удобства и стандартизации. Когда мы хотим что-то настроить в CA, то сначала должны указать его имя/объект. Поскольку у нас уже есть готовое решение для этого, то почему бы его и не использовать? Код на самом деле очень простой и в нём разберётся даже новичок:

#####################################################################
# Start-Stop CA.ps1
# Version 1.0
#
# Starts and stops Certification Authority service on specified CA
# Requires Get-CertificationAuthority cmdlet:
# http://www.sysadmins.lv/PermaLink,guid,947401b2-312a-4129-860b-4296dfe46cb2.aspx
#
# Usage:
# Get-CertificationAuthority "CAName" | Stop-CertificationAuthority
# stops CA service on CA named CAName
# Get-CertificationAuthority | Start-CertificationAuthority
# starts all Enterprise CAs in current forest
#
# Vadims Podans (c) 2009
# http://www.sysadmins.lv/
#####################################################################

function Start-CertificationAuthority ($CA) {
    process {
        $status = (gwmi Win32_Service -ComputerName $CA.Computer -Filter "Name = 'CertSvc'").StartService()
        if ($status.ReturnValue -eq 0) {
            Write-Host $CA.CAName Certificate Services successfully started on $CA.Computer -ForegroundColor Green
        } else {
            Write-Warning "Unable to start $($CA.CAName) certificate services"
        }
    }
}

function Stop-CertificationAuthority ($CA) {
    process {
        $status = (gwmi Win32_Service -ComputerName $CA.Computer -Filter "Name = 'CertSvc'").StopService()
        if ($status.ReturnValue -eq 0) {
            Write-Host $CA.CAName Certificate Services successfully stopped on $CA.Computer -ForegroundColor Green
        } else {
            Write-Warning "Unable to stop $($CA.CAName) certificate services"
        }
    }
}

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

Update: хочу ещё раз отметить на одну из частых ошибок, которые допускают при написании скриптов в PowerShell. Вы можете просто использовать переменные в двойных кавычках и при использовании этой строки в неё будет подставляться содержимое переменной. Но этого не будет, если вы используете свойство объекта, который хранится в этой переменной, поскольку переменные экспандятся только 1 раз. Этого достаточно для простой переменной, но недостаточно для извлечения содержимого свойста объекта в этой переменной. Ведь сначала нужно извлечь содержимое переменной, а потом прочитать содержимое указанного свойства. Чтобы экспандить такие вещи, переменные в двойных кавычках следует заключать в подвыражение $().

Thursday, October 15, 2009 7:55:57 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

В предыдущей части бэкапа в Windows Server 2008 R2 (Windows Server 2008 R2 и Windows Backup (часть 1)) мы рассмотрели основные моменты создания бэкапа средствами PowerShell. Сегодня мы рассмотрим вопросы самого процесса бэкапа и о его хранении.

Проверка успешности бэкапа

Когда процесс бэкапа закончится, можно посмотреть его статус:

[↑] [Administrator] Get-WBSummary NextBackupTime : 0001.01.01. 0:00:00 NumberOfVersions : 5 LastSuccessfulBackupTime : 2009.10.07. 14:39:42 LastSuccessfulBackupTargetPath : \\?\Volume{ca6dbf07-14ad-11de-937f-806e6f6e6963} LastSuccessfulBackupTargetLabel : Camelot Share-1 LastBackupTime : 2009.10.07. 14:39:42 LastBackupTarget : E: DetailedMessage : LastBackupResultHR : 0 LastBackupResultDetailedHR : 0 CurrentOperationStatus : NoOperationInProgress

Здесь вы увидите основные сведения о результатах бэкапа. Свойство LastBackupResultHR содержит код возврата. Если это 0, то всё хорошо. Если это не 0, то бэкап не был выполнен удачно. А вот свойство NumberOfVersions показывает сколько уже копий бэкапа содержится в текущем архиве. Более подробно этот момент будет рассмотрен ниже.

Процесс создания и хранения бэкапа

При выполнении бэкапа происходит несколько вещей:

  • В корне диска назначения бэкапа создаётся структура папок: <путь до диска или сетевой папки>\WindowsImageBackup\<имя компьютера>
  • После окончания бэкапа система делает теневую копию этого тома (кроме сетевых папок) тома, на котором хранится бэкап.
  • Обновляется каталог бэкапов.

Если это сетевая папка, то в пути \\Server\BackupShare\WindowsImageBackup создаст папку для каждого компьютера и в ней будет хранить бэкап соответствующего компьютера. При этом последующие операции бэкапа будут копировать архив в эту же папку.  Во времена ntbackup.exe мы могли выбирать метод выполнения бэкапа — с использованием VSS или без (это не относилось к SystemState бэкапам), а теперь этот вопрос решён однозначно — VSS используется всегда. Это обусловлено ещё тем, что Server Backup использует VSS для ведения истории бэкапов, что исключает путаницу в архивных копиях. Внимательные читатели могут заметить, что внутри папки бэкапа есть VHD файл (по одному VHD на каждый архивируемый том), который содержит актуальное состояние бэкапа. И тут появляется интересная вещь: каждый новый бэкап копируется в один VHD файл — а куда же деваются предыдущие копии? На самом деле все они хранятся в этом VHD файле, но скрыты за теневыми копиями, которые создаются при каждой операции бэкапа и закрепляются за архивом:

[↑] [Administrator] Get-WBBackupSet VersionId : 10/07/2009-10:49 BackupTime : 2009.10.07. 13:49:41 BackupTarget : E: RecoverableItems : Volumes, Files Volume : {System (C:)} Application : {} VssBackupOption : VssCopyBackup SnapshotId : 8d6aa8ef-bb24-4ffc-93da-08831bc4ae88 VersionId : 10/07/2009-11:15 BackupTime : 2009.10.07. 14:15:36 BackupTarget : E: RecoverableItems : Volumes, Files Volume : {System (C:)} Application : {} VssBackupOption : VssCopyBackup SnapshotId : ab2c6d39-3447-4c9b-b072-f03d746045c4 <...>

Команда Get-WBBackupSet показывает историю бэкапов системы и ID номер теневой копии, которая содержит файлы архива на момет выполнения конкретного задания бэкапа. При восстановлении из бэкапа консоль MMC считывает эти копии и позволяет восстановить файлы на любой момент времени выполнения бэкапа. Чтобы дать более понятное представление об этом, покажу простой пример:

  1. Выполняется первый бэкап №1.
  2. В BackupTarget создаётся папка с архивом и в VHD файл записываются данные, которые мы архивируем;
  3. В BackupTarget создаётся теневая копия, которая так же содержит эти файлы и закрепляется за этим бэкапом;
  4. Время бэкапа и ID теневой копии записывается в каталог бэкапа;
  5. Выполняется следующий бэкап этого же задания под № 2.
  6. В BackupTarget уже ничего не создаётся, а новые данные добавляются в VHD файл, скрывая данные из бэкапа №1;
  7. В BackupTarget создаётся теневая копия, которая так же содержит новые данные и закрепляется за этим бэкапом (№2);
  8. Время бэкапа и ID теневой копии записывается в каталог бэкапа.
  9. повторяются пункты 5-8.

Несмотря на то, что при непосредственном просмотре VHD файла мы видим только данные сохранённые после последней операции бэкапа, в нём по прежнему хранятся и все предыдущие копии, которые система различает по теневым копиям, которые закреплены за каждым бэкапом. Именно здесь теневые копии играют огромную роль в хранении истории бэкапа. И пока эти теневые копии живы, мы имеем доступ к предыдущим версиям файлов внутри VHD архива. Это даёт следующие преимущества:

  • больше нет кучи файлов архивов, которые нужно самому собирать в какое-то хранилище и как-то идентифицировать;
  • для восстановления файлов и томов не обязательно наличие самого бэкапа. Ведь теневая копия, которая закрепляется за этим бэкапом тоже может использоваться для восстановления.

В большинстве случаев это решение будет являться достаточным для любых операций восстановления. Единственное критичное место здесь будет наличие этих теневых копий. Это может вызвать трудности только при повреждении теневых копий на архивном томе. Но обычно это уже будет означать потерю всех бэкапов. Такие дела.

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

Хранятся они там сколь угодно долго, пока есть свободное место. Когда свободное место заканчивается, то Server Backup автоматически пытается отыскать себе место. Если у нас выполняются только полные бэкапы, то наиболее старые версии архивов просто удаляются. Если у нас комбинируются полные бэкапы с инкрементальными/дифференциальными, то берётся наиболее старый архив и в него вписываются инкрементальные/дифференциальные архивы, которые были выполнены в промежутках между полными бэкапами до тех пор, пока не освободится достаточно для нового бэкапа места. Таким образом обеспечивается сохранность наиболее новых архивов с удалением более старых. Такая схема автоматической ротации так же будет востребована в большинстве случаев. Для экономии места Server Backup для запланированного задания автоматически делает комбинирование полных и инкрементальных бэкапов. Каждые 2 недели выполняется полный бэкап и ежедневно в промежутках между полными будет выполняться только инкрементальное архивирование.

Такая автонастройка режимов для запланированных бэкапов и авторотация будет достаточно эффективна и проста в сегменте SOHO/SMB, не отвлекая на себя слишком много внимания администратора. От администратора потребуется только создание задания и организация отказоустойчивости тома с архивами.

Совмещение нескольких заданий и индивидуальная ротация архивов

Разработчики Server Backup сделали всё, чтобы упростить процесс выполнения бэкапа в стандартных случаях SOHO/SMB. Но когда появляются особые условия, то тут начинаются свои сложности, хотя это всё относительно преодолимо. Например, вы создали несколько заданий бэкапов, которые отдельно что-то архивируют в одну и ту же точку. Но к каждому заданию предъявляются свои требования по сроку хранения бэкапа.

Пример: это файл-сервер и вы архивируете папку с документами пользователей ежедневно и следует хранить только 7 последние копии. Другое задание архивирует инсталляционные файлы вашей сети раз в неделю и требуется наличие только 4 последних копий. Так же все копии должны копироваться в сетевую папку или на съёмный диск на случай катастрофы и/или ада и Израиля. В такой ситуации мы потеряем возможность использования авторотации архивов и прочих плюшек. Давайте посмотрим, как будет выглядеть примерный скрипт:

# подключаем оснастку Server Backup
Add-PSSnapin Windows.Serverbackup
# создаём задание бэкапа
$profiles = New-WBPolicy
# создаём и добавляем в задание бэкапа архивируемую папку
$source = New-WBFileSpec -FileSpec "D:\Users"
Add-WBFileSpec -Policy $profiles -FileSpec $source
# указываем локальный том, на который будет копироваться архив
$target = New-WBBackupTarget -VolumePath "E:"
Add-WBBackupTarget -Policy $profiles -Target $target
# выполняем бэкап
Start-WBBackup -Policy $profiles
# проверяем код возврата с результатом выполнения бэкапа
if ((Get-WBSummary).LastBackupResultHR -eq 0) {
    # переименовываем архив в более понятное имя
    $newname = "Profiles_$(Get-Date -f dd.MM.yyyy)"
    Ren E:\WindowsImageBackup -NewName $newname
    # копируем архив в сетевую папку
    copy e:\$newname \\server\backups\profiles
    # удаляем все архивы из сетевой папки, которые старше 7 дней
    dir \\server\backups\profiles | ?{$_.lastwritetime -lt (Get-Date).AddDays(-7)} | del -Force
} else {
    # ругаемся, что бэкап не был завершён успешно
}

И уже этот файл отдельно зашедулить в Task Scheduler. В такой ситуации дополнительных шагов не требуется, т.к. пока живы теневые копии, вы можете восстанавливать файлы из них (наличие самого архива не требуется). А если теневых копий уже не осталось (например, том с архивами был отформатирован), то для восстановления данных просто копируете папку с архивом в корень любого тома с именем WindowsImageBackup и тогда этот архив будет определён системой как пригодный для восстановления. Так вы можете делать несколько раздельных заданий с индивидуальным расписанием бэкапа и ротацией.

Если ротация архивов в сетевой папке достаточно проста и укладывается в одну строчку, то с локальными архивами придётся подключать утилиты CMD, а именно — diskshadow.exe! Вам нужно внутри diskshadow выполнить Delete Shadows ID {GUID}, где GUIDID теневой копии, которая закреплена за конкретным бэкапом и его можно получить из вывода Get-WBBackupSet (свойство SnapshotID)

[↑] [Administrator] diskshadow Microsoft DiskShadow version 1.0 Copyright (C) 2007 Microsoft Corporation On computer: CAMELOT, 2009.10.13. 22:18:05 DISKSHADOW> delete shadows ID {8d6aa8ef-bb24-4ffc-93da-08831bc4ae88} Deleting shadow copy {8d6aa8ef-bb24-4ffc-93da-08831bc4ae88}... 1 shadow copy deleted. DISKSHADOW>

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

Delete Shadows Oldest E:

где E: — путь к тому с архивами.

Сами данные из VHD файла будут удалены только при следующей операции бэкапа. Однако, это не относится к архивам, которые содержат SystemState. Для ротации архивов SystemState придётся воспользоваться уже другой утилитой — wbadmin.exe:

wbadmin delete systemstatebackup –version: datetime

где datetime — дата и время выполнения бэкапа. Эту дату можно получить так же из вывода командлета Get-WBBackupSet (свойство VersionID). Чтобы удалить все бэкапы SystemState, кроме текущего следует выполнить:

wbadmin delete systemstatebackup –backuptarget:E: –deleteoldest

и для удаления всех наиболее старых архивов SystemState с сохранением N копий выполнить:

wbadmin delete systemstatebackup –keepversions:N

где N — количество копий SystemState, которые должны быть сохранены.

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

# подключаем оснастку Server Backup
Add-PSSnapin Windows.Serverbackup
# создаём задание бэкапа
$profiles = New-WBPolicy
# создаём и добавляем в задание бэкапа архивируемую папку
$source = New-WBFileSpec -FileSpec "D:\Users"
Add-WBFileSpec -Policy $profiles -FileSpec $source
# указываем локальный том, на который будет копироваться архив
$target = New-WBBackupTarget -VolumePath "E:"
Add-WBBackupTarget -Policy $profiles -Target $target
# выполняем бэкап
Start-WBBackup -Policy $profiles
# проверяем код возврата с результатом выполнения бэкапа
if ((Get-WBSummary).LastBackupResultHR -eq 0) {
    # переименовываем архив в более понятное имя
    $newname = "Profiles_$(Get-Date -f dd.MM.yyyy)"
    Ren E:\WindowsImageBackup -NewName $newname
    # копируем архив в сетевую папку
    copy e:\$newname \\server\backups\profiles
    # удаляем все архивы из сетевой папки, которые старше 7 дней
    dir \\server\backups\profiles | ?{$_.lastwritetime -lt (Get-Date).AddDays(-7)} | del -Force
    # читаем наш собственный каталог бэкапов
    $csv = Import-Csv E:\ProfileBackup.csv
    # и считаем сколько там записей
    $count = $csv.count
    # если записей больше 7, то считаем сколько лишних архивов нужно удалить.
    # если меньше 7 записей, то ничего удалять не надо и просто добавляем новую запись
    if ($count -gt 7) {
        $old = $count - 7
        # генерируем случайное имя для скрипта, который будет использоваться в diskshadow
        $file = [System.IO.Path]::GetRandomFileName()
        # выбираем все лишние архивы и пропускаем их по конвейеру на удаление
        $csv | sort | select -First $old | %{
            # записываем команду во временный файл
            "delete shadows ID {$($_.SnapshotID)}" > $Env:TEMP\$file
            # и запускаем diskshadow в режиме скрипта
            diskshadow -s $Env:TEMP\$file
        }
        del $Env:TEMP\$file
    }
    # считываем данные о последнем бэкапе
    $current = Get-WBBackupSet | select -Last 1  | select VersionID, SnapshotId
    # и добавляем его в массив объектов действующих бэкапов
    $csv += $current
    # чтобы не было путаницы, снова сортируем объекты и пишем обратно в CSV файл
    $csv | sort | select -Last 7 | Export-Csv E:\ProfileBackup.csv -NoTypeInformation
} else {
    # ругаемся, что бэкап не был завершён успешно
}

В принципе, это только один вариант реализации подобной задачи и не обвешена никакими проверками. Однако, учитывая, что данный код публикуется на правах ТЗ (ТЗТайное Знание), поэтому может использоваться как шаблон алгоритма такой кастомной ротации. Данный скрипт только демонстрирует логику, которой вы можете воспользоваться и подпилить под свои условия самостоятельно.

Вот и всё, наверное, что я хотел рассказать про бэкап в Windows Server 2008 R2. В Windows 7 нет командлетов для бэкапа,  поэтому свои хотелки придётся реализовывать только средствами CMD (wbadmin, vssadmin, diskshadow). И это будет значительно сложнее, чем вариант с командлетами повершела.

Wednesday, October 14, 2009 11:29:30 PM (FLE Daylight Time, UTC+03:00)   Comments [6]    

 

2 недели назад я выступал на рижском IT Pro с темой бэкапа в Windows Server 2008 R2 с помощью PowerShell. Доклад получится немного скомканный и на ряд вопров из зала я не смог ответить, т.к. эти вопросы требуют достаточно много времени на формулировку ответа. Поэтому я здесь подниму этот вопрос снова и постараюсь ответить на неотвеченные вопросы.

Я думаю, что уже все знают про отсутствие ntbackup.exe в системах начиная с Windows Server 2008. Его теперь заменяет отдельный системный компонент Windows Backup (или Server Backup) и, который, устанавливается как компонент сервера в оснастке Server Manager. Первая версия Server Backup была достаточно грубой и примитивной. Она позволяла бэкапить только тома целиком блочным методом без возможности выбора отдельных файлов и папок. Блочный метод работает на уровень ниже, чем файловая система, поэтому никакой речи о файлах быть не могло. Так же ему требовался выделенный том под хранение бэкапа и при инициализации тома под бэкапы, он форматировался. Бэкап SystemState производился отдельно и его нельзя было включить в состав бэкапа отдельного тома. Иными словами, первая версия была непотребна чуть более чем полностью, поэтому в отношении него говорить просто не о чем.

С выходом Windows Server 2008 R2 ситуация немного улучшилась. После тысяч жалоб от покупателей и срачей на форумах в новой системе добавился бэкап на уровне файловой системы (как и ntbackup), сохранив и улучшив первоначальную версию Server Backup. Новая версия бэкапа отличается следующими характеристиками по сравнению с первой версией:

  • Добавлена возможность бэкапа файлов и папок
  • Добавлена возможность комбинирования объектов бэкапа. Например, к бэкапу отдельных папок и файлов можно добавлять бэкап SystemState
  • Теперь можно делать инкрементальный бэкап для SystemState (включено по умолчанию)
  • Улучшенная поддержка PowerShell

Как и раньше, командлеты для Server Backup поставляются в виде отдельной оснастки, которая подключается командой:

Add-PSSnapin Windows.ServerBackup

И все командлеты для бэкапа будут иметь префикс WB (от Windows Backup):

[↑] [Administrator] Add-PSSnapin windows.serverbackup [↑] [Administrator] gcm *-wb* CommandType Name Definition ----------- ---- ---------- Cmdlet Add-WBBackupTarget Add-WBBackupTarget [-Policy] [-Target... Cmdlet Add-WBBareMetalRecovery Add-WBBareMetalRecovery [-Policy] [-V... Cmdlet Add-WBFileSpec Add-WBFileSpec [-Policy] [-FileSpec] ... Cmdlet Add-WBSystemState Add-WBSystemState [-Policy] [-Verbose... Cmdlet Add-WBVolume Add-WBVolume [-Policy] [-Volume] [-Verbos... Cmdlet Get-WBBareMetalRecovery Get-WBBareMetalRecovery [-Policy] [-V... Cmdlet Get-WBDisk Get-WBDisk [-Verbose] [-Debug] [-ErrorAction [-Ve... Cmdlet Get-WBJob Get-WBJob [[-Previous] ] [-Verbose] [-De... Cmdlet Get-WBPolicy Get-WBPolicy [-Editable] [-Verbose] [-Debug] [-E... Cmdlet Get-WBSchedule Get-WBSchedule [-Policy] [-Verbose] [... Cmdlet Get-WBSummary Get-WBSummary [-Verbose] [-Debug] [-ErrorAction ... Cmdlet Get-WBSystemState Get-WBSystemState [-Policy] [-Verbose... Cmdlet Get-WBVolume Get-WBVolume [-Disk] [-Verbose] [-Debug... Cmdlet Get-WBVssBackupOptions Get-WBVssBackupOptions [-Policy] [-Ve... Cmdlet New-WBBackupTarget New-WBBackupTarget [-Disk] [[-Label] [-NonRecur... Cmdlet New-WBPolicy New-WBPolicy [-Verbose] [-Debug] [-ErrorAction <... Cmdlet Remove-WBBackupTarget Remove-WBBackupTarget [-Policy] [-Tar... Cmdlet Remove-WBBareMetalRecovery Remove-WBBareMetalRecovery [-Policy] ... Cmdlet Remove-WBFileSpec Remove-WBFileSpec [-Policy] [-FileSpe... Cmdlet Remove-WBPolicy Remove-WBPolicy [[-Policy] ] [-All] [-... Cmdlet Remove-WBSystemState Remove-WBSystemState [-Policy] [-Verb... Cmdlet Remove-WBVolume Remove-WBVolume [-Policy] [-Volume] <... Cmdlet Set-WBPolicy Set-WBPolicy [-Policy] [-Force] [-Ver... Cmdlet Set-WBSchedule Set-WBSchedule [-Policy] [-Schedule] ... Cmdlet Set-WBVssBackupOptions Set-WBVssBackupOptions [-Policy] [-Vs... Cmdlet Start-WBBackup Start-WBBackup [-Policy] [-Async] [-F... [↑] [Administrator]

Мы видим достаточно приличное количество командлетов и часть из них мы используем для работы. Все задания бэкапов в ServerBackup являются политиками бэкапа. Следовательно, командлетом New-WBPolicy мы будем создавать каждое задание бэкапа:

[↑] [Administrator] $pol = New-WBPolicy [↑] [Administrator] $pol Schedule : BackupTargets : VolumesToBackup : FilesSpecsToBackup : FilesSpecsToExclude : BMR : False SystemState : False VssBackupOptions : VssCopyBackup [↑] [Administrator]

Мы создали объект новой политики бэкапа, который имеет ряд свойств. Эти свойства по названиям уже отражают своё назначение. Давайте сначала выберем объекты бэкапа. Например, добавим какую-нибудь папку, которую мы захотим бэкапить. Новые объекты бэкапа создаются в 2 этапа:

  1. Создание самого объекта командой New-WBFileSpec
  2. Добавление этого объекта в политику бэкапа командой Add-WBFileSpec

Будь то отдельная папка или отдельный том. Это не относится только к Bare Metal Recovery (полный бэкап системного тома, SystemState и всех системных файлов) и к самому SystemState. Они могут сразу добавляться в политику.

[↑] [Administrator] $source1 = New-WBFileSpec -FileSpec "C:\Users" [↑] [Administrator] $source1 | ft -a FilePath FileName IsRecursive IsIncludeSpec -------- -------- ----------- ------------- C:\Users\ * True True [↑] [Administrator] $exclusion = New-WBFileSpec -FileSpec "C:\Users\vpodans" -Exclude [↑] [Administrator] $exclusion | ft -a FilePath FileName IsRecursive IsIncludeSpec -------- -------- ----------- ------------- C:\Users\vpodans\ * True False [↑] [Administrator] $source2 = New-WBFileSpec -FileSpec "D:\Users" -NonRecursive [↑] [Administrator] $source2 | ft -a FilePath FileName IsRecursive IsIncludeSpec -------- -------- ----------- ------------- D:\Users\ * False True [↑] [Administrator] $source1, $source2, $exclusion | Add-WBFileSpec -Policy $pol [↑] [Administrator] $pol Schedule : BackupTargets : VolumesToBackup : FilesSpecsToBackup : {C:\Users\*, D:\Users\*} FilesSpecsToExclude : {C:\Users\vpodans\*} BMR : False SystemState : False VssBackupOptions : VssCopyBackup [↑] [Administrator]

Первой командой мы задали бэкап всей папки C:\Users. Второй командой мы посмотрели объект этой точки. Как видно из таблички, эта папка будет бэкапить полностью включая все подпапки. Но я не хочу бэкапить папку профиля одного из пользователей. Для этого в командлете New-WBFileSpec есть ключ –Exclude, который исключит эту папку из бэкапа. В следующей строке это видно по состоянию свойства IsIncludeSpec = $false. И чтобы исключить рекурсивную обработку папки (т.е. нам нужно бэкапить содержимое только текущей папки не трогая подпапки совсем) для неё достаточно указать ключ –NonRecursive. под каждую категорию вы можете добавлять пути для бэкапа через запятую в одной команде. Но для каждой категории надо писать новую команду. В конце мы видим, что наша политика уже обросла какими-то данными. Если мы захотим сюда добавить ещё бэкап целого тома или физического диска, то нам уже придётся работать с командлетами Get/Add-WBDisk и Get/Add-WBVolume:

[↑] [Administrator] Get-WBDisk DiskName : WDC WD3200JS-00PDB0 ATA Device DiskNumber : 1 DiskId : d5fae841-0000-0000-0000-000000000000 TotalSpace : 320072933376 FreeSpace : 3591163904 Volumes : {New Volume (S:\VM\Core), Camelot Share-2 (F:)} ContainsBackup : False BackupVolumeId : 00000000-0000-0000-0000-000000000000 Properties : Dynamic, ValidTarget <...> [↑] [Administrator] $disk = Get-WBDisk | ?{$_.disknumber -eq 1} [↑] [Administrator] Get-WBVolume -Disk $disk VolumeLabel : New Volume MountPath : S:\VM\Core MountPoint : \\?\Volume{06d04bb0-1949-11de-a731-001fd08fc2f1} FileSystem : NTFS Property : ValidSource FreeSpace : 2133585920 TotalSpace : 18875416576 VolumeLabel : Camelot Share-2 MountPath : F: MountPoint : \\?\Volume{62bda1e6-b515-4102-b03f-40b7896ab0f3} FileSystem : NTFS Property : ValidSource FreeSpace : 1453031424 TotalSpace : 301192970240 [↑] [Administrator]

Командлет Get-WBDisk отобразит нам все физические диски, которые подключены к системе (с учётом аппаратного рейда, разумеется). Если захотим добавить этот диск в бэкап, то отфильтровываем через Where-Object (или просто вопросительный знак) и добавляем его в политику командой Add-WBDisk. Get-WBDisk нам так же потребуется и для просмотра логических томов, поскольку Get-WBVolume в качестве аргумента принимает только объекты, полученные от команды Get-WBDisk. Т.е. сначала выбираем диск и только потом просматриваем нужные тома. И отфильтровав нужный том добавляем в политику, например:

[↑] [Administrator] Get-WBVolume -Disk $disk VolumeLabel : New Volume MountPath : S:\VM\Core MountPoint : \\?\Volume{06d04bb0-1949-11de-a731-001fd08fc2f1} FileSystem : NTFS Property : ValidSource FreeSpace : 2133585920 TotalSpace : 18875416576 VolumeLabel : Camelot Share-2 MountPath : F: MountPoint : \\?\Volume{62bda1e6-b515-4102-b03f-40b7896ab0f3} FileSystem : NTFS Property : ValidSource FreeSpace : 1453031424 TotalSpace : 301192970240 [↑] [Administrator] $volume = Get-WBVolume -Disk $disk | ?{$_.volumelabel -eq "new volume"} [↑] [Administrator] Add-WBVolume -Policy $pol -Volume $volume VolumeLabel : New Volume MountPath : S:\VM\Core MountPoint : \\?\Volume{06d04bb0-1949-11de-a731-001fd08fc2f1} FileSystem : NTFS Property : ValidSource FreeSpace : 2133585920 TotalSpace : 18875416576 [↑] [Administrator] $pol Schedule : BackupTargets : VolumesToBackup : {New Volume (S:\VM\Core)} FilesSpecsToBackup : {C:\Users\*, D:\Users\*} FilesSpecsToExclude : {C:\Users\vpodans\*} BMR : False SystemState : False VssBackupOptions : VssCopyBackup [↑] [Administrator]

Если я вдруг не захочу уже бэкапить добавленный том, то его можно спокойно удалить (фактически командами Remove-WB* можно удалить что угодной из политики):

Remove-WBVolume -Policy $pol -Volume $volume

Теперь настало время выбрать точки, в которые мы будем копировать наш бэкап. Точки назначения добавляются в бэкап тоже задаются в 2 этапа:

  1. New-WBBackupTarget — создаёт объект точки назначения бэкапа
  2. Add-WBBackupTarget — добавляет эту точку в политику бэкапа

[↑] [Administrator] $target = New-WBBackupTarget -VolumePath "F:" [↑] [Administrator] $target = New-WBBackupTarget -VolumePath "E:" [↑] [Administrator] Add-WBBackupTarget -Policy $pol -Target $target Label : Camelot Share-1 WBDisk : WBVolume : Camelot Share-1 (E:) Path : \\?\Volume{ca6dbf07-14ad-11de-937f-806e6f6e6963} TargetType : Volume InheritAcl : False PreserveExistingBackup : False [↑] [Administrator] $pol Schedule : BackupTargets : {E:} VolumesToBackup : {} FilesSpecsToBackup : {C:\Users\*, D:\Users\*} FilesSpecsToExclude : {C:\Users\vpodans\*} BMR : False SystemState : False VssBackupOptions : VssCopyBackup [↑] [Administrator]

Вы так же можете указать бэкап сразу в сеть. Для этого в команде New-WBBackupTarget вместо параметра –VolumePath использовать параметр –NetworkPath и за ним уже указывать UNC путь к сетевой папке. Однако, следует учесть несколько нюансов:

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

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

Теперь у нас есть 2 варианта: начать выполнение бэкапа немедленно, или регистрация нашей политике в системе для периодического выполнения в автоматическом режиме (по внутреннему шедулеру). В первом случае достаточно выполнить команду:

Start-WBBackup –Policy $pol

или зашедулить командой Set-WBSchedule:

[↑] [Administrator] Set-WBSchedule -Policy $pol -Schedule "10.08.2009 21:00" tresdiena, 2009. gada 7. oktobri 21:00:00 [↑] [Administrator] $pol Schedule : {2009.10.07. 21:00:00} BackupTargets : {E:} VolumesToBackup : {} FilesSpecsToBackup : {C:\Users\*, D:\Users\*} FilesSpecsToExclude : {C:\Users\vpodans\*} BMR : False SystemState : False VssBackupOptions : VssCopyBackup [↑] [Administrator] Set-WBPolicy –Policy $pol

И командой Set-WBPolicy наша политика регистрируется в системе. Шедулинг следует указывать в следующей форме:

Month.Day.Year Hours:Minutes

И теперь каждый день в 21:00 будет выполняться наше задание.

Сегодня мы рассмотрели основные моменты создания политики бэкапа в Windows Server 2008 R2 с использованием PowerShell. В следующей (или следующих) рассмотрим вопросы управления этими политиками и вопросы каталогизации/ротации архивов.

Wednesday, October 07, 2009 11:43:07 PM (FLE Daylight Time, UTC+03:00)   Comments [1]    

 

А знаете ли вы как можно легко получить список всех Enterprise CA в текущем домене? А в текущем лесу? Оказывается это очень легко! ADSI — самый лучший способ, если вы хотите пошариться в своей базе AD. Список таких CA находится по пути:

CN=Enrollment Services, CN=Public Key Services, CN=Services, CN=Configuration, DC=Domain, DC=COM

Последние 2 значения уже будут отличаться в зависимости от имени домена. Чтобы получить список объектов по этому пути нужно просто создать соответствующий LDAP объект. Объект делается просто, сначала указывается тип объекта [ADSI], следом идёт префикс ссылки LDAP:// (почти как HTTP://) и после префикса уже этот путь (который называется Distinguished Name или просто DN):

[Administrator] $CA = [ADSI]"LDAP://CN=Enrollment Services, CN=Public Key Services, CN=Services, CN=Configuration, DC=co ntoso,DC=COM" [Administrator] $CA distinguishedName : {CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com} Path : LDAP://CN=Enrollment Services, CN=Public Key Services, CN=Services, CN=Configuration, DC=contoso,DC =COM [Administrator] $CA.distinguishedName CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com

Если посмотреть этот путь в ADSIEdit.msc, то мы увидим, что это контейнер. А раз это контейнер, то нам нужно в него заглянуть. Здесь, к сожалению, нельзя сделать dir $CA.distinguishedName, а так хочется. Чтобы посмотреть содержимое нужно использовать свойство Children (ворненк, дети отаке!):

[Administrator] $ca.Children distinguishedName : {CN=contoso-DC2-CA,CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=co ntoso,DC=com} Path : LDAP://CN=contoso-DC2-CA,CN=Enrollment Services,CN=Public Key Services, CN=Services, CN=Configurati on, DC=contoso,DC=COM distinguishedName : {CN=Contoso CA,CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=contos o,DC=com} Path : LDAP://CN=Contoso CA,CN=Enrollment Services,CN=Public Key Services, CN=Services, CN=Configuration, DC=contoso,DC=COM [Administrator]

Уже отсюда невооружённым глазом видны имена CA. Собственно, можно показать только имя самого CA и компьютера, на котором работает этот CA:

[Administrator] $ca.Children | ft Name, DNSHostName Name DNSHostName ---- ----------- {contoso-DC2-CA} {dc2.contoso.com} {Contoso CA} {DC1.contoso.com} [Administrator]

Здесь есть один важный нюанс. Если получить этот LDAP объект в PowerShell 1.0, то он будет содержать только Distinguished Name, а свойство Children будет отсутствовать в нём. Для этого нужно воспользоваться свойством PSBase, в котором уже будет Children. Командой Select можете выводить на экран и другие свойства, какие вы захотите:

PS C:\> $ca.children MemberType : Method OverloadDefinitions : TypeNameOfValue : System.Management.Automation.PSMethod Value : Name : children IsInstance : True PS C:\> $ca.psbase.children distinguishedName ----------------- {CN=contoso-DC2-CA,CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com} {CN=Contoso CA,CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com} PS C:\> $ca.psbase.children | select Name, DNSHostname Name DNSHostname ---- ----------- {contoso-DC2-CA} {dc2.contoso.com} {Contoso CA} {DC1.contoso.com} PS C:\>

Поэтому для обратной совместимости между версиями лучше использовать PSBase. В этом объекте будут содержаться не только CA вашего домена, а во всех доменах вашего леса, поскольку эта часть AD реплицируется как Forest naming context, т.е. между всеми контроллерами в лесу. Жизнь была бы неинтересной, если в каждом новом домене приходилось бы переписывать хвост (которая определяет домен, в котором следует искать) каждый раз. Для универсальности можно пойти на военную хитрость — раздобыть FQDN текущего домена, разобрать его и воткнуть в LDAP запрос. Получить имя текущего домена можно очень просто, с использованием статического метода GetCurrentDomain() класса System.DirectoryServices.ActiveDirectory.Domain. На самом деле у этого класса есть ещё куча других полезных методов, поэтому не лишним будет заглянуть по ссылке.

PS C:\> [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() Forest : contoso.com DomainControllers : {DC1.contoso.com} Children : {} DomainMode : Windows2003Domain Parent : PdcRoleOwner : DC1.contoso.com RidRoleOwner : DC1.contoso.com InfrastructureRoleOwner : DC1.contoso.com Name : contoso.com PS C:\>

И свойство Name будет содержать имя нашего домена. Что дальше? А дальше, вполне очевидно, что нам надо заменить все точки на строку вида ", DC=". Вот так:

PS C:\> "contoso.com" -replace "\.", ", DC=" contoso, DC=com

А перед первым именем эту часть можно написать ручками. В итоге универсальная часть кода получится вот такая:

$domain = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).Name
$domain = "DC=" + $domain -replace '\.', ", DC="
$CA = [ADSI]"LDAP://CN=Enrollment Services, CN=Public Key Services, CN=Services, CN=Configuration, $domain"

Вот теперь у нас есть всё необходимое, чтобы написать простеньку функцию:

function Get-CertificationAuthority ([string]$CAName) {
    $domain = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).Name
    $domain = "DC=" + $domain -replace '\.', ", DC="
    $CA = [ADSI]"LDAP://CN=Enrollment Services, CN=Public Key Services, CN=Services, CN=Configuration, $domain"
    $CAs = $CA.psBase.Children | %{
        $current = "" | Select CAName, Computer
        $current.CAName = $_ | %{$_.Name}
        $current.Computer = $_ | %{$_.DNSHostName}
        $current
    }
    if ($CAName) {$CAs = @($CAs | ?{$_.CAName -eq $CAName})}
    if ($CAs.Count -eq 0) {throw "Sorry, here is no CA that match your search"}
    $CAs
}

Если выполнить эту функцию без аргументов, то она вернёт все CA в лесу. Но можно указать и какой-то один для каких-то других целей.

Tuesday, October 06, 2009 9:15:39 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

По мотивам предыдущего поста и срача в ньюсгруппе microsoft.public.windows.powershell перепубликовываю скрипт Роберта Робело для определения кодировки, в которой сохранён файл.

filter Get-TextEncoding {
#requires -Version 2.0
    begin {
        [string]$BOM_Unicode = [Text.Encoding]::Unicode.GetPreamble()
        [string]$BOM_UTF7 = [Text.Encoding]::UTF7.GetPreamble()
        [string]$BOM_UTF8 = [Text.Encoding]::UTF8.GetPreamble()
        [string]$BOM_BigEndian = [Text.Encoding]::BigEndianUnicode.GetPreamble()
        [string]$BOM_UTF32 = [Text.Encoding]::UTF32.GetPreamble()
    }
    process {
        if ($_ -is 'IO.FileInfo') {
            $bytes = Get-Content -Literal $_.pspath -Encoding Byte -Total 4 -ErrorAction SilentlyContinue
            $value = if ($bytes) {
                if ($bytes[0..1] -as 'String' -eq $BOM_Unicode) {'Unicode'}
                elseif ($bytes[0..2] -as 'String' -eq $BOM_UTF7) {'UTF7'}
                elseif ($bytes[0..2]  -as 'String' -eq $BOM_UTF8) {'UTF8'}
                elseif ($bytes[0..1] -as 'String' -eq $BOM_BigEndian) {'BigEndian'}
                elseif ($bytes[0..3] -as 'String' -eq $BOM_UTF32) {'UTF32'}
                # undetermined, no BOM
                else {'Unknown'}
            # undetermined, zero bytes
            } else {'Unknown'}
            $_ | Add-Member NoteProperty Encoding $value -PassThru
        # not an IO.FileInfo
        } else {$_}
    }
}

ну и использование достаточно простое:

dir *.ps1 | Get-TextEncoding | Format-Table Name, Encoding

Уже тут видно мелкий косячок подсветки в PowerGUI. Name должно быть такого же цвета, что и Encoding. А выглядеть это будет примерно так:

[↓] [vPodans] dir desktop\*.ps1 | Get-TextEncoding | ft name, encoding Name Encoding ---- -------- Untitled1.ps1 BigEndian untitled2.ps1 UTF8 [↓] [vPodans]

Не забудьте, что это будет работать только в V2. Для PowerShell 1.0 придётся чуточку подпилить его.

Sunday, October 04, 2009 2:07:34 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Когда пользователь начинает работать с PowerShell, то со временем перед ним возникает вопрос — какой редактор (вернее сказать, среду разработки) выбрать? Решений уже достаточно много, чтобы было из чего выбирать:

  • PowerShell ISE
  • PowerGUI
  • PrimalScript
  • PowerShellPlus

в большинстве случаев выбор делают между первыми двумя продуктами. Иногда споры об этом переходят в разряд религиозных войн. Истинные фанаты PowerShell выбирают ISE. В принципе, как просто редактор он вполне неплох, т.к. уже есть в коробке (начиная с первых CTP версий PowerShell V2) и для работы с ним никаких телодвижений делать не надо. Однако с ним есть ряд трудностей:

  • IntelliSense

В ISE отсутствует IntelliSense (автозавершение команд и свойств/методов объекта). А это весьма необходимая функция в среде разработки. Мне кажется, что это поняли уже все, кроме разработчиков PowerShell ISE. Вот как это может выглядеть:

PowerGUI IntelliSense support

  • Подсказки (ToolTips)

При наведении курсора на команду, не отображаются подсказки (Tooltips). В PowerGUI это выглядит действительно классно, точь-в-точь как в Visual Studio:

PowerGUI ToolTips

Причём, на этой картинке можно нажимать на стрелочки вверх/вниз для просмотра типов данных, которые принимаются в качестве аргумента. При наведении на переменную PowerGUI показывает даже тип и содержимое переменной (в разумных пределах).

  • Multiple runspaces

В ISE включена поддержка одноврменного редактирования нескольких скриптов (за счёт табов). Однако, все табы выполняются в одном runspace, что часто приводит к негативным последствиям, когда в двух разных табах используются одинаковые имена переменных. В связи с этим, данные из определённой переменной одного таба будут мигировать во второй таб, если эта переменная ещё не определена. Это может привести к неожиданным результатам работы скрипта.

  • Кодировка сохраняемых файлов

ISE по умолчанию сохраняет файлы в Big Endian кодировке. Вроде бы ничего криминального, но... Set-Authenticodesignature не умеет подписывать скрипты в Big Endian кодировке! Поэтому вы не сможете подписать штатными средствами ни один скрипт, который был сохранён в ISE! Для этого нужно прибегать к грязным хакам. Т.е. использовать такую строку, которая переопределит кодировку, в которой скрипт будет сохранён:

$psISE.CurrentFile.Save([Text.Encoding]::UTF8)
  • Исполнение сохранённых скриптов и Execution Policy

Вы можете в нём исполнять свой сценарий до тех пор, пока не сохраните его в файл. А вот когда вы редактируете уже сохранённый скрипт, то получаете бонус в виде того, что скрипт не будет исполняться внутри ISE, если у вас политика исполнения скриптов выставлена в AllSigned. ISE об этом честно предупреждает:

The script you are about to run will be saved 
Т.е. он сохраняет файл и пытается его запустить. В PowerGUI сделано куда более гуманно. Из основного редактора код условно копируется и вставляется в консоль PowerShell. Т.е. вы можете спокойно редактировать и отлаживать свой скрипт и только когда он будет готов к работе — подписывать скрипт. В случае с ISE вам придётся либо после каждого изменения сохранять и переподписывать скрипт (а это дико неудобно), либо выделять весь код и выбирать Run Selection. А это тоже не очень удобно. Плюс, сохранение скрипта перед исполнением ведёт к другой проблеме. Вы не сможете отменить изменения на более раннее состояние.

  • Поддержка профиля $Profile

В ISE вы не можете использовать свой профиль (который в консоли содержится в переменной $Profile), а только поддерживать дополнительный профиль, который находится в той же папке, что и основной, но под именем Microsoft.PowerShellISE_profile.ps1

  • Отслеживание изменений в файле

ISE не поддерживает проверку файла на изменения, хотя это должно быть удобно. Я часто редактирую свои файлы дома на нотебуке и на работе. Папка со скриптами синхронизируется между домом и работой через Live Sync. Я достаточно редко закрываю редактор, поэтому вечером сохраняю файл (который уже открыт в редакторе на работе) и всё. Утром, придя на работу я могу без переоткрытия файла могу продолжать его редактировать. PowerGUI просто сообщит, что файл был изменён и сам предложит загрузить последнюю сохранённую версию. С ISE придётся вручную переоткрывать файл.

  • About

Я не могу вспомнить программ, которые бы имели меню Help, но внутри не имели подменю About. но это просто мелочи уже, которые не влияют на удобство разработки скриптов.


Updated 04.10.2009

Если подвести это всё в табличку, то получится примерно так:

  PowerShell ISE PowerGUI PowerShell Plus
Built-In Yes, of course! No! No!
Is free Yes, of course! Yes, of course! No!
Fast start No! No! No!
Powershell Support 100% <100% <100%
External PowerShell window Yes, of course! Yes, of course! Yes, of course!
Remote PowerShell tab Yes, of course! No! No!
IntelliSense No! Yes, of course! Yes, of course!
Multiple Runspaces Yes, of course! Yes, of course! No!
ToolTips No! Yes, of course! Yes, of course!
Syntax highlighting Yes, of course! Yes, of course! Yes, of course!
Error syntax highlighting No! Yes, of course! Yes, of course!
Error autocorrection No! No! Yes, of course!
Changed lines highlighting No! Yes, of course! Yes, of course!
Outline support No! Yes, of course! Yes, of course!
Support for signing No! Yes, of course! Yes, of course!
Can sign within IDE No! No! Yes, of course!
Run signed scripts in external window Yes, of course! No! Yes, of course!
Readable command help Yes, of course! Yes, of course! Yes, of course!
Configurable editor panes Yes, of course! Yes, of course! Yes, of course!
Variable pane No! Yes, of course! Yes, of course!
PS $Profile support No! Yes, of course! Yes, of course!
BreakPoints Yes, of course! Yes, of course! Yes, of course!
Code templates No! Yes, of course! Yes, of course!
Print from editor No! Yes, of course! Yes, of course!
Script autosave No! Yes, of course! Yes, of course!

Примечание: последнее изменение таблицы 16.03.2010

Вобщем, я обозначил те вещи, которые я хотел бы видеть в хорошем редакторе. И значительное большинство моих хотелок уже есть в PowerGUI. О достоинствах PowerGUI можно говорить сколько угодно, но не буду смущать Диму Сотникова, поэтому скажу, что их продукт очень крутой. Но, в то же время, есть к чему стремиться. :)

Однако, хочу ещё раз напомнить, что PowerGUI никогда не закроет первую строчку в таблице, что для религиозных фанатиков будет самым главным преимуществом и, который, затмит остальные недостатки ISE. Но мой выбор в этом вопросе достаточно очевиден.

Saturday, October 03, 2009 6:19:24 PM (FLE Daylight Time, UTC+03:00)   Comments [11]    

 

После небольшого перерыва продолжаю допиливать свой вариант FCIV на PowerShell. И радостно могу сообщить, что уже есть версия 1.0, т.е. полностью отвечающая нашим требованиям. Что изменилось в новой версии?

  • Основная команда переименована с Get-PsFCIV в Start-PsFCIV;
  • Включена предварительная проверка файлов на блокировку;
  • В связи с предыдущим пунктом, к  параметру Show добавлен аргумент – Locked. В это свойство помещаются заблокированные файлы (а для них нельзя подсчитать хеш);
  • Включена проверка пути. Если путь к исходной папке не является путём файловой системы, то скрипт будет генерировать ошибку;
  • Исправлена ошибка невозврата на исходный путь, если в процессе работы произошла фатальная ошибка;
  • Параметр Show теперь выводит файлы из указанных категорий в красивое графическое окошко с использованием Out-GridView. Однако, следует учесть, что использование Out-GridView требует, чтобы был установлен .NET Framework 3.5 SP1.
  • Добавлен режим Quiet, который ничего не выводит на экран, а только генерирует коды возврата (LastExitCode). Коды возврата обновлены.
  • Пофиксены сообщения режимов Verbose и Debug.
  • Исправлены мелкие неточности в коде и произведена небольшая оптимизация кода.

А теперь и on-line справка по всем параметрам по просьбе трудящихся.

  • Path <String> — путь к папке, файлы которой следует посчитать или проверить. Допускаются относительные пути. Данный параметр обязателен.
  • XML <String> — путь к XML файлу, который содержит сведения о файлах. Если указанный файл не существует, то после пересчёта файлов по этому пути будет создан новый файл БД со сведениями о файлах. Параметр обязателен.
  • Include <String> — опциональный параметр в котором вы можете указать только конкретный файл из папки. В таком случае будет проверена не вся папка, а только указанный файл. В настоящее время параметр не поддерживает подстановочные знаки (wildcard), типа '*.ext' из-за обеспечения поддержки файлов с метасимволами (например, в имени содержатся квадратные скобки).
  • Action <String> — опциональный параметр, который указывает на действие над файлами, у которых не совпадают хеши или сведения о дате изменения и размере файла. Может принимать значение Rename или Delete. В первом случае к проблемным файлам добавляется расширение .BAD, а во втором файл просто удаляется.
  • Show <String[]> — опциональный параметр, в котором указываются категории файлов для дальнейшего анализа. В зависимости от результата проверки каждый файл (его имя) помещается в ту или иную категорию. Требует установленного .Net Framework 3.5 SP1. Может принимать один или несколько аргументов из списка:
    • Bad — содержит имена всех файлов, у которых обнаружен несовпадающий хеш или не совпадает дата/время изменеия и/или размер файла;
    • Locked — в эту категорию помещаются файлы хеш которых проверить не удалось по причине блокировки файла в монопольном режиме каким-то приложением;
    • Missed — содержит имена файлов, для которых есть запись в XML файле БД, но самого файла уже не существует (по любым причинам);
    • New — в эту категорию попадают только имена новых файлов, для которых соответствующей записи ещё нет в XML файле. Новые файлы добавляются только в режиме Rebuild или когда файл БД создаётся с нуля.
    • Ok — сюда попадают файлы с успешным статусом проверки, т.е. хеш, дата/время изменения и размер файла совпадают со значениями в XML файле;
    • Total — содержит имена всех обработанных файлов вне зависимости от результата проверки;
    • Unknown — содержит имена файлов, для которых нельзя сопоставить хеш. Такая ситуация возможна, если в параметрах функции указан только хеш SHA1, но для файла в БД записан только MD5 хеш и наоборот.
  • Recurse <Switch> — ключ, который включает проверку файлов не только в указанной папке, но и во всех вложенных папках.
  • Rebuild <Swtich> — ключ, который задаёт особый режим работы скрипта — «освежение». Если этот ключ указан, то производится сверка файлов из БД с реальными файлами. Если файл более не существует, то соответствующая запись для него удаляется из БД. После чего целевая папка (и подпапки при указанном ключе –Recurse) проверяется на наличие новых файлов. Для каждого нового файла добавляется соответствующая запись в XML файл.
  • SHA1 <Switch> — задаёт алгоритм хеширования, который будет использоваться для подсчёта новых файлов и/или для проверки уже существующих записей в XML файле.
  • MD5 <Switch> — задаёт алгоритм хеширования, который будет использоваться для подсчёта новых файлов и/или для проверки уже существующих записей в XML файле.
  • Quiet <Switch> — включает несопровождаемый режим работы скрипта. Если ключ указан, то итоговая статистическая информация не выводится на экран, а в зависимости от результата проверки генерируется код возврата (LastExitCode). Код возврата может принимать одно из следующих значений:
    • 0 — в процессе работы скрипта все файлы были успешно проверены и их хеш, дата/время изменения и размер соответствуют записям в БД. Так же, данный код возврата будет сгенерирован, если создавался новый файл БД и все файлы были успешно в него добавлены;
    • 1 — в процессе проверки были обнаружены файлы с несовпадающим хешем и/или датой/временем и размером;
    • 2 — в процессе проверки было обнаружено, что для записи в БД нет соответствующего файла;
    • 3 — в процессе проверки были обнаружены файлы, для которых не удалось сопоставить алгоритм хеширования с хранимым в БД алгоритмом хеширования для файла. Такая ситуация возможна, если в параметрах функции указан только хеш SHA1, но для файла в БД записан только MD5 хеш и наоборот;
    • 4 — в процессе работы скрипта для некоторых файлов не удалось подсчитать хеш по причине блокировки файла в монопольном режиме другим приложением;
    • 5 — скрипт выполнялся в режиме Rebuild (освежения файла БД)
  • Verbose <Switch> — включает отображение дополнительной информации о ходе проверки файлов.
  • Debug <Switch> — включает отображение отладочной информации о ходе работы скрипта.

Несколько примеров использования:

Start-PsFCIV C:\Files db.xml -SHA1 -Recurse -Show Bad, Missed

будет проверена папка C:\Files и все вложенные папки. Файл db.xml должен быть размещён непосредственно в этой папке. Если файл не существует, то будет создан с нуля. После проверки будет показано графическое окно с именами файлов, которые попали в категорию Bad и Missed. Для каждой категории будет отдельное графическое окно.

Start-PsFCIV C:\Files db.xml -SHA1 -MD5 -Include data.dat

будет проверен только файл data.dat в папке C:\Files с использованием SHA1 алгоритмом хешиования. Если для файла в БД записан только MD5 хеш, то проверка будет произведена с использованием MD5. Если файл БД (db.xml) не существует, то создастся новый файл БД со сведениями о файле data.dat. Файл будет подсчитан с использованием как SHA1, так и MD5.

Start-PsFCIV C:\Files db.xml -SHA1 -MD5 -Rebuild

будет произведено освежение файла БД для папки C:\Files. Все записи, для которых соответствующего файла не обнаружено, будут удалены. Если в папке есть файлы, для которых нет соответствующей записи, то они будут обсчитаны с использованием алгоритмов SHA1 и MD5 и будут добвлены в XML файл. Файл db.xml должен существовать, иначе скрипт вернёт фатальную ошибку.

Start-PsFCIV C:\Files db.xml -SHA1 -Quiet

Папка C:\Files будет проверена в несопровождаемом режиме с использованием алгоритма SHA1. По умолчанию никакой информации на экране не будет. После окончания работы, в зависимости от результатов проверки, скрипт сгенерирует соответствующий код возврата (0-5).

И, собственно, сам скрипт:

И как обычно, любые замечания, комментарии постить в каменты.

Thursday, October 01, 2009 10:12:11 PM (FLE Daylight Time, UTC+03:00)   Comments [2]    

 

 · 
All content © 2008 - 2010, Vadims Podāns
"Spaces" Theme provided by: Vadims Podāns
About


E-mail - Send mail to the author(s)
Live Messenger -
My former blog -
For english language visitors
Библиотека
Календарик
<September 2010>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

Карта расположения посетителей
Favorites





Disclaimer
Вся информация на сайте предоставляется на условиях «как есть», без предоставления каких-либо гарантий и прав.

При использовании материалов c данного сайта ссылка на оригинальный источник обязательна.