Discover where are your BindTuning products installed

In some instances you may want to verify where, on your tenant,  you have installed any BindTuning products, comprising both the Design (themes) and Build (web part) features. 

Being that accessing each site collection and verifying its correspondent associated solutions, can be a daunting process, BindTuning has devised a PowerShell script that will allow you to loop through all your pre-existing site collections automatically, listing the concrete number of BindTuning products installed on your environment. 

Note: This same script was devised to work with SharePoint Online tenants. 

Before we begin

Before running the PowerShell script below, note that some pre-requisites may be appliable:

  • Windows PowerShell v4.0 (or above)
  • SharePoint PnP cmdlets; 
  • Windows Management Framework.

If, however, the pre-requisites are not fulfilled, the provided script will prompt you to proceed with the corresponding modules installation and/or version upgrade. 

Likewise, and considering the script to access your SharePoint Administration Center, be sure to run it utilizing a SharePoint (Tenant) Admin account.

Running the script

After all the dependencies have been sorted out, proceed to copy the PowerShell script below, pasting it on a newly created file with the .ps1 extension, so as to create a file with the following naming convention myScriptName.ps1.


pushd (Split-Path -Path $MyInvocation.MyCommand.Definition -Parent)

$ErrorActionPreference = "Stop"
$pnpRequiredVersion = [Version]"3.25.2009.1"
$PnPBinFolder = "{0}\BTResources\" -f $env:temp
$today = [DateTime]::UtcNow
$reportsFolder = ".\Reports";

if(!(Test-Path $reportsFolder)) { New-Item -Path ".\Reports" -ItemType Directory | Out-Null }

$fileReportPath = Join-Path $reportsFolder ("TENANT APP CATALOG_{0:yyyyMMdd-HHmmss}.log" -f $today)

