Get all Groups and Users in a Site Collection or Web

Recently a user contacted me asking how to get all the groups and users in a site and subsites.

Both example will output to the C:\ drive of the server.

This script will output all the groups and user from the root of a site collection and all the subsites.

$site = Get-SPSite "http://sharepointed.com"

$userOutput = @()

foreach($subWebs in $site.AllWebs)
{
Write-Host $subWebs.Title

$groups = $subWebs.sitegroups

foreach($group in $groups)
{
$object = New-Object PSobject
$object | Add-Member -Name ‘Web URL’ -MemberType Noteproperty -Value $web.URL
$object | Add-Member -Name ‘Web Title’ -MemberType Noteproperty -Value $web.Title
$object | Add-Member -Name ‘Group’ -MemberType Noteproperty -Value $group.Name
$userOutput += $object

foreach($userG in $group.users)
{
$object = New-Object PSobject
$object | Add-Member -Name ‘Web URL’ -MemberType Noteproperty -Value $web.URL
$object | Add-Member -Name ‘Web Title’ -MemberType Noteproperty -Value $web.Title
$object | Add-Member -Name ‘Group’ -MemberType Noteproperty -Value $group.Name
$object | Add-Member -Name ‘Account’ -MemberType Noteproperty -Value $userG.Name

$userOutput += $object
}
}
}

$userOutput | export-csv c:\site_collection_$(get-date -f yyyy-MM-dd-hhmmss).csv -notypeinformation

 

This script will output all the groups and users from a single web.

$web = Get-SPWeb "http://sharepointed.com/sites/taco/SubSite"

$userOutput = @()

$groups = $web.Groups
$users = $web.Users

#get groups and users in the groups
foreach($group in $groups)
{
$object = New-Object PSobject
$object | Add-Member -Name 'Web URL' -MemberType Noteproperty -Value $web.URL
$object | Add-Member -Name 'Web Title' -MemberType Noteproperty -Value $web.Title
$object | Add-Member -Name 'Group' -MemberType Noteproperty -Value $group.Name
$userOutput += $object

foreach($userG in $group.users)
{
$object = New-Object PSobject
$object | Add-Member -Name 'Web URL' -MemberType Noteproperty -Value $web.URL
$object | Add-Member -Name 'Web Title' -MemberType Noteproperty -Value $web.Title
$object | Add-Member -Name 'Group' -MemberType Noteproperty -Value $group.Name
$object | Add-Member -Name 'Account' -MemberType Noteproperty -Value $userG.Name

$userOutput += $object
}
}
#get users not in groups
foreach($user in $users)
{
$object = New-Object PSobject
$object | Add-Member -Name 'Web URL' -MemberType Noteproperty -Value $web.URL
$object | Add-Member -Name 'Web Title' -MemberType Noteproperty -Value $web.Title
$object | Add-Member -Name 'Group' -MemberType Noteproperty -Value ""
$object | Add-Member -Name 'Account' -MemberType Noteproperty -Value $user.Name

$userOutput += $object
}

$userOutput | export-csv c:\web_$(get-date -f yyyy-MM-dd-hhmmss).csv -notypeinformation

Add and Remove Shell Access in SharePoint Using PowerShell

How do you add or remove shell access to a web apps content databases?

This script will grant shell access to a user on all the content databases associated with a content database.

$contentDbs = Get-SPContentDatabase -WebApplication "http://yourSharePointWebApp.com/"

foreach($db in $contentDbs)
{
	Add-SPShellAdmin -UserName "domain\user"  -database $db
}

Remove shell access

$contentDbs = Get-SPContentDatabase -WebApplication "http://yourSharePointWebApp.com/"

foreach($db in $contentDbs)
{
	Remove-SPShellAdmin -UserName "domain\user"  -database $db
}

After running the remove script, make sure you check the WSS_Admin_WPG and WSS_WPG groups on the servers in your farm.

Error Microsoft.Office.RecordsManagement.RecordsRepository.Record

One one my testers was receiving this error when testing a PowerShell script that was doing records management in SharePoint.

