Gestion des mises à niveau des solutions en bac à sable
Gestion des mises à niveau des solutions en bac à sable
Corps :
Les solutions en bac à sable constituent une fonctionnalité formidable sur la plateforme SharePoint 2010. Toutefois, ces solutions étant déployées à différents emplacements dans votre batterie de serveurs ou dans votre collection de sites, se pose la question de savoir comment les gérer.
Nous fournissons un script PowerShell qui vous aide dans cette tâche. Il est assez simple, mais peut être étendu en fonction des outils que vous utilisez déjà pour gérer vos batteries de serveurs et vos collections de sites.
Permettez-moi de décrire ce que fait ce script :
À chaque solution en bac à sable déployée est associé un ID de solution. Cette solution comporte une ou plusieurs fonctionnalités dotées d’un ID de fonctionnalité et d’un numéro de version. Pour un ID de solution donné, ce script passe en revue la totalité de votre batterie de serveurs ou de votre collection de sites et génère un journal vous indiquant tous les emplacements auxquels se trouve cette solution. Ensuite, il examine chaque fonctionnalité dans la solution et ajoute une entrée de journal indiquant la version déployée de cette fonctionnalité. En outre, il peut effectuer la mise à niveau si une nouvelle version de la solution est fournie. Il s’agit d’un indicateur afin que le script puisse effectuer la mise à niveau ou être simplement utilisé pour la collecte d’informations sur la solution.
Le script prend les paramètres suivants :
· Chemin d’accès et nom de la solution (fichier .wsp) à mettre à niveau
· Paramètre de commutateur indiquant si la mise à niveau doit être réalisée
Autorisations :
· Le script suppose que la personne qui l’exécute est l’administrateur local et qu’elle est autorisée à effectuer les actions de mise à niveau.
· Les niveaux d’autorisation sont modifiés afin que l’administrateur bénéficie d’un contrôle total.
· Au terme de la mise à niveau, les paramètres d’origine des autorisations sont restaurés.
Le format du journal est le suivant :
· Détails sur le chemin d’accès et sur le nom de la solution en cours d’examen, notamment l’ID de solution.
· Détails sur chaque collection de sites examinée.
· Pour chaque collection de sites
o Informations indiquant si une solution correspondante a été trouvée.
o Si tel est le cas, détails sur le fichier de solution en cours de suppression afin qu’il puisse être remplacé par la nouvelle version.
o Si la mise à niveau est réalisée, détails sur l’issue de l’opération (réussite ou échec).
· Un récapitulatif comprenant les informations suivantes :
o Nombre de collections de sites examinées.
o Si la mise à niveau n’est pas réalisée, une liste récapitulative des sites nécessitant une mise à niveau, ainsi que des détails sur la version actuelle de chaque fonctionnalité (ID) dans la solution.
o Si la mise à niveau est réalisée, les informations sur la fonctionnalité et la version, accompagnées des détails relatifs à la réussite de la mise à niveau, à la réussite de la nouvelle installation ou à la non-modification de la fonctionnalité du fait qu’il s’agit déjà d’une fonctionnalité actuelle.
Voici quelques éléments à prendre en compte :
La plateforme SharePoint 2010 ne prend en charge la mise à niveau d’une solution que si les noms de fichier de solution sont différents. S’ils sont identiques, vous obtenez une erreur indiquant que la solution est déjà active. Lorsque vous mettez à niveau une solution, l’ID de solution est utilisé pour la recherche des versions existantes de cette solution et, ensuite, pour l’examen des détails de la version. La mise à niveau constitue uniquement à passer à une nouvelle version. Elle ne rétablit pas une ancienne version. En outre, si les versions sont identiques, elle ne réalise aucune opération.
Nous espérons que ce script vous aide à gérer les solutions que vous déployez et qu’il facilite sensiblement les mises à niveau.
# SharePoint DLL
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
#Cmdlet Install
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
#Log file definition
$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
#Assume that the current "Domain\User" is the box admin entitled to perform the reconaissance and upgrade actions.
$admin = $env:UserDomain + "\" + $env:UserName
if ($env:UserDomain -eq $null)
{
$admin = $admin.substring(1)
}
# Get the script's parent folder. This is where the log file will be written.
$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
}
# Add the log file name to the path.
$logFileFullPath = Join-Path $logFolder "usersolutionupgrader-$date.txt"
# Open file
$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)
}
#Confirm log file on screen
Write-Host ("Log Path: " + $logFileFullPath)
#Write intro line
$f.WriteLine()
$f.WriteLine($intro)
function WriteLog
{
$dt = Get-Date
$logMsg = $dt.ToShortDateString() + " " + $dt.ToShortTimeString() + "`t" + $args[0]
$f.WriteLine($logMsg)
Write-Output($logMsg) #Turn this on when you need console output
}
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
#Parse the commandline parameters
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
}
#Validate parameters
$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
}
#Confirm parameter values captured
$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 ""
}
#Get solution ID from given 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)
#Take the file name ("yourfile.wsp") from the original path
$tempSolPath = Join-Path $tempPath $fileName
if (!(Test-Path $tempSolPath))
{
WriteLog "Error: Failed to copy WSP file to temp location."
exitScript
}
#Rename the WSP file to have CAB extension in order to facilitate CAB extraction
$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
#Validate Solution 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("")
#Now delete temp folder.
Remove-Item $tempPath\*
Remove-Item $tempPath
#Go through Content DBs
WriteLog ("Looking for Solution Id: " + $targetSolutionId + " in all Content Databases`n")
$dbs = Get-SPContentDatabase
foreach ($contentdb in $dbs)
{
#Web App Level
$webAppUrl = $contentdb.WebApplication.Url
#Get WebApp
$webApp = Get-SPWebApplication -Identity $webAppUrl
$policy = $webApp.Policies[$admin]
$policyAdded = $false
$roleAdded = $false
#If the admin doesn't have Full Control, it will be granted as follows:
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
}
#Done. Have Full Control
WriteLog ("Entering Web Application '$webAppUrl'.`n")
Get-SPSite -WebApplication $webApp -Limit ALL | % {
$site = $_
$solution = $null
$foundSolution = $false
#Scan for solution here
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 ++
#DoUpgrade here
if ($upgrade -and $foundSolution)
{
$successAdd = $false
WriteLog ("Uploading new solution file as: $fileName")
#Add + Upgrade solution
$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...")
#First check for already updated solution
if ($addedSolution.Signature -eq $solutionHash)
{
#This means we have the same version installed. Just skip it. And delete our copy!
WriteLog ("New solution is already active on this site collection.")
if ($successAdd -eq $true)
{
#Remove the new solution (the dupe)
WriteLog ("Removing file: $fileName")
Remove-SPUserSolution -Identity $addedSolution -Site $site -Confirm:$false
if (!($solFailSites.Contains($site.Url)))
{
$solFailSites.Add($site.Url) | Out-Null
}
}
}
else
{
#Perform upgrade
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)))
{
#Upgrade succeeded
WriteLog("Site collection '" + $site.Url + "' has been upgraded to the new solution.")
WriteLog ""
$solSites.Add($site.Url) | Out-Null
#Record results AFTER upgrade
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();
}
#Close permissions and webApp
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 ""
}
#Final tally of site collections
WriteLog("Analysis of site collection upgrades for solution ID $targetSolutionId ...")
WriteLog("We have processed a total of $numSites site collections.")
WriteLog("")
if ($upgrade)
{
#Site Collection Summary
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)
}
}
#Feature Upgrade Summary
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]
#Feature ID and DisplayName
WriteLog("Feature ID: " + $fDef2.Id + "`t DisplayName: " + $fDef2.DisplayName)
#Feature Version
if ($fDef -eq $null)
{
#New feature added.
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 ""
#Close the file handle.
if ($f -ne $null)
{
$f.Close()
}
Date de publication : 15/8/2010 23:30
Ce billet de blog a été traduit de l’anglais. L’article d’origine est disponible à la page Managing Upgrades on Sandbox Solutions.