Sometimes you wish there was an easier way to identify all custom columns, content types and lists of your SharePoint sites. Although you have defined a few best practices before you started the project – e.g. “Don’t apply any changes to the out-of-the-box columns and content types”, “Collect all your custom columns in the same group” and “Don’t use any spaces or special characters when creating a new column” – some columns and content types fall between the cracks.

Sometimes you are looking for a quick way to get a good overview of the site architecture of your SharePoint sites to add to your project documentation and you don’t feel like listing all the columns and content types manually in an Excel-file.

o365_ps_featured

Sometimes you need PowerShell to export
the site architecture of your SharePoint sites!

That’s why I created the PowerShell script below to quickly export all columns, content types, lists and list views to CSV-files. After running the PowerShell script, open the CSV-files in MS Excel, create a table and apply some filters to easily search through your SharePoint site architecture. The PowerShell script uses the SharePoint client-side object model to iterate through all SharePoint sites in your site collection recursively.

To download the PowerShell script, click here.

If you are a SharePoint developer and you are looking for an easy way to explore the properties of a column, content type, list, site property bag, user profile or whatever, then try SharePoint Manager for SharePoint on-premise environments or SharePoint Client Browser for Office 365 environments.

# LOAD CSOM LIBRARIES
Write-Host "Load CSOM libraries" -foregroundcolor black -backgroundcolor yellow
Add-Type -Path "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.SharePoint.Client\v4.0_16.0.0.0__71e9bce111e9429c\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.SharePoint.Client.Runtime\v4.0_16.0.0.0__71e9bce111e9429c\Microsoft.SharePoint.Client.Runtime.dll"
Add-Type -Path "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.SharePoint.Client.Taxonomy\v4.0_16.0.0.0__71e9bce111e9429c\Microsoft.SharePoint.Client.Taxonomy.dll"
 
# SHAREPOINT PARAMS
$url = "https://<<yourtenant>>.sharepoint.com"
$username = "<<youruser>>@<<yourtenant>>.onmicrosoft.com"
$password = "<<yourpassword>>"
$password = $password | ConvertTo-SecureString -AsPlainText -force
 
# SHAREPOINT CONTEXT
$context = New-Object Microsoft.SharePoint.Client.ClientContext($url)
 
# AUTHENTICATE SHAREPOINT CONTEXT
try
{
    Write-Host "Authenticate to SharePoint Online site collection '$url' and get ClientContext object" -foregroundcolor black -backgroundcolor yellow
    $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password)
    $context.Credentials = $credentials
    $context.ExecuteQuery()
    Write-Host "Authentication succeeded!" -foregroundcolor black -backgroundcolor green
}
catch
{
    Write-Host "Authentication failed!" -foregroundcolor black -backgroundcolor red
    throw
}
 
# EXPORT SITE ARCHITECTURE VARIABLES
[System.Collections.ArrayList] $exportedColumns = @()
[System.Collections.ArrayList] $exportedContentTypes = @()
[System.Collections.ArrayList] $exportedLists = @()
[System.Collections.ArrayList] $exportedListViews = @()
[System.Collections.ArrayList] $exportedTerms = @()
 
# EXPORT SITE ARCHITECTURE FUNCTIONS
function ExportIA([Microsoft.SharePoint.Client.Web] $web)
{
    ExportColumns $web
    ExportContentTypes $web
    ExportLists $web
 
    $subWebs = $web.Webs
    $context.Load($subWebs)
    $context.ExecuteQuery()
 
    foreach ($subWeb in $subWebs)
    {
        ExportIA $subWeb
    }
}
 
function ExportColumns([Microsoft.SharePoint.Client.Web] $web)
{
    $fields = $web.Fields
    $context.Load($fields)
    $context.ExecuteQuery()
 
    foreach ($field in $fields)
    {
        $exportedColumn = New-Object -TypeName PSObject -Property @{
            Site = $web.Title
            SiteUrl = $web.Url
            ColumnGroup = $field.Group
            ColumnTitle = $field.Title
            ColumnInternalName = $field.InternalName
            ColumnDescription = $field.Description
            ColumnType = $listField.TypeDisplayName
            ColumnRequired = $field.Required
            ColumnHidden = $field.Hidden
        } | Select-Object Site,SiteUrl,ColumnGroup,ColumnTitle,ColumnDescription,ColumnInternalName,ColumnType,ColumnRequired,ColumnHidden
 
        $exportedColumns.Add($exportedColumn) > $null
    }
}
 
