Cleaning up a drop off library mess

You installed SharePoint, then your power users enabled the content organizer feature all over your farm. This isn’t necessarily a bad thing, but if it’s not being used, turn it off! Less load on the farm, the better off you will be.

You will find plenty of examples on the web about cleaning up this mess, but I took a slightly different approach. Before disabling the feature and deleting the library, I first checked to make sure the library was empty and no content org rules had been created.

Start-SPAssignment -Global

$web = Get-SPWeb "http://webapp.sharepointed.com"
$feature = Get-SPFeature "7ad5272a-2694-4349-953e-ea5ef290e97c" 

   if ($web.Features[$feature.ID]) 
   {
            $corList = $web.lists["Content Organizer Rules"]
            $dolList = $web.lists["Drop Off Library"]
            
            #check for Cont Org Rules and items in Drop Off Library
            if(($corList.ItemCount -eq 0) -and ($dolList.ItemCount -eq 0))
            {
                  #disable the feature
                  Disable-SPFeature -Identity "7ad5272a-2694-4349-953e-ea5ef290e97c" -Url $_.URL -Confirm:$false
                  
                  #remove the Drop Off Library
                  $dolList.AllowDeletion = $true
                  $dolList.Update()
                  $dolList.Delete()
            }
   } 

Stop-SPAssignment -Global

Use PowerShell to Create Year and Month Folders in Library

How do you create a year and month folder structure in a SharePoint document library?

Every year, I needed to create a folder structure in a report library that looks like this:

Next Year – 01 …… 12

Using PowerShell I’m able to create the parent folder (year), then create the subfolders (months).

*NOTE*
This can be executed using a scheduled task or using a workflow in SharePoint Designer with the help of the iLoveSharePoint add in.

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

$spWeb = Get-SPWeb "http://sharepointed.com/sites/reports"
$spDocLib = $spWeb.Lists["TacoReports"]

$yearFolder = (get-date (Get-Date).AddYears(1) -UFormat "%Y")

$targetFolder = $spWeb.GetFolder($spDocLib.RootFolder.ServerRelativeUrl + "/$yearFolder")

if($targetFolder.Exists -eq $false)
{
    $spFolder = $spDocLib.AddItem("",[Microsoft.SharePoint.SPFileSystemObjectType]::Folder,$yearFolder)
	$spFolder.Update()

	$i = 1
	While ($i -le 12)
	{
		$subFolder= "{0:D2}" -f $i
		$spSubFolder = $spDocLib.AddItem($spFolder.Folder.ServerRelativeUrl,[Microsoft.SharePoint.SPFileSystemObjectType]::Folder,$subFolder)
		$spSubFolder.Update()

		$i++
	}
}

Get the current year and add one.
Check if the new folder already exist.
Create the year folder.
Then create the twelve subfolders (one for each month).

Change List or Library URL and Name

Recently had a scenario where I needed to move a bunch of libraries from different sites, to another farm. Every library was titled the same, so when I went to import the libraries, I ran into a small issue. Using a combo of Export-SPweb and Import-SPweb, I moved a library over, then updated the library url and name to the source site name.

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

$webURL = “http://sharepointed.com/sites/taco”

$before = “LibBefore”
$after = “LibAfter”

#Change the URL of the library
$web = Get-SPWeb $webURL
$web.lists[$before].RootFolder.MoveTo($after)
$Web.Dispose()

#Update the library name
$web = Get-SPWeb $webURL
$list = $web.Lists[$after]
$list.Title = $after
$list.Update()
$web.Dispose()

Couple notes.
You might be able to shorten the script to rename and update in a couple lines of code.
If you use the script to change the URL of a list, the list will moved to the root of the site. it will no longer live at site/lists.

*I have not tested this with libraries or list that workflows associated with them. It’s also safe to assume any links to documents would be broken.*

One or more field types are not installed properly. Go to the list settings page to delete these fields

Error:
One or more field types are not installed properly. Go to the list settings page to delete these fields.

After much research, I have found that this error can be the result of a couple things.
1. Programmatically create a site column / content type and the process fails. The result is a broken column that you have to hunt down and remove from the content database.
2. Using a combination of Export-SP and Import-SPweb, an imported list or library will display the error.

In my case, #2 needed to be fixed. Basically, I had a lookup column that had become orphaned from its parent list.
When you look at the field settings (Library/List Settings –> click on your lookup column) you will see that the Get information from: property is empty.

So how do you reconnect or refresh the lookup field property?
Using PowerShell, the fields web ID and source list ID schemaXML need to be populated.

if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null)
{
Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}
$web = Get-SPWeb "http://sharepoint.com/site/"
$list = $web.Lists["Broken LoopField List"]
$lookupList = $web.Lists["Loopup Source List"]

$fixColumn = $list.Fields["My Broken Lookup Field"]