System.Management.Automation.RuntimeException: Unable to find type [Microsoft.Office.RecordsManagement.RecordsRepository.Records]: make sure that the assembly containing this type is loaded.
at System.Management.Automation.TypeLiteral.resolveType()
at System.Management.Automation.TypeNode.ResolveType()
at System.Management.Automation.TypeNode.Execute(Array input, Pipe outputPipe, ExecutionContext context)
at System.Management.Automation.AssignmentStatementNode.Execute(Array input, Pipe outputPipe, ExecutionContext context)
at System.Management.Automation.StatementListNode.ExecuteStatement(ParseTreeNode statement, Array input, Pipe outputPipe, ArrayList& resultList, ExecutionContext context)

User was remoted into a SharePoint server, but did not have the right permissions on the server. I tried adding the user to Power Users group, but that didn’t help. Ended up adding the users to the local server Administrators group and the error went away.

Error When Using PowerShell to Call SharePoint Web Service

New-WebServiceProxy : The HTML document does not contain Web service discovery information.
At C:\myScript.ps1:91 char:17
+ ...    $Service = New-WebServiceProxy -UseDefaultCredential -uri $webServ
+                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (http://mySharePoint....bin/Search.asmx:Uri) [New-WebServiceProxy], InvalidOperationException
    + FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.NewWebServiceProxy

First attempt at calling the search.asmx service.

$webServ = "http://mySharePointsite.com/_vti_bin/Search.asmx"
$Service = New-WebServiceProxy -UseDefaultCredential -uri $webServ
$Results = $Service.Query($QueryPacket.OuterXml)

After adding ?wsdl to the uri string, it worked.

$webServ = "http://mySharePointsite.com/_vti_bin/Search.asmx?wsdl"
$Service = New-WebServiceProxy -UseDefaultCredential -uri $webServ
$Results = $Service.Query($QueryPacket.OuterXml)

Use UserGroup.asmx to get all the users in a SharePoint site

This is a little tricky. I was looking for a way to list all the users that you see in Site Settings –> People and Groups, but I found more info than I needed. This post will be updated once I can track down the actual site users.

The script uses the Users and Groups web service to pull all the users in the site collection.
UserGroup.asmx

In my case, the LoginName is setup like this: domain\username

First try:

$webServ = "http://sharepointed/sites/MySiteCollection/_vti_bin/UserGroup.asmx"
$Service = New-WebServiceProxy -UseDefaultCredential -uri $webServ 
$Users = $Service.GetUserCollectionFromSite().Users 
$UserNames = New-Object System.Collections.Generic.List[System.Object]

$Users.User | ForEach-Object {
	$spUser = $_.LoginName.Split('\')[1]
	$UserNames.Add($spUser)
}

Found the answer to my question. Using the Lists.asmx service, I was able to query the UserInfo list for all the site users.

$webServ = "http://sharepointed/sites/MySiteCollection/_vti_bin/Lists.asmx"
$Service = New-WebServiceProxy -UseDefaultCredential -uri $webServ 

$UserNames = New-Object System.Collections.Generic.List[System.Object]

$listname = 'UserInfo'
$listItems = $Service.GetListItems($listname, $null, $null, $null, $null, $null, $null)

for ($counter = 0;$counter -lt $listItems.data.row.Count;$counter++)
{
	$UserNames += $listItems.data.row[$counter].ows_Name
}

Using c# to get at the information. Here I output the LoginName (domain\userName).

            #create a Web Reference to http://yoursiteURL/_vti_bin/usergroup.asmx?wsdl
            #in my case, i named the reference wsUsersGroups
            wsUsersGroups.UserGroup _WSUsersGroups = new wsUsersGroups.UserGroup();
            _WSUsersGroups.Url = "http://sharepointSite/sites/SiteCollectionName/_vti_bin/usergroup.asmx";
            _WSUsersGroups.Credentials = System.Net.CredentialCache.DefaultCredentials;
            XmlNode ndUsers = _WSUsersGroups.GetAllUserCollectionFromWeb();

            StringReader rdrGroups = new StringReader(ndUsers.OuterXml);
            DataSet dsGroups = new DataSet();
            dsGroups.ReadXml(rdrGroups);

            StringBuilder sb = new StringBuilder();

            foreach (DataRow item in dsGroups.Tables[1].Rows)
            {
                sb.AppendLine(item[3].ToString());
            }

            File.WriteAllText("C:\\Users\\myname\\Desktop\\siteUSers.csv", sb.ToString());

PowerShell to get all users group and objects from Active Directory

Get every object and property:

Get-ADUser -Filter * -Properties *| select * | Export-CSV "C:\PS_Every_Object.csv"

^ Depending on the number of user this could take a few minutes to run. This is also handy to hunt for properties.

This will export userId, email, employee Id, and company name.

Get-ADUser -Filter * -Properties SamAccountName,EmailAddress,EmployeeID,Company | select SamAccountName,EmailAddress,EmployeeID,Company | Export-CSV "C:\Email_Addresses.csv"

SOLVED: Exception calling “StartWorkflow” with “X” argument(s)

Trying to start a SharePoint workflow using PowerShell and I couldn’t get past this error:

Exception calling “StartWorkflow” with “4” argument(s): “Object reference not set to an instance of an object.”
or
Exception calling “StartWorkflow” with “3” argument(s): “Object reference not set to an instance of an object.”
 
NO clue if there is a bug in my farm, but the script below works.  Ended up having to re-get the item when running the workflow. $manager.StartWorkflow($list.GetItemById($item.ID),$assoc,$data,$true)

$web = Get-SPWeb "http://rootSiteCollection.com"
$list = $web.Lists["Shared Documents"]

$assoc = $list.WorkFlowAssociations |Where { $_.Name -eq "tacoWF"}
$data = $assoc.AssociationData
$manager = $web.Site.WorkflowManager

$sQuery = New-Object Microsoft.SharePoint.SPQuery 

#Get all items with an ID greater than 5 
$caml = '<Where><Gt><FieldRef Name="ID" /><Value Type="Counter">5</Value></Gt></Where>'
$sQuery.Query = $caml
$fItems = $list.GetItems($sQuery)

Foreach($item in $fItems)
{
	$manager.StartWorkflow($list.GetItemById($item.ID),$assoc,$data,$true)
}

 
Update.
Ran into this again on a SharePoint 2016 farm.
The following commands fixed the problem:
$webapp = Get-SPWebApplication -identity http://
$webapp.UpdateWorkflowConfigurationSettings()
https://support.microsoft.com/en-us/help/2674684/sharepoint-2010-workflow-fails-to-run-after-pause

Are SharePoint Designer Workflows Using Custom Features or Solutions (iLoveSharePoint)

Needed to audit a farm to see if a CodePlex solution was being used in SharePoint Designer workflows.  In my case, I needed to see where the iLove SharePoint  solution was being used. The script below is only targeted at one web and is looking for word “ILoveSharePoint” in the XML.

 


if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) 
{
	Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}

[Microsoft.SharePoint.SPSecurity]::RunWithElevatedPrivileges(
	{

		$resultsarray =@()
		#output file name
		$fileName = "C:\ilsp-" + $(Get-Date -Format "yyyyMMddHHmmss") + ".csv"
		#name of the feature we are looking for
		$wFeatureName = "ILoveSharePoint"

		Function GetFiles($folder)
 { 
			foreach($file in $folder.Files)
			{
				if($file.Name.Split('.')[-1] -eq "xoml")
				{
					$web2 = Get-SPWeb $file.Web.Url
					$wFile = $web2.GetFileOrFolderObject($web2.URL +"/"+ $file.URL)

					if ($wFile.Exists -eq "True")
					{
						$wXml = (New-Object System.Text.UTF8Encoding).GetString($wFile.OpenBinary());
						$nsDetail = $wXml.OuterXml.ToLower()
						
						$wFeatureName = $wFeatureName.ToLower()
							
						if($nsDetail -Like "*$wFeatureName*")
						{
							$outFolder = $folder -replace "Workflows/",""

							$outObject = new-object PSObject
							$outObject | add-member -membertype NoteProperty -name "URL" -Value $web2.Url
							$outObject | add-member -membertype NoteProperty -name "Workflow" -Value $outFolder
							$outObject | add-member -membertype NoteProperty -name "Created By" -Value $wFile.Author
							$outObject | add-member -membertype NoteProperty -name "Created Date" -Value $wFile.TimeCreated
							$outObject | add-member -membertype NoteProperty -name "Modified By" -Value $wFile.ModifiedBy
							$outObject | add-member -membertype NoteProperty -name "Modified Date" -Value $wFile.TimeLastModified

							$global:resultsarray += $outObject
						}
					} 
				} 
			}

			# Use recursion to loop through all subfolders.
			foreach ($subFolder in $folder.SubFolders)
			{
				GetFiles($Subfolder)
			}
		}

		$WebApplications = Get-SPWebApplication

		foreach($webApp in $WebApplications)
		{
			foreach($site in $webApp.Sites)
			{
				if ((Get-SPSite $site.url -ErrorAction SilentlyContinue) -ne $null) 
				{
					foreach($web in $site.AllWebs)
					{
						if ((Get-SPWeb $web.url -ErrorAction SilentlyContinue) -ne $null) 
						{
							$list1 = $web.Lists.TryGetList("Workflows")
							if($list1 -ne $null)
							{
								GetFiles($list1.RootFolder)
							}
						}
					}
				}
			}
		}

		#output file
		$resultsarray | Export-csv $fileName -notypeinformation

	}
)

CAML Query to Return Items Less Than or Greater Than Now

Quick example how to query a list or library for items created more than 5 minutes ago. You can use the Get-Date cmdlet to increment all sorts of values.

$nowMinus5Minutes = [Microsoft.SharePoint.Utilities.SPUtility]::CreateISO8601DateTimeFromSystemDateTime((Get-Date).AddMinutes(-5))
$nowPlus5Minutes = [Microsoft.SharePoint.Utilities.SPUtility]::CreateISO8601DateTimeFromSystemDateTime((Get-Date).AddMinutes(5))

#get items created less than 5 minutes ago
		$caml = '<Where><Lt><FieldRef Name="Modified" /><Value Type="DateTime">' + $nowMinus5Minutes + '</Value></Lt></Where>'
		$sQuery.Query = $caml

SharePoint Workflow Not Starting

For one reason or another, SharePoint Designer Workflows are not always starting when an item is added to a library.  This happens with document libraries and InfoPath form libraries.  I’ve seen this happen with SharePoint 2007 and SharePoint 2010.

What I’ve created is a way to identify, monitor, and start workflows.

Create two lists:

Workflow Monitor

Fields:

Site URL, List Name, Workflow Name

Workflow Monitor Logging

Fields:

Site URL, List Name, Workflow Name, List Item ID

All of the field types are the default single line of text.

Workflow Monitor will be populated with the site url, list name, and workflow name of the workflow you want to monitor.

Place the script in a folder on one of your SharePoint server. Then setup a scheduled task to run the script as needed.


&amp;lt;#
 loop through workflow monitor list
 get workflow item
 query associated list by created date between yesterday and today AND workflow field is empty
 if item is returned, start workflow on item.
 log item that was not started
 if there are any errors send email to DL SharePoint
#&amp;gt;

if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null)
{
    Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}

