Apr 082019
 

I was on a customer case where SharePoint requests were taking significant time to complete. The issue eventually turned out to be because of a low value for MaxConcurrentAPI which in turn caused incoming SID resolution calls to pile up at the DC. This post is not about MaxConcurrentAPI, if you want to know more about MaxConcurrentAPI please read following article:
https://blogs.msdn.microsoft.com/spatdsg/2006/01/05/maxconcurrentapi-or-how-fast-can-you-authenticate-users/.

But the reason I’m writing this post is because of a script that I wrote to reproduce the SID resolution issue with a DC. At the heart of this script is the following script block….

#Script block which will be used by Powershell Jobs
$ADSidResolve = {
    param($sid)
    Write-Host "SID: $sid"
    $objSID = New-Object System.Security.Principal.SecurityIdentifier($sid)
    $objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
    $objUser.Value
}

I’m using the System.Security.Principal.SecurityIdentifier.Translate API to translate a SID to username. To translate a username to its SID, use System.Security.Principal.NTAccount.Translate API instead. Here’s a sample script which accomplishes that…

#Script to translate currentuser's username to SID
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$currentUserName = $currentUser.User.Translate([System.Security.Principal.NTAccount])
$objSID = New-Object System.Security.Principal.NTAccount($currentUserName)
$objUser = $objSID.Translate( [System.Security.Principal.SecurityIdentifier])

Write-Host "Current user: $currentUserName, SID: $($objUser.Value)"

Here’s the full script to translate a file full of SIDs to username, I used this script to stress test DC. Note that this script executes SID resolution in parallel using PowerShell jobs so this script is a good tutorial for using PowerShell jobs as well.

When running this script please make sure you’ve got C:\Sids.txt created with all the SIDs in it. Also note that there is a better way (using System.Security.Principal.IdentityReferenceCollection.Translate) for translating these SIDs but since I am simulating a customer scenario so I had to follow this route.

#follow this script on github: https://github.com/nibubt/powershellsnips/blob/master/ADSIDResolve.ps1

$filePath = "C:\sids.txt" #make sure this file contains SIDs that needs to be resolved.

# Verify file status #
if((Test-Path $filePath) -eq $false)
{
    Write-Error -Message "File $filePath does not exist. Please correct file path before using this script. Exiting!"
    return
}

#Read in UserName and Password securely
$username = Read-Host -Prompt "Please enter username: "
$pwd = Read-Host -Prompt "Please enter your password: " -AsSecureString
$cred = New-Object System.Management.Automation.PSCredential($username,$pwdz
if($null -eq $cred)
{
    Write-Error -Message "Invalid credentials! Exiting!"
    return;
}

# Count of lines to show progress
$sidlines = Get-Content -Path $filePath | ForEach-Object{$_.Trim()}
$totalLines = ( $sidlines | Measure-Object -Line).Lines

if(0 -ge $totalLines)
{
    Write-Error -Message "File $filePath empty?! This file must contain the SIDs you're intending to resolve! Exiting!"
    return    
}


#Script block which will be used by Powershell Jobs
$ADSidResolve = {
    param($sid)
    Write-Host "SID: $sid"
    $objSID = New-Object System.Security.Principal.SecurityIdentifier($sid)
    $objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
    $objUser.Value
}

$alljobs = @()
$curLine = 0
foreach($line in $sidlines)
{
    ++$curLine;
    $JobName = "PSJOB-SIDResolve-$line"    
    $alljobs += Start-Job -ScriptBlock $ADSIDResolve -Name $JobName -Credential $cred -ArgumentList $line
  
    $PercentComplete = [math]::round((($curLine/$totalLines)*100),0)
    Write-Progress -Activity "Progress resolving SID... (Current line: $curLine of $totalLines)" -Status "$PercentComplete% Complete:" -PercentComplete $PercentComplete
}

#Wait for jobs to finish, in the end print the output
Write-Host "Waiting on jobs to finish..."
Get-Job | Wait-Job | Receive-Job

Remove-Variable alljobs,sidlines

Since output of this script is confidential, I’m skipping output but note that this does work :-).

Also note that there is a SysInternals tool that does a similar job. Its called PsGetSid: https://docs.microsoft.com/en-us/sysinternals/downloads/psgetsid

Jun 242016
 

What’s ServerRender?

ServerRender is a property of a webpart which disables client-side rendering for a webpart. You’ll lose some cool features of a webpart once you enable this feature for e.g.

  1. Inline editing of a list item, or in other words “Quick Edit”.
  2. Look and feel of the list changes to the traditional view, from a excel like view.

But this feature is quite handy when it comes to workaround some bugs related to client-side JavaScript.

 

How do we enable this feature via SharePoint UI?

  1. GoTo Site settings->Edit page
    clip_image001
  2. Select list web part and then select WEB PART on the ribbon bar, then select the list web parts “Web Part Properties” on this ribbon.
    clip_image003
  3. On the properties window that pops out on the right side, enable “Server Render”, at the bottom of this screenshot…
    clip_image005

 

How do we enable this feature via PowerShell?

I had to dig around a bit to figure this out. Here’s how we do this via PowerShell…

#Web URL, change to your web's URL
$WebUrl = "http://sp/sites/TaskList"
#ListName, change to your list's name
$ListName = "MyTasks"
$web = Get-SPWeb $webUrl

