Apr 122019
 

System.Security.Claims.ClaimsIdentity is the .Net class that helps management of a claims based user identity using which you can add claims to a .Net application for more granular user control based on the user claims provided by the issuer in your application.

MSDN Link to class documentation:
https://docs.microsoft.com/en-us/dotnet/api/system.security.claims.claimsidentity?view=netframework-4.7.2

For example…
1. switch the UI to a different view if role of user logged in is the company CEO.
2. switch site theme based on users favorite color.
3. switch site locale based on user country or show a customized greeting.
4. restrict access to site based on user age.
5. etc…

Here is a short script to assign claims to the current thread principal and verify user claims using PowerShell.

# Add assembly for displaying message box
Add-Type –AssemblyName System.Windows.Forms

# Claims
$claims = New-Object System.Collections.Generic.List[System.Security.Claims.Claim]

# Add claims to claims list
$claims.Add(((New-Object System.Security.Claims.Claim([System.Security.Claims.ClaimTypes]::Email, "nibu.bt@gmail.com"))))
$claims.Add(((New-Object System.Security.Claims.Claim("WebSite", "http://ntcoder.com"))))
$claims.Add(((New-Object System.Security.Claims.Claim("Primary Skill", "C++"))))
$claims.Add(((New-Object System.Security.Claims.Claim([System.Security.Claims.ClaimTypes]::Country, "India"))))
$claims.Add(((New-Object System.Security.Claims.Claim([System.Security.Claims.ClaimTypes]::Role, "CEO"))))


# Instantiate claims identity object
$cid = New-Object System.Security.Claims.ClaimsIdentity($claims, "Password")

# Assign principal
[System.Threading.Thread]::CurrentPrincipal =  New-Object System.Security.Claims.ClaimsPrincipal($cid)

Write-Host "Current Thread Principal: " -NoNewline
[System.Threading.Thread]::CurrentPrincipal | Select-Object -ExpandProperty Claims | Select-Object Issuer, Type, value | Format-Table -AutoSize

# Write authentication status
Write-Host "Authenticated: $($cid.IsAuthenticated)"

$tp = [System.Threading.Thread]::CurrentPrincipal