$cSite = Get-SPWeb "http://sharepointed.com"
$cList = $cSite.Lists["Workflow Monitor"]
$cListLog = $cSite.Lists["Workflow Monitor Logging"]
$errorCount = 0
$errorString = ""

foreach($config in $cList.Items)
{
	try
	{
		$fSite = Get-SPWeb $config["Site URL"].ToString()
		$wfManager = $fSite.Site.WorkFlowManager
		$fList = $fSite.Lists[$config["List Name"].ToString()]
		$fWFfield = $fList.Fields[$config["Workflow Name"].ToString()].InternalName

		#Get the list workflow
		$wfAssoc = $fList.WorkflowAssociations.GetAssociationByName($config["Workflow Name"].ToString(),"en-US")
		$wfData = $wfAssoc.AssociationData

		$sQuery = New-Object Microsoft.SharePoint.SPQuery 

		#Get all item that were created in the past day and a workflow has not ran.
		$caml = '&amp;lt;Where&amp;gt;&amp;lt;And&amp;gt;&amp;lt;IsNull&amp;gt;&amp;lt;FieldRef Name="' + $fWFfield + '" /&amp;gt;&amp;lt;/IsNull&amp;gt;&amp;lt;Geq&amp;gt;&amp;lt;FieldRef Name="Created" /&amp;gt;&amp;lt;Value Type="DateTime"&amp;gt;&amp;lt;Today OffsetDays="-1" /&amp;gt;&amp;lt;/Value&amp;gt;&amp;lt;/Geq&amp;gt;&amp;lt;/And&amp;gt;&amp;lt;/Where&amp;gt;'
		$sQuery.Query = $caml
		$fItems = $fList.GetItems($sQuery)

		foreach($lItem in $fItems)
		{
			#Start workflow
			$wf = $wfManager.StartWorkFlow($lItem,$wfAssoc,$wfData,$true)

			#Create Log entry
			$newLogItem = $cListLog.Items.Add()
			$newLogItem["Site URL"] = $config["Site URL"]
			$newLogItem["List Name"] = $config["List Name"]
			$newLogItem["List Item ID"] = $lItem["ID"]
			$newLogItem["Workflow Name"] = $config["Workflow Name"]
			$newLogItem.Update()
		}
	}
	Catch
	{
		#string.format
		$errorMessage = $_.Exception.Message.ToString()
		$errorString += $config["ID"].ToString() + " " + $errorMessage + " --- "
		$errorCount++
	}
}