function ExportContentTypes([Microsoft.SharePoint.Client.Web] $web)
{
    $contenttypes = $web.ContentTypes
    $context.Load($contenttypes)
    $context.ExecuteQuery()
 
    foreach ($contenttype in $contenttypes)
    {
        $contenttypeFields = $contenttype.Fields
        $context.Load($contenttypeFields)
        $context.ExecuteQuery()
 
        foreach ($contenttypeField in $contenttypeFields)
        {
            $exportedContentType = New-Object -TypeName PSObject -Property @{
                Site = $web.Title
                SiteUrl = $web.Url
                ContentTypeGroup = $contenttype.Group
                ContentTypeID = $contenttype.Id
                ContentTypeName = $contenttype.Name
                ContentTypeDescription = $contenttype.Description
                ColumnTitle = $contenttypeField.Title
                ColumnInternalName = $contenttypeField.InternalName
                ColumnType = $listField.TypeDisplayName
                ColumnRequired = $contenttypeField.Required
                ColumnHidden = $contenttypeField.Hidden
            } | Select-Object Site,SiteUrl,ContentTypeGroup,ContentTypeID,ContentTypeName,ContentTypeDescription,ColumnTitle,ColumnInternalName,ColumnType,ColumnRequired,ColumnHidden
 
            $exportedContentTypes.Add($exportedContentType) > $null
        }
    }
}
 
function ExportLists([Microsoft.SharePoint.Client.Web] $web)
{
    $lists = $web.Lists
    $context.Load($lists)
    $context.ExecuteQuery()
 
    foreach ($list in $lists)
    {
        $listRootFolder = $list.RootFolder
        $context.Load($listRootFolder)
        $listViews = $list.Views
        $context.Load($listViews)
        $listContentTypes = $list.ContentTypes
        if ($list.AllowContentTypes)
        {
            $context.Load($listContentTypes)
        }
        $context.ExecuteQuery()
 
        if ($list.AllowContentTypes)
        {
            foreach ($listContentType in $listContentTypes)
            {
                $contenttypeFields = $listContentType.Fields
                $context.Load($contenttypeFields)
                $context.ExecuteQuery()
 
                foreach ($contenttypeField in $contenttypeFields)
                {
                    $exportedList = New-Object -TypeName PSObject -Property @{
                        Site = $web.Title
                        SiteUrl = $web.Url
                        ListTitle = $list.Title
                        ListUrl = $listRootFolder.ServerRelativeUrl
                        ListType = $list.BaseType
                        ListTemplate = $list.BaseTemplate
                        ListContentType = $listContentType.Name
                        ListContentTypeID = $listContentType.Id
                        ColumnTitle = $contenttypeField.Title
                        ColumnInternalName = $contenttypeField.InternalName
                        ColumnType = $listField.TypeDisplayName
                        ColumnRequired = $contenttypeField.Required
                        ColumnHidden = $contenttypeField.Hidden
                    } | Select-Object Site,SiteUrl,ListTitle,ListUrl,ListType,ListTemplate,ListContentType,ListContentTypeID,ColumnTitle,ColumnInternalName,ColumnType,ColumnRequired,ColumnHidden
 
                    $exportedLists.Add($exportedList) > $null
                }
            }
        }
        else
        {
            $listFields = $list.Fields
            $context.Load($listFields)
            $context.ExecuteQuery()
 
            foreach ($listField in $listFields)
            {
                $exportedList = New-Object -TypeName PSObject -Property @{
                    Site = $web.Title
                    SiteUrl = $web.Url
                    ListTitle = $list.Title
                    ListUrl = $listRootFolder.ServerRelativeUrl
                    ListType = $list.BaseType
                    ListTemplate = $list.BaseTemplate
                    ColumnTitle = $listField.Title
                    ColumnInternalName = $listField.InternalName
                    ColumnType = $listField.TypeDisplayName
                    ColumnRequired = $listField.Required
                    ColumnHidden = $listField.Hidden
                } | Select-Object Site,SiteUrl,ListTitle,ListUrl,ListType,ListTemplate,ColumnTitle,ColumnInternalName,ColumnType,ColumnRequired,ColumnHidden
 
                $exportedLists.Add($exportedList) > $null
            }
        }
 
        foreach ($listView in $listViews)
        {
            if (-not $listView.PersonalView)
            {
                $viewFields = $listView.ViewFields
                $context.Load($viewFields)
                $context.ExecuteQuery()
 
                [System.Collections.ArrayList] $viewFieldArray = @();
                foreach ($viewField in $viewFields)
                {
                    $viewFieldArray.Add($viewField) > $null
                }
 
                $exportedListView = New-Object -TypeName PSObject -Property @{
                    Site = $web.Title
                    SiteUrl = $web.Url
                    ListTitle = $list.Title
                    ListUrl = $listRootFolder.ServerRelativeUrl
                    ListType = $list.BaseType
                    ListTemplate = $list.BaseTemplate
                    ListView = $listView.Title
                    ListViewType = $listView.ViewType
                    ListViewQuery = $listView.ViewQuery
                    ColumnInternalNames = [System.String]::Join(",", $viewFieldArray.ToArray())
                } | Select-Object Site,SiteUrl,ListTitle,ListUrl,ListType,ListTemplate,ListView,ListViewType,ListViewQuery,ColumnInternalNames
 
                $exportedListViews.Add($exportedListView) > $null
            }
        }
    }
}

