Резервное копирование конфигурации PKI

04.01.2012 - 11:15

В один прекрасный день, ещё в Старом Году, мне поручили сделать резервное копирование конфигурации PKI service, поднятом на отдельном сервере. Скажу сразу, резервное копирование самой ОС уже мной настроено, но конфигурация должна быть выгружена отдельно. Полностью задача звучит так: Реализовать схему еженедельного копирования конфигурации сервиса с возможностью сохранения предыдущей копии и ведения журнала выполняемых действий.

В интернете материала не сильно и много, но это прямо зависит от того, что вопрос довольно простой, скажу даже - прямолинейный.
Первым делом куда? - Правильно! На TechNet!
Там всё написано просто и правильно:

certutil -backup <BackupDirectory>
однако при попытке запуска, обнаружил, что команда требует ручного ввода пароля, якобы, для защиты резервной копии. Нужно - значит нужно!
Запускаем командную строку и команду
certutil -backup /?
, на что она нам выдаёт:
Usage:
  CertUtil [Options] -backup BackupDirectory [Incremental] [KeepLog]
  Backup Active Directory Certificate Services
    BackupDirectory -- directory to store backed up data
    Incremental -- perform incremental backup only (default is full backup)
    KeepLog -- preserve database log files (default is to truncate log files)

Options:
  -f                -- Force overwrite
  -gmt              -- Display times as GMT
  -seconds          -- Display times with seconds and milliseconds
  -v                -- Verbose operation
  -privatekey       -- Display password and private key data
  -config Machine\CAName    -- CA and Machine name string
  -p Password               -- Password

CertUtil -?              -- Display a verb list (command list)
CertUtil -backup -?      -- Display help text for the "backup" verb
CertUtil -v -?           -- Display all help text for all verbs

Исходя из всего вышесказанного, понимаю, что для нормальной автоматической работы скрипта, нужно запустить команду:
certutil -backup -p <Password> <BackupDirectory>

Половина дела сделана! Теперь нужно реализовать автоматизацию заданного процесса... Возьмёмся за написание скрипта, но на каком языке? Cmd, VBS, PowerShell?
Я выбрал 2-й, исходя из тех соображений, что VBS можно конвертировать в исполняемый файл, а это даёт нам выполнение действий при закрытом пароле.
Схема работы такова: Еженедельно запускается задание на запуск скрипта. Резервное копирование проводится в каталог "Today", предыдущая конфигурация складывается в каталог "Yesterday". На момент выполнения бэкапа у нас уже есть 2 каталога "Today" и "Yesterday", поэтому "Yesterday" мы удаляем, "Today" переименовываем в "Yesterday" и создаём "Today", куда и копируем текущую конфигурацию.
Итак, листинг скрипта с пояснениями:
Option Explicit
' объявление переменных
Dim     objFSO, objLogFile, objShell, objExecObject, strSource, strArray, i, LogDate, LogFile
'
определение пути к логу и его имени. Имя представляет собой <цифра месяца>-<год>.log, например, для текущего месяца - "1-2012.log".
' Такая схема именования позволит вести отдельный лог для каждого месяца.
LogFile = "c:\tmp\" & Month(Date) & "-" & Year(Date) & ".log"
'
Определение классов с параметрами дописывания в Лог-файл.
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objLogFile = objFSO.OpenTextFile(LogFile,8,-2)
' Начальная запись в лог. "Начинаем резервное копирование: Текущее время-дата."
objLogFile.WriteLine("Start backUp PKI: " & Now)
'
Определение класса для выполнения команды с возможностью получения отчёта о выполняемых действиях.
Set objShell = WScript.CreateObject("Wscript.Shell")
Set objExecObject = objShell.Exec("cmd /c certutil -backup -p P@ssw0rd c:\tmp\Today") ' Команда экспорта конфигурации
'
Получение сообщений о выполнении
Do While Not objExecObject.StdOut.AtEndOfStream
' Получение сообщений построчно. Тут вынужден сообщить, что у меня получить все сообщения о выполнении команды получилось (прошу прощения за тавтологию), но
'
вот в чём незадача была: процент выполнения процесса в файл выгружался в одну строку, даже если я вставлял символ перевода строки. Поэтому был вынужден ту
' часть, в которой идут проценты, форматировать в массив и потом уже выводить. Confused
        strSource = objexecobject.StdOut.ReadLine()
        '
Если строка содержит "%" - поделить строку. Так как разделительных символов, кроме самого знака "%" лучше нет - делил по нему,
' но в конце строки его нужно добавлять, он удаляется функцией "Split".
        If (InStr(strSource,"%")) Then
                strArray =  Split(strSource,"%")
'
Выводим каждую строку (элемент массива) в файл и не забываем добавлять "%".
                For i = 0 To (UBound(strArray)-1)
                        objLogFile.WriteLine(strArray(i) & "%")
                Next
        Else
        ' Если строка без "%" - просто записываем в файл.
                objLogFile.WriteLine(strSource & vbCrLf)
        End If
Loop
'
Закрываем файл.
objLogFile.Close

Я думал, что этого достаточно, но я ошибался. Мой хороший товарищ посоветовал мне одну статейку, где подробно описывался процесс резервного копирования конфигурации и различного поэтапного восстановления сервиса. Очень полезная статья!
Поэтому было принято решение всё написать на PowerShell, он мне немного роднее, а данный скрипт компилировать в ехе-шник и включить в ps1.
Особенность работы связки VBS и PowerShell в обращении к логу - Сначала PShell создаёт/начинает запись в лог (одиночная запись в файл командлетом Out-File), потом работа VBS (выгрузка), потом опять PShell. Работа сделана таким образом, чтобы обращения скриптов к логу не перекрещивались. Если в процессе выполнения скрипта обнаружена ошибка - работа скрипта прерывается с записью ошибки в лог.
Листинг скрипта PowerShell:
# Зачем-то вытягиваем название ПК, можно использовать в логе. Осталась с предыдущего скрипта.
$ComputerName = Get-Content env:COMPUTERNAME
# Определение переменных (адрес и имя лога - такой же, как и в скрипте VBS)
$log = "C:\tmp\" + (Get-Date -Format "M-yyy") + ".log"
$Today = "
C:\tmp\Today"
$Yesterday = "
C:\tmp\Yesterday"