#If there are any errors send email
if($errorCount -gt 0)
{
	$errorString = $errorString.TrimEnd(" --- ")

	$emailSubject = "Workflow Montior Script Error"
	$emailBody = "Error running the Workflow Monitor script. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt; &amp;lt;b&amp;gt;Error: &amp;lt;/b&amp;gt;"
	$emailBody += "$errorString &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;"
	$emailBody += "&amp;lt;a href=$cList.URL.ToString()&amp;gt;Workflow Monitor List &amp;lt;/a&amp;gt;"
	$emailsmtpServer = "mail.doman.net"
	$emailTo = "you@sharepointed.com"
	$emailFrom = "alerts@sharepointed.com"

	Send-MailMessage -From $emailFrom -To $emailTo -Subject $emailSubject -BodyAsHtml $emailBody -SmtpServer $emailsmtpServer
}

This script will inventory your entire farm and output workflows that have failed to start int the past 59 days. *You can adjust the 59 day setting, but my farm is setup to truncate workflow history every 60 days.*


$contentWebAppServices = (Get-SPFarm).services |
? {$_.typename -eq "Microsoft SharePoint Foundation Web Application"}

$stringBuilder = New-Object System.Text.StringBuilder
$list = New-Object System.Collections.Generic.List[System.String]
$counter = 0

