Управление обновлениями в решениях песочницы

Управление обновлениями в решениях песочницы

Текст:

Решения песочницы являются крайне удобной возможностью платформы SharePoint 2010.  Однако по мере развертывания таких решений в различных местах фермы или семейства сайтов возникает вопрос об их эффективном управлении.

Для этих целей мы предлагаем скрипт PowerShell.  Он довольно прост, однако его функциональность можно расширить, включив средства, которые уже используются для управления фермами и семействами сайтов.

Я хочу объяснить, что может этот скрипт: 

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

У скрипта имеются следующие параметры:

·         путь к файлу и имя решения (WSP-файла) для обновления

·         параметр-переключатель, указывающий, следует ли проводить обновление

Разрешения:

·         Запускать скрипт может только пользователь, который является администратором песочницы и имеет разрешение на проведение обновления.

·         Уровень разрешений меняется, и администратор получает полный доступ.

·         После окончания обновления восстанавливаются исходные значения разрешений.

Журнал имеет следующий формат:

·         Сведения о пути к файлу и имени рассматриваемого решения, включая его идентификатор.

·         Сведения о каждом просматриваемом семействе сайтов

·         Для каждого семейства сайтов

o   Сведения о том, было ли найдено искомое решение

o   Если найдено, сведения о файле удаляемого решения, чтобы его можно было заменить новой версией.

o   Если проводится обновление, сведения об успешном или неудачном завершении

·         Сводка, включающая

o   Число просмотренных семейств сайтов

o   Если обновление не проводится, сводный список сайтов, для которых требуется обновление вместе со сведениями по текущей версии каждого компонента (его идентификатора) в решении.

o   Если обновление ВЫПОЛНЯЕТСЯ, сведения о компоненте и версии вместе с данными об успешном обновлении, успешной новой установке или отсутствии изменений для компонента, поскольку тот имеет текущую версию.

Некоторые вещи, которые стоит отметить:

При обновлении решения платформа SharePoint 2010 ожидает, что имена файлов будут отличаться.  Если они совпадают, вы получите ошибку, в которой сообщается, что решение уже активно.  При обновлении решения его идентификатор используется для поиска существующих версий этого решения, а ЗАТЕМ для просмотра сведений о версии.  Обновление позволяет только перейти к новой версии.  Оно не выполняет отката к старой версии.  А если версии одинаковы, то ничего не происходит.

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

# DLL-библиотека SharePoint

