I figured it out! SharepointPNPPowershell to the rescue! Here's the script I wrote to keep the two sites in sync: param ($credspo, $credW3, $idToSync) # $csomPath = "C:\Program Files\PackageManagement\NuGet\Packages\Microsoft.SharePointOnline.CSOM.16.1.19515.12000\lib\net45" $htmlPath = "<LocalPath>\Scripts\htmlagilitypack.1.11.17\lib\Net45" #IMport modules and types import-module SharepointPNPPowershellONline Add-Type -path $csomPath\Microsoft.SharePoint.Client.dll Add-Type -path $csomPath\Microsoft.SharePoint.Client.Runtime.dll Add-Type -Path $htmlPath\HtmlAgilityPack.dll #connect to SPO and on prem if (!$credspo){ $credspo = get-credential -Message "Enter SPO credentials"} if (!$credOnP) { $credOnP = get-credential -message "Enter on Prem Credentials"} Connect-PnPOnline -Url https://<SharepointSPoURL> -Credentials $credspo $context = New-Object Microsoft.SharePoint.Client.ClientContext("<SPOnPremUrl>") $context.Credentials = $credOnP $web = $context.Web $context.Load($web) #just a test to ensure connection $context.ExecuteQuery() $caml = new-object Microsoft.SharePoint.Client.CamlQuery $caml.ViewXml = "<View> <Query> <Where><Geq><FieldRef Name='Created' /><Value Type='DateTime'>2017-10-09T12:00:00Z</Value></Geq></Where> </Query> </View>" #pull the list of news posts to sync. only needed posts after Oct 2017 $posts = $web.Lists.GetByTitle("Posts") if (!$idToSync){ $postItems = $posts.GetItems($caml)} else {$postItems = $posts.GetItemById($idToSync)} $context.Load($posts) $context.Load($postitems) $context.ExecuteQuery() #these characters are not allowed in titles $specialChar = '[#?/\///:/*/?/"/</>/|/#/%]' #iterate though the collection of news items foreach ($post in $postItems){ $title = $Post["Title"] -replace $specialChar, " " #clean out the title if ($title.Length -gt 374){ $title = $Title.substring(0,375) } #max length for a title is 400 char. using 375 to give myself some headroom $newPage = Add-PnPClientSidePage -Name $title -LayoutType Article $html = $post.FieldValues.Body Add-PnPClientSidePageSection -Page $newPage -SectionTemplate OneColumn Add-PnPClientSideText -Page $newPage -Section $1 -text $html -Column 1 $doc= New-Object HTMLAgilityPack.HTMLDocument $doc.LoadHTML($html) $img = $doc.DocumentNode.SelectNodes("//img")[0] #being fancy - pull the first image to use for the thumbnail $newPage.ThumbnailUrl = $img.Attributes["src"].Value $Categories = "" $post.FieldValues.PostCategory | % {$Categories +="$($_.LookupValue); "} $newPage.PromoteAsNewsArticle() $newPage.save() $itemUPdate = Set-PnPListItem -List "Site Pages" -Identity $newPage.PageListItem.Id -Values @{"Categories0" = $Categories; "SyncedID" = $post.Id; "SyncDate" = get-date } #this is dumb but if you don't put the command above into a variable it fails with format-default error. remove-variable img #clear out the img var so if there's not a picture in the next post it won't get the old image }