# Check if user is CEO, if yes then display a messagebox, check above role that we added 
if($tp.IsInRole("CEO"))
{
    $null = [System.Windows.Forms.MessageBox]::Show("Welcome CEO, opening CEO view...", "User", [System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information)
}

# Check country of user, if India then display an Indian greeting message.
if($tp.HasClaim([System.Security.Claims.ClaimTypes]::Country, "India"))
{
    $null = [System.Windows.Forms.MessageBox]::Show("Namaskar!", "Greetings!", [System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information)
}

Script output follows...

PS C:\> c:\NibuRoot\Powershell\powershellsnips\Claims.ps1
Current Thread Principal:
Issuer Type Value
------ ---- -----
LOCAL AUTHORITY http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress email@gmail.com
LOCAL AUTHORITY WebSite http://ntcoder.com
LOCAL AUTHORITY Primary Skill C++
LOCAL AUTHORITY http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country India
LOCAL AUTHORITY http://schemas.microsoft.com/ws/2008/06/identity/claims/role CEO

Authenticated: True

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

Feb 212019
 

I needed to find a particular SPFeature across a SharePoint farm so wrote up this PowerShell script. Sharing with you all this PowerShell script, also this script has some neat formatting code which you could find useful.

#Follow this script on GitHub: https://github.com/nibubt/powershellsnips/blob/master/FindFeature.ps1

Add-PSSnapin *SharePoint*

function WriteFeature($features, $tabs)
{
    if($features -ne $null)
    {
        ForEach($feature in $Features)
        {
            Write-Host $("`t" * $tabs)"-> Found Feature: Title: $($feature.GetTitle(1033)), DisplayName: $($feature.DisplayName)" -ForegroundColor Cyan
        }
    }
}

$FeatureDisplayName = "SiteLevelPolicy"

Write-Host -ForegroundColor Green "Script output in DisplayName and Title format..."
Write-Host "Looking for feature: $FeatureDisplayName" -ForegroundColor Yellow

$tab = 0
Write-Host $("`t" * $tab)"-> Farm"
$feature = Get-SPFeature -Farm -ErrorAction SilentlyContinue | Where-Object{$_.DisplayName -imatch $FeatureDisplayName}
WriteFeature $feature ($tab + 1)

ForEach($WebApp in Get-SPWebApplication)
{
    $tab = 1
    Write-Host $("`t" * $tab)"-> WebApp: $($WebApp.Url)"
    $feature = Get-SPFeature -WebApplication $WebApp.Url -ErrorAction SilentlyContinue | Where-Object{$_.DisplayName -imatch $FeatureDisplayName}
    WriteFeature $feature ($tab + 1)

    ForEach($Site in $WebApp.Sites)
    {
        $tab = 2
        Write-Host $("`t" * $tab)"-> Site: $($Site.Url)"
        $feature = Get-SPFeature -Site $Site.Url -ErrorAction SilentlyContinue | Where-Object{$_.DisplayName -imatch $FeatureDisplayName}
        WriteFeature $feature ($tab + 1)

        ForEach($Web in $Site.AllWebs)
        {
            $tab = 3
            Write-Host $("`t" * $tab)"-> Web: $($Web.Url)"
            $feature = Get-SPFeature -Web $Web.Url -ErrorAction SilentlyContinue | Where-Object{$_.DisplayName -imatch $FeatureDisplayName}
            WriteFeature $feature ($tab + 1)
        }#End ForEach
    }#End ForEach
}#End ForEach
Script output from my local farm
Jan 182018
 

PowerShell provides multiple ways to access Excel. My favorite so far is via COMObject Excel.Application. This object provides direct access to Excel workbooks, worksheets. In this short article we’ll see how to accomplish this using PowerShell and Excel’s COMObject.

Instantiating Excel COM Object using New-Object PowerShell CmdLet

Using following line of code to instantiate Excel COM object.

1
$excelObj = New-Object -ComObject Excel.Application
Creating an Excel workbook
1
$excelWB = $excelObj.WorkBooks.Add()
Get reference to a worksheet
1
$excelWS = $excelWB.WorkSheets.Item(1)
Get reference to an excel worksheet cell

Change attributes of a Cell using following code…

1
2
$excelWS.Cells.Item(1, 1) = "Test"
$excelWS.Cells.Item(1, 1).Font.Bold = $true
Sample code to fill out an Excel worksheet

I’m here using Get-Process cmdlet to generate some data in tabular format. But basically, below script uses above logic to fill out the sheet…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$excelObj = New-Object -ComObject Excel.Application
$excelWB = $excelObj.workbooks.Add()
$excelWS = $excelWB.WorkSheets.Item(1)
#Get processes running on my machine
$procs = Get-Process
#Get all column names, process class' property name
$ColumnNames = $procs[0].GetType().GetMembers() | Where-Object{$_.MemberType -eq "Property" -and $_.MemberType -ne "Module"} | %{$_.Name}
#Make main excel window visible
$excelObj.Visible = $true
#Add excel column title
$cIndex = 0; #Column index
$ColumnNames | %{$cIndex++;$excelWS.Cells.Item(1, $cIndex).Font.Bold=$true;$excelWS.Cells.Item(1, $cIndex)=$_}
#Add process information into excel rows
$rIndex = 1;$procs | %{$cIndex = 0;$rIndex++;$proc=$_;$ColumnNames | %{$cIndex++;$excelWS.Cells.Item($rIndex, $cIndex) = "$($proc.$($_))"}}
Output

Another option

PowerShell now does come with built in Excel support if you’re ok to install following module…

1
Install-Module ImportExcel

With this module in place you can run commands like…

1
Get-Process | Where Company | Export-Excel C:\Temp\ps.xlsx -Show -IncludePivotTable -PivotRows Company -PivotData @{Handles=”sum”} -IncludePivotChart -ChartType PieExploded3D

This will create an excel workbook with data in a pivot table. For this case we’ve set PivotData to sum of handles based on product company.

References

https://blogs.technet.microsoft.com/heyscriptingguy/2015/11/25/introducing-the-powershell-excel-module-2/

Jan 082018
 

Why?

While debugging a crash on a customer SharePoint farm, we had to do constant checks on all SharePoint servers for latest entries in the ‘System’ eventlog. So wrote this PowerShell one-liner, using Get-EventLog, to help him out…

How?

Get-SPServer | Where-Object{$_.Role -ne "Invalid" } | %{ Write-Host "++++++++++++++++++ComputerName: $($_.Name.Split('.')[0])+++++++++++++++++++++++";
Get-EventLog -ComputerName $_.Name -LogName System -Newest 10} | ft -AutoSize

Command explained

  1. Get-SPServer: return all servers in the farm.
  2. Checking role of each server to make sure we’re accessing just the SharePoint servers.
  3. Drawing a header right before dumping out event log entries. If name is FQDN then we just take the first part, not necessary though.
  4. Call Get-EventLog by passing in computer name and log category as ‘System’, we’re retrieving newest 10 entries. Feel free to change this.

If you run into any issues or if you have any better way of doing this please let me know.

Sample output

PS C:\Windows\system32> Get-SPServer | Where-Object{$_.Role -ne "Invalid" } | %{ Write-Host "++++++++++++++++++ComputerName: $($_.Name.Split('.')[0])+++++++++++++++++++++++";
Get-EventLog -ComputerName $_.Name -LogName System -Newest 10} | ft -AutoSize
++++++++++++++++++ComputerName: SP+++++++++++++++++++++++

Index Time         EntryType   Source                  InstanceID Message                                                                 
----- ----         ---------   ------                  ---------- -------                                                                 
19665 Jan 08 12:00 Information Service Control Manager 1073748860 The Software Protection service entered the stopped state.              
19664 Jan 08 12:00 Information Service Control Manager 1073748860 The Software Protection service entered the running state.              
19663 Jan 08 12:00 Information EventLog                2147489661 The system uptime is 1026236 seconds.                                   
19662 Jan 08 10:21 Information Service Control Manager 1073748860 The Windows Modules Installer service entered the stopped state.        
19661 Jan 08 10:19 Information Service Control Manager 1073748860 The Windows Update service entered the stopped state.                   
19660 Jan 08 10:19 Information Service Control Manager 1073748860 The Windows Modules Installer service entered the running state.        
19659 Jan 08 10:14 Information Service Control Manager 1073748860 The Windows Store Service (WSService) service entered the stopped state.
19658 Jan 08 10:09 Information Service Control Manager 1073748860 The Windows Store Service (WSService) service entered the running state.
19657 Jan 08 10:07 Information Service Control Manager 1073748860 The Windows Update service entered the running state.                   
19656 Jan 08 07:18 Information Service Control Manager 1073748860 The Windows Modules Installer service entered the stopped state.
Dec 232017
 

Use following PowerShell commands to list all printers installed on any machine…

First command uses WMI (Windows Management Instrumentation)…

Get-WmiObject -Class "Win32_Printer" -ComputerName "localhost" | ft -Property Name, DriverName, SystemName, ShareName -AutoSize
Command Output
Name                           DriverName                                  SystemName        ShareName   
----                           ----------                                  ----------        ---------   
Send To OneNote 2016           Send to Microsoft OneNote 16 Driver         MYMACHINE                    
Microsoft XPS Document Writer  Microsoft XPS Document Writer v4            MYMACHINE                    
Microsoft Print to PDF         Microsoft Print To PDF                      MYMACHINE                    
Fax                            Microsoft Shared Fax Driver                 MYMACHINE                     

The next command uses built-in PowerShell command Get-Printer

Get-Printer -ComputerName mymachine
Command Output
Name                           ComputerName    Type         DriverName                PortName        Shared   Published  DeviceType     
----                           ------------    ----         ----------                --------        ------   ---------  ----------     
Send To OneNote 2016           mymachine       Local        Send to Microsoft OneN... xxx:            False    False      Print          
Microsoft XPS Document Writer  mymachine       Local        Microsoft XPS Document... xxxxxxxxxx:     False    False      Print          
Microsoft Print to PDF         mymachine       Local        Microsoft Print To PDF    xxxxxxxxxx:     False    False      Print          
Fax                            mymachine       Local        Microsoft Shared Fax D... xxxxxx:         False    False      Print
Please note that I’ve removed some printers from this list due to security issues.
Aug 142015
 

In this post we’ll be discussing on how to manipulate strings using PowerShell. PowerShell comes with the following string manipulation operators…

  • split – case sensitive split
  • isplit – case insensitive split
  • csplit – case sensitive split
  • join – joins an array into one string
  • replace – case sensitive split
  • ireplace – case insensitive split
  • creplace – case sensitive split
Split Demo Using Split Function Call
#store comma delimited wild animals list into a string
$wildanimals = "Tiger,Lion,Elephant,Bear,Fox,Jackal,Hyena"
#split the string using Split function based on ","
$wildanimals.Split(",")
Output
========
Tiger
Lion
Elephant
Bear
Fox
Jackal
Hyena
Split Demo Using -Split Operator
#split array using -split operator
$wildanimals -split ","
Output
========
Tiger
Lion
Elephant
Bear
Fox
Jackal
Hyena
#split into three strings, this will leave the third string unsplit
$wildanimals -split ",", 3
Output
========
Tiger
Lion
Elephant,Bear,Fox,Jackal,Hyena
#replace all "," with " "
$wildanimals = ($wildanimals -replace ",", " ")
#alternate syntax, with no split criteria the default is to use " " as split criteria
-split $wildanimals
#restore "," back instead of " "
$wildanimals = ($wildanimals -replace " ", ",")
Join Demo
#store into an array
$wildanimalsarray = $wildanimals -split ","
#join operator takes an array and makes it a string, each element separated by ","
$wildanimalsarray -join ","
Output
========
Tiger,Lion,Elephant,Bear,Fox,Jackal,Hyena
Replace Demo
#replace all instances of Elephant with Zebra
$wildanimals -replace "Elephant","Zebra"
Output
========
Tiger,Lion,Zebra,Bear,Fox,Jackal,Hyena
Conclusion

The case sensitive and insensitive operators are similar in usage except they are sensitive/insensitive to case. Hope this helps you.

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

May 012015
 

Here are some sample PowerShell commands to manipulate XML contents in memory and then later dump them to an XML file. I’ve added comments inline to explain a line of PowerShell command.

#Clear output
cls

#Sample XML content
$xml = @'
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book genre="novel" style="hardcover">
<title>The Handmaid's Tale</title>
<author>
<first-name>Margaret</first-name>
<last-name>Atwood</last-name>
</author>
<price>19.95</price>
</book>
<book genre="novel" style="other">
<title>The Poisonwood Bible</title>
<author>
<first-name>Barbara</first-name>
<last-name>Kingsolver</last-name>
</author>
<price>11.99</price>
</book>
<book genre="novel" style="paperback">
<title>The Bean Trees</title>
<author>
<first-name>Barbara</first-name>
<last-name>Kingsolver</last-name>
</author>
<price>5.99</price>
</book>
</bookstore>
'@

# Dump XML to output window
$xml.InnerXml

#Get all attributes named "genre" from above XML
$nodes = $xml.SelectNodes("//@genre")
$nodes.Count 
#Output: 3

#Get first book in the XML
$firstbook = $xml.SelectSingleNode("/bookstore/book[1]")
$firstbook.InnerXml 
#Output: <title>The Handmaid's Tale</title><author><first-name>Margaret</first-name><last-name>Atwood</last-name></author><price>19.95</price>

# Using Select-Xml: Get last book in the XML
$selxml = Select-Xml -Xml $xml -XPath "/bookstore/book[last()]"
$selxml.ToString() 
#Output: <title>The Bean Trees</title><author><first-name>Barbara</first-name><last-name>Kingsolver</last-name></author><price>5.99</price>

# Filter out nodes based on conditions, dump titles of books whose price is greater than 11
$xml.SelectNodes("/bookstore/book[price>11]/title") | Format-Table
#Output: #text 
 # ----- 
 #The Handmaid's Tale 
 #The Poisonwood Bible
 
# Modify XML value of a node, change price
$xml.SelectSingleNode("/bookstore/book[price<11]/price") #Old price
$xml.SelectSingleNode("/bookstore/book[price<11]/price").InnerText = 10 #New Price
$xml.SelectSingleNode("/bookstore/book[price<11]/price") #Print new price
#Output: #text 
 #----- 
 #5.99 
 #10

#Save XML
$xml.Save("$env:userprofile\\desktop\\testxml.xml")