[Из песочницы] Автоматическое обновление и резервное копирование 1С при помощи powershell

habr.png

Всем доброго дня.

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

1) Предисловие


Вопрос необходимости резервного копирования в автоматическом режиме не подлежит сомнению ни у корифеев, ни у новичков. В статье рассмотрим резервное копирование средствами 1С (что имеет ряд преимуществ перед копированием средствами СУБД). При этом будут применены средства пакетного запуска платформы 1С, powershell и планировщик задач Windows.

Задачи обновления информационных давно автоматизированы, но только для типовых конфигураций, либо тех, что используют библиотеку стандартных подсистем. В моем случае мы работаем со старенькой Альфа-Авто редакции 4, которая распространяется на 12 серверов. Изменения вносятся примерно два раза в неделю, поэтому выгода от автоматизации налицо.

В обоих случаях мы имеем следующие исходные данные:

  • Операционная система Windows Server (версии от 2008 до 2012);
  • Клиент-серверный вариант платформы 1С 8.3 (с обязательно установленным компонентом COM-соединение).


Разумеется, у нас есть учетные данные ОС и 1С с административными правами.
Для обновления конфигурации так же понадобится ftp-сервер.

Чтобы не раздувать статью, рекомендую прочесть про powershell и про планировщик Windows отдельно.

2) Резервное копирование


После прочтения указанных ссылок мы уже знаем, что надо сделать, чтобы запустить скрипт powershell, поэтому сразу перейду к делу.

Сделать резервную копию информационной базы в пакетном режиме очень просто, надо только «выгнать» всех пользователей. Делать мы это будем, подключившись COM-объектом к базе данных. Это в нашем примере делает функция ExitAll. В тело функции зашито, что она вызывается на том сервере, на котором, собственно, установлен кластер серверов 1С. Вызовите эту функция безо всяких параметров в своем скрипте на сервере — и ВСЕ пользователи из ВСЕХ баз кластера вылетят.

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

После этого следует вызвать функцию BackUpBase с параметром — имя информационной базы. У меня во всех ИБ есть служебный администратор с одинаковыми учетными данными, поэтому я их просто захардкодил. При необходимости можно их параметризовать, либо обойтись аутентификацией ОС.

Итоговый скрипт сохраняем в файл.

В планировщике задач создаем «Простую задачу», имя, разумеется, на ваше усмотрение.
У меня работает ежедневно, но и тут хозяин — барин. Запускать лучше всего ночью, когда никто не работает, например, в 3:00. Действие для задачи — «Запустить программу». Сама программа у нас «powershell.exe». А вот ее аргументы —

-File "D:\1C_automatization\ExitAllUsersAndBackup.ps1" -ExecutionPolicy RemoteSigned


где ExitAllUsersAndBackup.ps1 — как раз наш сохраненный скрипт.
-ExecutionPolicy RemoteSigned — ключ, который разрешает выполнение пакетных скриптов powershell, если в системе они глобально не разрешены. Работает через раз (возможно, не хватает компетенции чтобы разобраться, но закономерности не нашёл). В случаях, когда не работает с этим ключом, приходится разрешать выполнение скриптов для всего сервера.

Для этого Win+R, powershell.exe,

Set-ExecutionPolicy RemoteSigned


и подтверждаем действие.

Время работы с данными скриптами — более трех месяцев. Перебои были, но связаны с отключением электричества и прочими внешними факторами.

3) Обновление конфигурации


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

Конфигурацию мы храним на ftp-сервере, на который помещаем ее вручную. Файл конфигурации называется GK.cf, в приведенном примере обновляется одна единственная конфигурация. Потенциально можно так же обновлять и несколько различных конфигураций.
На ftp рядом с GK.cf помещаем файл с названием flag.txt. Наличие этого файла сигнализирует о том, что обновляться надо. Можно проверять наличие самого фйла GK.cf, но мы используем флаг так же для других целей.

Скрипт работает следующим образом:

  • Удаляет GK.cf и flag.txt, если таковые есть в рабочем каталоге (у пользователя, от имени которого будет запускаться планировщик, должно быть право на запись в этот каталог);
  • Предпринимается попытка скачать файл флага;
  • Если такой файл скачать получилось — скачиваем .cf;
  • Собственно, обновление функцией UpdateCf.


В отличии от предыдущего случая, параметры запуска 1С переданы не массивом, а в привычном по командной строке режиме.

Надежность этого скрипта чуть меньше. Обновление проходит до конца на 100% в тех случаях, когда меняется структура метаданных. В других случаях бывает, как я предупреждал ранее, появление активного пользователя. В результате конфигурация загружена в базу, но конфигурация базы данных не обновлена (снова прошу прощения за подобную кривоватую терминологию перед людьми, не связанными с 1С). В остальном — полет нормальный.

Скрипт резервного копирования


# параметры общие ++ 
$BAKUPDIR="D:\1C_automatization\BACKUP\"
$CEXE="C:\Program Files (x86)\1cv8\common\1cestart.exe"
# параметры общие --