foreach($webApp in $contentWebAppServices.WebApplications)
{
	$webApp = Get-SPWebApplication $webApp.Url

	if($webApp -ne $null)
	{
		foreach($siteColl in $webApp.Sites)
		{
			if($siteColl -ne $null)
			{
				foreach($subWeb in $siteColl.AllWebs)
				{
					if($subWeb -ne $null)
					{
						foreach($list in $subWeb.Lists)
						{
							foreach($wf in $list.WorkflowAssociations)
							{
								if ($wf.Name -notlike "*Previous Version*")
								{
									$subWeb.Site.WorkflowManager
									$wfManager = $subWeb.Site.WorkFlowManager
									$fWFfield = $list.Fields[$wf.Name.ToString()].InternalName

									#Get the list workflow
									$wfAssoc = $list.WorkflowAssociations.GetAssociationByName($wf.Name,"en-US")
									$wfData = $wfAssoc.AssociationData

									if($wfAssoc.AutoStartCreate -eq $true)
									{
										$counter++

										$sQuery = New-Object Microsoft.SharePoint.SPQuery 

										#Get all item that were created in the past day and a workflow has not ran.
										$caml = '&amp;lt;Where&amp;gt;&amp;lt;And&amp;gt;&amp;lt;IsNull&amp;gt;&amp;lt;FieldRef Name="' + $fWFfield + '" /&amp;gt;&amp;lt;/IsNull&amp;gt;&amp;lt;Geq&amp;gt;&amp;lt;FieldRef Name="Created" /&amp;gt;&amp;lt;Value Type="DateTime"&amp;gt;&amp;lt;Today OffsetDays="-59" /&amp;gt;&amp;lt;/Value&amp;gt;&amp;lt;/Geq&amp;gt;&amp;lt;/And&amp;gt;&amp;lt;/Where&amp;gt;'
										$sQuery.Query = $caml
										$fItems = $list.GetItems($sQuery) 

										$null = $stringBuilder.Append($subWeb.URL)
										$null = $stringBuilder.Append(",")
										$null = $stringBuilder.Append($list.Title)
										$null = $stringBuilder.Append(",")
										$null = $stringBuilder.Append($wf.Name.ToString())
										$null = $stringBuilder.Append("`r`n")

									}
								}
							}

						}

						$subWeb.Dispose()
					}
				}
				$siteColl.Dispose()
			}
		}
	}
}

if($counter -gt 0)
{
	out-file -filepath C:\WorkflowOutput.csv -inputobject $stringBuilder.ToString()
}