Add a person as a site collection administrator to every Office 365 Site / SharePoint Online Site Collection

The Problem:

In SharePoint online (at least as of early 2015) site collection administrators have to be granted on a site by site basis.

When you create a new site collection using  https://yoururl-admin.sharepoint.com, you are only allowed to pick ONE administrator for the Site collection (In on premise, you used to pick two)

NewSiteCollectionSPOdialog

Now a little trick you can use is, after the site collection is created, you can check the site collection then click the “owners” tab:

SPOadminBar

and from that screen you can add as many site collection administrators as you’d like:

AddSPOadminDialog

 

But there is a downside, you can’t “select all” on all your site collections and add a user to all site collections at once.

Now, I hear you saying “Jack: What if I have 500 site collections and we add a new member to our team?” There’s got to be a better way, right? And it turns out, there is.

The Solution: PowerShell…

A Quick note before we get to the script: You’ll need the SharePoint Online Management Shell installed on your PC before this will work.
Here’s a quick overview of how to use the script:

Update all the relevant variables:

  1. Admin site URL ($adminurl), and the $username that has permissions to log into the admin site url to make the change.
  2. put in your $tenantURL
  3. Update the list of $SiteCollectionAdmins with the list of users you want to make site collection admins

Run the script.

When you run the script it will try to logon to your SPO account and will prompt you for your SPO password, then you should see some slow and steady progress as it runs through each site collection. Finally, at the end you can review the log file to see if there were any issues.

The Script:

# Jack Fruh - sharepointjack.com
# add a user or users to the site collection admin role on every site collection in Office 365 sites (SharePoint Online)

#setup a log path
$path = "$($(get-location).path)\LogFile.txt"
#note we're using start-transcript, this does not work from inside the powershell ISE, only the command prompt

start-transcript -path $Path
write-host "This will connect to SharePoint Online"

#Admin Variables:
$Adminurl = "https://yoururl-admin.sharepoint.com"
$username = "your@email.com"

#Tenant Variables:
$TenantURL = "https://yoururl.sharepoint.com"

$SiteCollectionAdmins = @("firstuser@yourdomain.com", "seconduser@yourdomain.com", "etc@yourdomain.com")

#Connect to SPO
$SecurePWD = read-host -assecurestring "Enter Password for $username"
$credential = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $SecurePWD

Connect-SPOService -url $Adminurl -credential $credential
write-host "Connected" -foregroundcolor green


$sites = get-sposite
Foreach ($site in $sites)
{
    Write-host "Adding users to $($site.URL)" -foregroundcolor yellow
	#get the owner group name
	$ownerGroup = get-spoSitegroup -site $site.url | where {$_.title -like "*Owners"}
	$ownertitle = $ownerGroup.title
	Write-host "Owner Group is named > $ownertitle > " -foregroundcolor cyan
	
	#add the Site Collection Admin to the site in the owners group
	foreach ($user in $SiteCollectionAdmins)
	{
		Write-host "Adding $user to $($site.URL) as a user..."
		add-SPOuser  -site $site.url -LoginName $user -group $ownerTitle
		write-host "Done"
		
		#Set the site collection admin flag for the Site collection admin
		write-host "Setting up $user as a site collection admin on $($site.url)..."
		set-spouser -site $site.url -loginname $user -IsSiteCollectionAdmin $true
		write-host "Done"	-foregroundcolor green
	}
}
Write-host "Done with everything" -foregroundcolor green 
stop-transcript

 

Delete a site collection from SharePoint Online

Do you need to delete a site collection from SharePoint online?

Do you want to re-create it afterwards?

You’ll need to delete it, then remove it from the recyclebin, but you can’t do that from the gui.

Using Powershell,

1) connect to your SPO admin tenant.

2) Get a list of your deleted sites in the site collection recycle bin with:

get-spodeletedsite

Now create a statement to get just the one you want:

get-spodeletedsite | Where {$_.url -eq "https://tenant.sharepoint.com/sites/sitecollection"}

Does that return just one?

If so,  hit the up arrow on your keyboard, then the end key, now add some text until your line looks like this:

get-spodeletedsite | where {$_.url -eq "https://tenant.sharepoint.com/sites/sitecollection"} | Remove-SPODeletedSite

 

 

 

Quick Powershell script for connecting to SharePoint Online/ Office 365

At work, we’ve started working with Sharepoint online/ O365, and it’s a bit tedious to type in the whole connection command each time.

I wanted a simple script that would store the name of our tenant, my username, and would then securely prompt me for my password:

