Contents tagged with PowerShell

  • Office 365 Groups for Admins - simple reporting using PowerShell

    Tags: Office 365, Unified Groups, PowerShell

    In this post, in the Office 365 Groups for Admins series, I will leverage what we learned in the previous posts, combine it with some PowerShell magic and create some basic reports. You can use these reports as a base for your Office 365 Groups reporting in your organization.

    Note: all these reports require that you have connected to your Exchange Online tenant with appropriate permissions, see this post about more details.

    List all Groups

    The most simple and most obvious report is of course to use the Get-UnifiedGroup cmdlet. That one gives you the basic details such as group names, creation dates etc:

    Get-UnifiedGroup | Format-Table Alias, PrimarySmtpAddress, WhenCreated, WhenChanged

    Simple Group report

    Show some more details

    Most often you need more details, specifically with the member details. Here's how you can combine the Get-UnifiedGroup cmdlet with the Get-UnifiedGroupLinks cmdlet to show how many members and owners each group have:

    Get-UnifiedGroup | 
        select Id,Alias, AccessType, Language,Notes, PrimarySmtpAddress, `
        HiddenFromAddressListsEnabled, WhenCreated, WhenChanged, `
        @{Expression={([array](Get-UnifiedGroupLinks -Identity $_.Id -LinkType Members)).Count }; `
        Label='Members'}, `
        @{Expression={([array](Get-UnifiedGroupLinks -Identity $_.Id -LinkType Owners)).Count }; `
        Label='Owners'} |
        Format-Table Alias, Members, Owners
    

    Another Group report

    This command might take a while if you have many Groups and many members. So if want to do some operations on this you should store it in a local variable.

    You can also use this result and store it in a file (or upload it to SharePoint) if you would like to compare how your Groups change over time. For this I prefer to use the Export-CliXml and Import-CliXml cmdlets to persist the data as an XML file and then the Compare-Object cmdlet to find differences. You can do something like this to compare Groups from one day to another:

    # Get Group data (using the command above)
    $allGroups = Get-UnifiedGroup | select ...
    
    # Get previous days Groups
    $previousData = Import-Clixml -LiteralPath c:\temp\groups-data.xml
    
    # Compare the results
    Compare-Object -ReferenceObject $previousData -DifferenceObject $allGroups `
        -Property Members,Owners -PassThru | `
        Format-Table  Alias, Members, Owners, SideIndicator
    
    # Export the new data
    $allGroups | Export-CliXml c:\temp\groups-data.xml 
    

    Comparing Groups

    The SideIndicator indicates the changes; => is new stuff and <= is old stuff. In the picture above you can see that the Members and Owners properties of one Group has increased to 3, from 2. And we also have one brand new Group, with 1 member.

    Finding users in Groups

    Sometimes you need to find out which users are members or owners of certain Groups. Of course you can use Azure AD cmdlets to find memberships but another way is to use a modification of the command above:

    $allGroupsObj = Get-UnifiedGroup | 
        select Id,Alias,  `
        @{Expression={Get-UnifiedGroupLinks -Identity $_.Id -LinkType Members | `
        select Name}; Label='Members'}, `
        @{Expression={Get-UnifiedGroupLinks -Identity $_.Id -LinkType Owners | `
        select Name}; Label='Owners'}
    

    This allows you to find out which Groups a specific user is Member and/or Owner of. To find which Groups Katie Jordan is owner of you fire off a command like this:

    $allGroupsObj | Where-Object{$_.Owners | Where-Object{ $_.Name -eq 'KatieJ'}} | `
        select Alias
    

    KatiJ is the owner

    Summary

    I hope this post showed you how you can create reports of your Office 365 Groups, in wait for Microsoft to provide us with better tools. And I hope you actually do reporting on your Groups, since that is an essential part of your Office 365 Governance and your understanding on how your users work.

  • Office 365 Groups for Admins - managing Group memberships with PowerShell

    Tags: Office 365, Unified Groups, PowerShell

    In the last post of the Office 365 Groups for Admins series I showed you how to manage the Unified Groups using PowerShell. Let's continue on that journey and take a look at how you can manage the Group memberships using PowerShell.

    All membership management are done using the *-UnifiedGroupLinks cmdlets, you can access them using PowerShell and connecting to Exchange Online as shown in the previous post. The cmdlets is at the moment that well documented. If that changes I'll make sure to update this post (and please remind me).

    Public vs Private Groups

    Before we dive into Memberships let's take a look at Public vs Private Groups, something you configure when creating the Group. In Public Groups anyone within your Organization can participate in discussions and access its content, whereas in Private Groups you have to be a member.

    Listing members of Groups

    First of all assume that we want to list the members of a Group. There are three types of Memberships in Office 365 Groups:

    • Members
    • Owners
    • Subscribers

    To show all the Members of a Group issue the following command:

    Get-UnifiedGroupLinks -Identity "XT1000 Marketing" -LinkType "Members"

    The LinkType parameter can be either Members, Owners or Subscribers.

    Adding members to a Group

    To add one or more accounts as a Member to a Group you use the Add-UnifiedGroupLinks cmdlet as follows:

    Add-UnifiedGroupLinks `
        -Identity "XT1000 Marketing" `
        -LinkType "Members" `
        -Links @("garthf@contoso.com","robinc@contoso.com")

    Identity is the Group, LinkType has to be either Members, Owners or Subscribers and Links should be a single or an array of mailboxes.

    To add a new admin to the Group, you use the LinkType set to Owners. But, before issuing that command you need to make the user a Member of the Group, so it is a two-stage rocket like this to make someone an admin:

    Add-UnifiedGroupLinks `
        -Identity "XT1000 Marketing" `
        -LinkType "Members" `
        -Links "garthf@contoso.com"
    Add-UnifiedGroupLinks `
        -Identity "XT1000 Marketing" `
        -LinkType "Owners" `
        -Links "garthf@contoso.com"

    Subscribers are basically just members but they have decided to Subscribe to events from the Group. They are added just like Owners - first a Member Group Link and then a Subscriber Group Link.

    Note that the user creating the Group, using PowerShell, will always be an Owner (and Member).

    Removing members from Groups

    Removing users from a Group are very similar to adding them. If they are Owners or Subscribers that Group Link has to be removed first, then the Member Group Link:

    Remove-UnifiedGroupLinks `
        -Identity "XT1000 Marketing" `
        -LinkType "Owners" `
        -Links @("garthf@contoso.com") `
        -Confirm:$false
    Remove-UnifiedGroupLinks `
        -Identity "XT1000 Marketing" `
        -LinkType "Members" `
        -Links @("garthf@contoso.com") `
        -Confirm:$false

    Summary

    This post showed with a few examples on how to manage memberships in Office 365 Groups, using PowerShell and the *-UnifiedGroupLinks cmdlets. In the upcoming posts we'll take a look at how to combine the different cmdlets to produce some good reports of the Unified Groups.

  • Office 365 Groups for Admins - managing Groups with PowerShell

    Tags: Office 365, Unified Groups, PowerShell

    One of the loudest complaints I hear from people when we talk about Groups is the lack of management features, so in this post in the Office 365 Groups for Admins series we will take a look at how you can manage your Unified Groups using PowerShell. In the previous post I actually already showed you how to use PowerShell to create Groups, but let's take a step back.

    Connecting PowerShell to Exchange Online

    To start working with the Unified Groups in PowerShell we need to connect to Exchange Online and we do that by establishing a PowerShell session to a specific Uri, see code sample below, and then import that session to our local session. This means we do not have to install any PowerShell module or similar. This is how it should look like:

    # We need some credentials of course
    $UserCredential = Get-Credential
    
    # Create the session
    $Session = New-PSSession -ConfigurationName Microsoft.Exchange `
        -ConnectionUri https://outlook.office365.com/powershell-liveid/ `
        -Credential $UserCredential `
        -Authentication Basic -AllowRedirection
    
    # Import the session
    Import-PSSession $Session
    
    # Do Stuff...
    
    # Kill the session
    Remove-PSSession $Session
    

    There is nothing you need to modify here, just enter your credentials when asked for. Note that I close the remote session!

    Unified Group cmdlets

    There are a couple of Unified Group cmdlets available for us to use. Issuing Get-Help UnifiedGroup shows the following operations:

    • Add-UnifiedGroupLinks
    • Get-UnifiedGroup
    • Get-UnifiedGroupLinks
    • New-UnifiedGroup
    • Remove-UnifiedGroup
    • Remove-UnifiedGroupLinks
    • Set-UnifiedGroup

    As you can see there are two types of cmdlets; one for creating, updating and removing Unified Groups and one listing, adding and removing "Group Links" (membership stuff), but more about those in the next post.

    Get-UnifiedGroup

    This cmdlet lists all you current Office 365 Groups. It's perfect for creating reports. It has support for filtering options (very similar to Exchange mailbox management) or you can retrieve a specific group like this:

     Get-UnifiedGroup -Identity <groupalias>

    Listing Groups

    New-UnifiedGroup

    We discussed this one in the latest post, so we keep this description short: this cmdlet creates new Groups.

    Set-UnifiedGroup

    This is most likely the most important cmdlet for managing Groups. It allows you to modify properties of the Groups.

    Here are a couple of the operations you need to understand and use:

    Hide a Group from the Address lists (GAL)

    The request I hear the most often is  - I don't want (certain) groups to be visible in the GAL. Then just use the HiddenFromAddressListsEnabled parameter and it will disappear from the address lists (a resync of the GAL is needed for client apps):

    Set-UnifiedGroup -Identity <alias>  `
         -HiddenFromAddressListsEnabled $true

    Changing the appearance

    You can change the display name, primary and other e-mail addresses and even add Mail tips to a Group.

     Set-UnifiedGroup -Identity BossesGroup `
         -DisplayName "Contoso Bosses" `
         -MailTip "You're sending mail to a Group!!!!" `
         -PrimarySmtpAddress "bosses@contoso.com"

    Group Mail Tips

    Accept or Reject certain users from sending mails to groups

    There might be situations when you do not want certain people to send messages to a group or you only want specific people to send messages. Then you can use the RejectMessagesFromSendersOrMembers or AcceptMessagesOnlyFromSendersOrMembers parameters. They work in a similar fashion as the similar parameters on the Set-DistributionGroup cmdlet. For instance this prohibits Garth Forth from sending messages to the Bosses Group.

    Set-UnifiedGroup -Identity BossesGroup `
        -RejectMessagesFromSendersOrMembers garthf@contoso.com

    Remove-UnifiedGroup

    Pretty obvious what this one does - it removes a Group.

    Summary

    After reading this post you should find yourself comfortable starting managing Groups using PowerShell and even start writing your own reports (unless you can't wait a couple of more days). In the next post I'll continue with the Group Links and show you how to manage the membership of Groups.

  • SharePoint Online: App Only policy PowerShell tasks with ACS

    Tags: SharePoint, Office 365, PowerShell, Apps

    Here’s a little nugget that I’ve planned to blog about for some time, that I needed today for a small task. I needed to do a background job to SharePoint Online that at a scheduled interval downloads list data, process them and optionally updates some data in my site. This can of course be done by creating an executable storing username and password combos, and with the help of the TokenHelper.cs class from the App for SharePoint Web Toolkit NuGet package and some stored username and password combos we can make the Auth pieces quite easy. I don’t like that approach. There’s two big drawbacks with that approach. The first one is storing the username and password – we can solve that with an AppOnly policy, which I blogged about in the SharePoint 2013: Using the App Only policy and App Principals instead of username and password combos post. The second issue is that I very much prefer to script these kind of tasks, it makes it more flexible. Problem with that approach is that we need to manually do the Auth pieces. But from now on you just copy and paste from this post.

    Creating the App Principal

    In order to create our PowerShell script we need to create an App for it. This step is exactly the same as we did in the blog post mentioned above. But let’s repeat it. Note! I do use the traditional way of registering apps in this scenario using ACS – I do not use an Azure AD app. The reason for this is I want every Site Collection admin to be able to script like this. Azure AD apps requires way to much permissions for the normal user.

    imageIn your site collection, navigate to /_layouts/15/appregnew.aspx. Click on both Generate buttons, so that you get one Client Id and one Client Secret. Next enter a Title, an App Domain and the Redirect URI. The App Domain and Redirect URI can be basically anything in this scenario. Then click Create to create the App Principal. On the next screen you will get all the details of your App. Copy and paste that data so you don’t loose it.

    Next head on over to /_layouts/15/appinv.aspx. Paste your Client Id in the App Id (Microsoft has never been good in naming conventions) text box and click Lookup. This will retrieve the rest of the app details. Since we will not have any UI or install any App we need to manually ask for permissions and then grant the permissions. What we do is that we create a permission request XML. Depending on your requirements your XML may be different from the one below. The following permission request XML asks for Full Read permissions on the whole web.

    <AppPermissionRequests AllowAppOnlyPolicy="true">
      <AppPermissionRequest
        Scope="http://sharepoint/content/sitecollection/web"
        Right="Read"/>
    </AppPermissionRequests>

    Note the AllowAppOnlyPolicy=”true” attribute – that one is the key to allowing the App to run without username and password. Once you paste the XML into the permission request XML textbox and then click on Create. You will see the Trust screen for your app. Make sure the permission request is what you expect and if so, click on Trust it!.

    Auth in PowerShell

    Now, let’s get to the core of this post and let’s create a PowerShell script that uses this app to read items from a list. I split it up in a few different parts to make it easier to follow. In the end of the post you will get a link to the full code sample.

    Defining some constants

    Let’s start by defining some constants:

    $clientId = "16119847-8ac7-4a3a-a2e5-18debd9fc9d2"
    $secret = "KuZj5UD22oy2.....=";
    $redirecturi = "https://localhost"
    
    $url = "https://tenant.sharepoint.com/sites/thesite/"
    $domain = "tenant.sharepoint.com"
    $identifier = "00000003-0000-0ff1-ce00-000000000000"
    
    

    The $clientId, $secret and $redirecturi are copied directly from the results of my app registration. The $url parameter is the URL of the site where I registered the app, and $domain is just the server part of that URL. Finally the $identifier is a static Guid value, which represents SharePoint (Exchange, Lync, Workflow etc has their own Id’s).

    Retrieving the Realm

    The next step is to retrieve the Realm or Tenant Id. You might already know this or you might just run these commands once and store it as a static variable.

    $realm = ""
    $headers = @{Authorization = "Bearer "} 
    try { 
        $x = Invoke-WebRequest -Uri "$($url)_vti_bin/client.svc" -Headers $headers -Method POST -UseBasicParsing
    } catch {
        #We will get a 401 here
          $realm = $_.Exception.Response.Headers["WWW-Authenticate"].Substring(7).Split(",")[0].Split("=")[1].Trim("`"")
    }
    

    What we do here is to send a request to the client.svc endpoint and actually expect to get thrown a 401 back. When we get the 401 we’ll locate the WWW-Authenticate headers and retrieve the Realm property. Yea, that PoSh line could be a bit more prettier and failsafe, but it works on my machine.

    Retrieving the access token

    When we have the realm we can create the authorization code. This is how we combine all our variables into an authorization code:

    [System.Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null
    $body = "grant_type=client_credentials"
    $body += "&client_id=" +[System.Web.HttpUtility]::UrlEncode( $clientId + "@" + $realm)
    $body += "&client_secret=" +[System.Web.HttpUtility]::UrlEncode( $secret)
    $body += "&redirect_uri=" +[System.Web.HttpUtility]::UrlEncode( $redirecturi)
    $body += "&resource=" +[System.Web.HttpUtility]::UrlEncode($identifier + "/" + $domain + "@" + $realm)
    

    Let’s walk this through. First of all I load the System.Web assembly, if you run this as a scheduled task this assembly is not loaded in your app domain, compared to when running it in PowerShell ISE for instance, and we need that assembly for some encoding.

    The actual authorization code starts with a grant_type which we set to the static variable of client_credentials, which means that we do not pass any user credentials or refresh tokens. Client_Id is not exactly the same Client Id as above, here we need to append “@” and the realm to scope the request to our tenant. The Client secret and redirect Uri is the same as when creating the app. Finally we have the resource token which is a combination of the SharePoint identifier, the domain and the realm. Note that if you’re targeting the anything in a Personal Site, you not only have to update the $url variable but also the $domain variable.

    We send all this data to the Azure Access Control Services (ACS), remember we did not use Azure AD, endpoint like this:

    $or = Invoke-WebRequest -Uri "https://accounts.accesscontrol.windows.net/$realm/tokens/OAuth/2" `
        -Method Post -Body $body `
        -ContentType "application/x-www-form-urlencoded"
    $json = $or.Content | ConvertFrom-Json
    
    When invoking the endpoint, using our authorization code above, we will get a JSON formatted string back. We convert this string into an object using ConvertFrom-Json.

    Use the access token

    Finally we can use the result from the ACS endpoint and get our access token which we’ll pass into the REST end point (as an Authorization Bearer token) of the site where we want to do operations.

    $headers = @{
        Authorization = "Bearer " + $json.access_token;
        Accept ="application/json"
    } 
    
    Invoke-RestMethod -Uri "$($url)_api/lists/GetByTitle('Documents')/Items" -Method Get -Headers $headers
    

    Summary

    That wasn’t to hard right? All we needed to know was the basic process of OAuth 2.0 and know how to create and pars the requests and responses. The full code sample can be found here: https://gist.github.com/wictorwilen/db67725a66a3e40789e3

About Wictor...

Wictor Wilén is the Nordic Digital Workplace Lead working at Avanade. Wictor has achieved the Microsoft Certified Architect (MCA) - SharePoint 2010, Microsoft Certified Solutions Master (MCSM) - SharePoint  and Microsoft Certified Master (MCM) - SharePoint 2010 certifications. He has also been awarded Microsoft Most Valuable Professional (MVP) for seven consecutive years.

And a word from our sponsors...