function Main(){


        #connecto to -admin
        $tenantName = Read-Host ("`nPlease enter the SharePoint Tenant name (https://[TENANT]")
        $loginUrl = ("https://{0}" -f $tenantName)        

        connectTo $loginUrl

        $webInfo = Get-PnPWeb
        $tenantApps = Get-PnPApp -Scope Tenant

            "***** Tenant App Catalog *****" | Out-File -FilePath $fileReportPath -Append -NoClobber
            $tenantApps.ForEach({[PSCustomObject]$_}) | Format-Table -AutoSize -Property Title, AppCatalogVersion | Out-File -FilePath $fileReportPath -Append -NoClobber

        [System.Collections.ArrayList]$allSitesList = Get-PnPTenantSite
        $allSites = New-Object System.Collections.ArrayList
		$allSitesList | Where-Object { $_.Url -match $tenantName } | %{
			$allSites.Add($_) | Out-Null
        Write-Host ("Found {0} sites" -f $allSites.Count)      

        foreach($site in $allSites){
			Write-Host ($site.Url)
            $fileReportPath = Join-Path $reportsFolder ("{0}_{1:yyyyMMdd-HHmmss}.log" -f ($site.Url -replace "https://","" -replace "/","_"), $today)	
			#connect on each site
            connectTo $site.Url
            try {
                $siteApps = Get-PnPApp -Scope Site
                Write-Host (" # {0} apps found" -f $siteApps.Count)

                    "-> Modern Apps <-" | Out-File -FilePath $fileReportPath -Append -NoClobber
                    $siteApps.ForEach({[PSCustomObject]$_}) | Format-Table -AutoSize -Property Title, AppCatalogVersion | Out-File -FilePath $fileReportPath -Append -NoClobber
            } catch {
                #no app catalog
				if($_.ErrorDetails.Message -match "Attempted to perform an unauthorized operation.") {
					Write-Host (" # Unauthorized - The user does not have permissions to the site collection {0}" -f $site.Url)
				} else {
					Write-Host (" # No App Catalog found")

            if(!(isModern $site.Template)) {
                # get solutions
                $siteSolutions = $null
                $listPath = "/_catalogs/solutions/"
                $fieldsArr = @("FileLeafRef","SolutionId","Status","Created","Modified","SolutionTitle")

                    $siteSolutions = Get-PNPListItem -List $listPath -fields $fieldsArr
                    Write-Host (" # {0} solutions found" -f $siteSolutions.Count)

                        "-> Classic Solutions <-" | Out-File -FilePath $fileReportPath -Append -NoClobber
                        $siteSolutions.FieldValues.FileLeafRef | Format-Table -AutoSize | Out-File -FilePath $fileReportPath -Append -NoClobber
                } catch { }
    } catch {
        errorMessage $_

    Write-Host "`nProcess completed."

function connectTo($loginUrl){
    Write-Host ("- Connecting to: {0} ... " -f $loginUrl) -NoNewline    

    #- Connect to site
        $wpref = $WarningPreference
        $WarningPreference = 'SilentlyContinue'        

        #check if was already connected

        if(isSharePointOnline $loginUrl){
            Connect-PnPOnline -Url $loginUrl -NoTelemetry -useWebLogin
            try{ $connection = Get-PnPConnection } catch {}
                $loginUsername = $connection.PSCredential.UserName
                $secpasswd = $connection.PSCredential.Password
                $userCreds = New-Object System.Management.Automation.PSCredential ($loginUsername, $secpasswd)

                Connect-PnPOnline -Url $loginUrl -Credentials $userCreds -NoTelemetry
            } else {
                Connect-PnPOnline -Url $loginUrl -Credentials (Get-Credential) -NoTelemetry

        $WarningPreference = $wpref

    } catch {
        errorMessage "Unable to connect."

    successMessage "Done"

function checkPnPCmdlets(){
    $showPNPMenu = $false
    #check powershell version
    $PSversion = $PSVersionTable.PSVersion

        errorMessage "`n* Unable to check installed PowerShell version (Requires Windows Management Framework v4.0 installed)."
        errorMessage ("`n* Use option 5 to download it and install.")
        $showPNPMenu = $true
    } elseif($PSVersion.Major -lt 4) { 
        errorMessage ("`n* The installed PowerShell version is lower than the requirement (required: 4 or above , installed: {0})." -f $PSVersion)
        errorMessage ("`n* Use option 5 to download it and update.")

        $showPNPMenu = $true

    #Check PnP Cmdlets
    if(!(Test-Path $PnPBinFolder)){ New-Item -Path $PnPBinFolder -Name "SharePointPnPPowerShellOnline" -ItemType "directory" | Out-Null }
    $PnPModule = ("{0}\SharePointPnPPowerShellOnline\{1}\SharePointPnPPowerShellOnline.psd1" -f $PnPBinFolder, $pnpRequiredVersion)

    if(Test-Path $PnPModule){
        Import-Module -Name $PnPModule -Force -Verbose:$false -DisableNameChecking -WarningAction SilentlyContinue

    $PNPCmdlets = Get-Module SharePointPnPPowerShellOnline

        errorMessage ("`n* SharePoint Patterns and Practices PowerShell Online (v$pnpRequiredVersion) Cmdlets not found to run this script.")
        errorMessage ("`n* Use option 1 to download them.")

        $showPNPMenu = $true
    } else {
        #check version
        if($PNPCmdlets[0].Version -lt $pnpRequiredVersion){
            errorMessage ("`n* SharePoint Patterns and Practices (PnP) Cmdlets installed are not at the required version to run this script.")
            Write-Host ("`n* Latest version installed: {0}" -f ($PNPCmdlets[0].Version).ToString())
            Write-Host ("* Required version: {0}" -f $pnpRequiredVersion.ToString())

            [string] $option="0"

            if(($option = Read-Host "Proceed with the update? (Y)es or (N)o") -match 'y') {
                    Write-Host "`n* Downloading 'SharePointPnPPowerShellOnline' (v$pnpRequiredVersion) module ... "
                    Save-Module SharePointPnPPowerShellOnline -path $PnPBinFolder -RequiredVersion $pnpRequiredVersion

                } catch {
                    throw $_
            } else {

        errorMessage ("`nPlease, check the requirements before proceding with the installation.")

        # Menu
        Write-Host ("`n#--- Requirements ---#`n") 
        Write-Host ("1 - Download SharePoint PnP PowerShell Online (v{0}) Cmdlets  " -f $pnpRequiredVersion) -NoNewline 
        Write-Host "(Required)" -BackgroundColor Yellow -ForegroundColor Black
        Write-Host ""
        Write-Host "2 - Open Windows Management Framework v4.0 download page  " -NoNewline
        Write-Host "(Prerequisites - PowerShell version 4 or above )" -BackgroundColor Yellow -ForegroundColor Black
        Write-Host ""
        Write-Host "0 - Exit"

        [string] $option="0"
        $option = Read-Host "`nSelect option"

            "1" { 
                Write-Host "`n* Downloading 'SharePointPnPPowerShellOnline' (v$pnpRequiredVersion) module ... "
                Save-Module SharePointPnPPowerShellOnline -path $PnPBinFolder -RequiredVersion $pnpRequiredVersion
            "2" { (New-Object -Com Shell.Application).Open(''); checkPnPCmdlets; break }
            "0" { exit }
            default { cls }

function successMessage($message){
    Write-Host $message -BackgroundColor Green -ForegroundColor Black

function errorMessage($message){
    Write-Host $message -ForegroundColor Red

function warningMessage($message){
    Write-Host $message -BackgroundColor Yellow -ForegroundColor Black

function isModern($webTemplate){
    return (@("GROUP#0","SITEPAGEPUBLISHING#0","STS#3") -contains $webTemplate)

function isSharePointOnline($url){
    return $url -match ".+?\"

function closeWindow(){
        Write-Host "Press any key to close this window."
        $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
    } catch {}


& Main

While running the script, the only input necessary corresponds to your SharePoint tenant name. When prompted, provide the necessary information.

Considering a scenario where your tenant URL is similar to, be sure to input myTenant as the parameter.


The script will proceed to run automatically, listing all the present site collections, and their correlative number of installed BindTuning applications. 




Have more questions? Submit a request


BindTuning logo
Copyright Ⓒ 2021 Bind.
All rights reserved.
Privacy Policy
Cookie Policy
              Linkedin BindTuning Twitter BindTuning YouTube BindTuning Instagram BindTuning
Powered by Zendesk