Know your content - Part 2
In one of my previous posts I was showing a script to analyze your SharePoint content. This script came from a customer engagement where I was able to run the script on the SharePoint server. Currently I am on an engagement where I am facing the same requirements but will not have any access to the server.
I was wondering on how to get the same metrics without having server access and the only solution that came into my mind was using the SharePoint web services. Mainly I will use the following two web services:
- https://msdn.microsoft.com/en-us/library/webs.webs(v=office.12).aspx - Methods for working with sites and subsites
- https://msdn.microsoft.com/en-us/library/lists.lists(v=office.12).aspx - Methods for working with lists and items
With these two web services I am able to get information on my content from the client side assuming that I have access to the content. At the end of this posting you will find the entire PowerShell code to get the content information.
The code will create a .csv-file that you can then analyze with Excel. The following screenshot will show you an example of such an analysis:
PowerShell code:
The infile for this code is simply a notepad file that contains the URLs that should be analyzed. The following screenshot will show you an example:
# THIS SCRIPT WILL:
# ------------------------------------------
# This script will analyze a given list of SharePoint sites regarding their content.
# Especially the lists and libraries will analyzed to get an impression on how many information
# is stored to the site and how complex the structure of the site is
clear-host
# activate/deactivate debug mode
$DEBUG = $false
# URLs to be analyzed will be fetched from this file
$inFile = "infile.txt"
# Parameters for this script
$noiseLists = @("Content and Structure Reports", "Form Templates", "Reusable Content", "Style Library", "Workflow Tasks")
$fileInfo = "AnalysisResults.csv"
$errorFile = "error.txt"
# *************************************
# S C R I P T L O G I C
# Do not edit anything from now on
# *************************************
# for speeding up execution time we keep a list with all processed URLs not not analyze them multiple times
$global:processedURLs = @()
# clean the error variable
$error.clear()
# *************************************
# F U N C T I O N S
# *************************************
function AnalyzeWeb {
param ($siteUrl, $webURL)
# initialize variable that will capture our needed data
$numLists = 0
$numLibs = 0
$numItems = 0
$numFolders = 0
Write-Host ("Analyzing web with URL: {0}" -f $webURL)
# set the URI of the webservice
$uriLists = ("{0}/_vti_bin/Lists.asmx?WSDL" -f $webURL)
# create the web service handles
$wsLists = New-WebServiceProxy -Uri $uriLists -Namespace SpWs -UseDefaultCredential
# call the needed web service method
$xmlListCollectionResult = $wsLists.GetListCollection()
foreach ($oneList in $xmlListCollectionResult.List) {
if ($oneList.Hidden -eq $false) {
if (!($noiseLists -contains $oneList.Title) ) {
$xmlListResult = $wsLists.GetList($oneList.Title)
Write-Host (" List '{0}' ({1}, {2}) has '{3}' items." -f $xmlListResult.Title, $xmlListResult.ID, $xmlListResult.ServerTemplate, $xmlListResult.ItemCount)
# capture data about the list/library
if ($xmlListResult.ServerTemplate -eq "101") {
$numLibs++
} else {
$numLists++
} # if..else
# Analyze the library / list content
$x = new-object "System.Xml.XmlDocument"
$q = $x.CreateElement("Query")
$v = $x.CreateElement("ViewFields")
$qo = $x.CreateElement("QueryOptions")
$v = $x.CreateElement("ViewFields")
$q.set_InnerXml(“”)
$qo.set_InnerXml("<Folder></Folder>")
# clear capture data
$numFolders = 0
$numItems = 0
$maxFolderDepthLocal = 0
# Call the web service. Details about parameterization: https://msdn.microsoft.com/en-us/library/lists.lists.getlistitems(v=office.12).aspx
$xmlListItemResult = $wsLists.GetListItems($xmlListResult.ID, $null, $q, $v, "5000", $qo, $null)
foreach ($oneListItem in $xmlListItemResult.data.row) {
if ($oneListItem.ows_FSObjType -ne $null) {
if ($DEBUG) { Write-Host (" ID: '{0}', Title: '{1}', FSObjType: '{2}'" -f $oneListItem.ows_ID, $oneListItem.ows_Title, $oneListItem.ows_FSObjType) }
$temp = $oneListItem.ows_FSObjType.Split(";#")
if ($temp[2] -eq "1") {
if ($DEBUG) { Write-Host ("*-*-* FOUND the folder '{0}' *-*-*" -f $oneListItem.ows_Title) }
# calculate folder depth
if ($xmlListResult.ServerTemplate -eq "101") {
# $tempDepth = $oneListItem.ows_ServerUrl.length - ($oneListItem.ows_ServerUrl.Replace("/","")).Length - 3
$tempDepth = $oneListItem.ows_EncodedAbsUrl.replace($webUrl,"").length - $oneListItem.ows_EncodedAbsUrl.replace($webUrl,"").replace("/","").length - 1
if ($maxFolderDepthLocal -lt $tempDepth) {
$maxFolderDepthLocal = $tempDepth
if ($DEBUG) { Write-Host (" Setting new local folder depth: {0}" -f $maxFolderDepthLocal) }
} # if
$numFolders++
} # if ServerTemplate -eq 101
} # if
} # if
if ($oneListItem.ows_DocIcon -ne $null) {
if ($DEBUG) { Write-Host " ======> Found a document <====== " }
$numItems++
} # if..else
} # foreach $oneListItem
# dump info to lib log file
# ("BaseURL`tWebURL`tType`tTitle`tItems`tDocuments`tFolders`tFolderDepth") | Out-File $fileInfo
("{0}`t{1}`t{2}`t{3}`t{4}`t{5}`t{6}`t{7}" -f $siteUrl, $webURL, $xmlListResult.ServerTemplate, ("{0} ({1})" -f $xmlListResult.Title, $xmlListResult.ServerTemplate), $xmlListResult.ItemCount,$numItems, $numFolders, $maxFolderDepthLocal) | Out-File $fileInfo -append
} # if
} # if
} # foreach $oneList
} # function AnalyzeWeb
function startAnalysis {
param ($url)
# clear the URL
$url = $url.Replace("/default.aspx", "")
# set the URI of the webservice
$uri = ("{0}/_vti_bin/Webs.asmx?WSDL" -f $url)
# create the service handle
$wsWebCollection = New-WebServiceProxy -Uri $uri -Namespace SpWs -UseDefaultCredential
# check if an error occured an log the info to the error file
if ($error.count -ne 0) {
("Error with URL: '{0}'" -f $uri) | Out-File $errorFile -append
$error.clear()
}
# call the needed web service method
$xmlWebCollectionResult = $null
$xmlWebCollectionResult = $wsWebCollection.GetAllSubWebCollection()
if ($xmlWebCollectionResult -ne $null) {
# analyze the sites within site collection
foreach($oneWeb in $xmlWebCollectionResult.Web) {
if ($global:processedURLs -match $oneWeb.Url) {
if ($DEBUG) { Write-Host ("Skipping URL '{0}' since it has already been processed" -f $oneWeb.Url) }
} else {
if ($DEBUG) { Write-Host ("Adding URL '{0}' to the processed list" -f $oneWeb.Url) }
$global:processedURLs += $oneWeb.Url
# run analysis
AnalyzeWeb $url $oneWeb.Url
} # if..else
} # foreach
} else {
Write-Host ("Cannot access site at URL: {0}" -f $url) -foregroundcolor red
} # if..else
} # function startAnalysis
# *************************************
# M A I N
# *************************************
# Create the lib output file
$timeStamp = (Get-Date -format "yyyyMMdd_HHmmss")
$fileInfo = $fileInfo.Replace(".csv", ("_{0}_{1}.csv" -f $timeStamp, $inFile.Replace(".txt","")))
$errorFile = $errorFile.Replace(".txt", ("_{0}_{1}.txt" -f $timeStamp, $inFile.Replace(".txt","")))
if ($DEBUG) {
Write-Host ("fileInfo = {0}" -f $fileInfo)
}
# Create the header information of log files
("BaseURL`tWebURL`tType`tTitle`tItems`tDocuments`tFolders`tFolderDepth") | Out-File $fileInfo
measure-command {
$i = 1
$inFileContent = Get-Content $inFile
foreach ($oneUrl in $inFileContent) {
Write-Host ("[{0}/{1}] {2}" -f $i, $inFileContent.Length, $oneUrl) -backgroundcolor blue -foregroundcolor yellow
startAnalysis $oneUrl
$i++
} # foreach
} # measure-command
Write-Host "Finished."