Tag: Graph API

  • How to create SharePoint Sites using Graph

    In the previous article I was showcasing how to create a SharePoint Site with PNP.PowerShell. In this article I want to elaborate how to create a SharePoint Site with MS Graph.

    Creating SharePoint Sites with MS Graph can be interesting for integration scenarios as part of a overarching process. Let’s assume, that you want to create a SharePoint Site for each Sales opportunity or whenever a new Project is initiated, for this purpose it makes sense to create SharePoint Sites using Graph.

    The benefit for this approach in comparison to the PNP.PowerShell is, that you don’t need PowerShell for it, so you could theoretically also leverage every other programming language. In this article, I will create SharePoint sites using Graph in PowerShell, but you can easily replicate the approach to every different programing language as long as you stick to the foundation.

    You could create the SharePoint Sites using either a user account or an Azure Application Permission. In this article I will explain the SharePoint site creation based on an Azure Application Registration.

    In this article, I’ll leverage the MS Graph call POST /groups. With the creation of the M365 Group, a SharePoint teams site will be created.

    Why would you prioritize direct Graph Access vs Pnp.Powershell?

    There are integration scenarios, where you cannot install PowerShell modules. Either because it is not permitted by your organization or because your automation execution host is not using PowerShell as the programing language. This might be the case when you use ServiceNow Orchestration. Having an standard REST API adds flexibility in comparison to a PowerShell module, which can be only used on PowerShell execution hosts.

    Prerequisites to Create SharePoint Sites using Graph

    In order to create SharePoint Sites using Graph, you need to setup an App Registration in Azure Portal. The App Registration needs to have following permission.

    Permission typeLeast privileged permissionsHigher privileged permissions
    Delegated (work or school account)Group.ReadWrite.AllDirectory.ReadWrite.All
    Delegated (personal Microsoft account)Not supported.Not supported.
    ApplicationGroup.CreateDirectory.ReadWrite.All, Group.ReadWrite.All
    Permission Reference for creating SharePoint sites using Graph

    Looking forward, I’ll utilize the Application permission Group.Create following least privilege principles.

    Azure App Registration to Create SharePoint Sites using graph

    In order to create SharePoint sites using Graph, we need the Azure App registration considering the aforementioned application permission Group.Create.

    To create the Azure App Registration, browse to the Azure App Registration page in the Microsoft Azure Portal:

    Click on New registration:

    Provide a meaningful name and register the App by clicking on Register.

    Click on API Permissions

    By default the App is able to read user information from Entra ID. Click on Add a permission to add permissions.

    Now select Microsoft Graph

    Now click on Application Permissions, search for Group and provide the Group.Create permission.

    Now you have requested the permission. Make sure that the permission is also granted. You can grant it by a Global Administrator, Application Administrator, or Cloud Application Administrator.

    Once the permission is granted, the page should look like this:

    Now we made sure that the App registration is created and the necessary permissions are granted. We just need to make sure to configure the authentication of the Application. There are two approaches, which can be used to authenticate with Entra ID:

    1. Certificate based authentication
    2. Secret based authentication

    For this article, I’ll use the secret based authentication. However in production use, consider using a certificate, which is considered to be more secure.

    Browse to the Certificates and secrets blade, click on Client secrets and on New client secret.

    Provide a meaningful name to the client secret. I’ll use GroupMgmt_GroupCreate_180days and configure the lifetime. I’ll use the default lifetime of 180 days. Before expiration you need to make sure to create an additional client secret, and change it in your code. Else your app and the use case won’t work.

    After addition, you’ll be shown only once the value of the client secret. Make sure to store it in a secure place like a key vault.

    Create SharePoint Site Using Graph: PowerShell code

    With the code below you can create SharePoint Sites using Graph with PowerShell. Before you run the code, make sure that you change the parameters.

    If you trigger the code below, you’ll get prompted the app secret, which you have created previously.

    How can I avoid getting prompted on Windows?

    To avoid getting prompted each time for the app secret value, make sure to export it like this:

    Make sure that you export the credential with the user account, with which you want to run the code, and also the machine, on which the code shall run. If you copy the credential xml to another machine or try to decode the credential file with a different user account, you’ll notice that you won’t be able to do so.

    If you use the PowerShell code in Azure (like in Azure Automation, Azure Functions), make sure that you store the credentials in a secure place e.g. Azure Key Vault.

    # Export the Credential path to specified path   
    Get-Credential | Export-CliXml -Path c:\creds\credential.xml
    
    # Import the credential object from specified path
    $Credential = Import-CliXml -Path c:\creds\credential.xml

    The code below is first attempting to obtain a bearer token from Entra Id. Once the token is obtained, it is intended to create an Entra ID group, based on which the SharePoint Site is getting created.

    # Define parameters for the tenant, application ID, site name, and mail nickname
    Param (
        # The Azure AD tenant ID for authentication
        $Tenant = "m365x16735261", 
        # The Application (client) ID of the registered Azure AD application
        $AppID = "730efedf-b000-4deb-92c6-25da0e27e24d",   
        # The display name of the group to be created
        $SiteName = "GraphSite7",   
        # The mail nickname for the group (used for email addressing)
        $Mailnickname = "GraphSite7"   
    )
    
    # Securely prompts for application credentials (Client ID and Secret)
    #$AppCredential = Get-Credential -UserName $AppID -Message "Enter the client secret"
    
    # Function to obtain the Microsoft Graph API token
    function Get-GraphToken {
        param (
            [string]$Tenant,
            [PSCredential]$AppCredential
        )
        
        # Define the required scope for accessing the Microsoft Graph API
        $Scope = "https://graph.microsoft.com/.default"
    
        # Prepare the body for the token request with client ID, client secret, and scope
        $Body = @{
            client_id = $AppCredential.UserName                     # Client ID from app registration
            client_secret = $AppCredential.GetNetworkCredential().password   # Securely retrieve the client secret
            scope = $Scope                                         # API scope required for MS Graph API access
            grant_type = 'client_credentials'                      # Grant type for app-only authentication
        }
    
        # Azure AD endpoint to request an OAuth token
        $GraphUrl = "https://login.microsoftonline.com/$($Tenant).onmicrosoft.com/oauth2/v2.0/token"
    
        try {
            # Request the access token from Azure AD
            $AuthorizationRequest = Invoke-RestMethod -Uri $GraphUrl -Method "Post" -Body $Body
            # Retrieve the access token from the response
            return $AuthorizationRequest.Access_token
        } catch {
            # Handle authentication errors, if any
            Write-Error "Error during token retrieval: $_"
            return $null
        }
    }
    
    # Call the Get-GraphToken function and store the result
    $Access_token = Get-GraphToken -Tenant $Tenant -AppCredential $AppCredential
    
    # Check if access token is successfully obtained
    if (-not $Access_token) {
        Write-Error "Failed to retrieve access token. Exiting script."
        return
    }
    
    # Set the Authorization header with the bearer token for API requests
    $Header = @{
        Authorization = "Bearer $Access_token"              # Bearer token for API authorization
        "Content-Type" = "application/json"                 # JSON content type for the request body
    }
    Write-Output "Access token retrieved successfully."
    
    #region create site
    # Define the group properties for the new group creation request
    $Body = @{
        displayName = $SiteName                   # Name displayed for the new group in Azure AD
        groupTypes = @("Unified")                 # "Unified" indicates an Office 365 group
        mailEnabled = $false                       # Enable mail functionality for the group
        mailNickname = $Mailnickname              # The mail alias for the group, unique for Microsoft 365 groups in the organization
        securityEnabled = $false                  # Indicates that this is not a security group
    }
    
    # Set the endpoint URL for creating a group in Microsoft Graph API
    $Url = "https://graph.microsoft.com/v1.0/groups"
    # Convert the group properties to JSON format required for the API request
    $BodyJSON = $Body | ConvertTo-Json -Compress
    
    try {
        # Send the HTTP POST request to create the group with the defined properties
        $response = Invoke-RestMethod -Uri $Url -Headers $Header -Method Post -Body $BodyJSON
        # Output the ID of the newly created group for confirmation
        Write-Output "Group created successfully: $($response.id)"
        Write-Output "Site created succesfully: $SiteName"
    } catch {
        # Handle errors that may occur during the group creation request
        Write-Error "Failed to create group: $_"
    }
    #endregion

    Upon submission of the code, you’ll get following similar output:

    With the execution of the code, the SharePoint site https://m365x16735261.sharepoint.com/sites/GraphSite8 was just created:

    Create SharePoint Sites using Graph: Postman

    I want to showcase the strength of the usage of MS Graph by showing how to create SharePoint sites with Postman. The approach consists of a two staged process: Obtain Bearer Token and creation of Entra ID group.

    Create SharePoint Sites using Graph: Postman – Prerequisites

    Make sure that you install Postman or utilize the web version of postman. You can download it from the official website: Postman API Platform

    Create SharePoint Sites using Graph: Postman – Obtain bearer token

    To create the bearer token, you need to trigger a POST call for the endpoint https://login.microsoftonline.com/<tenanturl>/oauth2/v2.0/token

    In my example it is: https://login.microsoftonline.com/m365x16735261.onmicrosoft.com/oauth2/v2.0/token

    Make sure you maintain following body as x-www-form-urlencoded:

    KeyValue
    grant_typeclient_credentials
    client_id<app id of the Azure App registration>
    In my example:
    730efedf-b000-4deb-92c6-25da0e27e24d
    client_secret<secret of the Azure App registration>
    scope.default

    Your call shall look like this:

    After calling it you should receive a bearer token as part of a response:

    {
        "token_type": "Bearer",
        "expires_in": 3599,
        "ext_expires_in": 3599,
        "access_token": "eyJ0eXAiOiJKV1QiLCJub2...<SANITIZED>"
    }

    Copy the bearer token value, you’ll need it in the next step.

    Create SharePoint Sites using Graph: Postman – Create Group

    Now as you have obtained the bearer token, create another tab and make sure to Enter following header information:

    KeyValue
    Content-Typeapplication/json
    AuthorizationBearer eyJ0eXAiOiJKV1QiLCJub25jZSI6ImkyQUxLTktKMVpoRXdTU2xoWDJrYXZ1a09IRVFocUdQMnRESzhGWS1naGciLCJhbGciOiJSUzI1NiIsIng1dCI6IjNQYUs0RWZ5Qk5RdTNDdGpZc2EzWW1oUTVFMCIsImtpZCI6<SANITIZED>

    Proceed with the body configuration:

    Provide the body as JSON. Make sure to adjust it based on your scenario:

    {
      "mailEnabled": false,
      "displayName": "GraphSite10",
      "mailNickname": "GraphSite10",
      "securityEnabled": false,
      "groupTypes": [
        "Unified"
      ]
    }

    After submitting the POST request, you’ll get the Entra ID group and the Site created. This is a sample response, upon submission of the request.

    {
        "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#groups/$entity",
        "id": "d32e29f1-68e2-4a55-a9cd-bda027125522",
        "deletedDateTime": null,
        "classification": null,
        "createdDateTime": "2024-11-01T23:32:57Z",
        "creationOptions": [],
        "description": null,
        "displayName": "GraphSite10",
        "expirationDateTime": null,
        "groupTypes": [
            "Unified"
        ],
        "isAssignableToRole": null,
        "mail": "GraphSite10@M365x16735261.onmicrosoft.com",
        "mailEnabled": true,
        "mailNickname": "GraphSite10",
        "membershipRule": null,
        "membershipRuleProcessingState": null,
        "onPremisesDomainName": null,
        "onPremisesLastSyncDateTime": null,
        "onPremisesNetBiosName": null,
        "onPremisesSamAccountName": null,
        "onPremisesSecurityIdentifier": null,
        "onPremisesSyncEnabled": null,
        "preferredDataLocation": null,
        "preferredLanguage": null,
        "proxyAddresses": [
            "SMTP:GraphSite10@M365x16735261.onmicrosoft.com"
        ],
        "renewedDateTime": "2024-11-01T23:32:57Z",
        "resourceBehaviorOptions": [],
        "resourceProvisioningOptions": [],
        "securityEnabled": false,
        "securityIdentifier": "S-1-12-1-3543017969-1247111394-2696793513-576000551",
        "theme": null,
        "uniqueName": null,
        "visibility": "Public",
        "onPremisesProvisioningErrors": [],
        "serviceProvisioningErrors": []
    }

    As one can see below, the SharePoint site was created.

    With the Postman approach, you can see the benefit: You really don’t rely anymore on PowerShell. With Graph you can create SharePoint Sites, with every REST API execution host.

    Conclusion

    With the approach, we have a straight forward approach to create SharePoint sites, without being dependent on PowerShell. Now I am curious about your setup: Do you create SharePoint Sites as part of a workflow? Which business use case are you supporting with it and what else are you creating apart from the SharePoint Site?

    References

  • Access SharePoint via Graph API in PowerShell

    Access SharePoint via Graph API in PowerShell

    Sometimes the use of PNP.PowerShell might not be sufficient. I encountered this experience, when I wanted to find out the usage of all sites. The Graph API provides methods, which you can use in your PowerShell Scripts. So in my example I wanted to get unused Sites with PowerShell. If you want to make use of it, you have to register an enterprise application and afterwards you can retrieve the information with an HTTP-Webrequest. In the following I will show you step by step how to access your SharePoint tenant with Graph API in PowerShell.


    Considerations – Find the right Graph API Method

    The Graph API has multiple methods, which we can use to analyze and change the content of our M365 services. In order to find the right method for your plan, check folllowing resources to see what the Graph API is capable of Microsoft Graph REST API v1.0 reference – Microsoft Graph v1.0 | Microsoft Docs. Based on the needed methods, you have to set up your enterprise application.

    Let’s assume, that you want to see the site usage of all sites in your tenant. In order to do this, you have to make use of following API method:

    GET /reports/getSharePointSiteUsageDetail(period='{period_value}’)
    GET /reports/getSharePointSiteUsageDetail(date={date_value})

    This API requires following permissions. We will consider them in this article. I want to analyze the sharepoint usage and want to update it to a list afterwards, that’s why I will make use of Application – Reports.Read.All

    Permission typePermissions (from least to most privileged)
    Delegated (work or school account)Reports.Read.All
    Delegated (personal Microsoft account)Not supported.
    ApplicationReports.Read.All
    SharePointSiteUsage Method Screenshot Graph API
    reportRoot: getSharePointSiteUsageDetail – Microsoft Graph v1.0 | Microsoft Docs

    Register the Enterprise Application

    After we figured out what permissions we need, we register the app.

    Prerequisites

    You have to have the role ‘Global Administrator’ to grant the permissions for an Enterprise Application.

    Registration

    Visit the Azure Portal URL and switdh to the app registrations sites. Directlink: App registrations – Microsoft Azure

    Click on new registration

    Register New App for Graph Api

    Give your application a name, click on Accounts in this organizational directory only, select mobile as platform, after that click on register.

    Application Registration for Graph Api

    Take a note of the Application (client) ID, you will need it to authenticate against the Graph API.

    Enterprise Application

    Grant API Permissions for App Registration

    After creating the app, we have to give it the permissions, which we have defined in the first step.

    Enterprise Application API permission

    Click on Microsoft Graph.

    Graph API screenshot

    Grant it Application permissions

    Application Permissions

    Now you have to select the permissions, for which you want to use the Graph API. I just need the information for Reports.Read.All. If you don’t know which permission to take, check the considerations part of this post.

    reports.read.all permission for Graph API

    As you can see, the permission is not granted for this tenant.

    not granted permissions screenshot for Graph API

    Create Client Secret for App Registration

    In order to authenticate to the Graph API in PowerShell, you have to create a client secret.

    Click on Certificates & secrets and then on New client secret

    Create client secret for Graph API enterprise application

    Set a Description and define when it will be expiring. I would recommend to give it a description, which you can recognize, for what it will be used in future. I have set 24 months, because I want to make use it in an automation, which should run for a long term. When finished, click Add.

    Usage Scripts client secret for Graph API

    Take Note of the value! You wont see it again, if you leave the site.

    Client Secret for Graph API obfuscated

    Consent the Requested permissions for App Registration

    Caution: You have to consent the created application with the global administrator role.

    https://login.microsoftonline.com/TENANTDomain/adminconsent?client_id=CLIENTID
    

    The URL for my dev tenant is like:

    https://login.microsoftonline.com/devmodernworkplace.onmicrosoft.com/adminconsent?client_id=949710fd-8d80-48ee-8c1b-a6f5e9e32be3

    Choose an account with global administrator role.

    Global administrator account login to grant permission for Graph API

    As you can see the permissions, which we have configured, are showing up:

    permission grant for created app for Graph API

    Since you have not set a redirect url, you will encounter this issue, which you can ignore.

    this ocurs, since we have not configured a redirect url

    Check Permission consent

    You can check that the permission is granted, if you see the green check marks.

    granted permission for enterprise application for Graph API

    Script To Acess SharePoint via the Graph API (PowerShell)

    The script contains two parts. The first part is about authentication and the second is about getting the data provided.

    Authentication

    I am making use of a credential export to be sure, that nobody steals the credentials, when it is in plain text. If you don’t know how to, check out: Use credentials in PowerShell – SPO Scripts

    Function Export-CredentialFile 
    {
        param(
        [Parameter(Mandatory=$true,Position=0)]
        $Username,
        [Parameter(Mandatory=$true,Position=1)] 
        $Path
        )
        
        
        While ($Path -eq "")
        {
            $Path = Read-Host "The path does not exist. Where should the credentials be exported to?"
        }
        $ParentPath = Split-Path $Path
        If ((Test-Path $ParentPath) -eq $false)
        {
            New-Item -ItemType Directory -Path $ParentPath
        }
        $Credential = Get-Credential($Username)
        $Credential | Export-Clixml -Path $Path
        Return $Credential
    }
    Function Import-CredentialFile ($Path)
    {
        if (! (Test-Path $Path))
        {
            Write-Host "Could not find the credential object at $Path. Please export your credentials first"
            Export-CredentialFile
        }
        Import-Clixml -Path $Path
    }
    $AppId = '949710fd-8d80-48ee-8c1b-a6f5e9e32be3'
    $CredentialPath = "C:\temp\$AppId.key"
    Export-CredentialFile -Username $AppId -Path $CredentialPath

    After doing this, we notice, that the file with the app id as name, has an encrypted password. So we splitted credentials from script to increase the security. This credential file can only be used on the machine and with the user, who has created it.

    PowerShell credential object

    If we run follwing script afterwards, we will notice, that the $AuthorizationRequest will show us a token with an bearer token.

    $AppId = '949710fd-8d80-48ee-8c1b-a6f5e9e32be3'
    $CredentialPath = "C:\temp\$AppId.key"
    $AppCredential = Import-CredentialFile -Path $CredentialPath
    
    $Scope = "https://graph.microsoft.com/.default"
    $Url = "https://login.microsoftonline.com/devmodernworkplace.onmicrosoft.com/oauth2/v2.0/token"
    
    $Body = @{
        client_id = $AppCredential.UserName
        client_secret = $AppCredential.GetNetworkCredential().password
        scope = $Scope
        grant_type = 'client_credentials'
    }
    
    $AuthorizationRequest = Invoke-RestMethod -Uri $Url -Method 'post' -Body $Body
    $AuthorizationRequest
    
    answer to the authorization request

    Access SharePoint Online with Authorization Token

    Now that we got the access token, we can connect to SharePoint Online with following script. You can use the uris (methods), defined in Microsoft docs.

    $Uri = "YOURURI"
    
    $Header = @{Authorization = "$($AuthorizationRequest.token_type) $($AuthorizationRequest.access_token)"}
    $SitesRequest = Invoke-RestMethod -Uri $Uri -Method 'Get'  -Headers $Header

    Get Site Usage Details

    You can get the site usage with following uri “https://graph.microsoft.com/beta/reports/getSharePointSiteUsageDetail(period='{D90}’)?`$format=application/json”. The number next to the D means the amount of days. So for my example it shows the usage of all sites for the last 90 days. You can replace D90 with D7, D30, and D180.

    With this script you can get the site usage for the last 90 days:

    $Uri = "https://graph.microsoft.com/beta/reports/getSharePointSiteUsageDetail(period='{D90}')?`$format=application/json"
    
    $Header = @{Authorization = "$($AuthorizationRequest.token_type) $($AuthorizationRequest.access_token)"}
    $SitesRequest = Invoke-RestMethod -Uri $Uri -Method 'get'  -Headers $Header 
    
    $Sites.value | Out-GridView -PassThru

    Bonus: Ready-to-Use Script

    If you want to make use of the script, you have to change the parameters $GraphUrl and $AppID.

    Param(
        $AppId = '949710fd-8d80-48ee-8c1b-a6f5e9e32be3',
        $GraphUrl = "https://login.microsoftonline.com/devmodernworkplace.onmicrosoft.com/oauth2/v2.0/token",
        $Scope = "https://graph.microsoft.com/.default",
        $Uri = "https://graph.microsoft.com/beta/reports/getOffice365GroupsActivityDetail`(`period=`'`D90`'`)?`$format=application/json",
    )
    
    Function Export-CredentialFile 
    {
        param(
        [Parameter(Mandatory=$true,Position=0)]
        $Username,
        [Parameter(Mandatory=$true,Position=1)] 
        $Path
        )
        
        
        While ($Path -eq "")
        {
            $Path = Read-Host "The path does not exist. Where should the credentials be exported to?"
        }
        $ParentPath = Split-Path $Path
        If ((Test-Path $ParentPath) -eq $false)
        {
            New-Item -ItemType Directory -Path $ParentPath
        }
        $Credential = Get-Credential($Username)
        $Credential | Export-Clixml -Path $Path
        Return $Credential
    }
    Function Import-CredentialFile ($Path)
    {
        if (! (Test-Path $Path))
        {
            Write-Host "Could not find the credential object at $Path. Please export your credentials first"
            Export-CredentialFile
        }
        Import-Clixml -Path $Path
    }
    
    $CredentialPath = "C:\temp\$AppId.key"
    Export-CredentialFile -Username $AppId -Path $CredentialPath
    
    $AppCredential = Import-CredentialFile -Path $CredentialPath
    
    $Body = @{
        client_id = $AppCredential.UserName
        client_secret = $AppCredential.GetNetworkCredential().password
        scope = $Scope
        grant_type = 'client_credentials'
    }
    
    $AuthorizationRequest = Invoke-RestMethod -Uri $GraphUrl -Method 'post' -Body $Body
    
    $Header = @{Authorization = "$($AuthorizationRequest.token_type) $($AuthorizationRequest.access_token)"}
    $SitesRequest = Invoke-RestMethod -Uri $Uri -Method 'get'  -Headers $Header 
    
    $SitesRequest.value | Out-GridView -PassThru

    Conclusio

    In this article you saw how to find the right permission for the enterprise application, which you need to access the SharePoint via the Graph API with PowerShell. After doing this, you can authenticate and analyze the data.

    Further Docs

    reportRoot: getSharePointSiteUsageDetail – Microsoft Graph v1.0 | Microsoft Docs