#Get all web parts in this collection, please note this is for default view, if you've got other views please use URL for that view
$WebParts = $web.GetWebPartCollection($web.Lists[$ListName].DefaultViewUrl, [Microsoft.SharePoint.WebPartPages.Storage]::Shared)
#Dump names of all web parts on this page
$WebParts.DisplayName

#Assuming you just have one web part on this page.
$WebParts[0].ServerRender=$true
$WebParts.SaveChanges($WebParts[0].ViewGuid)

$web.Dispose()
Sep 232015
 
Introduction

I’ve been working on a case where customer accidently deleted a bunch of timer jobs. He came to us with a question on how to restore the timer jobs back. We found a way to restore the timer jobs but just a few of them. So if the timer jobs are out of the box (OOB) jobs we can restore them in this manner. If there are other timer jobs you’ll have to just re-deploy the timer job solution again.

Farm Level Timer Jobs

From code I see that SPTimerService.EnsureDefaultJobs should restore the following farm level timer jobs…

job-config-refresh
job-ceip-datacollection
job-delete-job-history
job-password-management
job-app-installation
SPAppStatisticsProviderJobDefinition
job-autohosted-appinstance-counter
job-admin-product-version
job-timer-recycle
job-timer-locks
job-spapp-statequery
job-internalspapp-statequery
Web Application Level Timer Jobs

There’s another one SPWebApplication.EnsureDefaultJobs which restores following web application level timer jobs.

job-immediate-alerts
job-dead-site-delete
job-diskquota-warning
job-change-log-expiration
job-recycle-bin-cleanup
job-workflow-autoclean
job-workflow-failover
job-workflow
job-site-deletion
job-audit-log-trimming
job-solution-resource-usage-log
job-solution-resource-usage-update
job-solution-daily-resource-usage
job-upgrade-workitems
job-filefragments-cleanup
job-storage-metrics-processing
job-create-upgrade-eval-sites
job-delete-upgrade-eval-sites
job-upgrade-sites
Sample Script

So the question is how to execute these functions via PowerShell…

# Ensures the default farm level timer job definitions exist
Add-PSSnapin Microsoft.Sharepoint.Powershell
$farm = Get-SPFarm
$farm.TimerService.EnsureDefaultJobs();

#Ensures the default timer job definitions exist for the specified web application
$webapp = Get-SPWebApplication http://webapp/
$webapp.EnsureDefaultJobs();

Thanks to my colleague Stephen Woodard and Trevor Barkhouse for pointing me to this public function.

Disclaimer

As always please make sure you test this out well before implementing this in production.

Jul 232015
 
Very important:

Please make sure necessary backups are in place before you perform this operation. First test in your dev/test environment and only then use in your production environment.

Ideally a feature will be scoped to a Farm, WebApplication, Site, or Web, but orphaned features won’t be having a scope. The command to list all orphaned features in a SharePoint farm is as follows…

PowerShell command to list all orphaned features…
Get-SPFeature | ? { $_.Scope -eq $null }
Sample output…
PS C:\windows\system32> Get-SPFeature | ? { $_.Scope -eq $null }

DisplayName                    Id                                       CompatibilityLevel   Scope                         
-----------                    --                                       ------------------   -----                         
ReportServerCentralAdmin       5f2e3537-91b5-4341-86ff-90c6a2f99aae     14                                                 
ReportServerStapling           6bcbccc3-ff47-47d3-9468-572bf2ab9657     14                                                 
PowerView                      bf8b58f5-ebae-4a70-9848-622beaaf2043     14                                                 
ReportServerCentralAdmin       5f2e3537-91b5-4341-86ff-90c6a2f99aae     15                                                 
ReportServerStapling           6bcbccc3-ff47-47d3-9468-572bf2ab9657     15                                                 
PowerView                      bf8b58f5-ebae-4a70-9848-622beaaf2043     15
PowerShell command to delete an orphaned feature…
foreach($f in @(Get-SPFeature | ? { $_.DisplayName -eq "ReportServerCentralAdmin" }))
{
    $f.Delete()
}

Why did we need a foreach? Because a feature could be listed twice with different “CompatibilityLevel” values. So in this case “ReportServerCentralAdmin” is listed twice with “CompatibilityLevel” set to 14 and 15. So we delete both via $f.Delete().

Jul 222015
 

Introduction

Get-SPFeature is a command used to list out all the features in a SharePoint farm (scope can be set). You can scope this command to different levels, the levels are…

  • Farm – Only SharePoint farm level features are displayed
  • Web Application – Only web application level features are displayed
  • Site – Only site level features are displayed
  • Web – For sub sites

Examples…

#Display all SharePoint farm features
Get-SPFeature –Limit All
Scoping features…
#Display all SharePoint features scoped to farm
Get-SPFeature -Limit ALL | Where-Object {$_.Scope -eq "FARM"}

#Display all SharePoint features scoped to web application
Get-SPFeature -Limit ALL | Where-Object {$_.Scope -eq "webapplication"}

#Display all SharePoint features scoped to site
Get-SPFeature -Limit ALL | Where-Object {$_.Scope -eq "site"}

#Display all SharePoint features scoped to web
Get-SPFeature -Limit ALL | Where-Object {$_.Scope -eq "web"}
List all features in a Site Collection…

#Displays all SharePoint features for a site collection using Grid view

Get-SPSite http://mysiteurl | Get-SPWeb -Limit ALL |%{ Get-SPFeature -Web $_ } | Out-GridView –Title “All features for this site”

Sample Grid Output…
All Features in a Sharepoint Site Collection

Help…

#For detailed help type in… Get-Help Get-SPFeature –Full