#Admin Variables:
$Adminurl = "https://tenantname-admin.sharepoint.com"
$username = "youremail@youremail.com"

#Connect to SPO
$SecurePWD = read-host -assecurestring "Enter Password for $username"
$credential = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $SecurePWD
write-host "Connecting"
Connect-SPOService -url $Adminurl -credential $credential
write-host "Connected" -foregroundcolor green

 

Start off 2015 by fixing a messed up edit page in SP2007…

It’s the start of 2015…

Who needs the cloud, SP2013 or even SP2010! It’s time to get sucked into a support issue on a legacy SP2007 site 🙁

One of our legacy SP2007 sites had a custom list with an edit page that got corrupted.

Seems a user might have edited the edit page, and removed the web part that makes the edit page an edit page.

The solution wasn’t exactly quick or easy – I hadn’t worked with SP2007 in a long while and neither did our support engineer at Microsoft.

We ended up solving the issue ourselves.  We have 3rd tier support with Microsoft, which has been great on the 2010 and 2013 side, but in this case the product in question is 2007 and I can’t say I blame Microsoft if all the people who were good at 2007 have moved on to other things by now.

Here’s a rough outline of what we did:

1) The EditForm.aspx page was broken

2) A known good DB backup of the site was recovered in a test environment  – this had a good copy of the form.

3) The good working list in the test environment was saved as a template with and without data in the test environment. The STP file was then copied to production

5) In production, in the same sitecollection/subweb, a new empty user list was created, using the template file that was imported from the test environment. One test record was created (ID=1) so that we could validate that the restored, empty,  list worked.

6) SharePoint designer had to be enabled on production – it was found to be disabled via the onet.xml file for the STS template http://support.microsoft.com/kb/940958

7) Using some of the ideas from this article: http://hightechdave.com/recover-newform-aspx-from-the-dead-in-sharepoint-2007/

8) Editing the HTML of the broken EditForm.aspx in SharePoint Designer 2007,  We copied the webpart section from the restored ‘good’ list over to the ‘bad’ list. Note: this MUST be done in SharePoint Designer – editing the files from explorer will not work because part of this file is stored in the content database.

9) Saved the broken EditForm.aspx list and looked at it – it worked but referenced data from the new ‘good’ list (I had to construct the edit URL by hand, based on the edit url of the restored list, since the edit links on our allitems page were also broken…)

10) Edited the broken EditForm.aspx and also opened the newform.aspx page from the same list (the original list with the problem) in SharePoint Designer’s ‘code/html’ view.

11) Copied the guid from the NewForm.aspx to the EditForm.aspx

12) Saved it and looked at it.

13) Edited the broken EditForm.aspx in SPD and changed the title in the webpart to the name of the list –

14) Saved it and it was fixed.

Note some things that did not work:

The article mentioned using a new GUID – that failed for me.

Also, when I pasted the webpart code in AND changed the list guid AT THE SAME TIME – it did not work. It seemed I needed to save it after pasting in the list, then go back and edit it.

And last but not least, I remember having to change the title on the webpart (via SPD’s html editor) before the webpart displayed correctly. And I think there might be a chance that doing it at the same time as another change didn’t work – for all I know it would have/should have and I just made a mistake when I tried to do it all at once, but it definitely worked when done as described above.

Ok time to get back to 2015 and start working on some SharePoint Online initiatives!

– Jack

Sync an Active Directory Group with a SharePoint Group

Have you ever wanted to keep the members of a SharePoint group in sync with those of an Active Directory Group?

If so, you’re in luck, I happen to have just such as script.

Just a quick note, this was written for and tested on a 2010 site – in 2013, the default authentication is Claims – those funny looking strings like this: “i:0#.w|domain\user” (more info here) and you’ll need to work this script over a few times to make that work.

That said, this script will keep a SharePoint group in sync with an AD group.

The AD group is considered the “master”

That is to say, if the AD group has extra users that aren’t in SharePoint, they will be added to SharePoint.

If a user is removed from the AD group, they will also be removed from the SharePoint Group.

If a user is added to the SP Group, but isn’t in the AD group? They will be removed from the SP group.

This is one of those scripts that makes sense to run as a scheduled task once you get it working.  If you need help with that, see my post: Schedule your PowerShell scripts using the Windows task scheduler.

#AD to SharePoint Sync Script
#Jack Fruh 2014 Sharepointjack.com
write-host "NOTE: This has only been tested on NTLM authentication - it will likely need work to work with claims based authentication in 2013" -foregroundcolor red

