Skip to main content

Locate BindTuning Products on your tenant (with PowerShell)

BindTuning Team avatar
Written by BindTuning Team
Updated over 3 months ago

This article provides a PowerShell script to help you verify where BindTuning products, including both Design (themes) and Build (web part) features, are installed across your SharePoint Online tenant. This script automates the process of checking each site collection, saving you time and effort.


Before you begin

Before running the PowerShell script, ensure you meet the following prerequisites:

  • Windows PowerShell v4.0 (or above)

  • SharePoint PnP PowerShell cmdlets

  • Windows Management Framework

Note: If these prerequisites are not met, the provided script is designed to prompt you for the necessary module installations or version upgrades.

Additionally, to ensure the script can access your SharePoint Administration Center, it must be run using a SharePoint (Tenant) Admin account.


Running the Script

Follow these steps to execute the PowerShell script:

  1. Save the Script: Copy the entire PowerShell script provided below and paste it into a new file. Save this file with an .ps1 extension (e.g., GetBindTuningInstallations.ps1).

  2. Execute the Script: Open PowerShell and navigate to the directory where you saved your .ps1 file. Run the script.

  3. Provide Tenant Name: When prompted, enter your SharePoint tenant name. For example, if your tenant URL is https://myTenant.sharepoint.com, you should input myTenant.
    ​

    mceclip0.png

The script will then run automatically, listing all present site collections in your tenant and the corresponding number of BindTuning applications installed on each.

run-script.png


The PowerShell Script

clspushd (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(){    try{
        checkPnPCmdlets            #connecto to -admin
        $tenantName = Read-Host ("`nPlease enter the SharePoint Tenant name (https://[TENANT].sharepoint.com")
        $loginUrl = ("https://{0}-admin.sharepoint.com/" -f $tenantName)                connectTo $loginUrl        $webInfo = Get-PnPWeb
        $tenantApps = Get-PnPApp -Scope Tenant        if($tenantApps.Count){
            "***** 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)                if($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")                try{
                    $siteSolutions = Get-PNPListItem -List $listPath -fields $fieldsArr
                    Write-Host (" # {0} solutions found" -f $siteSolutions.Count)                    if($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 $_
        closeWindow
    }    Write-Host "`nProcess completed."
    closeWindow
}function connectTo($loginUrl){
    Write-Host ("- Connecting to: {0} ... " -f $loginUrl) -NoNewline        #- Connect to site
    try{
        $wpref = $WarningPreference
        $WarningPreference = 'SilentlyContinue'                #check if was already connected        if(isSharePointOnline $loginUrl){
            Connect-PnPOnline -Url $loginUrl -NoTelemetry -useWebLogin
        }else{
            try{ $connection = Get-PnPConnection } catch {}
                    
            if($connection){
                $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    if(!$PSVersion){
        cls
        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) { 
        cls
        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    if(!$PNPCmdlets){
        cls
        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') {
                try{
                    Write-Host "`n* Downloading 'SharePointPnPPowerShellOnline' (v$pnpRequiredVersion) module ... "
                    Save-Module SharePointPnPPowerShellOnline -path $PnPBinFolder -RequiredVersion $pnpRequiredVersion                    Menu
                } catch {
                    throw $_
                }
            } else {
                closeWindow
            }
        }
    }    if($showPNPMenu){
        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"        switch($option){
            "1" { 
                Write-Host "`n* Downloading 'SharePointPnPPowerShellOnline' (v$pnpRequiredVersion) module ... "
                Save-Module SharePointPnPPowerShellOnline -path $PnPBinFolder -RequiredVersion $pnpRequiredVersion
                break
            }
            "2" { (New-Object -Com Shell.Application).Open('https://www.microsoft.com/en-us/download/details.aspx?id=40855'); 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 ".+?\.sharepoint.com"
}function closeWindow(){
    try{
        Write-Host "Press any key to close this window."
        $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
    } catch {}    exit
}& Main
  

Did this answer your question?