#Change schema XML on the lookup column
$fixColumn.SchemaXml = $fixColumn.SchemaXml.Replace($fixColumn.LookupWebId.ToString(), $web.ID.ToString())
$fixColumn.SchemaXml = $fixColumn.SchemaXml.Replace($fixColumn.LookupList.ToString(), $lookupList.ID.ToString())
$fixColumn.Update()

What’s going on here?
Load the SharePoint snapin.
Get site that holds the broken list / library.
Get broken list / library.
Get source list / library that the broken lookup references.
Get broken field.
Update the broken field schemaXML.

Parameter name: userProfileApplicationProxy

Had a contractor leave the company and return. Often this creates issues because we don’t run a ‘dead account cleaner’ often enough.

To get around this, I run the script below to get the ‘new’ Active Directory account synced with the new Active Directory account.

Script:

$user = Get-SPUser -web "http://sharepointed.com" -Identity "Mydomain\TheUser"
Move-SPUser -Identity $user -newalias "Mydomain\TheUser" -IgnoreSID 

Where I went wrong… I remoted’ into one of the servers in the farm using my personal NT login. By doing this, I was receiving the error below. Once I remoted’ into the server using the Admin account, I was able to run the script.

Error:
Move-SPUser : Value cannot be null.
Parameter name: userProfileApplicationProxy
At line:1 char:12
+ Move-SPUser <<<< -Identity $user -newalias "Mydomain\TheUser" -IgnoreSID + CategoryInfo : InvalidData: (Microsoft.Share...PCmdletMoveUser: SPCmdletMoveUser) [Move-SPUser], ArgumentNullException + FullyQualifiedErrorId : Microsoft.SharePoint.PowerShell.SPCmdletMoveUser

Inventory Web App Using PowerShell

Ask: We need an Excel spreadsheet that will list out all the Sites, List, Libraries for a given Web App in SharePoint.

Found this post link, then modified it for my needs.

