Fix: The trust relationship between this workstation and the primary domain failed
This short post is really just for my own memory as I keep bumping into this with my virtual machines but I figured others might also find it useful. Typically when I do SharePoint development I do everything on an all-up server but with SharePoint 2013 I’ve moved my Domain Controller to a separate server (where I also will install the Office Web Apps); however, if I leave any of my machines off for a while then the computer password will expire which means that things start to break and you’ll see errors like "The trust relationship between this workstation and the primary domain failed." The common fix is to remove the server from the domain and then join it back in but that takes some time so what I prefer to do is to simply run the following command which will reset the password:
netdom.exe resetpwd /s:domain_controller_name /ud:domain\administrator /pd:*
Be sure to replace the domain_controller_name placeholder with the name of your domain controller server. If you’re not already logged in to the server then you’ll have to log in using a local administrator account. After running the command it will prompt for a password for the specified account (it’s an odd prompt as it doesn’t show any characters being typed – not even masked). After providing the password reboot the server and you should be good to go.
If you want to prevent the server from changing its password in the first place (or prevent the DC from accepting the password change) you can follow the steps in this support article to disable the setting and avoid the issue altogether: http://support.microsoft.com/kb/154501 (more times than not I forget to do this in new development environments which prompted me to post the reset password fix).
UPDATE 4/12/2013: Seems you can do all this using PowerShell: "Test-ComputerSecureChannel -Repair". Thanks Alexey for pointing this out - learn something new every day!
-Gary
Service Accounts and Managed Service Accounts in SharePoint 2010
With SharePoint 2010 we now have the ability to allow SharePoint to manage various service accounts thus foregoing the need to have IT administrators manually manage password changes. This new feature is a great benefit to SharePoint administrators and security conscious admins in general as it allows us to easily enforce our corporate security policies by changing these passwords on a schedule, and the administrators don’t even know what the password is so the likelihood of a compromise due to a disgruntled admin, though not eliminated, is somewhat reduced.
But the introduction of this new feature isn’t all good. The complication comes from the fact that SharePoint 2010 doesn’t implement this capability consistently. So an account that is configured as a Managed Service Account and set to have its password changed automatically could also be used in certain places that don’t understand the managed account concept. When the managed account password is changed the feature that uses that account and only knows the username and password (so it does not use the managed account details) will effectively be broken. As an example, if you configure the Enterprise Search Service to use a managed account whose password is scheduled to be changed every 30 days and you use that same account for the content crawl account then when that password is changed the content crawl will cease to function as it will be unable to authenticate the account. It’s important to note, however, that this issue only comes to light when you configure the managed account to have it’s password changed automatically.
So what things can be managed accounts and what cannot? The following lists what I’ve come across so far (if I’ve missed anything please leave a comment so I can update these lists):
Managed Service Accounts:
- All Service Application Pool Accounts
- Access Service Application
- BCS Service Application
- Excel Services Service Application
- Metadata Service Application
- PerformancePoint Service Application
- Enterprise Search Service Application
- Secure Store Service Application
- Subscription Settings Service Application
- User Profile Service Application
- Visio Services Service Application
- Web Analytics Service Application
- Word Automation Service Application
- Word Viewing Service Application
- PowerPoint Viewing Service Application
- Security Token Service Application
- All Content Web Application Pools
- Service Instances
- Claims to Windows Token Service
- Document Conversion Launcher Service
- Document Conversion Load Balancer Service
- Microsoft SharePoint Foundation Sandboxed Code Service
- SharePoint Foundation Help Search
- SharePoint Server Search (Enterprise Search)
- Web Analytics Data Processing Service
Service Accounts (should not be managed):
- Search Crawl Accounts
- For Foundation Search and Server (Enterprise) Search
- Unattended User Accounts
- Excel Services Service Application
- Visio Services Service Application
- PerformancePoint Service Application
- (in general, any Secure Store application credentials)
- Object Cache Portal Accounts
- Super User Account
- Super Reader Account
- User Profile
- Synchronization Service Account (listed incorrectly on the FarmCredentialManagement.aspx page)
- Synchronization Connection Account
- Server Search Custom Crawl Rule Accounts
- Any crawl rule that specifies an account other than the default crawl account
Again, these are just the accounts that I’ve personally bumped up against so it may not be a complete listing.
Viewing and Creating Managed Accounts
To see the current list of Managed Service Accounts using Central Admin go to Security –> Configure managed accounts:
You can edit the settings for any managed account by simply clicking the edit icon associated with the account you wish to modify. Once on the Manage Account screen you can configure the automatic password change settings:
To perform the same tasks using Windows PowerShell we can use the Get-SPManagedAccount cmdlet to retrieve the list of managed accounts:

Or we can retrieve a specific account using the -Identity parameter or by passing in a Web Application or Service:

clTo change the settings for a Managed Account we can use the Set-SPManagedAccount cmdlet:
To create a new Managed Account we use the New-SPManagedAccount cmdlet. In the example below I’m manually creating a PSCredential object so that I can specify my password (pa$$w0rd) in script (very useful for building out dev or test environments – otherwise you should use Get-Credential to prompt for the password so that it is not hard coded anywhere):

Applying Managed Accounts
Once you have your Managed Accounts created you can begin to use them for things such as Service Instances and Service and Content Application Pools. To associate a managed account with a specific Service Instance using Central Admin you can go to Security –> Configure service accounts. On the Service Accounts page you can set the account used for the Farm Account, Service Instances, Web Content Application Pools, and Service Application Pools. The Service Instances are highlighted in the following image:
Service Instances
To set the account associated with a particular Service Instance using Windows PowerShell we simply get the ProcessIdentity property of the Service Instance and set its Username property. Once set we call Update() to update the Configuration Database and then Deploy() to push the change out to all Service Instances. To make this easier I put this code in a function that I can call by passing in the Service Instance and credentials to use:
function Set-ServiceIdentity($svc, $username)
{
$pi = $svc.Service.ProcessIdentity
if ($pi.Username-ne $username) {
$pi.Username= $username
$pi.Update()
$pi.Deploy()
}
}
Here’s an example of how you can call this function:

Service Application Pools
To create a new Service Application pool we use the New-SPServiceApplicationPool cmdlet and pass in the name of the Application Pool to create and the Managed Account to assign as the Application Pool identity:

It’s extremely important to note that the application pool that you create using the New-ServiceApplicationPool cmdlet cannot be used for your content Web Applications. Unfortunately there is no out-of-the-box equivalent for creating Application Pools for Web Applications.
Web Application Pools
As previously noted there is no cmdlet for creating Application Pools for Web Applications. Instead what you need to do is first check if the Application Pool you need already exists by using the SPWebService’s ContentService static property. If it exists then pass in just the name of the Application Pool to the New-SPWebApplication cmdlet, otherwise pass in the name and the Managed Account to use as the Application Pool’s identity:

