#BrightWork Licensing Script #Parameters: Web Application URL #Results: Written to a log file in the same location as the script is running in #Include the powershell snapin If ((Get-PsSnapin |?{$_.Name -eq "Microsoft.SharePoint.PowerShell"})-eq $null) { Add-PsSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue -WarningAction SilentlyContinue } $licensedUsers = @{} #hashtable #Add in Design Sync .... $licensedUserInfo = @{ UniqueId = $UniqueId LoginName = $LoginName Name = $Name Email = $Email HasReadAccess = $HasReadAccess WebURL = $WebUrl SiteCollection = $SiteCollection Issues = $Issues } #Define what BrightWork Consider Read Access $readPermissionRights = [Microsoft.SharePoint.SPBasePermissions]::EmptyMask -bor [Microsoft.SharePoint.SPBasePermissions]::ViewListItems -bor [Microsoft.SharePoint.SPBasePermissions]::OpenItems -bor [Microsoft.SharePoint.SPBasePermissions]::ViewVersions -bor [Microsoft.SharePoint.SPBasePermissions]::CreateAlerts -bor [Microsoft.SharePoint.SPBasePermissions]::ViewFormPages -bor [Microsoft.SharePoint.SPBasePermissions]::ViewPages -bor [Microsoft.SharePoint.SPBasePermissions]::BrowseUserInfo -bor [Microsoft.SharePoint.SPBasePermissions]::UseRemoteAPIs -bor [Microsoft.SharePoint.SPBasePermissions]::UseClientIntegration -bor [Microsoft.SharePoint.SPBasePermissions]::Open ; #Clear all the variables $site = $null $webAppUrl = $null $webapp = $null # Ensure we have a valid $Web App Url $webAppUrl = $args[0] # If a web application url has not been supplied then ask for one here while ($webapp -eq $null) { #Ensue an actual web applicaiton URL is specified if( $webAppUrl -eq $null ) { $webAppUrl = Read-Host " Web Application URL " } #Try to generate a web application from the URL try { $webapp = Get-SPWebApplication $webAppUrl -errorAction Stop } catch { $error.Clear() } #If none could be generated - then output the message to the user and try again if( $webapp -eq $null) { Write-Output "* * * Please specify a valid web application URL. * * *" #Reset the web app url to $null $webAppUrl= $null $webapp = $null } } #Define some useful variables $BWSiteCollection = 16781 $pathOfScript = $MyInvocation.MyCommand.Definition $rootPath = [System.IO.Path]::GetDirectoryName($pathOfScript) #Ensure results file is unique $fileCode = $(Get-Date).ToString("yyyy-MM-ddTHHmmss"); #Function to list the users in the Site Collection #Parameters: elevated site [ This is to avoid any access issues processing a site colleciton the logged-on user doesn't have access to] #Parameters: root web - the top level web of the site collection - (used to call the GetWebsAndListsWithUniquePermissions ) function ListUsersInSiteCollection( $elevatedSite, $rootWeb ) { Write-Host " ListUsersInSiteCollection $($rootWeb.Url) BEGIN" #Clear out the users from the collection or they would be re-written out to the log file... $licensedUsers.Clear(); #Pull back all the Webs and Lists with unique permissions #Note: The top level site collection may me the only result per SC if no webs are set to have unique access further down the hierarchy #Note: This is the code that needs to be ran elevated $webListInfos = $rootWeb.GetWebsAndListsWithUniquePermissions(); #Iterate though all the web list information objects and only process the web level ones foreach( $spWebListInfo in $webListInfos ) { #We are only interested in unique access webs if( ($spWebListInfo.Type -eq 'Web') -And ($spWebListInfo.HasUniqueRoleAssignments -eq $true ) ) { #Generate the web object using the elevated site collection $spWebToProcess = $elevatedSite.OpenWeb( $spWebListInfo.WebId ) #Process the elevated web and pass down the site collection root web ProcessWeb $spWebToProcess $rootWeb #Ensure the wevb object is disposed if( $spWebToProcess -ne $null ) { $spWebToProcess.Dispose(); } } } #Now we have processed all the unique access webs in the SC - we can write out the results to the csv file ##User| Login Name| Email | BrightWork Site Collection | Web | Billable | Issues #Iterate through the collection that we have now built up of the licensed users foreach ($userEnumerator in $licensedUsers.GetEnumerator()) { # Pull back the licensed user to process $licensedUserToProcess = $userEnumerator.Value '"' + $licensedUserToProcess.Name + '","' + $licensedUserToProcess.LoginName + '","' + $licensedUserToProcess.Email + '","' + $licensedUserToProcess.SiteCollection + '","' + $licensedUserToProcess.WebURL + '","' + $licensedUserToProcess.HasReadAccess + '","' + $licensedUserToProcess.Issues + '"' | Out-File -Encoding Default -Append -FilePath $OutputLicensingScript; } Write-Host " ListUsersInSiteCollection $($rootWeb.Url) END " } #Function to process the unique access web in the site collection #Parameters: elevated web [ The uniquye access web open with elevated site to avoid any access issues ] #Parameters: root web - the top level web of the site collection for saving off into the licensed user object function ProcessWeb( $spWebToProcess, $rootWeb ) { Write-Host " ProcessWeb $($spWebToProcess.Url) BEGIN " #Iterate through the collection of users in the spWebToProcess AllUsers Collection foreach( $spUser in $spWebToProcess.AllUsers ) { #Determine if the user is an application principal - if so move to the next user if ( $spUser.IsApplicationPrincipal -eq $true ) { Write-Output " $($spUser.LoginName) IsApplicationPrincipal " #Move onto the next user continue; } #Determine if the isers is Domain Group Admin - if so move to the next user if ( $spUser.IsDomainGroup -eq $true ) { Write-Output " $($spUser.LoginName) IsDomainGroup " continue; } if( [string]::IsNullOrEmpty($spUser.LoginName) -eq $false ) { #Determine if the users is sharepoint\system - if so move to the next user if ( [string]::Equals($spUser.LoginName,"sharepoint\system", "CurrentCultureIgnoreCase" ) -eq $true) { Write-Output " Note: Not processing user as sharepoint system user : $($spUser.LoginName) " continue; } #Determine if the user LOGIN NAME starts with NT Authority - if so move to the next user if( $spUser.LoginName.StartsWith("NT AUTHORITY\","CurrentCultureIgnoreCase") -eq $true ) { Write-Output " Note: Not processing user as NT Authority user :$($spUser.LoginName) " continue; } } # when we are processing a site collection we may get duplicate users added with windows and claims based records # so we need to reduce these using the sid and comes from the SPUser.UserId.NameId ; #We are using sharepoint 2010 - we cannot use this logic as the SPUser.UserId does not exist if( $spUser.UserId -eq $null) { #Note: We may look into using display name and SID comparissons based on feedback. $uniqueUserId = $spUser.ID; } else { $uniqueUserId = $spUser.UserId.NameId.ToUpperInvariant(); } #Create/Update the licensed user $licensedUser = $null; #Determine if the licensed user collection contain the unique user id if ( $licensedUsers.ContainsKey($uniqueUserId) -eq $true ) { #Pull back the existing licensed user $licensedUser = $licensedUsers.Get_Item($uniqueUserId) # user already added so ensure an email and display name have been specified if( [string]::IsNullOrEmpty($licensedUser.Email) -eq $true ) { #Update the licensed user email if required $licensedUser.Email = $spUser.Email; } #user already added - determine if the name needs updating if( [string]::IsNullOrEmpty($licensedUser.Name) -eq $true ) { #Update the licensed user name if required $licensedUser.Name = $spUser.Name; } try { #Derermine if the current user has permissions #Note: This should NOT return an error - but I have seen an error where the user hasn't been added correctly if($spWebToProcess.DoesUserHavePermissions($spUser.LoginName,$readPermissionRights)) { #We need to update the HasReadAccess to be true if( $licensedUser.HasReadAccess -eq $false ) { $licensedUser.HasReadAccess = $true #Save off the Web URL with the unique access that the user now has permissions for $licensedUser.WebURL = $spWebToProcess.Url } } } catch { #Set the Has Read Access to false and save off the exception meesage into the issues property $licensedUser.HasReadAccess = $false $licensedUser.Issues = $_.Exception.Message } } else { # user does not exist so add record $licensedUser = New-Object PSObject -Property $licensedUserInfo $licensedUser.UniqueId = $uniqueUserId; $licensedUser.Name = $spUser.Name; $licensedUser.Email = $spUser.Email; $licensedUser.LoginName = $spUser.LoginName; $licensedUser.WebURL = $spWebToProcess.Url $licensedUser.SiteCollection = $rootWeb.Url try { if($spWebToProcess.DoesUserHavePermissions($spUser.LoginName,$readPermissionRights)) { $licensedUser.HasReadAccess = $true } else { $licensedUser.HasReadAccess = $false } } catch { $licensedUser.Issues = $_.Exception.Message $licensedUser.HasReadAccess = $false } $licensedUsers.Add( $licensedUser.UniqueId, $licensedUser ); } } Write-Host " ProcessWeb $($spWebToProcess.Url) END " } #Iterate through all the brightwork sites and return information regarding the licensing setup Write-Output "Generating the Content for the BrightWork User Access -BEGIN" $OutputLicensingScript = $rootPath + "\BrightWork($fileCode)-ListUsersInWebApplication.csv" #User| Login Name| Email | BrightWork Site Collection | Web | Billable | Issues "User" + "," + "Login Name"+","+"Email"+","+"BrightWork Site Collection" +","+"Web" +","+"Reader Access (or Higher)" +","+"Issue"| Out-File -Encoding Default -FilePath $OutputLicensingScript; #Iterate through all the sites in the Web Application Foreach ($site in $webapp.Sites) { try { #Only interested in brightwork sites if( $site.RootWeb.WebTemplateId -eq $BWSiteCollection) { #Logged onto the server as aimware-inet\ruane and came back with access denied from the site collection: #http://rob-spf15:7777/sites/JBtest as I had no access to the site here - and calling the code to get the webs caused it to crash $elevatedSite = New-Object Microsoft.SharePoint.SPSite([Guid]$site.ID,$site.SystemAccount.UserToken) ListUsersInSiteCollection $elevatedSite $elevatedSite.RootWeb } } catch { Write-Host "Unexpected exception thrown executing script." -ForegroundColor Red Write-Host $_.Exception.Message -ForegroundColor Red } finally { #Ensure the site is disposed if( $site -ne $null) { $site.Dispose() } if( $elevatedSite -ne $null) { $elevatedSite.Dispose() } } } Write-Output "Generating the Content for the BrightWork User Access File -END"