function Get-WebAppInventory($WebAppName) {
    [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
    $farm = [Microsoft.SharePoint.Administration.SPFarm]::Local
    foreach ($spService in $farm.Services) {
        if (!($spService -is [Microsoft.SharePoint.Administration.SPWebService])) {
            continue;
        }

        foreach ($webApp in $spService.WebApplications) {
            if ($webApp -is [Microsoft.SharePoint.Administration.SPAdministrationWebApplication]) { continue }
			if($webApp.name -eq $WebAppName)
			{
            foreach ($site in $webApp.Sites) {
                foreach ($web in $site.AllWebs) {
                        foreach ($list in $web.Lists) {
							$data = @{
								"Site" = $site.Url
                                "Web" = $web.Url
                                "list" = $list.Title
								"Type" = $list.BaseType	
								"Item Count" = $list.ItemCount
								"Last Item Modified" = $list.LastItemModifiedDate
							}
							
                            New-Object PSObject -Property $data
         
                    }
                }
            }
		}
        }
    }
}
#Get-WebAppInventory "SharePointed - name.sharepointed.com80" | Out-GridView
Get-WebAppInventory "SharePointed - name.sharepointed.com80" | Export-Csv -NoTypeInformation -Path c:\inventory2.csv

Above you have two options, Out-GridView or Export-CSV. You can use both or comment out one of the lines.

To get the parameter I’m passing to the function, you will need to look in Central Admin at your Web Apps list. Central Admin –> Application Management section –> Manage web applications. (_admin/WebApplicationList.aspx). In there, copy the name of the Web App.

You can modify the $data = @{ section to bring in other properties of the List.

Use PowerShell to Query The SharePoint Search Index

Needed to write a script to validate that the SharePoint crawler was picking up all items in a library.
One of my document libraries had over a 100,000 document, and I needed to make sure all of them were being indexed.
Document library can support millions of documents, IF you use a good foldering structure.

function GetSearchIndex ($site, $file)
	{
		$fOutPut = $null
		$kq = new-object Microsoft.Office.Server.Search.Query.KeywordQuery($site)
		
		$kq.ResultTypes= [Microsoft.Office.Server.Search.Query.ResultType]::RelevantResults
		$kq.QueryText = $file
		$kq.HiddenConstraints = 'scope:"All Sites"'
		$kq.RowLimit = 10
		$res = $kq.Execute()
		
		$table = new-object System.Data.DataTable
		$table.Load($res[$kq.ResultTypes],[System.Data.LoadOption]::OverwriteChanges)
		
		if($table.Rows.Count -eq 0)
		{
			$fOut = "Failed"
		}
		else
		{
			$fOut = "Passed"
		}
		return $fOut
	}

$file = "c:\indexCheck.txt"
$cfcSite = Get-SPWeb "http://SOMEsite.sharepointed.com/sites/test"
$nv = $cfcSite.Lists["bigLibrary"]

$spQuery = New-Object Microsoft.SharePoint.SPQuery 
$spQuery.ViewAttributes = "Scope='Recursive'"
$spQuery.RowLimit = 2000 
$caml = '<OrderBy Override="TRUE"><FieldRef Name="ID"/></OrderBy>' 
$spQuery.Query = $caml 

do
{
    $listItems = $nv.GetItems($spQuery)
    $spQuery.ListItemCollectionPosition = $listItems.ListItemCollectionPosition
    foreach($item in $listItems)
    {
        $sResult = GetSearchIndex "http://test.sharepointed.com" $item.Name
		
		if($sResult -eq "Failed")
		{
			$item.Name | Out-File $file -Append			
		}
    }
}
while ($spQuery.ListItemCollectionPosition -ne $null)

Query the list/library in batches of 2,000.
Looping through the returned items.
Call function to see if the item is in the index (query SharePoint).
From the function, return a value of Passed or Failed.
If Failed is true, log it to a text file on the C:\ drive.

Use PowerShell to Create SharePoint Groups

Simple enough, needed to create a few SharePoint groups and thought PowerShell would be the best answer.


$SiteUrl = "http://sharepointed.com/sites/a"
$Web = Get-SPWeb $SiteUrl

$description = “Super cool stuff”
$permissionLevel = “Read”

$groups = “Group A”, “Group B”, “Group C”

foreach($groupName in $groups)
{
$web.SiteGroups.Add($groupName, $web.SiteUsers[“domain\SomeUser”], $web.SiteUsers[“domain\SomeUser”], $description)
$group = $web.SiteGroups[$groupName]
$roleAssignment = new-object Microsoft.SharePoint.SPRoleAssignment($group)
$roleDefinition = $web.Site.RootWeb.RoleDefinitions[$permissionLevel]
$roleAssignment.RoleDefinitionBindings.Add($roleDefinition)
$web.RoleAssignments.Add($roleAssignment)
$web.Update()
}

$web.SiteGroups.Add(name, owner, default user, description)

Parameters
name
Type: System.String
A string that represents the new group name.
owner
Type: Microsoft.SharePoint.SPMember
An SPMember object that specifies the owner.
defaultUser
Type: Microsoft.SharePoint.SPUser
An SPUser object that specifies the default user for the group.
description
Type: System.String
A string that contains a description for the group.

More details:
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spgroupcollection.add.aspx

To get all the uses / groups from a site or web see this post:

http://www.sharepointed.com/2016/10/17/get-all-groups-and-users-in-a-site-collection-or-web/

 

Use PowerShell to Update Item Permssions in SharePoint

What if you want to update the item permissions on every item in a list or library, BUT you don’t want to trigger an associated workflow?

Why?

Request came in to add a new SharePoint group to all the items in a library.  Options were to update EVERY item in the library and let the workflow update the permissions, update the items ONE AT A TIME…

OR

Use PowerShell to update item permissions and not stress the server(s).


# add powershell snapin
$web = Get-SPWeb -Identity "http://sharepointed.com"
$list = $web.Lists.TryGetList("Taco Time")
$group = "Taco Makers"
$PermissionLevel = "Read"

#get site group and setup permission/role
$group = $web.Groups[$group]
$roleAssignment = new-object Microsoft.SharePoint.SPRoleAssignment($group)
$roleDefinition = $web.RoleDefinitions[$PermissionLevel];
$roleAssignment.RoleDefinitionBindings.Add($roleDefinition);

if ($list -ne $null)
{
	foreach ($item in $list.Items)
	{
		if ($item.HasUniqueRoleAssignments -eq $False)
		{
			$item.BreakRoleInheritance($True)
		}
	       
                if ($web.SiteGroups[$group] -ne $null)
		{
			$item.RoleAssignments.Add($roleAssignment)
		}
		else
		{
			Write-Host "Group is not valid."
		}		
	}
}
$web.Dispose()

Update People Picker Using PowerShell

Recently had a new domain added to our forest. After this happened, users would see duplicate values in the SharePoint people picker. The values would both look like John Doe and John Doe, but their underlying domains were different. This caused all sorts of fun when people started emailing “john doe can’t access my site.”

To fix this, the people picker property for each web application in the farm needed to be updated.

There are a lot of properties and options that you can update. I’m only updating my people picker to show one domain.

More on the people picker:
http://msdn.microsoft.com/en-us/subscriptions/cc263012(v=office.12).aspx


#add the powershell snapin

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

foreach($webApp in $contentWebAppServices.WebApplications)
{
 Set-SPSite $webApp.Url -UserAccountDirectoryPath "DC=YourDomain,DC=Com"
}

What I’m doing is getting all the web applications in the SharePoint farm.  Looping through the applications and updating the people picker.