Forum Discussion

kcelmer's avatar
kcelmer
Brass Contributor
May 01, 2025

PowerShell Script to Follow a SharePoint Site for a User

Good morning!

I've been struggling with this for a while now. I've tried multiple scripts that are supposed to do this and run into many errors. I have a new script I found, which seems to mostly work, but it gives me this one error:

Write-Error: Response status code does not indicate success: Forbidden (Forbidden).

It looks like a permissions issue. I'm executing this in VSC, running under my user account, but when it connects to Graph, I'm authenticating it as my admin account, which has the following roles:

I do realize how easy it is for users to follow a site, but this is one of those messed-up political situations, so I need a way to do this. 

After the error, it just hangs here:

Add users to follow site(. [Adding user 'Ken Ce.]

Here is the script I'm using:

# Example: .\Add-FollowUserSite.ps1 -UsersMail "user1@[domain].com","user2@[domain].com","user3@[domain].com" -SitesUrl "https://[domain].sharepoint.com"

[CmdletBinding()]
param(
[Parameter(Mandatory=$true,HelpMessage="List of Users Mails")]
  [String[]]$UsersMail=@("user1@[domain].com","user2@[domain].com","user3@[domain].com"),
  [Parameter(Mandatory=$true,HelpMessage="List of SharePoint Url to follow")]
  [String[]]$SitesUrl=@("https://[domain].sharepoint.com")
)

Begin{

    # Validate Modules ffor Microsoft graph users exist
    if (Get-Module -ListAvailable -Name microsoft.graph.users) {
        Write-Host "Microsoft Graph Users Module Already Installed"
    } 
    else {
        try {
            Install-Module -Name microsoft.graph.users -Scope CurrentUser -Repository PSGallery -Force -AllowClobber 
        }
        catch [Exception] {
            $_.message 
        }
    }
    # Validate Modules ffor Microsoft graph users exist
    if (Get-Module -ListAvailable -Name microsoft.graph.sites) {
        Write-Host "Microsoft Graph Sites Module Already Installed"
    } 
    else {
        try {
            Install-Module -Name microsoft.graph.sites -Scope CurrentUser -Repository PSGallery -Force -AllowClobber 
        }
        catch [Exception] {
            $_.message 
        }
    }

    # Import Modules Microsoft.Graph.users and Microsoft.Graph.sites to be used
    Import-Module Microsoft.Graph.users
    Import-Module Microsoft.Graph.sites

    Write-Host "Connecting to Tenant" -f Yellow
    Connect-MgGraph -Scopes "Sites.ReadWrite.All", "User.Read.All"

    Write-Host "Connection Successful!" -f Green
}
Process{
    $count = 0

    $UsersMail | foreach {

        #Get user Graph properties
        $mail = $_

        $user = Get-MgUser -ConsistencyLevel eventual -Count 1 -Search ([string]::Format('"Mail:{0}"',$mail))
        $SitesUrl | foreach {

            #Get Site Graph properties
            $domain = ([System.Uri]$_).Host
            $AbsolutePath = ([System.Uri]$_).AbsolutePath
            $uriSite = [string]::Format('https://graph.microsoft.com/v1.0/sites/{0}:{1}',$domain,$AbsolutePath)
            $site = Invoke-MgGraphRequest -Method GET $uriSite

#Create Body for Post request
$body = @'
    {
        "value": [
            {
                "id": "{$SiteID}"
            }
        ]
    }
'@.Replace('{$SiteID}',$site.id)
            
            #Graph call that include user to follow site
            $uriFollow = [string]::Format('https://graph.microsoft.com/v1.0/users/{0}/followedSites/add',$user.Id)
           
            #Include follow option from user to SharePoint Site
            try{
                $response = Invoke-MgGraphRequest -Method POST $uriFollow -Body $body -ContentType "application/json"
                Write-Host "User '$($user.DisplayName)' is following site '$($AbsolutePath)'" -f Green
            }
            catch {
                Write-Error $_.Exception
            } 
            
        }
        
        $count += 1 
        #progress bar
        Write-Progress -Activity 'Add users to follow site(s)' -Status "Adding user '$($user.DisplayName)' to follow sites... ($($count)/$($UsersMail.Count))" -PercentComplete (($count / $UsersMail.Count) * 100)
    }
}
End {
    Disconnect-MgGraph
    Write-Host "Finished" -ForegroundColor Green
}
       

Any help would be greatly appreciated. 

4 Replies

  • Andres-Bohren's avatar
    Andres-Bohren
    Steel Contributor

    Hi kcelmer​ 

    I did play around a little bit with Interactive Permissions (like in your example).
    I was not able to add or remove a Follower (other than my own user)

    ###############################################################################
    # Connect with MgGraph Interactive
    ###############################################################################
    Connect-MgGraph -Scopes "Sites.ReadWrite.All","User.Read.All" -NoWelcome
    
    #Get User
    $UPN = "email address removed for privacy reasons"
    $User = Get-MgUser -UserId $UPN
    Write-Host "UserID: $($user.id)" -ForegroundColor Cyan
    
    #Details of SharePoint Site
    $SiteURL = "https://icewolfch.sharepoint.com/sites/DemoPrivate"
    $Domain = ([System.Uri]$SiteURL).Host
    Write-Host "Domain: $Domain" -ForegroundColor Cyan
    $AbsolutePath = ([System.Uri]$SiteURL).AbsolutePath.split("/")[2]
    Write-Host "$AbsolutePath" -ForegroundColor Cyan
    $uriSite = [string]::Format('https://graph.microsoft.com/v1.0/sites/{0}:{1}',$Domain,$AbsolutePath)
    $Site = Invoke-MgGraphRequest -Method GET $uriSite
    Write-Host "SiteID: $($site.id)" -ForegroundColor Cyan
    
    #Create Body for Add/Remove
    $params = @{
    	value = @(
    		@{
    			id = $Site.ID
    		}
    	)
    }
    
    #Create Body for Add/Remove
    $params = @{
    	value = @(
    		@{
    			id = $Site.ID
    		}
    	)
    }
    
    #Add Follower
    Write-Host "Add Follower to Site: $($Site.Id)" -ForegroundColor Cyan
    Add-MgUserFollowedSite -UserId $user.Id -BodyParameter $params
    
    #Remove Follower
    Write-Host "Remove Follower to Site: $($Site.Id)" -ForegroundColor Cyan
    Remove-MgUserFollowedSite -UserId $user.Id -BodyParameter $params

    Tried with an Entra App and Certificate for Authentication.

    Be aware that List followed sites is not Supported with Application Permissions

    https://learn.microsoft.com/en-us/graph/api/sites-list-followed?view=graph-rest-1.0&tabs=http

     

    ###############################################################################
    # Connect with Entra Application
    ###############################################################################
    # Application Permissions
    # - Sites.ReadWrite.All
    # - User.ReadBasic.All
    ###############################################################################
    $AppID = "2f79c9c9-4024-4d46-a06f-67c1f2d92b02"
    $TenantID = "icewolfch.onmicrosoft.com"
    $CertThumbprint = "A3A07A3C2C109303CCCB011B10141A020C8AFDA3"
    Connect-MgGraph -AppId $AppID -TenantId $TenantID -CertificateThumbprint $CertThumbprint -NoWelcome
    
    #Get User
    $UPN = "email address removed for privacy reasons"
    $User = Get-MgUser -UserId $UPN
    Write-Host "UserID: $($user.id)" -ForegroundColor Cyan
    
    #Details of SharePoint Site
    $SiteURL = "https://icewolfch.sharepoint.com/sites/DemoPrivate"
    $Domain = ([System.Uri]$SiteURL).Host
    Write-Host "Domain: $Domain" -ForegroundColor Cyan
    $AbsolutePath = ([System.Uri]$SiteURL).AbsolutePath.split("/")[2]
    Write-Host "$AbsolutePath" -ForegroundColor Cyan
    $uriSite = [string]::Format('https://graph.microsoft.com/v1.0/sites/{0}:{1}',$Domain,$AbsolutePath)
    $Site = Invoke-MgGraphRequest -Method GET $uriSite
    Write-Host "SiteID: $($site.id)" -ForegroundColor Cyan
    
    #Create Body for Add/Remove
    $params = @{
    	value = @(
    		@{
    			id = $Site.ID
    		}
    	)
    }
    
    #Add Follower
    Write-Host "Add Follower to Site: $($Site.Id)" -ForegroundColor Cyan
    Add-MgUserFollowedSite -UserId $user.Id -BodyParameter $params
    
    
    #Remove Follower
    Write-Host "Remove Follower to Site: $($Site.Id)" -ForegroundColor Cyan
    Remove-MgUserFollowedSite -UserId $user.Id -BodyParameter $params

    Hope that helps.

    Kind Regards

    Andres

    • kcelmer's avatar
      kcelmer
      Brass Contributor

      Thank you Andres!  It looks like you put considerable effort into this.  Just to clarify, were you able to follow a site for a user with the Entra/certificate approach? 

      • Andres-Bohren's avatar
        Andres-Bohren
        Steel Contributor

        Hi kcelmer​ 

        I found an error in the code

        The Graph Endpoint sites only returns the "Team site (classic experience)"

        https://graph.microsoft.com/v1.0/sites/<tenant>.sharepoint.com

        If you use the following command you get the Websites with the ID's

        Get-MgSite | where {$_.DisplayName -match "demo"}
        Get-MgSite | where {$_.DisplayName -eq "IcewolfDemo"}

         

        ###############################################################################
        # Connect with Entra Application
        ###############################################################################
        # Application Permissions
        # - Sites.ReadWrite.All
        # - User.ReadBasic.All
        ###############################################################################
        $AppID = "2f79c9c9-4024-4d46-a06f-67c1f2d92b02"
        $TenantID = "icewolfch.onmicrosoft.com"
        $CertThumbprint = "A3A07A3C2C109303CCCB011B10141A020C8AFDA3"
        Connect-MgGraph -AppId $AppID -TenantId $TenantID -CertificateThumbprint $CertThumbprint -NoWelcome
        
        #Get User
        $UPN = "email address removed for privacy reasons"
        $User = Get-MgUser -UserId $UPN
        Write-Host "UserID: $($user.id)" -ForegroundColor Cyan
        
        $SiteID = "icewolfch.sharepoint.com,e5167e43-7495-4611-b74c-bbf2ffd85ce5,0c772746-d2d9-4c13-8176-bd41df1b7a6e" #IcewolfDemo
        
        #Create Body for Add/Remove
        $params = @{
        	value = @(
        		@{
        			id = $SiteID
        		}
        	)
        }
        
        #Add Follower
        Write-Host "Add Follower to Site: $($Site.Id)" -ForegroundColor Cyan
        Add-MgUserFollowedSite -UserId $user.Id -BodyParameter $params
        
        #Remove Follower
        #Write-Host "Remove Follower to Site: $($Site.Id)" -ForegroundColor Cyan
        #Remove-MgUserFollowedSite -UserId $user.Id -BodyParameter $params

        The Graph Query on the User is updated
        https://graph.microsoft.com/v1.0/users/<userprincipalname>/followedSites

        And the Followed Sites in SharePoint is reflecting that (takes a few Minutes until that's visible here)

        So yes, it works but you have to figure out the SiteID and use that as a Parameter.

        Kind Regards
        Andres

Resources