function ExitAll{
    # Сценарий разрывает все сессии пользователей во всех ИБ на выбранном кластере сервера приложений 1С
    # Если часть сессий остается активными после этого, то он останавливает все рабочие процессы кластера

    # Параметры запуска сценария: адрес сервера, основной порт кластера
    #$SrvAddr = "tcp://y001-ap-01:1640"
    $SrvAddr = "tcp://127.0.0.1"
    ########################################
    $V83Com = New-Object -COMObject "V83.COMConnector"
    # Подключение к агенту сервера

    $ServerAgent = $V83Com.ConnectAgent($SrvAddr)
    $ClusterFound = $FALSE
    #Получим массив кластеров сервера

    $Clasters = $ServerAgent.GetClusters()
    foreach ($Claster in $Clasters){
        # Аутентификация к выбранному кластеру
        # если у пользователя, под которым будет выполняться сценарий нет прав на кластер,
        # можно прописать ниже имя пользователя и пароль администратора кластера

        $ServerAgent.Authenticate($Claster,"","")

        #получаем список сессий кластера и прерываем их
        $Sessions = $ServerAgent.GetSessions($Claster)
        write-host "Разрывается" $Sessions.Count "сессий..."
        foreach ($Session in $Sessions){
        $ServerAgent.TerminateSession($Claster,$Session)}
    	
        if (($Sessions.Count -eq 0)){
        continue}

        # Если часть сессий осталась активной можно остановить рабочие процессы
        # Они перезапустятся автоматически главным менеджером кластера, но без "зависших" сессий

        # Получаем список рабочих процессов кластера
        $WorkingProcesses = $ServerAgent.GetWorkingProcesses($Claster)

        foreach ($WorkingProcess in $WorkingProcesses){
            if ($WorkingProcess.Running -eq 1){
                write-host "Останавливаем процесс с PID =" $WorkingProcess.PID
                #!!!здесь хорошо бы проверить что компьютер с которого запущен сценарий это выбранный в параметрах запуска сервер приложений 1С
                Stop-Process $WorkingProcess.PID -Force}}

    }
    $V83Com = ""
}

function BackUpBase($IbName){
	#Сформируем текущие дату и время
    $NOWDATETIME = Get-Date -UFormat "%Y_%m_%d_%H-%M"
	#Укажем, куда копировать (можно так же параметризовать, чтобы разные базы копировались в разные каталоги)
    $TARGETDIR = $BAKUPDIR
	#Сформируем имя файла резервной копии
    $BAKFN=$TARGETDIR + $IbName + "_" + $NOWDATETIME + ".dt"
	#Сформируем массив параметров
    $PARAMS = ("DESIGNER","/UseHwLicenses+","/S","127.0.0.1\$IbName","/N","ИМЯАДМИНА1С","/P","ПАРОЛЬАДМИНА1С","/DumpIB",$BAKFN)
	#Собственно, поехали
    &$CEXE $PARAMS

}

#Выгонем всех
ExitAll

#Вот эти базы будут скопированы
BackUpBase RingAlpha
BackUpBase MazdaAlpha
BackUpBase RingAcc
BackUpBase RingZup
BackUpBase MazdaHRM
BackUpBase MazdaAcc
BackUpBase RingAcc48
BackUpBase RingHRM48
BackUpBase RingAccNorth
BackUpBase RingHRMNorth


Скрипт обновления конфигурации



function Download_File ($si_UserName, $si_Password, $si_Source_Path, $si_Target_Path){
$oi_Credentials = New-Object System.Net.NetworkCredential($si_UserName,$si_Password)
$oi_Web_Client = New-Object System.Net.WebClient
$oi_Web_Client.Credentials = $oi_Credentials
$oi_Web_Client.DownloadFile($si_Source_Path, $si_Target_Path)
while($oi_Web_Client.IsBusy){}
}

Clear-Host;
$curDir = $MyInvocation.MyCommand.Definition | split-path -parent
$curDir = $curDir + "\"
$server = "111.222.33.4" #FTP сайт
$flagfoldername = "/1C/flag.txt" #удаленная папка с флаг-файлом
$flagurl = "ftp://$server$flagfoldername" #url для закачки флага
$cffoldername = "/1C/GK.cf" #удаленная папка с файлом кофигурации
$cfurl = "ftp://$server$cffoldername" #url для закачки файла конфигурации
$login = "MYFTPUSER" #ftp-логин
$password = "MYFTPPWD" #ftp-пароль
$flagfile = $curDir + "flag.txt"
$cffile = $curDir + "GK.cf" #файл с конфигурацией на локальном диске

$SERVERNAME="127.0.0.1"
$CEXE="C:\Program Files (x86)\1cv8\common\1cestart.exe"
$ADMINNAME="1CUSER"
$ADMINPASS="1CPWD"

function UpdateCf($IBNameLocal){
    &$CEXE 'DESIGNER' '/S' $SERVERNAME'\'$IBNameLocal '/N' $ADMINNAME '/P' $ADMINPASS '/LoadCfg' $cffile '/UpdateDBCfg' '-Server' 
}

function FileExist($fpath){
    $isfile = Test-Path $fpath    
    return ($isfile -eq "True") 
}

#собственно, начало выполнения скрипта
if (FileExist $flagfile){
    Remove-Item $flagfile
}

if (FileExist $cffile){
    Remove-Item $cffile
}

Download_File $login $password $flagurl $flagfile

if (FileExist $flagfile){
    Download_File $login $password $cfurl $cffile
    if (FileExist $cffile){#стартуем обновление
        UpdateCf "MazdaAlpha"
	UpdateCf "RingAlpha"
    }
}


Благодарю за внимание. Готов к конструктивной критике.

© Habrahabr.ru