function main()
{   
    #setup the environment so this can run as a schedule task:
    #note that the AD PowerShell Commands must be installed on the SharePoint Server for this to work.
    Write-host "note that the AD PowerShell Commands must be installed on the SharePoint Server for this to work." -foregroundcolor blue
    Write-host "Adding SharePoint Snapin"
    $Host.Runspace.ThreadOptions = "ReuseThread"
    Add-PSSnapin microsoft.sharepoint.powershell -ErrorAction SilentlyContinue
    Write-host "Importing AD module"
    import-module activedirectory

    write-host "Starting work..."
    #Hard coded variables for testing, 
    # we need the name of the AD Group, the name of the corresponding group in Sharepoint to sync with, and the URL of the SPWeb where the SP group resides.
    $ADgroupname = "TheGroup"
    $SPGroupName = "SyncMeWithAD"
    $spweburl = "http://sharepoint2010"
    #note that it's reasonably easy to turn this hardcoded list into a CSV import and then loop through multiple groups

    #get a list of the AD Users in the AD Group
    #$ADGroupMembers = get-adgroupmember -Identity $ADgroupname | select @{name="LoginName";expression={$_.samaccountname}}
    $ADGroupMembers = get-adgroupmember -Identity $ADgroupname | select @{name="LoginName";expression={$(getDomain($_.distinguishedName)) + "\" +  $_.samaccountname.toupper()}}
    if ($ADGroupMembers -eq $null)
    {
        write-host "The AD Group we're syncing with is empty - this is usually a problem or typo - the SP group will be left alone" -foregroundcolor red
        exit
    }


    #get the list of users in the SharePoint Group
    $web = get-spweb $spweburl
    $group = $web.groups[$SPGroupName]
    if ($group -eq $null) {write-host "SPGroup Not found" ; exit }
    $spusers = $group.users | select @{name="LoginName";expression={$_.LoginName.toupper()}}
  
    write-host "Debug: at this point we should have a list of user ID's from SharePoint in domain\user format, uppercase" 
    foreach($x in $spusers)
    {
        write-host $x.LoginName -foregroundcolor green
    }
  
    if($spusers -eq $null)
    {
      write-host "The SPgroup is empty" -foregroundcolor cyan
      write-host "Adding all AD group members to the SP group"
      foreach ($ADGroupMember in $ADGroupMembers)
      {
            #add the AD group member to the SP group
            write-host "Adding $($ADGroupMember.LoginName)" 
            write-host "new-spuser -useralias $($ADGroupMember.LoginName) -web $($web.url) -group $SPGroupName" -foregroundcolor green
            new-spuser -useralias $ADGroupMember.LoginName -web $web.url -group $SPGroupName
           # $web.site.rooteweb.ensureUser($ADGroupMember.loginname)
            set-SPuser -identity $ADGroupMember.LoginName -web $web.url -group $spgroupname
            
      }
      write-host "Done adding users - script will now exit" -foregroundcolor magenta
      exit
    }

    #use compare-object to get a listing of what's different between AD and SP
    write-host "Comparing AD Group Users to SP group Users"
    $result = compare-object -referenceobject $adgroupmembers -differenceobject $spusers  -includeequal -property LoginName

    Write-host "Result of comparison is:"
    $result
    write-host "-------------------------"
    
  
    

    #filter the results of the comparison to show only the users that need to be added
    Write-host "Looking for users in AD that we need to add to SharePoint"
    $missingSPusers = $result |  Where {$_.SideIndicator -eq '<='} | select LoginName #users in AD that are missing from SharePoint
    if ($missingSPusers -ne $Null)
    {
        foreach ($missingSPuser in $missingSPusers)
        {
            write-host "Adding $($missingSPUser.LoginName) to sharepoint"
            write-host "new-spuser -useralias $($missingSPuser.LoginName) -web $($web.url) -group $SPGroupName" -foregroundcolor green
            new-spuser -useralias $missingSPuser.LoginName -web $web.url -group $SPGroupName
            set-SPuser -identity $missingSPuser.LoginName -web $web.url -group $spgroupname
        }
    }
    
    #now do the reverse
    #Filter the results of the comparison to show 'extra' users that need to be removed
    write-host "Looking for Extra users in SharePoint that are not in AD"
    $extraSPusers = $result |  Where {$_.SideIndicator -eq '=>'} | select LoginName #users in AD that are missing from SharePoint
    if ($extraSPusers -ne $null)
    {
        foreach ($extraSPuser in $extraSPusers)
        {
            write-host "Removing $($extraSPuser.LoginName) to sharepoint"
            Remove-SPUser -useralias $($extraSPuser.LoginName) -Web $web -group $SPGroupName -confirm:$false
        }
    }
} # end main function

function getDomain($distinguishedname)
{
  $dn = $distinguishedname
  #extract Domain name from Distinguished name
  #Domain name should be the first DN= Entry from left to right.
  #Examples:  CN=BOB,OU=Users,DN=DomainShortName,DN=Com
  #Examples:  CN=BOB,OU=Users,DN=DomainShortName,DN=Forrest,DN=Com
  $start = $dn.indexof(",DC=")+4 #find the first occurance of DC in the Distinguishedname  
  $end = $dn.indexof(",",$start)  #find the first comma, this is the end of the domain name
  $domain = $dn.substring($start, $end-$start).toupper()
  return $domain
}

main

Now for a friendly reminder and some advice…
#1) Always test code you find online before using it in production…
#2) when you test this code, follow this advice:

Testing this code

When you test the code, you might make a mistake I made during development – I’ll share that mistake with you to save you an hour of time and some frustration.

Here’s what I did…

While testing, I wanted to try adding users to an AD group and wanted to make sure they added in correctly.

For one test I wanted to remove ALL the users from the SharePoint Group, and confirm that they came back ok.

To do this I used the UI to remove all the users – I checked each user, then clicked “actions->remove users from group” like this:
removeusersscreenSP2010

I then ran my Super Awesome AD Sync PowerShell Script which Added the groups back in.

Now here’s where it got ugly.

When I checked the UI, they weren’t there.

In fact, if I ran the powershell script again it indicated that they were being added back a second time (the script should have told me there was nothing to change!)

What was the cause?

It was my use of the refresh button…refresh in IE

Recall that the very last thing I did was remove users using that screen.

Now interestingly, you know how we all click “OK” on a screen without paying attention?

After I hit refresh, I got this, and ignored it:
dialogIshouldhaveread

See what I did there?

I was refreshing the delete in the UI!

Don’t make that mistake!

Instead of clicking the refresh button, it’s easier (and safer) to click the group name on the left:
dontclickrefreshwhenyoudelete

Lessons learned:

  • Pay attention to dialog boxes, they may save you an hour.
  • Don’t ever click ‘refresh’ after performing a delete!

 

Is it time for an end-of-year email “Flush”?

The end of the year 2014 is nearing and I’m starting to get some ‘what a great year’ email in my inbox.

As I reflect back on the year, I’d have to agree.

As I look at my work inbox, It’s sitting at 71 items – that’s not exactly ‘inbox zero’ but it’s a low enough number that I can quickly scan all of it at the end of the day to make sure nothing slipped by.

There are some great advantages of “inbox zero” and while I rarely get to ‘true zero’, having a small number of items in my inbox does provide a certain amount of peace and comfort that lets me focus my energy on other things, instead of having a looming feeling that I’ve forgotten to address something critical.

I’m sure there are plenty of books and blog posts on how to achieve such inbox nirvana, And I can’t say that I’ve read any of that. I do have one suggestion…
When your inbox gets overwhelming, it’s time for a flush.

When your inbox gets overwhelming, it’s time for a flush.

Now this doesn’t mean you just delete everything, but why not make a folder, call it “Stuff I didn’t get to in 2014” and drag EVERYTHING in from your “inbox” to this new folder? Then start out 2015 with a clear inbox and see if that helps your productivity and focus.

– Jack

Filed under “Deep thoughts by SharePoint Jack”

Is ‘SharePoint Online’ becoming ‘Office 365 Sites’?

There’s been a bit of chatter for some time now about the future of SharePoint.
An article over a year ago portrayed SharePoint as a technology Stack, much like .net

I couldn’t help but notice in my office 365 site, that SharePoint “Sites” aren’t actually branded “SharePoint” in the panel now:
Office365Portal_Dec_12_2014

See that?
Every other branded product has its brand name.
We see “Excel Online” not “Spreadsheet”
OneDrive is there.
So is Delve

And both PowerPoint Online and and OneNote Online use two lines to describe what those do.
In fact, there are exactly the same number of characters in the word “PowerPoint” as there are in “SharePoint”

Lets look at one more thing…
See that tile with the “V” – that’s not Visio, it’s Video, you know, Office 365 Video:

Office365VideoTitle

Now, if “Video” by itself means “Office 365 Video”, does it follow suit that “Sites” by itself means “Office 365 Sites”?

Lets look at a few more things in the o365 interface.
When I clicked on “Sites” the first time, I got this screen:
Office365Sites_screen1
Now here we can relax, Microsoft hasn’t rolled Sites into OneDrive. If you note the url, you’ll see it’s going to a mysite location. It’s the first time I’ve used this tenant, and mysites have never been setup for the user that I’m using.

After the mysite was setup, I went back to the first screenshot, and again clicked on the “Sites” link.
This time I came here:
Office365Sites_screen2
Now lets take a moment and examine that screen shall we?

  • There’s no mention of SharePoint…
  • Those familiar with the SharePoint 2013 branding might recognize the “S>” logo.
  • Those familiar with SharePoint infrastructure might notice that the URL is a SharePoint MySite.

If that’s a mysite, maybe the “team site” is different?

Curious, I had to create a standard old “team site” and see what it looked like – I did this in the SharePoint online admin panel (yes that’s still there, under the hood) Note, that I did this before the above screenshot was taken, which is why you see “Public Site” and “Team Site” in the screenshot.

Ok so what does a “Team Site” look like?
Office365Sites_TeamSite

See the word SharePoint anywhere on that screenshot?
I didn’t think so.
I even brought down the settings menu to see if there was any mention of it. (no)

Times they are a changing. If I didn’t know any better, I’d say that the SharePoint Brand Name is on it’s way out.
Is it time to rethink the “SharePointJack” brand as well?

Discrepancy between SharePoint Online Recycle Bin and MS’s stated 90 day retention policy

In Office 365 Sites / SharePoint Online, deleted objects are supposed to hang around for 90 days.

While playing around with SharePoint Online I deleted a site collection, then went and had a look in the SharePoint Online admin portal. I was a little surprised to see that it showed that it only had 30 days to live – that’s a lot less than the 90 that MS states.

I searched the net, and confirmed, it is in fact supposed to be 90 days.

It turns out that between day 30 and 90, you must use powershell to recover a deleted SharePoint Online/o365 site.

The commands to do this are fairly straightforward:

#connect to SPO
Connect-SPOService -url https://tenantname-admin.sharepoint.com -credential email@domain.com

#take a look at the Sites in the long term recycle bin:
get-SPODeletedSite

Now the above commands will get you connected to SPO, and then show you what sites you have that are deleted.
If you only have one site, you can just pipe the output to Restore-SPODeletedSite

# RESTORE EVERYTHING
Get-SPODeletedSite | Restore-SPODeletedSite

What if you only want to restore one site?
Just use “where”
Here’s a full example:

#connect to SPO
Connect-SPOService -url https://tenantname-admin.sharepoint.com -credential email@domain.com

#take a look at the Sites in the long term recycle bin:
get-SPODeletedSite

#usepick one URL from above and use it here:
Get-SPODeletedSite | Where {$_.url -eq "https://yourURL/sites/yoursite"} | Restore-SPODeletedSite

Slides from SPFest Chicago 2014 SharePoint PowerShell Time Machine Session

I presented a session at SPFest Chicago titled: SharePoint PowerShell Time Machine.

A big thank you to everyone that attended!

The Slides and scripts used in that session are attached below:
SharePoint PowerShell Time Machine.pptx
SharePoint PowerShell Time Machine Scripts_Jack Fruh Sharepointjack.com.zip

If you are just getting started with PowerShell, the cheat sheet is a GREAT place to start! It’s 6 pages of very short form info about PowerShell:
Combined PowerShell and SharePoint Cheat Sheet

I look forward to hearing how you’re using PowerShell to save time!

– Jack

PS. If you download the scripts, you might find you have some trouble running them – Windows will sometimes tag the files as ‘downloaded’ and you’ll need to untag them before you can run them.

If all else fails, you can always open in notepad, and copy/paste into a new file.

– Jack

Note: Bamboo solutions did a write up of the session and put it on their blog:
http://community.bamboosolutions.com/blogs/bambooteamblog/archive/2014/12/10/sp-fest-chicago-jack-fruh-s-sharepoint-powershell-time-machine.aspx

Slides from SPFest Chicago 2014 SharePoint PowerShell Workshop

I did a PowerShell workshop with Michael Blumenthal today at SPFest Chicago.

The main slide deck is attached below, more to follow.

Introduction to PowerShell – Be a PowerShell Hero – SPFest 2014 Workshop

If you were at the session, thanks for attending!
If you missed it, thanks for looking up the slide deck!

Either way I look forward to hearing how you’re using PowerShell with SharePoint!

– Jack