# EXPORT TAXONOMY FUNCTIONS
function ExportTaxonomy()
{
    $taxonomySession = [Microsoft.SharePoint.Client.Taxonomy.TaxonomySession]::GetTaxonomySession($context)
    $context.Load($taxonomySession)
    $context.ExecuteQuery()

    $termStores = $taxonomySession.TermStores
    $context.Load($termStores)
    $context.ExecuteQuery()

    foreach ($termStore in $termStores)
    {
        $termGroups = $termStore.Groups
        $context.Load($termGroups)
        $context.ExecuteQuery()

        foreach ($termGroup in $termGroups)
        {
            $termSets = $termGroup.TermSets
            $context.Load($termSets)
            $context.ExecuteQuery()

            foreach($termSet in $termSets)
            {
                $terms = $termSet.Terms
                $context.Load($terms)
                $context.ExecuteQuery()

                ExportTerms $termGroup $termSet $terms
            }
        }
    }
}

function ExportTerms([Microsoft.SharePoint.Client.Taxonomy.TermGroup] $termGroup, [Microsoft.SharePoint.Client.Taxonomy.TermSet] $termSet, [Microsoft.SharePoint.Client.Taxonomy.TermCollection] $terms)
{
    foreach ($term in $terms)
    {
        #$termGroupName = [Microsoft.SharePoint.Taxonomy.TaxonomyItem]::NormalizeName($termGroup.Name)
        #$termSetName = [Microsoft.SharePoint.Taxonomy.TaxonomyItem]::NormalizeName($termSet.Name)
        #$termName = [Microsoft.SharePoint.Taxonomy.TaxonomyItem]::NormalizeName($term.Name)
        #$termPath = [Microsoft.SharePoint.Taxonomy.TaxonomyItem]::NormalizeName($term.PathOfTerm)

        $exportedTerm = New-Object -TypeName PSObject -Property @{
            TermGroupName = $termGroup.Name
            TermGroupId = $termGroup.Id
            TermSetName = $termSet.Name
            TermSetId = $termSet.Id
            TermName = $term.Name
            TermPath = $term.PathOfTerm
            TermId = $term.Id
        } | Select-Object TermGroupName,TermGroupId,TermSetName,TermSetId,TermName,TermPath,TermId
 
        $exportedTerms.Add($exportedTerm) > $null

        $subTerms = $term.Terms
        $context.Load($subTerms)
        $context.ExecuteQuery()

        ExportTerms $termGroup $termSet $subTerms
    }
}
 
# EXPORT SITE ARCHITECTURE TO CSV
try
{
    Write-Host "Exporting site architecture" -foregroundcolor black -backgroundcolor yellow

    $web = $context.Web
    $context.Load($web)
    $context.ExecuteQuery()
 
    ExportIA $web

    Write-Host "Writing site architecture to CSV-files" -foregroundcolor black -backgroundcolor yellow
 
    $exportedColumns | Export-Csv sp_sitecolumns.csv -Delimiter ";" -NoTypeInformation
    $exportedContentTypes | Export-Csv sp_sitecontenttypes.csv -Delimiter ";" -NoTypeInformation
    $exportedLists | Export-Csv sp_listcontenttypes.csv -Delimiter ";" -NoTypeInformation
    $exportedListViews | Export-Csv sp_listviews.csv -Delimiter ";" -NoTypeInformation
    $exportedNavNodes | Export-Csv sp_navigation.csv -Delimiter ";" -NoTypeInformation
 
    Write-Host "Export site architecture succeeded!" -foregroundcolor black -backgroundcolor green

    Write-Host "Exporting managed metadata" -foregroundcolor black -backgroundcolor yellow

    ExportTaxonomy

    Write-Host "Writing managed metadata to CSV-files" -foregroundcolor black -backgroundcolor yellow
 
    $exportedTerms | Export-Csv sp_managedmetadata.csv -Delimiter ";" -NoTypeInformation

    Write-Host "Export managed metadata succeeded!" -foregroundcolor black -backgroundcolor green
}
catch
{
    Write-Host "Export failed!" -foregroundcolor black -backgroundcolor red
    throw
}

Maarten Ghijsens

Maarten Ghijsens

Teamlead Office 365 Apps @ Spikes

Advertisements