# Определение команд выполнения
$backup_PKI = "
C:\tmp\Executive\BackUp.exe" # Запуск VBS скрипта в виде ехе-шника.
$backup_reg = "
reg export HKLM\System\CurrentControlSet\Services\CertSvc\Configuration C:\tmp\Today\regkey.reg"  # Экспорт ключей реестра
$backup_CSP = "
Certutil –getreg CA\CSP > C:\tmp\Today\CSP.txt"
$templates = "
Certutil –catemplates > C:\tmp\Today\CA_Templates.txt"
# Очистка лог-строки. Лог я сознательно записываю в строку, а в конце выполнения скрипта - выгружаю в файл, чтобы было меньше к нему обращений.
$log_string = "
"
# Начинаем запись в log
Out-File -FilePath $log -InputObject ("
`r`n================================================================== `r`n " + (Get-Date -Format "(yyyy-MM-dd) hh:mm:ss")+ "    Starting export PKI DB & config.`r`n") -Encoding UTF8 -Append
# Удаляем каталог Yesterday
Remove-Item -Path $Yesterday -Recurse -ErrorVariable err
if($err)
{
    $log_string = $log_string + "
Removing Yesterday folder failed. Backup stopped. `r`n" + $err +"`r`n"
}
else
{
    # Переименовываем Today -> Yesterday
    Rename-Item -Path $Today -NewName $Yesterday -ErrorVariable err
    if($err)
    {
        $log_string = $log_string + "
Renaming folders failed. BackUp stopped. `r`n" + $err
    }
    else
    {
        # Create Today
        New-Item -Path $Today -ItemType Directory -Force -ErrorVariable err
        if($err)
        {
            $log_string = $log_string + "
Creating Today folder failed. BackUp stopped.`r`n" + $err +"`r`n"
        }
        else
        {
            $log_string = $log_string + "
Renaming folder successfully finished.`r`n"
# Запуск выполнения команды из PS. Переменная Re в данном случае не нужна, но она содержит флаг выполнения команды. При успешном выполнении
# она содержит значение "
0".
            $Re = Invoke-Expression -Command $backup_PKI -ErrorVariable err
            if($err)
            {
                $log_string = $log_string + "
BackUp failed. `r`n" + $err +" `r`n"
            }
            else
            {
                $log_string = $log_string + "
Backup finished. `r`n" + $Re + "`r`n"
                $Re = Invoke-Expression -Command $backup_reg -ErrorVariable err
                    if($err)
                    {
                        $log_string = $log_string + "
BackUp registry failed. `r`n" + $err +" `r`n"
                    }
                    else
                    {
                        $log_string = $log_string + "
BackUp registry keys finished. `r`n"
                        $Re = Invoke-Expression -Command $backup_CSP
                        $log_string = $log_string + "
Get CA\CSP. `r`n"
                        $Re = Invoke-Expression -Command $templates
                        $log_string = $log_string + "
Get CA templates. `r`n"
                    }
            }
        }
    }
}
# Выгрузка лог-строки в файл.
Out-File -FilePath $log -InputObject $log_string -Encoding UTF8 -Append
# Если есть ошибка выполнения команды - вывод содержимого ошибки.
Out-File -FilePath $log -InputObject $Re -Encoding UTF8 -Append
Out-File -FilePath $log -InputObject ("
`r`n" +(Get-Date -Format "(yyyy-MM-dd) hh:mm:ss") + "    BackUp finished.") -Encoding UTF8 -Append

В результате выполнения, получаем лог-файл в таком виде:
 
==================================================================
 (2012-01-03) 01:02:26    Starting export PKI DB & config.

Start backUp PKI: 03.01.2012 13:02:42
Backed up keys and certificates for <имя ПК с ролью CA>.<имя домена>\<DNS имя роли CA> to c:\tmp\Today\<DNS имя роли CA>.p12.

Full database backup for <имя ПК с ролью CA>.<имя домена>\<DNS имя роли CA>.


Backing up Database files: 100%

Backing up Log files: 0%
Backing up Log files: 4%
Backing up Log files: 9%
Backing up Log files: 12%
Backing up Log files: 13%
Backing up Log files: 16%
Backing up Log files: 19%
Backing up Log files: 21%
Backing up Log files: 25%
Backing up Log files: 33%
Backing up Log files: 39%
Backing up Log files: 43%
Backing up Log files: 48%
Backing up Log files: 54%
Backing up Log files: 58%
Backing up Log files: 63%
Backing up Log files: 69%
Backing up Log files: 73%
Backing up Log files: 79%
Backing up Log files: 100%

Truncating Logs: 100%
Backed up database to c:\tmp\Today.

Database logs successfully truncated.
CertUtil: -backup command completed successfully.

Renaming folder successfully finished.
Backup finished.

BackUp registry keys finished.
Get CA\CSP.
Get CA templates.

(2012-01-03) 01:03:51    BackUp finished.

Просто и понятно. Следующие записи про резервное копирование добавляются в конец файла.

* Цвета кода в статье не соответствуют содержимому, советую на цвет не обращать внимания или копировать код в компилятор.
Спасибо Amerk [MSFT] за ценную статью.

Ваша оценка: Нет Средняя: 2.7 (3 голосов)