Use the SharePoint Pages API to Create News Items
After writing about the basics of using the SharePoint Pages Graph API, I wanted a practical example to illustrate how the API might be used. After thinking about the matter, I decided to follow the well-worn path of creating items from an RSS feed. Office 365 connectors were a popular way to create items from RSS feeds in Outlook Groups, SharePoint, and Teams until Microsoft retired the connectors in July 2024. Workflows (Power Automate) are now the approved Microsoft 365 mechanism to import RSS items and a standard connector is available for this purpose.
Following an established route has its advantages. Everyone knows the input and the expected output. Our task is to read details about published articles from an RSS feed and use that information to create news items in a target SharePoint Online site. Let’s explore what needs to be done.
Retrieve RSS Items
The first step is to retrieve information from an RSS feed. Sites that support RSS should publish a feed in XML format for RSS readers to interpret. For instance, the RSS feed for this site is available from https://practical365.com/feed/. The feed contains metadata for the site, such as its name, description, and last update. It also holds details of articles published on the site in a series of item entries.
To process the feed in PowerShell, we read the RSS file by using the Invoke-WebRequest cmdlet to extract the feed from the imported information. Details of the articles can be found by following the XML structure to the set of nodes marked with “<item>.” The code extracts this data to populate the $Feed array.
Write-Host ("Processing RSS feed {0} to find recent posts" -f $RSSFeed) Try { [xml]$Content = Invoke-WebRequest -Uri $RSSFeed -ErrorAction Stop } Catch { Write-Host ("Failed to download RSS feed {0}" -f $RSSFeed) Break } [array]$Feed = $Content.RSS.channel.item
The default information found in an RSS feeds is often a single page of articles. WordPress blogs use a page size of ten while other blogs like the Microsoft Technical Community use a higher value (20). If you want to fetch more articles, you must paginate to collect as many articles as you want. For instance, this code includes the page number to paginate through the RSS feed until 100 items are fetched. Adding the page number to the RSS feed URL works for WordPress and other blogs. I can’t guarantee that it will work for all blogs.
$Content = $null; [array]$Feed = $null [int]$NumberRequired = 100; $ContinueProcessing = $true $i = 1 While ($ContinueProcessing -eq $true) { $RSSPage = ("{0}?paged={1}" -f $RSSFeed, $i) Try { [xml]$PageContent = Invoke-WebRequest $RSSPage $PageFeed = $PageContent.RSS.channel $Feed += $PageFeed.Item } Catch { Write-Host ("Failed to fetch page {0}" -f $i) } If ($Feed.count -ge $NumberRequired) { $ContinueProcessing = $false } $i++ }
Extracting Articles from the RSS Feed
The next step is to extract the information about articles in the RSS feed so that it can be used to create new pages. The code below reads the set of articles and extracts any published in the last seven days. You could process all articles, but I decided to impose a date threshold on the basis that old news isn’t really news. The details of the extracted articles are captured in a PowerShell list.
$Report = [System.Collections.Generic.List[Object]]::new() $RSSDateThreshold = (Get-Date).AddDays(-7) ForEach ($Item in $Feed){ [datetime]$LastUpdate = $Item.pubdate.split('+')[0].trim() If ($LastUpdate -lt $RSSDateThreshold) { Continue } $ReportLine = [PSCustomObject][Ordered]@{ Title = $Item.title LastUpdated = Get-Date ($LastUpdate) -format 'dd-MMM-yyyy HH:mm:ss' Description = $Item.description.'#cdata-section' Category = $Item.category.'#cdata-section' -join ", " Author = $Item.creator.'#cdata-section' Link = $Item.link } $Report.Add($ReportLine) }
I’ve run this code against the RSS feeds for multiple sites and it works for all WordPress blogs I tested against. The format of an RSS feed depends on the platform that generates the feed, so some adjustments might be necessary to work with other blogs, such as those published in the Microsoft Technical Community like the popular EHLO, the Exchange team blog. This is where a tool like GitHub Copilot comes in useful because it’s very good at parsing data in PowerShell scripts.
As an example, processing items from Microsoft Technical Community blogs requires the code to be adjusted like this:
$Report = [System.Collections.Generic.List[Object]]::new() ForEach ($Item in $Feed){ [datetime]$LastUpdate = $Item.pubdate.split('+')[0].trim() If ($LastUpdate -lt $RSSDateThreshold) { Continue } $ReportLine = [PSCustomObject][Ordered]@{ Title = $Item.title LastUpdated = Get-Date ($LastUpdate) -format 'dd-MMM-yyyy HH:mm:ss' Description = $Item.description Category = “N/A” Author = $Item.creator Link = $Item.link } $Report.Add($ReportLine) }
Creating and Publishing New Pages for News Items with the SharePoint Pages API
With the input data defined and ready, we can finally move to the point of creating new SharePoint Online pages for each article in the RSS feed and then publishing (or promoting) the pages as news items so that they show up on the home page for the target site.
SharePoint pages can range from very simple to very complex in terms of their design and the webparts used by pages. SharePoint uses the concept of a canvas to arrange the different elements on a page. Pages used for news items are at the simple end of the scale, but if you want to create anything more complex, I recommend you spend some time reading about how to construct the different elements and pass their details to SharePoint for processing.
Before attempting to create a new page, the script runs some code to check if the page already exists because we don’t want to create duplicate news items. The check is facilitated by grabbing the one hundred most recent pages found in the target site to check against.
[array]$SitePages = Get-MgSitePage -SiteId $Site.Id -Top 100
As it processes each article from the RSS feed, the script checks the title from the article against the site pages and doesn’t attempt to create a new page if a match is detected:
If ($SitePages.Title -contains $NewPost.Title) { Write-Host ("News item {0} already exists in {1}" -f $NewPost.Title, $Site.DisplayName) Continue }
With all checks passed, the script runs the New-MgSitePage cmdlet to create a page. The $Params variable passed as the body parameter is a hash table containing the various canvas elements needed to create the page.
$Post = New-MgSitePage -SiteId $site.Id -BodyParameter $Params If ($Post) { Write-Host ("Successfully created new page {0}" -f $PostTitle) } Else { Write-Host ("Post {0} failed" -f $PostTitle) $PostSuccess = $false Continue }
After creating a page, it must be promoted to become a news item. As I noted in the original article, the cmdlet to update a site page didn’t work for me, so I resorted to running the underlying Graph API request, which is what you see here.
If ($PostSuccess) { # We have a page created, so promote it to be a news post $Description = $NewPost.Description $UpdateBody = @{} $UpdateBody.Add("@odata.type", "#microsoft.graph.sitePage") $UpdateBody.Add("promotionKind", "newsPost") $UpdateBody.Add("description", $Description) $Uri = ("https://graph.microsoft.com/V1.0/sites/{0}/pages/{1}/microsoft.graph.sitePage" -f $Site.Id, $Post.Id) $Status = Invoke-MgGraphRequest -Uri $Uri -Method Patch -Body $UpdateBody If ($Status) { Write-Host 'Post Promoted to News Item' } Else { Write-Host 'Post Update Failed' $PostSuccess = $false Continue } }
Figure 1 shows the script in action. As you can see, the script logs the new pages that it creates.

A Matter of Formatting
The Microsoft technical community blogs include the entire article in the description. This is OK when reading the news item published in SharePoint Online because the entire article content, including elements like embedded links, pictures, and videos, are preserved (Figure 2).

However, the news webpart on the target site’s home page is unable to render the advanced HTML formatting used by these blogs, so readers end up seeing a lot of HTML code in the descriptions of news items (Figure 3).

You could strip the HTML tags from the description before creating a news item, but the formatting of the information in the news item is obviously very valuable. It would be nice if SharePoint allowed for pages to have a simple description and full content. Maybe that’s possible and I just haven’t found out how to do it in the Site Pages documentation.
Running the Script Interactively or in App-Only Mode
You can download the script I used for testing from GitHub. The script can run in an interactive Graph SDK session, in which case delegated permissions are used and the author for each page created by the script is the signed-in user. The signed-in user must have write access to the target SharePoint site (all members of a Microsoft 365 group have this access to the site belonging to the group).
The script can also run in app-only mode. In this scenario, a registered Entra ID app authenticates to access the Graph, which means that application permissions are available and pages can be created in any site in the tenant, if the app has consent for the Sites.ReadWrite.All permission. New pages created by an app show up with “app@sharepoint” as their author. App-only mode is how the script runs when executed as an Azure Automation runbook using a managed identity for authentication.
Goal Accomplished to Build an Example of Using the SharePoint Site Pages API
As I wrote the script described here, I learned more than I intended to about the formats of RSS feeds. This knowledge is well understood by people who develop RSS readers but was new to me. I guess that an opportunity to learn arises every day. In any case, I accomplished the goal of exploring the principle of using the SharePoint Site Pages API to create news items from an information source. I won’t be applying for a job as an RSS reader developer, but I hope that the knowledge shared here is useful.