Applying Service Accounts
When it comes to applying non-managed accounts to the various features things get a little more complicated. Let’s start with the Crawl Accounts.
SharePoint Foundation Search Service
For SharePoint Foundation Search we can set the crawl account (or content access account) using Central Admin by navigating to the Services on Server page and clicking the SharePoint Foundation [Help] Search link which takes you to the settings page where we can set the crawl account:
To set the same information using Windows PowerShell we actually have to go old-school and use STSADM as there’s no PowerShell equivalent cmdlet. Here’s a snippet of PowerShell code that I use to accomplish this:
function ConvertTo-UnsecureString([System.Security.SecureString]$string) { $unmanagedString = [System.Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($string) $unsecureString = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($unmanagedString) [System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($unmanagedString) return $unsecureString } $searchSvcAccount = Get-Credential "localdev\spsearchsvc" $crawlAccount = Get-Credential "localdev\spcrawl" $stsadmArgs = "-o spsearch -action start " + ` "-farmserviceaccount `"$($searchSvcAccount.Username)`" " + ` "-farmservicepassword `"$(ConvertTo-UnsecureString $searchSvcAccount.Password)`" " + ` "-farmcontentaccessaccount `"$($crawlAccount.Username)`" " + ` "-farmcontentaccesspassword `"$(ConvertTo-UnsecureString $crawlAccount.Password)`" " + ` "-databaseserver `"spsql1`" " + ` "-databasename `"SharePoint_FoundationSearch`"" Write-Host "Running: stsadm $stsadmArgs" $stsadmoutput = cmd /c "stsadm $stsadmArgs" 2>&1 if ($lastexitcode -ne 0) { throw "Unable to start Foundation Search Service.`n$stsadmoutput" }
Note that I’m using a helper function to convert the secure password to a static string which I can then pass to the STSADM spsearch command.
SharePoint Server Search Service
To manage the crawl account for the SharePoint Server Search Service (also known as the Enterprise Search Service) using Central Admin we simply need to navigate to the Search Administration page of the Service Application that we wish to modify and click the link for the Default content access account. This will bring up the following screen:
Note that by default this account will be set to be the same account you used for the Search Service Instance which is a Managed Account. If you do not change this account and you have configured SharePoint to manage the account password then your crawls will fail when the password changes. To make this change using Windows PowerShell we use the Set-SPEnterpriseSearchServiceApplication cmdlet:
$crawlAccount = Get-Credential "localdev\spcrawl"
$searchApp | Set-SPEnterpriseSearchServiceApplication -DefaultContentAccessAccountPassword $crawlAccount.Password -DefaultContentAccessAccountName $crawlAccount.Username
Remember not to do this step until after you have provisioned the Administration Component.
Object Cache Accounts
Many administrators when they first configure SharePoint 2010 and hit a Web Application for the first time are likely to see a recurring event in the event log stating that the object cache has not been configured correctly. The specific error is as follows:
Object Cache: The super user account utilized by the cache is not configured. This can increase the number of cache misses, which causes the page requests to consume unneccesary system resources.
This is essentially telling you that you have missed a manual configuration step in which you need to run some PowerShell to set two accounts for SharePoint to use to access the object cache:
function Set-WebAppUserPolicy($webApp, $userName, $userDisplayName, $perm) { [Microsoft.SharePoint.Administration.SPPolicyCollection]$policies = $webApp.Policies [Microsoft.SharePoint.Administration.SPPolicy]$policy = $policies.Add($userName, $userDisplayName) [Microsoft.SharePoint.Administration.SPPolicyRole]$policyRole = $webApp.PolicyRoles | where {$_.Name -eq $perm} if ($policyRole -ne $null) { $policy.PolicyRoleBindings.Add($policyRole) } $webApp.Update() } $webApp = Get-SPWebApplication "http://content" $portalSuperUserAccount = Get-Credential "localdev\SPSuperUser" $webApp.Properties["portalsuperuseraccount"] = $portalSuperUserAccount.UserName Set-WebAppUserPolicy $webApp $portalSuperUserAccount.UserName $portalSuperUserAccount.UserName "Full Control" $portalSuperReaderAccount = Get-Credential "localdev\SPSuperReader" $webApp.Properties["portalsuperreaderaccount"] = $portalSuperReaderAccount.UserName Set-WebAppUserPolicy $webApp $portalSuperReaderAccount.UserName $portalSuperReaderAccount.UserName "Full Read"
Make sure that you do not use the same account for both the super user and super reader. (And of course make sure you change the URL and account names to match your environment). For more information about these settings see the following TechNet article: http://technet.microsoft.com/en-us/library/ff758656.aspx
Unattended Accounts
There are some services, specifically the Visio Services Service Application, the Excel Services Service Application, and the PerformancePoint Service Application, that allow us to set an account that we can use for access data sources behind the scenes. These are called unattended access accounts. To set these accounts we must create a new target application in the Secure Store Service Application and associate the target application’s ID with the appropriate Service Application. The following PowerShell code demonstrates how to do this for the Visio Services Service Application (the Excel Services Service Application is virtually identical and just uses cmdlets specific to Excel rather than Visio; PerformancePoint is a lot simpler):
#Get the Visio Service App $svcApp = Get-SPServiceApplication | where {$_.TypeName -like "*Visio*"} #Get the existing unattended account app ID $unattendedServiceAccountApplicationID = ($svcApp | Get-SPVisioExternalData).UnattendedServiceAccountApplicationID #If the account isn't already set then set it if ([string]::IsNullOrEmpty($unattendedServiceAccountApplicationID)) { #Get our credentials $unattendedAccount = Get-Credential "localdev\SPUnattended" #Set the Target App Name and create the Target App $name = "$($svcApp.ID)-VisioUnattendedAccount" Write-Host "Creating Secure Store Target Application $name..." $secureStoreTargetApp = New-SPSecureStoreTargetApplication -Name $name ` -FriendlyName "Visio Services Unattended Account Target App" ` -ApplicationType Group ` -TimeoutInMinutes 3 #Set the group claim and admin principals $groupClaim = New-SPClaimsPrincipal -Identity "nt authority\authenticated users" -IdentityType WindowsSamAccountName $adminPrincipal = New-SPClaimsPrincipal -Identity "$($env:userdomain)\$($env:username)" -IdentityType WindowsSamAccountName #Set the account fields $usernameField = New-SPSecureStoreApplicationField -Name "User Name" -Type WindowsUserName -Masked:$false $passwordField = New-SPSecureStoreApplicationField -Name "Password" -Type WindowsPassword -Masked:$false $fields = $usernameField, $passwordField #Set the field values $secureUserName = ConvertTo-SecureString $unattendedAccount.UserName -AsPlainText -Force $securePassword = $unattendedAccount.Password $credentialValues = $secureUserName, $securePassword #Get the service context $subId = [Microsoft.SharePoint.SPSiteSubscriptionIdentifier]::Default $context = [Microsoft.SharePoint.SPServiceContext]::GetContext($svcApp.ServiceApplicationProxyGroup, $subId) #Check to see if the Secure Store App already exists $secureStoreApp = Get-SPSecureStoreApplication -ServiceContext $context -Name $name -ErrorAction SilentlyContinue if ($secureStoreApp -eq $null) { #Doesn't exist so create. Write-Host "Creating Secure Store Application..." $secureStoreApp = New-SPSecureStoreApplication -ServiceContext $context ` -TargetApplication $secureStoreTargetApp ` -Administrator $adminPrincipal ` -CredentialsOwnerGroup $groupClaim ` -Fields $fields } #Update the field values Write-Host "Updating Secure Store Group Credential Mapping..." Update-SPSecureStoreGroupCredentialMapping -Identity $secureStoreApp -Values $credentialValues #Set the unattended service account application ID $svcApp | Set-SPVisioExternalData -UnattendedServiceAccountApplicationID $name }
When it comes to PerformancePoint we have a lot less work we need to do as the product team was nice enough to make it so that the Set-SPPerformancePointSecureDataValues does all the work of setting up the target application for us (note though that they did screw up how the Service Application is passed into the cmdlet requiring you to pass in the ID of the Service Application rather than the actual Service Application object):
$unattendedAccount = Get-Credential "localdev\SPUnattended" $secureValues = Get-SPPerformancePointSecureDataValues -ServiceApplication $svcApp.Id if ($secureValues.DataSourceUnattendedServiceAccount -ne $unattendedServiceAccount.UserName) { Write-Host "Setting unattended service account $($unattendedServiceAccount.UserName)..." $svcApp.Id | Set-SPPerformancePointSecureDataValues -DataSourceUnattendedServiceAccount $unattendedServiceAccount }
User Profile Synchronization Service Identity
One thing to watch out for is when setting the account for the User Profile Synchronization Service. This service wants you to use the Farm Account as the identity. This means that your Farm Admin account cannot have it’s password managed by SharePoint if you intend to use this service (or at least, it shouldn’t be unless you don’t mind manually fixing this service every time your password changes – good luck with that BTW). Your Farm Admin account will always be a Managed Account (you can’t change that) so be extra careful when changing this accounts password (either manually or automatically). To set this account using Central Admin you can click Start next to the User Profile Synchronization Service entry on the Services on Server page.
To accomplish the same thing using PowerShell we need to get an instance of the Synchronization Service and set a few properties and call the SetSynchronizationMachine method passing in the username and password of the Farm Admin account (note that it requires the password be passed in as a standard string and not a secure string so I use my previously defined ConvertTo-UnsecureString function):
$syncMachine = Get-SPServer "sp2010dev" $profApp = Get-SPServiceApplication | where {$_.Name -eq "User Profile Service Application 1"} $account = Get-Credential "localdev\spfarm" if ($syncMachine.Address -eq $env:ComputerName) { $syncSvc = Get-SPServiceInstance -Server $env:ComputerName | where {$_.TypeName -eq "User Profile Synchronization Service"} $syncSvc.Status = [Microsoft.SharePoint.Administration.SPObjectStatus]::Provisioning $syncSvc.IsProvisioned = $false $syncSvc.UserProfileApplicationGuid = $profApp.Id $syncSvc.Update() $profApp.SetSynchronizationMachine($syncMachine.Address, $syncSvc.Id, $account.UserName, (ConvertTo-UnsecureString $account.Password)) } if ($syncSvc.Status -ne "Online") { Write-Host "Starting User Profile Synchronization Service..." Start-SPServiceInstance $syncSvc } do {Start-Sleep 2} while ((Get-SPServiceInstance -Server $env:ComputerName | where {$_.TypeName -eq "User Profile Synchronization Service"}).Status -ne "Online")
Summary
As you can see setting the accounts that are used throughout SharePoint 2010 is anything but consistent and in some cases a real pain in the a$$. I know I didn’t cover how to set every account (custom crawl rule accounts, user profile sync connection accounts, others?) but hopefully someone out there has already documented these, or if not perhaps they’d be nice enough to post a comment here for others benefit from (maybe one day I’ll add them myself but for now I think this post is quite long enough). As always, please let me know if I’ve missed something or otherwise got something wrong as I certainly don’t claim to have all the answers.
Happy PowerShelling ![]()
Discovering Who Has Access to SharePoint 2010 Securable Objects
I've talked on several occasions about how we can easily use the SharePoint 2010 object model (OM) to discover who has access to a securable object (SPWeb, SPList, or SPListItem) and the fact that we can use the same mechanisms within PowerShell to create useful security/audit reports. On some of those occasions I've shown a version of a PowerShell script which gives you a dump to the screen or a text file of every securable object and who has access to it and how they were given access to it - today I'd like to share a new version of that script.
Before we get to the actual script let's first talk about how to get the information. All securable objects have a method named GetUserEffectivePermissionInfo which is defined in the abstract base class SPSecurableObject (in 2007 this method was defined directly on the SPWeb, SPList, and SPListItem objects). This method returns back an SPPermissionInfo object which we can use to inspect the various role definition bindings and corresponding permission levels.
Once we have the permission details we simple loop through the SPRoleAssignments objects via the RoleAssignments property. This will give us information about how the user is given access to the resource. Next we look at the RoleDefinitionBindings property which returns back a collection of SPRoleDefinition objects that tell us about the type of access granted (e.g., Full Control, etc.).
I then take all this information, stick it in a hash table which I then use to create a new object which gets written to the pipeline.
So with that, let's take a look at the code:
function Get-SPUserEffectivePermissions( [object[]]$users, [Microsoft.SharePoint.SPSecurableObject]$InputObject) { begin { } process { $so = $InputObject if ($so -eq $null) { $so = $_ } if ($so -isnot [Microsoft.SharePoint.SPSecurableObject]) { throw "A valid SPWeb, SPList, or SPListItem must be provided." } foreach ($user in $users) { # Set the users login name $loginName = $user if ($user -is [Microsoft.SharePoint.SPUser] -or $user -is [PSCustomObject]) { $loginName = $user.LoginName } if ($loginName -eq $null) { throw "The provided user is null or empty. Specify a valid SPUser object or login name." } # Get the users permission details. $permInfo = $so.GetUserEffectivePermissionInfo($loginName) # Determine the URL to the securable object being evaluated $resource = $null if ($so -is [Microsoft.SharePoint.SPWeb]) { $resource = $so.Url } elseif ($so -is [Microsoft.SharePoint.SPList]) { $resource = $so.ParentWeb.Site.MakeFullUrl($so.RootFolder.ServerRelativeUrl) } elseif ($so -is [Microsoft.SharePoint.SPListItem]) { $resource = $so.ParentList.ParentWeb.Site.MakeFullUrl($so.Url) } # Get the role assignments and iterate through them $roleAssignments = $permInfo.RoleAssignments if ($roleAssignments.Count -gt 0) { foreach ($roleAssignment in $roleAssignments) { $member = $roleAssignment.Member # Build a string array of all the permission level names $permName = @() foreach ($definition in $roleAssignment.RoleDefinitionBindings) { $permName += $definition.Name } # Determine how the users permissions were assigned $assignment = "Direct Assignment" if ($member -is [Microsoft.SharePoint.SPGroup]) { $assignment = $member.Name } else { if ($member.IsDomainGroup -and ($member.LoginName -ne $loginName)) { $assignment = $member.LoginName } } # Create a hash table with all the data $hash = @{ Resource = $resource "Resource Type" = $so.GetType().Name User = $loginName Permission = $permName -join ", " "Granted By" = $assignment } # Convert the hash to an object and output to the pipeline New-Object PSObject -Property $hash } } } } end {} }
Great - we've got the code - so now you're probably asking, "how the heck do I use it?" Well the first thing you need to do is save it to a file, let's call it SecurityReport.ps1 and we'll put it in the root of the C drive. Once saved we can load it in memory using the following:
C:\ PS> . .\SecurityReport.ps1
Now for the fun stuff
. The examples I'm going to show will build off of each other and will eventually conclude with an example that gives me a report for all users and all securable objects throughout the entire farm. The first example I want to show is how to retrieve a report for a single user and a single web (we'll reuse the $user variable throughout the script so I'll only define it once here):
$user = "sp2010\siteowner2" Get-SPWeb http://portal | Get-SPUserEffectivePermissions $user | Out-GridView -Title "Web Permissions for $user"
Running this command will generate a grid view as shown here:

Note that I could have just as easily saved the results to a CSV file which I could then open in Excel using the Export-Csv cmdlet:
Get-SPWeb http://portal | Get-SPUserEffectivePermissions $user | Export-Csv -NoTypeInformation -Path c:\perms.csv
For this next example I'm going to show the permissions for the same user for ALL webs throughout the entire farm (note that this won't include lists or items):
Get-SPSite -Limit All | Get-SPWeb | Get-SPUserEffectivePermissions $user | Out-GridView -Title "All Web Permissions for $user"
Now I want to get the permissions for the same user for all lists throughout the entire farm:
Get-SPSite -Limit All | Get-SPWeb | %{$_.Lists | Get-SPUserEffectivePermissions $user} | Out-GridView -Title "List Permissions for $user"
Now we're going to get nice and deep and show the permissions for every single item throughout the entire farm (probably don't want to run this on any front-end servers):
Get-SPSite -Limit All | Get-SPWeb | %{$_.Lists | %{$_.Items | Get-SPUserEffectivePermissions $user}} | Out-GridView -Title "Item Permissions for $user"
So now that I've shown you how to get the individual securable objects results throughout the farm for a single user let's now go ahead and stitch them together into one report:
Get-SPSite -Limit All | ForEach-Object { $site = $_ $webPermissions += $site | Get-SPWeb –Limit All | Get-SPUserEffectivePermissions $user $listPermissions += $site | Get-SPWeb –Limit All | %{$_.Lists | Get-SPUserEffectivePermissions $user} $itemPermissions += $site | Get-SPWeb –Limit All | %{$_.Lists | %{$_.Items | Get-SPUserEffectivePermissions $user}} $site.Dispose(); } $webPermissions + $listPermissions + $itemPermissions | Out-GridView -Title "Web, List, and Item Permissions for $user"
In this example I'm simply performing the same calls but appending to an array of objects and then dumping the combination of those arrays to the grid. Note that in this case I'm calling $site.Dispose() but below I'll be using the SPAssignmentCollection to dispose of objects - keep reading for an explanation.
So now lets take it one step further and see how we can get the same reports but this time for every user. We'll start with webs again - in this example we'll get the permissions for all users for a given site:
$gc = Start-SPAssignment $site = $gc | Get-SPSite http://portal $site | Get-SPWeb –Limit All | Get-SPUserEffectivePermissions ($site.RootWeb.SiteUsers | select LoginName) | Out-GridView -Title "Web Permissions for All Users In $($site.Url)" $gc | Stop-SPAssignment
As you can see I'm basically using the SiteUsers property from the root web and passing the login name for each user into the function. Note that here I'm using the Start-SPAssignment and Stop-SPAssignment cmdlets - that's because I'm using the SPSite object after the pipeline execution finishes (as opposed to the above) so I need to make sure it gets disposed (I could just as easily called Dispose on the object as I did above but I'm attempting to demonstrate when/why you'd use the assignment collections).
Now lets see the lists:
$gc = Start-SPAssignment $site = $gc | Get-SPSite http://portal $site | Get-SPWeb –Limit All | %{$_.Lists | Get-SPUserEffectivePermissions ($site.RootWeb.SiteUsers | select LoginName)} | Out-GridView -Title "List Permissions for All Users in $($site.Url)" $gc | Stop-SPAssignment
Starting to see a pattern? Let's take a look at the list items now:
$gc = Start-SPAssignment $site = $gc | Get-SPSite http://portal $site | Get-SPWeb –Limit All | %{$_.Lists | %{$_.Items | Get-SPUserEffectivePermissions ($site.RootWeb.SiteUsers | select LoginName)}} | Out-GridView -Title "Item Permissions for All Users in $($site.Url)" $gc | Stop-SPAssignment
Great! So now lets piece this last bit together so we can see the permissions for all webs, lists, and list items for every user within a single site collection:
$gc = Start-SPAssignment $site = $gc | Get-SPSite http://portal $webPermissions = $site | Get-SPWeb –Limit All | Get-SPUserEffectivePermissions ($site.RootWeb.SiteUsers | select LoginName) $listPermissions = $site | Get-SPWeb –Limit All | %{$_.Lists | Get-SPUserEffectivePermissions ($site.RootWeb.SiteUsers | select LoginName)} $itemPermissions = $site | Get-SPWeb –Limit All | %{$_.Lists | %{$_.Items | Get-SPUserEffectivePermissions ($site.RootWeb.SiteUsers | select LoginName)}} $webPermissions + $listPermissions + $itemPermissions Out-GridView -Title "Web, List, and Item Permissions for All Users in $($site.Url)" $gc | Stop-SPAssignment
Alright, we're almost done - let's now stitch this all together and generate a single report showing all permissions for all securable objects (webs, lists, and list items) for every user within every site collection:
Get-SPSite -Limit All | ForEach-Object { $site = $_ $webPermissions += $site | Get-SPWeb –Limit All | Get-SPUserEffectivePermissions ($site.RootWeb.SiteUsers | select LoginName) $listPermissions += $site | Get-SPWeb –Limit All | %{$_.Lists | Get-SPUserEffectivePermissions ($site.RootWeb.SiteUsers | select LoginName)} $itemPermissions += $site | Get-SPWeb –Limit All | %{$_.Lists | %{$_.Items | Get-SPUserEffectivePermissions ($site.RootWeb.SiteUsers | select LoginName)}} $site.Dispose(); } $webPermissions + $listPermissions + $itemPermissions | Out-GridView -Title "Web, List, and Item Permissions for All Users in All Sites"
Note in this last example, as I did previously when looping through all site collections, I'm calling the Dispose() method inside the ForEach-Object script block. I do this because objects wouldn't otherwise get disposed until the pipeline execution has finished and because it's continuing to iterate so the pipeline has not yet completed. If I used the assignment collection I wouldn't get a disposal until after I'm done iterating which would be too late - I want to dispose right when I'm done with the individual SPSite objects to avoid out of memory errors.
Reporting on who has access to what is one of the things I get asked about most frequently so hopefully this code sample and corresponding examples will prove to be useful to people. One possible area of improvement to the script would be to accommodate groups being passed in - right now I'm only considering users; and of course you could easily turn the example usages into functions. As always, if anyone has any feedback (bugs, improvements, etc.) please post here so that myself and others may benefit.
Setting Back Connection Host Names for SharePoint 2007 Using STSADM
Not too long ago Microsoft introduced a security fix which addresses a possible attack vector in which malicious software tries to impersonate a local request, thereby bypassing certain constraints. The problem with this fix is that it introduces some issues for SharePoint servers, effectively resulting in 401.1 Access Denied errors. Spence Harbar does a great write-up of the fix and the options available to get your SharePoint environment working again so I won't re-hash all that here: http://www.harbar.net/archive/2009/07/02/disableloopbackcheck-amp-sharepoint-what-every-admin-and-developer-should-know.aspx.
As Spence points out, the preferred way to fix this is to add the host names to the BackConnectionHostNames registry key and to not set the DisableLoopbackCheck registry key. You can of course do this using Group Policy but for those not managing their servers using GPO I decided to implement a custom STSADM command that would make setting the BackConnectionHostNames registry key really simple. I called this new command, oddly enough, gl-setbackconnectionhostnames.
The command has two ways to run it, you can run it without any parameters which will cause it to update only the server in which the command is executed on, or you can pass in an -updatefarm parameter along with a username and password which will cause it to update every server in the farm. There's no need to pass in the host header names as the code will dynamically determine them by inspecting each web application and their alternate access mappings (alternate URLs) and perform some logic to determine whether the host header is pointing to a local IP address or to a specific SharePoint server (I do this to exclude Central Admin which is usually accessed using a server name and non-standard port).
I accomplish the farm update by using a custom Timer Job which executes on each server. Unfortunately the timer service account does not have access to write to the registry (unless you've given it rights, which you shouldn't) so it was necessary to pass in a username and password and then use impersonation to update the registry. The custom timer job code is shown below, notice that all the core work is being done via the SetBackConnectionHostNames class which is shown below the timer job code:
1: using System;
2: using System.Collections.Generic;
3: using System.Runtime.InteropServices;
4: using System.Security.Principal;
5: using System.Text;
6: using Microsoft.SharePoint;
7: using Microsoft.SharePoint.Administration;
8:
9: namespace Lapointe.SharePoint.STSADM.Commands.WebApplications
10: {
11: public class SetBackConnectionHostNamesTimerJob : SPJobDefinition
12: {
13: public const int LOGON32_LOGON_INTERACTIVE = 2;
14: public const int LOGON32_LOGON_SERVICE = 3;
15: public const int LOGON32_PROVIDER_DEFAULT = 0;
16:
17: [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
18: public static extern bool LogonUser(
19: String lpszUserName,
20: String lpszDomain,
21: String lpszPassword,
22: int dwLogonType,
23: int dwLogonProvider,
24: ref IntPtr phToken
25: );
26:
27: [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
28: public extern static bool CloseHandle(IntPtr handle);
29:
30: private const string JOB_NAME = "job-set-back-connection-host-names-";
31: private const string KEY_USER = "userName";
32: private const string KEY_PWD = "password";
33:
34: private static readonly string jobId = Guid.NewGuid().ToString();
35:
36: public SetBackConnectionHostNamesTimerJob() : base() { }
37:
38: /// <summary>
39: /// Initializes a new instance of the <see cref="SetBackConnectionHostNamesTimerJob"/> class.
40: /// </summary>
41: public SetBackConnectionHostNamesTimerJob(SPService service)
42: : base(JOB_NAME + jobId, service, null, SPJobLockType.None)
43: {
44: Title = "Set BackConnectionHostNames Registry Key";
45: }
46:
47: /// <summary>
48: /// Executes the job definition.
49: /// </summary>
50: /// <param name="targetInstanceId">For target types of <see cref="T:Microsoft.SharePoint.Administration.SPContentDatabase"></see> this is the database ID of the content database being processed by the running job. This value is Guid.Empty for all other target types.</param>
51: public override void Execute(Guid targetInstanceId)
52: {
53: string user = Properties[KEY_USER] as string;
54: string password = Properties[KEY_PWD] as string;
55:
56: if (string.IsNullOrEmpty(user) || password == null)
57: throw new ArgumentNullException("Username and password is required.");
58:
59: if (user.IndexOf('\\') < 0)
60: throw new ArgumentException("Username must be in the form \"DOMAIN\\USER\"");
61:
62: IntPtr userHandle = new IntPtr(0);
63: WindowsImpersonationContext impersonatedUser = null;
64:
65: bool returnValue = LogonUser(
66: user.Split('\\')[1],
67: user.Split('\\')[0],
68: password,
69: LOGON32_LOGON_INTERACTIVE,
70: LOGON32_PROVIDER_DEFAULT,
71: ref userHandle
72: );
73:
74: if (!returnValue)
75: {
76: throw new Exception("Invalid Username");
77: }
78: WindowsIdentity newId = new WindowsIdentity(userHandle);
79: impersonatedUser = newId.Impersonate();
80:
81: SetBackConnectionHostNames.SetBackConnectionRegKey(SetBackConnectionHostNames.GetUrls());
82:
83: impersonatedUser.Undo();
84: CloseHandle(userHandle);
85: }
86:
87: /// <summary>
88: /// Submits the job.
89: /// </summary>
90: public void SubmitJob(string user, string password)
91: {
92: Properties[KEY_USER] = user;
93: Properties[KEY_PWD] = password;
94: Schedule = new SPOneTimeSchedule(DateTime.Now);
95: Update();
96: }
97: }
98: }
The following is the code for the SetBackConnectionHostNames class:
1: using System;
2: using System.Collections;
3: using System.Collections.Generic;
4: using System.Collections.Specialized;
5: using System.Net;
6: using System.Text;
7: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
8: using Microsoft.SharePoint;
9: using Microsoft.SharePoint.Administration;
10: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
11: using Microsoft.Win32;
12:
13: namespace Lapointe.SharePoint.STSADM.Commands.WebApplications
14: {
15: public class SetBackConnectionHostNames : SPOperation
16: {
17: /// <summary>
18: /// Initializes a new instance of the <see cref="SetBackConnectionHostNames"/> class.
19: /// </summary>
20: public SetBackConnectionHostNames()
21: {
22:
23: SPParamCollection parameters = new SPParamCollection();
24: parameters.Add(new SPParam("updatefarm", "uf"));
25: parameters.Add(new SPParam("username", "user", false, null, new SPNonEmptyValidator()));
26: parameters.Add(new SPParam("password", "pwd", false, null, new SPNullOrNonEmptyValidator()));
27:
28: StringBuilder sb = new StringBuilder();
29: sb.Append("\r\n\r\nSets the BackConnectionHostNames registry key with the URLs associated with each web application.\r\n\r\nParameters:");
30: sb.Append("\r\n\t[-updatefarm (update all servers in the farm)]");
31: sb.Append("\r\n\t[-username <DOMAIN\\user (must have rights to update the registry on each server)>]");
32: sb.Append("\r\n\t[-password <password>]");
33:
34: Init(parameters, sb.ToString());
35: }
36:
37: #region ISPStsadmCommand Members
38:
39: /// <summary>
40: /// Gets the help message.
41: /// </summary>
42: /// <param name="command">The command.</param>
43: /// <returns></returns>
44: public override string GetHelpMessage(string command)
45: {
46: return HelpMessage;
47: }
48:
49: /// <summary>
50: /// Runs the specified command.
51: /// </summary>
52: /// <param name="command">The command.</param>
53: /// <param name="keyValues">The key values.</param>
54: /// <param name="output">The output.</param>
55: /// <returns></returns>
56: public override int Execute(string command, StringDictionary keyValues, out string output)
57: {
58: output = string.Empty;
59:
60: if (!Params["updatefarm"].UserTypedIn)
61: SetBackConnectionRegKey(GetUrls());
62: else
63: {
64: SPTimerService timerService = SPFarm.Local.TimerService;
65: if (null == timerService)
66: {
67: throw new SPException("The Farms timer service cannot be found.");
68: }
69: SetBackConnectionHostNamesTimerJob job = new SetBackConnectionHostNamesTimerJob(timerService);
70:
71: string user = Params["username"].Value;
72: if (user.IndexOf('\\') < 0)
73: user = Environment.UserDomainName + "\\" + user;
74: job.SubmitJob(user, Params["password"].Value + "");
75:
76: output += "Timer job successfully created.";
77: }
78:
79: return OUTPUT_SUCCESS;
80: }
81:
82: public override void Validate(StringDictionary keyValues)
83: {
84: base.Validate(keyValues);
85:
86: if (Params["updatefarm"].UserTypedIn)
87: {
88: if (!Params["username"].UserTypedIn)
89: throw new SPSyntaxException("A valid username with rights to edit the registry is required.");
90: }
91: }
92:
93: #endregion
94:
95: public static List<string> GetUrls()
96: {
97: List<string> urls = new List<string>();
98: foreach (SPService svc in SPFarm.Local.Services)
99: {
100: if (!(svc is SPWebService))
101: continue;
102:
103: foreach (SPWebApplication webApp in ((SPWebService)svc).WebApplications)
104: {
105:
106: foreach (SPAlternateUrl url in webApp.AlternateUrls)
107: {
108: string host = url.Uri.Host.ToLower();
109: if (!urls.Contains(host) && // Don't add if we already have it
110: !url.Uri.IsLoopback && // Quick check to short circuit the more elaborate checks
111: host != Environment.MachineName.ToLower() && // Quick check to short circuit the more elaborate checks
112: IsLocalIpAddress(host) && // If the host name points locally then we need to add it
113: !IsSharePointServer(host)) // Don't add if it matches an SP server name (handles central admin)
114: {
115: urls.Add(host);
116: }
117: }
118: }
119: }
120: return urls;
121: }
122:
123: private static bool IsSharePointServer(string host)
124: {
125: foreach (SPServer server in SPFarm.Local.Servers)
126: {
127: if (server.Address.ToLower() == host)
128: return true;
129: }
130: return false;
131: }
132:
133: private static bool IsLocalIpAddress(string host)
134: {
135: try
136: {
137: IPAddress[] hostIPs = Dns.GetHostAddresses(host);
138: IPAddress[] localIPs = Dns.GetHostAddresses(Dns.GetHostName());
139:
140: // test if any host IP equals to any local IP or to localhost
141: foreach (IPAddress hostIP in hostIPs)
142: {
143: // is localhost
144: if (IPAddress.IsLoopback(hostIP)) return true;
145: // is local address
146: foreach (IPAddress localIP in localIPs)
147: {
148: if (hostIP.Equals(localIP)) return true;
149: }
150: }
151: }
152: catch { }
153: return false;
154: }
155:
156: public static void SetBackConnectionRegKey(List<string> urls)
157: {
158: const string KEY_NAME = "SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0";
159: const string KEY_VAL_NAME = "BackConnectionHostNames";
160:
161: RegistryKey reg = Registry.LocalMachine.OpenSubKey(KEY_NAME, true);
162: if (reg != null)
163: {
164: string[] existing = (string[])reg.GetValue(KEY_VAL_NAME);
165: if (existing != null)
166: {
167: foreach (string val in existing)
168: {
169: if (!urls.Contains(val.ToLower()))
170: urls.Add(val.ToLower());
171: }
172: }
173: string[] multiVal = new string[urls.Count];
174: urls.CopyTo(multiVal);
175:
176: reg.SetValue(KEY_VAL_NAME, multiVal, RegistryValueKind.MultiString);
177: }
178: else
179: {
180: throw new SPException("Unable to open registry key.");
181: }
182: }
183:
184: }
185: }
There's two core methods, GetUrls and SetBackConnectionRegKey. The SetBackConnectionRegKey method started out from a bit of sample code that my friend Ben Robb sent me - there's no much of his original code but it saved me some time in trying to remember how to manipulate the registry using C#. Essentially all this method does is get the current list of host names, add any missing items to the passed in list, and then reset the list (thus avoiding duplicate entries). The GetUrls method is the more interesting piece - I'm looping through all the Farm's Web Applications and their corresponding Alternate URLs and then building a list of URLs that meet some basic inclusion criteria:
- Don't add duplicates - you can get duplicates when both HTTP and HTTPS are used so we make sure that we exclude them
- Don't add loopback URLs - this shouldn't come up but if the URL is localhost or 127.0.0.1 it will be flagged as a loopback URL so we exclude them
- Don't add URLs that match the server name - if the host name matches the server name then exclude it (this is essentially just a short circuit for the next check which is a bit more thorough)
- Exclude host names that map to the local IP address - this is the most crucial bit (the previous steps were just short circuits for this step to avoid the additional querying necessary); I use the System.Net.Dns class's static GetHostAddresses method to check the local addresses against those associated with the provided host name
- Exclude host names that map to SharePoint servers - this step is necessary to address host names such as those belonging to Central Administration
The help for the command is shown below:
C:\>stsadm -help gl-setbackconnectionhostnames stsadm -o gl-setbackconnectionhostnames Sets the BackConnectionHostNames registry key with the URLs associated with each web application. Parameters: [-updatefarm (update all servers in the farm)] [-username <DOMAIN\user (must have rights to update the registry on each server)>] [-password <password>] |
The following table summarizes the command and its various parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-setbackconnectionhostnames | WSS v3, MOSS 2007 | Released: 9/20/2009
|
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| updatefarm | uf | No | If provided then update the BackConnectionHostNames registry key on all servers in the farm. | -updatefarm
-uf |
| username | user | Yes if updatefarm is provided | The username with sufficient rights to update the registry. If no domain part is specified then the current users domain is used. | -username domain\spadmin
-user spadmin |
| password | pwd | No | If the users password is blank then this parameter is not required (please change your password if this is the case!); otherwise, this parameter is required if the updatefarm parameter is provided. | -password pa$$w0rd
-pwd pa$$w0rd |
The following is an example of how to update the BackConnectionHostNames registry key on the current server only:
stsadm -o gl-setbackconnectionhostnames
The following is an example of how to update the BackConnectionHostNames registry key on all servers in the farm:
stsadm -o gl-setbackconnectionhostnames -updatefarm -username domain\spadmin -password pa$$w0rd
Creating Default Site Groups After Creating Site Collections Using STSADM
I got an email from Jennifer Davis today asking why, when she ran my gl-createsiteindb command, did the default site groups not show up in the site collection, specifically the “<site name> Members”, “<site name> Owners”, and “<site name> Visitors” groups. Upon digging further she realized that this behavior was not limited to my command as the out-of-the-box createsite and createsiteinnewdb commands exhibited the same behavior.
Basically what’s happening is that if you create the site collection via the browser an additional method call gets made on the root web site of the site collection: SPWeb.CreateDefaultAssociatedGroups. For whatever reason this method call is not made when using STSADM and as my gl-createsiteindb command just mimics the createsite command I too did not make the necessary method call. Well, I agree with Jennifer that this is just wrong so I decided to go ahead and modify my code so that those default site groups would get created. Fortunately it was a really simple change - here’s the code that I added to the command:
1: if (!string.IsNullOrEmpty(webTemplate))
2: {
3: using (SPWeb web = site.RootWeb)
4: {
5: web.CreateDefaultAssociatedGroups(ownerLogin, secondaryContactLogin, string.Empty);
6: }
7: }
If you’re using any of the existing out of the box commands you can easily achieve the same end result with a couple lines of PowerShell, as the following demonstrates (requires my custom cmdlets):
$url = "http://<site url>" $primaryOwner = "domain\user" $secondaryOwner = "domain\user" $site = Get-SPSite $url $site.SPBase.RootWeb.CreateDefaultAssociatedGroups($primaryOwner, $secondaryOwner, "") $site.SPBase.Dispose()
Configuring SSO via STSADM
I can’t actually take credit for this particular command – in fact I’ve never configured SSO so I personally don’t know much about it. I got the code from Stef van Hooijdonk who graciously provided the code he produced.
I made a few minor changes to Stef’s code just to bring it in line with the rest of my code but otherwise it’s as was provided to me – as such I’m not really prepared to support this command as I didn’t personally create it but it’s really straightforward. One word of caution – the code uses a class which has been flagged as being meant for internal use only and not for use in custom code – specifically the Microsoft.SharePoint.Portal.SignleSignon.Configuration class. Here’s the code:
1: #if MOSS
2: using System;
3: using System.Collections.Specialized;
4: using System.Text;
5: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
6: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
7: using Microsoft.SharePoint.Portal.SingleSignon;
8: using System.Diagnostics;
9:
10: namespace Lapointe.SharePoint.STSADM.Commands.Security
11: {
12: public class ConfigureSso : SPOperation
13: {
14: /// <summary>
15: /// Initializes a new instance of the <see cref="ConfigureSso"/> class.
16: /// </summary>
17: public ConfigureSso()
18: {
19: SPParamCollection parameters = new SPParamCollection();
20: parameters.Add(new SPParam("adminaccount", "admin", true, string.Empty, new SPNonEmptyValidator()));
21: parameters.Add(new SPParam("enterpriseapplicationaccount", "eaa", true, string.Empty, new SPNonEmptyValidator()));
22: parameters.Add(new SPParam("sqlserver", "sql", true, string.Empty, new SPNonEmptyValidator()));
23: parameters.Add(new SPParam("database", "db", true, string.Empty, new SPNonEmptyValidator()));
24: parameters.Add(new SPParam("tickettimeout", "tick", false, "2", new SPIntRangeValidator(1, 60), "Please specify minutes between 1 and 60"));
25: parameters.Add(new SPParam("auditlogs", "logs", false, "10", new SPIntRangeValidator(1, 90), "Please specify days between 1 and 90."));
26:
27: StringBuilder sb = new StringBuilder();
28: sb.Append("\r\n\r\nConfigure the SSO Service in the Farm. \r\n\r\nParameters:");
29: sb.Append("\r\n\t-admin <admin account or group that will admininster the SSO Service>");
30: sb.Append("\r\n\t-eaa <admin account or group that will administer the enterprise applications>");
31: sb.Append("\r\n\t-sql <sql server for the SSO Database>");
32: sb.Append("\r\n\t-db <database name for the SSO Storage>");
33: sb.Append("\r\n\t[-tick] <minutes a sso ticket is valid, defaults to 2>");
34: sb.Append("\r\n\t[-logs] <days to keep logs, defaults to 10>");
35:
36: Init(parameters, sb.ToString());
37: }
38:
39: #region ISPStsadmCommand Members
40:
41: /// <summary>
42: /// Gets the help message.
43: /// </summary>
44: /// <param name="command">The command.</param>
45: /// <returns></returns>
46: public override string GetHelpMessage(string command)
47: {
48: return HelpMessage;
49: }
50:
51: /// <summary>
52: /// Runs the specified command.
53: /// </summary>
54: /// <param name="command">The command.</param>
55: /// <param name="keyValues">The key values.</param>
56: /// <param name="output">The output.</param>
57: /// <returns></returns>
58: public override int Execute(string command, StringDictionary keyValues, out string output)
59: {
60: output = string.Empty;
61:
62: string adminAccount = Params["adminaccount"].Value;
63: string eaDefAccount = Params["eaa"].Value;
64: string sqlServer = Params["sqlserver"].Value;
65: string database = Params["database"].Value;
66:
67: uint ticketTimeout = 2;
68: if (Params["tickettimeout"].UserTypedIn)
69: ticketTimeout = uint.Parse(Params["tickettimeout"].Value);
70:
71: uint auditLogs = 10;
72: if (Params["auditlogs"].UserTypedIn)
73: auditLogs = uint.Parse(Params["auditlogs"].Value);
74:
75: Configure(adminAccount, eaDefAccount, sqlServer, database, ticketTimeout, auditLogs);
76:
77: return OUTPUT_SUCCESS;
78: }
79:
80: #endregion
81:
82: /// <summary>
83: /// Configures the SSO settings.
84: /// </summary>
85: /// <param name="adminAccount">The admin account.</param>
86: /// <param name="eaDefAccount">The ea def account.</param>
87: /// <param name="sqlServer">The SQL server.</param>
88: /// <param name="database">The database.</param>
89: /// <param name="ticketTimeout">The ticket timeout.</param>
90: /// <param name="auditLogs">The audit logs.</param>
91: private static void Configure(string adminAccount, string eaDefAccount, string sqlServer, string database, uint ticketTimeout, uint auditLogs)
92: {
93: try
94: {
95: Log("Connecting to {0}", sqlServer);
96: Configuration.ConfigureSecretServer(
97: adminAccount,
98: eaDefAccount,
99: sqlServer,
100: database,
101: ticketTimeout,
102: auditLogs);
103: Log("SSO Configured succesfull.");
104: }
105: catch (Exception exc)
106: {
107: if (exc.Message.Contains("-2147023143"))
108: Log("Error occured in setting the SSO Server settings. Is the SSOService configured to run as a domain account?", EventLogEntryType.Error);
109: throw;
110: }
111:
112: Log("Generating MasterSecret..");
113: Configuration.GenerateMasterSecret(false);
114: Log("Master Secret Key set.");
115: }
116:
117: }
118: }
119: #endif
The help for the command is shown below:
C:\>stsadm -help gl-configuresso
stsadm -o gl-configuresso
Configure the SSO Service in the Farm.
Parameters:
-admin <admin account or group that will administer the SSO Service>
-eaa <admin account or group that will administer the enterprise applications>
-sql <sql server for the SSO Database>
-db <database name for the SSO Storage>
[-tick] <minutes a sso ticket is valid, defaults to 2>
[-logs] <days to keep logs, defaults to 10>
|
The following table summarizes the command and its various parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-configuresso | MOSS 2007 | Released: 4/17/2009
|
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| adminaccount | admin | Yes | Admin account or group that will administer the SSO service. | -adminaccount domain\spadmin
-admin domain\spadmin |
| enterpriseapplicationaccount | eaa | Yes | Admin account or group that will administer the enterprise application. | -enterpriseapplicationaccount domain\spadmin
-eaa domain\spadmin |
| sqlserver | sql | Yes | SQL Server name for the SSO database. | -sqlserver sql1
-sql sql1 |
| database | db | Yes | Database name for the SSO storage. | -database SSODB
-db SSODB |
| tickettimeout | tick | No | Minutes an SSO ticket is valid, defaults to 2. | -tickettimeout 2
-tick 2 |
| auditlogs | logs | No | Day to keep logs, defaults to 10. | -auditlogs 10
-logs 10 |
The following is an example of how to configure SSO:
stsadm -o gl-configuresso –adminaccount domain\spadmin –enterpriseapplicationaccount domain\spadmin –sqlserver sql1 –database ssodb –tickettimeout 2 –auditlogs 10
Change Password Script
I'd been meaning to post this for quite some time but just haven't gotten around to it - as paranoid administrators we often find the need to change our service account passwords and doing so with a product like SharePoint can be a rather significant effort if you consider all the various accounts that may be used in a least privileges model. If you're just about to make hit this situation you're likely to do a quick search and find the following support article: http://support.microsoft.com/kb/934838 - this article provides you with the stsadm commands you need as well as a sample script that you can use.
The problem I have is that the article doesn't provide a complete script - the sample only addresses some SSP related settings and app pools - it makes mention of the commands needed to change the farm account but it doesn't include those commands in the script. It also has a line where you have to go and manually make a change via the browser - this is because the out of the box stsadm commands don't provide you with all the stuff you need to change all the passwords. Specifically there's two missing - the default content access account and the user profile import account. Seeing as I consider myself a developer and not an administrator (though sometimes I wonder) I decided to build those missing commands which I've previously blogged about here and here.
Using these two commands I created the script shown below - note that you don't necessarily need all the execadmsvcjobs calls but I prefer to make sure that all pending jobs complete before moving onto the next step. Also - you may not have as many accounts - you can either remove the unnecessary lines and/or change the variable values as needed but I'd encourage you to leave the variable names so that it is clearer what each account is used for. Of course this batch file will not actually make the password changes - if you need a script that will actually make the password change then look here. And finally - please, please, please do NOT leave this script on your server when you are done - it's a huge security risk storing all the passwords in a script like this so you need to make sure that you either store the file in a secure location and/or blank the passwords out when not being utilized.
As always - if you have any comments or suggestions please let me know as I'm always looking for ways to improve and I'm by now means a batch file expert.
@echo off SET PATH=C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN;%PATH% SET DOMAIN=spdev SET SSP=SSP1 rem *** Farm account (central admin app pool, timer jobs account) set APP_POOL_CA_USER="%DOMAIN%\spfarm" set APP_POOL_CA_PWD="pa$$w0rd" rem *** SharePoint SSP Service Account set SSPSVC_USER="%DOMAIN%\sspsvc" set SSPSVC_PWD="pa$$w0rd" rem *** SharePoint SSP Application Pool Account SET APP_POOL_SSP_USER="%DOMAIN%\sspapppool" SET APP_POOL_SSP_PWD="pa$$w0rd" rem *** SharePoint Server Search Service Account set SEARCH_USER="%DOMAIN%\sspsearch" set SEARCH_PWD="pa$$w0rd" rem *** SharePoint Services Help Search Service Account set SEARCH_HELP_USER="%DOMAIN%\sphelpsearch" set SEARCH_HELP_PWD="pa$$w0rd" rem *** Default content access account for office search set CONTENT_USER="%DOMAIN%\sspcontent" set CONTENT_PWD="pa$$w0rd" rem *** content access account for windows sharepoint services help search set CONTENT_HELP_USER="%DOMAIN%\spcontentsearch" set CONTENT_HELP_PWD="pa$$w0rd" rem *** User profile import account set PROFILE_IMPORT_USER="%DOMAIN%\sspuserprofilesvc" set PROFILE_IMPORT_PWD="pa$$w0rd" rem *** Portal application pool account set APP_POOL_PORTAL_USER="%DOMAIN%\spportalapppool" set APP_POOL_PORTAL_PWD="pa$$w0rd" rem *** Teams sites application pool account set APP_POOL_TEAMS_USER="%DOMAIN%\spcollabapppool" set APP_POOL_TEAMS_PWD="pa$$w0rd" rem *** My sites application pool account set APP_POOL_MYSITE_USER="%DOMAIN%\spmysitesapppool" set APP_POOL_MYSITE_PWD="pa$$w0rd" rem *** Excel Services Unattended User Account set SVC_EXCEL_USER="%DOMAIN%\SPSSAcct_dev" set SVC_EXCEL_PWD="Pa$$w0rd" goto startpoint :startpoint rem central admin ECHO %DATE% %TIME%: Updating Central Admin password stsadm -o updatefarmcredentials -userlogin %APP_POOL_CA_USER% -password %APP_POOL_CA_PWD% -identitytype configurableid if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Executing pending timer jobs stsadm -o execadmsvcjobs if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Run "stsadm -o updatefarmcredentials -userlogin %APP_POOL_CA_USER% -password %APP_POOL_CA_PWD% -identitytype configurableid -local" on each WFE before continuing pause ECHO %DATE% %TIME%: Run "stsadm -o execadmsvcjobs" on each WFE before continuing. pause iisreset /noforce rem application pools ECHO %DATE% %TIME%: Updating app pool passwords for Portal stsadm -o updateaccountpassword -userlogin %APP_POOL_PORTAL_USER% -password %APP_POOL_PORTAL_PWD% -noadmin if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Executing pending timer jobs stsadm -o execadmsvcjobs if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Updating app pool passwords for Teams stsadm -o updateaccountpassword -userlogin %APP_POOL_TEAMS_USER% -password %APP_POOL_TEAMS_PWD% -noadmin if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Executing pending timer jobs stsadm -o execadmsvcjobs if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Updating app pool passwords for MySite stsadm -o updateaccountpassword -userlogin %APP_POOL_MYSITE_USER% -password %APP_POOL_MYSITE_PWD% -noadmin if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Executing pending timer jobs stsadm -o execadmsvcjobs if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Updating app pool passwords for SSP stsadm -o updateaccountpassword -userlogin %APP_POOL_SSP_USER% -password %APP_POOL_SSP_PWD% -noadmin if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Executing pending timer jobs stsadm -o execadmsvcjobs if not errorlevel 0 goto errhnd rem ssp - new ECHO %DATE% %TIME%: Updating ssp password for new installs stsadm -o editssp -title %SSP% -ssplogin %SSPSVC_USER% -ssppassword %SSPSVC_PWD% if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Executing pending timer jobs stsadm -o execadmsvcjobs if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Executing pending timer jobs stsadm -o execadmsvcjobs if not errorlevel 0 goto errhnd rem osearch ECHO %DATE% %TIME%: Updating osearch password stsadm -o osearch -farmserviceaccount %SEARCH_USER% -farmservicepassword %SEARCH_PWD% if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Executing pending timer jobs stsadm -o execadmsvcjobs if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Updating default content access account stsadm -o gl-updatedefaultcontentaccessaccount -username %CONTENT_USER% -password %CONTENT_PWD% if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Executing pending timer jobs stsadm -o execadmsvcjobs if not errorlevel 0 goto errhnd iisreset /noforce rem spsearch ECHO %DATE% %TIME%: Updating spsearch password stsadm -o spsearch -farmserviceaccount %SEARCH_HELP_USER% -farmservicepassword %SEARCH_HELP_PWD% if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Executing pending timer jobs stsadm -o execadmsvcjobs if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Updating spsearch content access account stsadm -o spsearch -farmcontentaccessaccount %CONTENT_HELP_USER% -farmcontentaccesspassword %CONTENT_HELP_PWD% if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Executing pending timer jobs stsadm -o execadmsvcjobs if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Updating default profile import account stsadm -o gl-setuserprofiledefaultaccessaccount -username %PROFILE_IMPORT_USER% -password %PROFILE_IMPORT_PWD% -sspname %SSP% if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Executing pending timer jobs stsadm -o execadmsvcjobs if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Updating excel services unattended service account stsadm -o set-ecsexternaldata -ssp %SSP% -unattendedserviceaccountname %SVC_EXCEL_USER% -unattendedserviceaccountpassword %SVC_EXCEL_PWD% if not errorlevel 0 goto errhnd ECHO %DATE% %TIME%: Executing pending timer jobs stsadm -o execadmsvcjobs if not errorlevel 0 goto errhnd rem restarting IIS ECHO %DATE% %TIME%: Doing soft restart of IIS iisreset /noforce echo on goto end :errhnd echo An error occured - terminating script. :end
To use this script on WSS just remove the unnecessary elements (lines with the following commands: gl-setuserprofiledefaultaccessaccount, gl-updatedefaultcontentaccessaccount, editssp, osearch, and set-ecsexternaldata).
Changing the Application Pool Identity via STSADM
This past week I presented at the local Colorado Springs SharePoint User Group meeting on using and customizing STSADM. The talk was really less about STSADM and more about SharePoint administration tips, tricks, and best practices - but we did create a new STSADM command from scratch during the meeting. I've taken what we did during the meeting and reworked it to add proper validation and help documentation and have included it in my download as gl-updateapppoolaccount.
The purpose of the command is to change out the identity of the application pool associated with a specific web site. I thought this might be a good one to demo creating because I noticed that there was no existing command to do this (there's a command to change the password, but not change the account) and I recently ran into a situation where it would be beneficial. As a consultant I often come into environments where the clients MOSS implementation may not have been deployed in the most ideal way. My current client had all three of their MOSS environments (production, test, and development) using the same service accounts. I wanted to be able to work with IT to set each environment to run under their own accounts but I typically prefer to give IT a script that they can run so that they don't have to try and figure out how to do this via Central Admin (in this case the IT staff was not yet trained on SharePoint and timelines didn't allow me to wait for them to be trained). By using a script I could let the IT staff create the new accounts and update SharePoint without having to have them tell me what the passwords were.
The code to update the accounts is really quite simple - all we do is get an SPApplicationPool object via the ApplicationPool property of an SPWebApplication object. We then update the Username and Password properties and then call Update and finally Deploy (which is a member of the SPProcessIdentity class of which the SPApplicationPool class inherits from. Update saves the changes to the configuration database and Deploy will push those changes to each of your web servers.
1: /// <summary>
2: /// Updates the account.
3: /// </summary>
4: /// <param name="url">The URL.</param>
5: /// <param name="type">The type.</param>
6: /// <param name="login">The login.</param>
7: /// <param name="password">The password.</param>
8: public static void UpdateAccount(string url, IdentityType type, string login, string password)
9: {
10: if (type == IdentityType.LocalSystem)
11: throw new ArgumentException("Identity type of LocalSystem is not allowed.");
12:
13: SPWebApplication webApp = SPWebApplication.Lookup(new Uri(url));
14: SPApplicationPool pool = webApp.ApplicationPool;
15: pool.CurrentIdentityType = type;
16:
17: if (type == IdentityType.SpecificUser)
18: {
19: pool.Username = login.Trim();
20: pool.Password = password;
21: }
22:
23: pool.Update();
24: pool.Deploy();
25: }
The help for the command is shown below:
C:\>stsadm -help gl-updateapppoolaccount
stsadm -o gl-updateapppoolaccount
Updates the user account information for the application pool associated with the given URL.
Parameters:
-url <web application url>
[-userlogin <DOMAIN\user>]
[-password <password>]
[-type <localservice | networkservice | specificuser> (defaults to specificuser)]
|
The following table summarizes the command and its various parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-updateapppoolaccount | WSS v3, MOSS 2007 | Released: 10/12/2008
|
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| url | Yes | The URL of a web application associated with the application pool whose identity is to be changed. Note that this will not create a new application pool - any other web applications using this same application pool will also be affected. | -url http://portal | |
| userlogin | u | No, unless type is specificuser | The user account to set as the identity of the application pool. | -userlogin "domain\user"
-u "domain\user" |
| password | pwd | No, Unless type is specificuser | The password associated with the specified user account. | -password "pa$$w0rd"
-pwd "pa$$w0rd" |
| type | t | No | Either localservice, networkservice, or specificuser. Defaults to specificuser if not specified. | -type specificuser
-t specificuser |
The following is an example of how to set the user account for the portal web application:
stsadm -o gl-updateapppoolaccount -url http://portal -userlogin spdev\spportalapppool -password pa$$w0rd
Updating the Default Content Access Account via STSADM
This is one that I've been wanting to address for a while and I finally decided to sit down and just do it. If you've had your environment in place long enough to have to change the passwords you know that you can change most of the passwords using the out of the box STSADM commands - many refer to this support article from Microsoft on how to do this: http://support.microsoft.com/kb/934838.
Because there's so many accounts to change and so many places to visit this is definitely one of those things you want to have scripted (just be careful where you store your script). If you look at the article though you'll notice that it doesn't address updating the user profile import account and it mentions that you have to manually change the default content access account. I already have a command to change the user profile import account but I didn't have anything for changing the default content access account and having scripts with manual steps just kind of defeats the purpose in my opinion. So, I created a new command which I called gl-updatedefaultcontentaccessaccount.
Setting the default content access account through code is real easy - you just call the SetDefaultGatheringAccount method of an instance of the Content class which can be obtained by calling the static GetContext method from the SearchContext object:
1: /// <summary>
2: /// Updates the account.
3: /// </summary>
4: /// <param name="sspName">Name of the SSP.</param>
5: /// <param name="user">The user.</param>
6: /// <param name="pwd">The PWD.</param>
7: public static void UpdateAccount(string sspName, string user, string pwd)
8: {
9: ServerContext context;
10: if (string.IsNullOrEmpty(sspName))
11: context = ServerContext.Default;
12: else
13: context = ServerContext.GetContext(sspName);
14:
15: SearchContext parent = SearchContext.GetContext(context);
16:
17: Content content = new Content(parent);
18: try
19: {
20: content.SetDefaultGatheringAccount(user, Utilities.CreateSecureString(pwd));
21: }
22: catch (RemotingException)
23: {
24: throw new Exception("Invalid login.");
25: }
26: catch (COMException)
27: {
28: throw new Exception("Invalid login.");
29: }
30: }
The help for the command is shown below:
C:\>stsadm -help gl-updatedefaultcontentaccessaccount
stsadm -o gl-updatedefaultcontentaccessaccount
Sets the account to use as the default account when crawling content. This account must have read access to the content being crawled. To avoid crawling unpublished versions of documents, ensure that this account is not an administrator on the target server.
Parameters:
[-ssp <SSP name>]
-username <DOMAIN\name>
-password <password>
|
The following table summarizes the command and its various parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-updatedefaultcontentaccessaccount | MOSS 2007 | Released: 8/15/2008 |
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| ssp | No | The SSP that the account is associated with. If omitted then the default SSP is used. | -ssp SSP1 | |
| username | u | Yes | The username of the account to use. The account must have read access to the content being crawled. To avoid crawling unpublished versions of documents, ensure that the account is not an administrator on the target server. | -username "domain\sspcontent"
-u "domain\sspcontent" |
| password | pwd | Yes | The password associated with the specified username. | -password "pa$$w0rd"
-pwd "pa$$w0rd" |
The following is an example of how to set the default content access account:
stsadm -o gl-updatedefaultcontentaccessaccount -username "domain\sspcontent" -password "pa$$w0rd"
I'll follow up this post with a sample password change script that I use which includes this command.
Delete All Users from a Site Collection
I was chatting with my buddy and fellow MVP, Todd Klindt, the other day and he was asking if I had a custom command that would allow him to quickly delete all the users added to a site collection. For his purposes he needed this to troubleshoot an issue he was having with a site collection that contained many thousands of users.
I figured this would be pretty easy to do - a simple while loop with a conditional check for site administrators to prevent them from being deleted so I quickly typed out the following code during our IM conversation:
using (SPSite site = new SPSite(url))
using (SPWeb web = site.OpenWeb())
{
int offsetIndex = 0;
while (web.SiteUsers.Count > offsetIndex)
{
if (web.SiteUsers[offsetIndex].IsSiteAdmin || web.SiteUsers[offsetIndex].ID == web.CurrentUser.ID)
{
offsetIndex++;
continue;
}
web.SiteUsers.Remove(offsetIndex);
}
}
We can't use a for loop for this because we're modifying the collection and that would result in an error so we need to use a while loop and just keep deleting until there's no more to delete. But, we have to watch out for site administrators as we can't directly delete them in this fashion (and it's a good idea to keep them regardless) so we use an offset that gets incremented whenever we hit an administrator. We also can't delete the current user so I check for that as well.
One thing to note is that if you have a lot of users (Todd was dealing with 17,000) then it will run real slow - he was seeing about 300 deleted per hour with progressively more as the size dwindled - so be patient
I figured since I had the code that I might as well turn it into a new stsadm command so I created gl-deleteallusers. There's not really much to this command - it just takes in a single URL parameter which points to your site collection. I thought about adjusting this so that you could delete users from a specific web that had broken inheritance but I didn't really have time. Here's the help output:
C:\>stsadm -help gl-deleteallusers
stsadm -o gl-deleteallusers
Deletes all site collection users. Will not delete site administrators.
Parameters:
-url <site collection url>
|
The following tables summarizes the command and its parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-deleteallusers | WSS v3, MOSS 2007 | 7/23/2008 |
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| url | Yes | The URL of the site collection of which the users will be deleted. If a sub-site of the site collection is passed in it will only look at the site collection itself and ignore the sub-site information. | -url http://portal/ |
Using the command is real simple - the following will delete all users, except site administrators, from the http://portal/sites/site1 site collection:
stsadm -o gl-deleteallusers -url http://portal/sites/site1