[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")

 

#Установка командлета

Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue

 

#Определение файла журнала

$intro = "User Solution Upgrader v1.0 - Log File"

$date = (Get-Date).ToString('yyyyMMdd')

 

Set-Variable -Name ForReading -value 1 -Option Constant

Set-Variable -Name ForWriting -value 2 -Option Constant

Set-Variable -Name ForAppending -value 8 -Option Constant

 

#Предполагается, что текущий пользователь "Домен\Пользователь" является администратором песочницы с правом на проведение проверки и обновления.

$admin  = $env:UserDomain + "\" + $env:UserName

if ($env:UserDomain -eq $null)

{             

                $admin = $admin.substring(1)

}

 

# Получение родительской папки скрипта. Здесь сохраняется файл журнала.

$logFolder = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent

$getACL = Get-Acl $logFolder

$access = $getACL.Access | Where { $_.IdentityReference -eq $admin }

if ($access -eq $null -or

    ($access.FileSystemRights -ne [System.Security.AccessControl.FileSystemRights]::FullControl -and

     ($access.FileSystemRights -ne [System.Security.AccessControl.FileSystemRights]::Write -and

      $access.FileSystemRights -ne [System.Security.AccessControl.FileSystemRights]::CreateFiles)))

{

                $logFolder = $env:userprofile

}

 

# Добавление имени файла журнала в путь.

$logFileFullPath = Join-Path $logFolder "usersolutionupgrader-$date.txt"

 

# Открытие файла

$fo = New-Object -com Scripting.FileSystemObject

$f = $fo.OpenTextFile($logFileFullPath, $ForAppending, $true)

 

if ($f -eq $null)

{

                $logFolder = $env:userprofile

                $logFileFullPath = Join-Path $logFolder "usersolutionupgrader-$date.txt"

                $f = $fo.OpenTextFile($logFileFullPath, $ForAppending, $true)

}

 

#Подтверждение файла журнала на экране

Write-Host ("Log Path: " + $logFileFullPath)

 

#Вывод начальной строки

$f.WriteLine()

$f.WriteLine($intro)

 

function WriteLog

{

                $dt = Get-Date

                $logMsg = $dt.ToShortDateString() + "  " + $dt.ToShortTimeString() + "`t" + $args[0]

                $f.WriteLine($logMsg)

                Write-Output($logMsg)  #Включите, если нужен вывод на консоль

}

 

function exitScript()

{

                if ($f -ne $null)

                {

                                $f.Close()

                }

                Write-Host "Exiting script due to error."

                exit

}

 

WriteLog ("Current user (admin):   $admin")

 

#solution details

$solFile = ""

$targetSolutionId = ""

$upgrade = $false

$myError = $null

 

$haveCurrentFeatures = $false

$haveNewFeatures  = $false

$numSites = 0

$oldFeatures = @{}

$newFeatures = @{}

$solSites = New-Object System.Collections.ArrayList

$solFailSites = New-Object System.Collections.ArrayList

 

#Анализ параметров командной строки

if (($args -eq $null) -or ($args.Count -lt 2))

{

                WriteLog("Error: This script has been called without enough parameters. Listing your parameters below:")

                WriteLog($args)

                if ($f -ne $null)

                {

                                $f.Close()

                }

                exitScript

}

else

{

                $badParameters = $false

                foreach ($arg in $args)

                {

                                switch ($arg)

                                {

                                                "-upgrade"  {$upgrade = $true}

                                                "-path" {[void] $foreach.MoveNext(); $solFile = $foreach.Current}

                                                default {$badParameters = $true}

                                }

                }

 

                if ($badParameters -eq $true)

                {

                                WriteLog("You passed in a bad parameter.")

                                exitScript

                }                             

 

                #Проверка параметров

                $extension = [IO.Path]::GetExtension($solFile)

                if (($extension -eq $null) -or ($extension -ne ".wsp") -or (!(Test-Path $solFile)))

                {

                                WriteLog("Error: The solution file name is not of WSP format or is an invalid file.")

                                exitScript

                }

 

                #Подтверждение извлеченных значений параметров

                $fileName = Split-Path $solFile -Leaf

                WriteLog("Solution Path is: `t" + $solFile)

                WriteLog("Solution Name is: `t" + $fileName)

                if ($upgrade)

                {

                                WriteLog "The tool will attempt to perform UPGRADE on the site collections"

                }

                WriteLog ""

}

 

#Получение идентификатора решения из заданного WSP-файла

$tempPath = Join-Path $env:temp "upgrader" | Join-Path -ChildPath $fileName

$shell = New-Object -ComObject "Shell.Application" -ErrorAction:SilentlyContinue -ErrorVariable myError

if ($myError -ne $null)

{

                WriteLog("FAILED to create the Shell.Application object.")

                WriteLog("Error: " + $myError)

                $myError = $null

}

 

[IO.Directory]::CreateDirectory($tempPath)  | Out-Null

$tempFolder = $shell.NameSpace($tempPath)

$tempFolder.CopyHere($solFile)

 

#Получение имени файла ("ваш_файл.wsp") из исходного пути

$tempSolPath = Join-Path $tempPath $fileName

 

if (!(Test-Path $tempSolPath))

{

                WriteLog "Error: Failed to copy WSP file to temp location."

                exitScript

}

 

#Изменение расширения WSP на CAB с целью извлечения из CAB-файла

$cabFileName = [System.IO.Path]::GetFileNameWithoutExtension($fileName) + ".cab"

 

Rename-Item $tempSolPath $cabFileName

$cabPath = Join-Path $tempPath $cabFileName

 

$sourceWsp = $shell.NameSpace($cabPath).items()

$tempFolder.CopyHere($sourceWsp)

$manifestPath = Join-Path $tempPath "manifest.xml"

[xml]$manifest = Get-Content $manifestPath

$targetSolutionId = $manifest.Solution.SolutionId

 

#Проверка GUID решения

[Guid]$testGuid = "B80D56EC-5899-459d-83B4-1AE0BB8418E4"

if (($targetSolutionId -eq $null) -or ($targetSolutionId.Length -lt 36) -or

    ([System.ComponentModel.TypeDescriptor]::GetConverter($testGuid).ConvertFromString($targetSolutionId) -eq $null))

{

                WriteLog("Error: Target solution ID is invalid: " + $stringSolutionId)

                exitScript

}

 

WriteLog("Extracted solution ID: $targetSolutionId from manifest.xml")

WriteLog("")

 

#Удаление временной папки.

Remove-Item $tempPath\*

Remove-Item $tempPath

 

#Просмотр баз данных контента

WriteLog ("Looking for Solution Id: " + $targetSolutionId + " in all Content Databases`n")

$dbs = Get-SPContentDatabase

 

foreach ($contentdb in $dbs)

{

                #Уровень веб-приложения

                $webAppUrl = $contentdb.WebApplication.Url

 

                #Получение веб-приложения

                $webApp = Get-SPWebApplication -Identity $webAppUrl

 

                $policy = $webApp.Policies[$admin]

                $policyAdded = $false

                $roleAdded = $false

 

                #Если у администратора нет полного доступа, он предоставляется следующим образом:

                if ($policy -eq $null)

                {

                                $policy = $webApp.Policies.Add($admin, "") 

                                $webAppModified = $true

                                $policyAdded = $true

                                WriteLog "Added a policy entry for user '$admin'."

                }

 

                $fullRole = $webApp.PolicyRoles.GetSpecialRole([Microsoft.SharePoint.Administration.SPPolicyRoleType]::FullControl)

                if ($policy.PolicyRoleBindings[$fullRole] -eq $null)

                {

                                $policy.PolicyRoleBindings.Add($fullRole) 

                                $webAppModified = $true

                                $roleAdded = $true

                                WriteLog "Full Control added for '$admin' to Web Application '$webAppUrl'"

                }

 

                if ($webAppModified)

                {

                                $webApp.Update()

                                $webAppModified = $false

                }

 

                #Готово. Имеется полный доступ

                WriteLog ("Entering Web Application '$webAppUrl'.`n")

                Get-SPSite -WebApplication $webApp -Limit ALL | % {

                                $site = $_

                                $solution = $null

                                $foundSolution = $false

 

                                #Просмотр решения

                                Get-SPUserSolution -Site $_ | Where { $_.Status -eq [Microsoft.Sharepoint.SPUserSolutionStatus]::Activated -and $_.SolutionId -eq $targetSolutionId } | % {

                                                if ($foundSolution -eq $false)

                                                {

                                                                $foundSolution = $true

                                                                $solution = $_

 

                                                                if ($haveCurrentFeatures -eq $false)

                                                                {                             

                                                                                Get-SPFeature -Sandboxed -Site $site | Where { $_.SolutionId -eq $targetSolutionId } | % {$oldFeatures.Add($_.Id, $_)}

                                                                                $haveCurrentFeatures = $true

                                                                }

 

                                                                if ($upgrade -eq $false)

                                                                {             

                                                                                $solSites.Add($site.Url)  | Out-Null         

                                                                }

                                                                $solutionHash = $_.Signature

                                                                WriteLog ("Found site collection: " + $site.Url)

                                                }

                                }

                                $numSites ++

                               

                                #Проведение обновления

                                if ($upgrade -and $foundSolution)

                                {

                                                $successAdd = $false

                                                WriteLog ("Uploading new solution file as: $fileName")

 

                                                #Добавление и обновление решения

                                                $myError = $null

                                                Add-SPUserSolution -LiteralPath $solFile -Site $site -ErrorAction:SilentlyContinue -ErrorVariable myError -Confirm:$false

                                                if ($myError -ne $null)

                                                {

                                                                WriteLog("Site collection '" + $site.Url + "' FAILED to upload the new solution.")

                                                                WriteLog("Error: " + $myError)

                                                                $myError = $null

                                                }

                                                else

                                                {

                                                                $successAdd = $true

                                                }

                                               

                                                $addedSolution = Get-SPUserSolution -Identity $fileName -Site $site

                                                if ($addedSolution -ne $null)

                                                {

                                                                WriteLog ("Found solution $fileName in the Solutions Gallery. Attempting to use it...")

 

                                                                #Первая проверка уже обновленного решения

                                                                if ($addedSolution.Signature -eq $solutionHash) 

                                                                {

                                                                                #Это означает, что установлена такая же версия. Пропустите ее. И удалите нашу копию!

                                                                                WriteLog ("New solution is already active on this site collection.")

               

                                                                                if ($successAdd -eq $true)

                                                                                {

                                                                                                #Удаление нового решения (дубликат)

                                                                                                WriteLog ("Removing file: $fileName")

                                                                                                Remove-SPUserSolution -Identity $addedSolution -Site $site -Confirm:$false

                                                                                                if (!($solFailSites.Contains($site.Url)))

                                                                                                {

                                                                                                                $solFailSites.Add($site.Url) | Out-Null

                                                                                                }

                                                                                }

                                                                }

                                                                else

                                                                {

                                                                                #Проведение обновления

                                                                                Update-SPUserSolution -Identity $solution -Site $site -ToSolution $addedSolution -ErrorAction:SilentlyContinue -ErrorVariable myError -Confirm:$false

                                                                                if ($myError -ne $null)

                                                                                {

                                                                                                WriteLog("Site collection '" + $site.Url + "' FAILED to upgrade to the new solution.")

                                                                                                WriteLog("Error: " + $myError)

                                                                                                if (!($solFailSites.Contains($site.Url)))

                                                                                                {

                                                                                                                $solFailSites.Add($site.Url) | Out-Null

                                                                                                }

                                                                                                $myError = $null

                                                                                }

 

                                                                                if (!($solFailSites.Contains($site.Url)))

                                                                                {

                                                                                                #Обновление успешно завершено

                                                                                                WriteLog("Site collection '" + $site.Url + "' has been upgraded to the new solution.")

                                                                                                WriteLog ""

                                                                                                $solSites.Add($site.Url)  | Out-Null

 

                                                                                                #Запись результатов ПОСЛЕ обновления

                                                                                                if ($haveNewFeatures -eq $false)

                                                                                                {                             

                                                                                                                Get-SPFeature -Sandboxed -Site $site | Where { $_.SolutionId -eq $targetSolutionId } | %{$newFeatures.Add($_.Id, $_)}

                                                                                                                $haveNewFeatures = $true

                                                                                                }

                                                                                }

                                                                }

                                                }

                                                else

                                                {

                                                                if (!($solFailSites.Contains($site.Url)))

                                                                {

                                                                                $solFailSites.Add($site.Url) | Out-Null

                                                                }

                                                }

                                }

                                $site.Close();

                }             

 

                #Отзыв разрешений и закрытие веб-приложения

                if ($roleAdded)

                {

                                $policy.PolicyRoleBindings.RemoveById($fullRole.Id)

                                $webAppModified = $true

                        WriteLog "Removed Full Control for '$admin' from Web Application '$webAppUrl'"

                }

                if ($policyAdded)

                {

                                $webApp.Policies.Remove($admin)

                                $webAppModified = $true

                                WriteLog "Removed the policy entry for user '$admin'."

                }

                if ($webAppModified)

                {

                                $webApp.Update()

                                $webAppModified = $false

                }

                WriteLog ""

                WriteLog "Done with Web Application."

                WriteLog ""

}

 

#Окончательный список семейств сайтов

WriteLog("Analysis of site collection upgrades for solution ID $targetSolutionId ...")

WriteLog("We have processed a total of $numSites site collections.")

WriteLog("")

 

if ($upgrade)

{

                #Сводка по семействам сайтов

                if ($solSites.Count -eq 0)

                {

                                WriteLog("No site collections were upgraded. Refer to this log for any upgrade errors.")

                }

                else

                {

                                WriteLog("Listing site collections that have been upgraded:")

                                foreach ($siteUrl in $solSites)

                                {

                                                WriteLog($siteUrl)

                                }

                }

 

                if ($solFailSites.Count -ne 0)

                {

                                WriteLog("Listing site collections that FAILED to upgrade:")

                                foreach ($siteUrl in $solFailSites)

                                {

                                                WriteLog($siteUrl)

                                }

                }

 

                #Сводка по обновлению компонентов

                if ($newFeatures.Count -gt 0)

                {

                                WriteLog ""

                                WriteLog "Feature upgrade summary for the New User Solution:"

                                foreach($fKey in $newFeatures.Keys)  

                                {

                                                $fDef = $oldFeatures[$fKey]

                                                $fDef2 = $newFeatures[$fKey]

                               

                                                #Идентификатор и отображаемое имя компонента

                                                WriteLog("Feature ID: " + $fDef2.Id + "`t DisplayName: " + $fDef2.DisplayName)

 

                                                #Версия компонента

                                                if ($fDef -eq $null)

                                                {

                                                                #Новый компонент добавлен

                                                                WriteLog("This feature has been newly added.`t`t`t Version " + $fDef2.Version)

                                                }

                                                else

                                                {

                                                                if ($fDef.Version -eq $fDef2.Version)

                                                                {

                                                                                WriteLog("This feature has been unchanged.`t`t`t Version " + $fDef2.Version)

                                                                }

                                                                else

                                                                {

                                                                                WriteLog("Feature went from Version " + $fDef.Version + " to Version " + $fDef2.Version)

                                                                }

                                                }                             

                               

                                                WriteLog ""

                                }

                                WriteLog ("The new solution holds a total of " + $newFeatures.Count + " feature(s).")

                }

}

else

{

                if ($solSites.Count -eq 0)

                {

                                WriteLog("No site collections have been found.")

                }

                else

                {

                                WriteLog("We have found " + $solSites.Count + " site collections, as follows (no upgrade action performed):")

                                foreach ($siteUrl in $solSites)

                                {

                                                WriteLog($siteUrl)

                                }

                }

 

                WriteLog ""

                WriteLog "Feature summary for current User Solution:"

                foreach($fKey in $oldFeatures.Keys)

                {

                                $fDef = $oldFeatures[$fKey]

                                WriteLog ("Feature DisplayName: `t" + $fDef.DisplayName)

                                WriteLog ("Feature Version: `t" + $fDef.Version)

                                WriteLog ("Feature Id: `t`t" + $fDef.Id)

                }

 

                WriteLog ""

                WriteLog ("Found a total of " + $oldFeatures.Count + " feature(s).")

                WriteLog ""

}

WriteLog ""

 

#Закрытие дескриптора файла.

if ($f -ne $null)

{

                $f.Close()           

}

 

Дата публикации: 15.08.2010 23:30

Это локализованная запись блога. Оригинальная статья находится по адресу Managing Upgrades on Sandbox Solutions