Tag Archives: PowerShell
PNP Connect to SharePoint Online with 3 DEMOS – Classic, Certificate, and Runbook
Wanted to share step-by-step procedures for how to connect PNP.PowerShell console to SharePoint Online. Three major methods are outlined below, each slightly more advanced than the previous. Drop any questions or comments at bottom of post. Cheers.

VIDEO 1 – Client ID and Client Secret plain text
Demo how to connect with Client ID and Client Secret plain text running PNP.PowerShell.
Steps are included for
- Register application with SharePoint Online (SPO) by opening “appregnew.aspx”
- Grant permission with SharePoint Online (SPO) by opening “appinv.aspx”
- Connect-PNPOnline using Client ID and Client Secret plain text Cheers
CODE
# PNP Client Secret # https://medium.com/ng-sp/sharepoint-add-in-permission-xml-cheat-sheet-64b87d8d7600 # https://www.koskila.net/fastest-way-to-verify-your-client-id-and-client-secret-are-valid-with-powershell/ <# The app identifier has been successfully created. Client Id: 12306f98-2d2f-49b8-88b3-0eddd71ec25f Client Secret: OhYnQV2Hq888LoZOz7C8QSKr81VCNyOWQG9XEjQP111= Title: PNP-PowerShell App Domain: localhost Redirect URI: https://localhost #> # Scope $tenant = "spjeff" $clientId = "1236f98-2d2f-49b8-88b3-0eddd71ec25f" $clientSecret = "OhYnQV2Hq888LoZOz7C8QSKr81VCNyOWQG9XEjQP111=" # Connect Connect-PnPOnline -Url "https://$tenant.sharepoint.com/" -ClientId $clientId -ClientSecret $clientSecret Get-PnPWeb | Format-Table -AutoSize
VIDEO 2 – PFX Certificate running PNP.PowerShell locally
Demo how to connect with PFX Certificate running PNP.PowerShell locally given PFX input file.
Steps are included for
- Register Application with Azure AD
- Generate certificate (PFX and CER) with private key saved locally
- Connect-PNPOnline using local PFX input file and private key password
PNP-Register.ps1
# PNP Register # https://pnp.github.io/powershell/articles/connecting.html # https://pnp.github.io/powershell/articles/authentication.html # https://docs.microsoft.com/en-us/powershell/module/sharepoint-pnp/register-pnpazureadapp?view=sharepoint-ps # https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps # https://mmsharepoint.wordpress.com/2018/12/19/modern-sharepoint-authentication-in-azure-automation-runbook-with-pnp-powershell/ # Scope $tenant = "spjeff" $clientFile = "PnP-PowerShell-$tenant.txt" # Register $password = ConvertTo-SecureString -String "password" -AsPlainText -Force $reg = Register-PnPAzureADApp -ApplicationName "PnP-PowerShell-$tenant" -Tenant "$tenant.onmicrosoft.com" -CertificatePassword $password -Interactive $reg."AzureAppId/ClientId" | Out-File $clientFile -Force
PNP-Connect.ps1
# PNP Connect # https://pnp.github.io/powershell/articles/connecting.html # https://pnp.github.io/powershell/articles/authentication.html # https://docs.microsoft.com/en-us/powershell/module/sharepoint-pnp/register-pnpazureadapp?view=sharepoint-ps # https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps # https://mmsharepoint.wordpress.com/2018/12/19/modern-sharepoint-authentication-in-azure-automation-runbook-with-pnp-powershell/ # Scope $tenant = "spjeff" $clientFile = "PnP-PowerShell-$tenant.txt" # Connect $clientId = Get-Content $clientFile $password = "password" $secPassword = $password | ConvertTo-SecureString -AsPlainText -Force Connect-PnPOnline -ClientId $clientId -Url "https://$tenant.sharepoint.com" -Tenant "$tenant.onmicrosoft.com" -CertificatePath '.\PnP-PowerShell-$tenant.pfx' -CertificatePassword $secPassword Get-PnPTenantSite | Format-Table -AutoSize
VIDEO 3 – PFX Certificate in Azure Automation Runbook
Demo how to connect with PFX Certificate running PNP.PowerShell in Azure Automation Runbook given PFX input file.
Steps are included for
- Register Application with Azure AD
- Generate certificate (PFX and CER) with private key saved locally
- Upload PFX into Azure Automation with [Exportable=Yes] and password
- Runbook code to download PFX at runtime (Get-AutomationCertificate)
- Connect-PNPOnline using Azure temp PFX file and private key password
CODE
# PNP Connect
# https://pnp.github.io/powershell/articles/connecting.html
# https://pnp.github.io/powershell/articles/authentication.html
# https://docs.microsoft.com/en-us/powershell/module/sharepoint-pnp/register-pnpazureadapp?view=sharepoint-ps
# https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps
# https://mmsharepoint.wordpress.com/2018/12/19/modern-sharepoint-authentication-in-azure-automation-runbook-with-pnp-powershell/
# Scope
$tenant = "spjeff"
# Azure Certificate
$password = "password"
$secPassword = $password | ConvertTo-SecureString -AsPlainText -Force
$cert = Get-AutomationCertificate -Name 'PNP-PowerShell-$tenant'
$pfxCert = $cert.Export("pfx" , $password ) # 3=Pfx
$certPath = "PNP-PowerShell-$tenant.pfx"
Set-Content -Value $pfxCert -Path $certPath -Force -Encoding Byte
# Connect
$clientId = Get-Content $clientFile
$password = "password"
$secPassword = $password | ConvertTo-SecureString -AsPlainText -Force
Connect-PnPOnline -ClientId $clientId -Url "https://$tenant.sharepoint.com" -Tenant "$tenant.onmicrosoft.com" -CertificatePath '.\PnP-PowerShell.pfx' -CertificatePassword $secPassword
# Display
Get-PnPTenantSite | Format-Table -AutoSize
How to switch to PNP.PowerShell (PS7) from SharepointPnPPowerShellOnline (PS5.1)
Wanted to record a quick demo for how to switch from the current established “SharepointPnPPowerShellOnline” module into new “PNP.PowerShell” module based on .Net Core. Shout out to @ToddKlindt for the great blog post. Cmdlets and references below. Cheers ![]()
Video
Source Code
Uninstall-Module SharePointPnPPowerShellOnline –AllVersions Install-Module PnP.PowerShell –AllowPrerelease Register-PnPManagementShellAccess
References

M365 Ultimate PowerShell
Loads all Microsoft 365 cloud PowerShell modules in a single window. No more wasted time jumping between download pages and locating various Import-Module from each product team. Collected all here centrally to provide M365 admins with a single PowerShell PS1 to install and import every command they might need. Enables cloud admin productivity to quickly access every command.
Video
Screenshot
GitHub Repo
References
- https://docs.microsoft.com/en-us/microsoft-365/enterprise/connect-to-all-microsoft-365-services-in-a-single-windows-powershell-window
- https://docs.microsoft.com/en-us/microsoft-365/enterprise/connect-to-microsoft-365-powershell
- https://docs.microsoft.com/en-us/powershell/exchange/exchange-online-powershell-v2?view=exchange-ps#install-and-maintain-the-exo-v2-module
- https://docs.microsoft.com/en-us/powershell/module/msonline/?view=azureadps-1.0
- https://docs.microsoft.com/en-us/powershell/sharepoint/sharepoint-online/connect-sharepoint-online
- https://docs.microsoft.com/en-us/powershell/sharepoint/sharepoint-pnp/sharepoint-pnp-cmdlets
- https://docs.microsoft.com/en-us/microsoftteams/teams-powershell-install
- https://docs.microsoft.com/en-us/powershell/power-bi/overview?view=powerbi-ps
- https://docs.microsoft.com/en-us/power-platform/admin/powerapps-powershell#powerapps-cmdlets-for-app-creators-preview
- https://docs.microsoft.com/en-us/power-platform/admin/powerapps-powershell#power-automate-commands
- https://docs.microsoft.com/en-us/powershell/powerapps/overview
- https://www.microsoft.com/en-us/download/confirmation.aspx?id=39366
- https://github.com/microsoftgraph/msgraph-sdk-powershell
VIDEO – PowerShell PNP to parse InfoPath XML Attachments
Wanted to share quick tutorial on how to parse InfoPath attachment XML. Source Form Library contains XML with Base64 encoded attachments which we can parse into local TEMP folder and then upload to destination Document Library. Extract filename and file content for each InfoPath attachment XML node. Save into subfolders and match original file naming. Helpful for Office 365 migration and scenarios where InfoPath client is no longer available and users prefer to view attachments directly.
Video, screenshots, and source code below.
GitHub Repo
Video
Screenshots
SSMS Quick Install from PowerShell
Practical PowerShell Logs
All scheduled PowerShell files should generate LOGs. “Start-Transcript” is a great cmdlet for this. Adding a few more features can boost operations support. LOGs answer essentials support questions like:
- Which parent PS1 generated this LOG?
- Did script perform needed functions?
- Any errors?
- How long did it take?
- Which user and computer ran the script?
Below is code template I recommend to for scheduled PowerShell jobs to generate a LOG. Key features include:
- Auto detect current folder and script PS1
- Create \LOG\ subfolder
- Prefix to match parent PS1
- Suffix with unique date time stamp
- Elapsed run duration (Days, Hours, Minutes)
Thank you to @ToddKlindt for improvements to formula and elapsed time. Cheers. ![]()
Video
Code
function Main() {
### YOUR CODE HERE
}
# Open Log
$prefix = $MyInvocation.MyCommand.Name
$host.UI.RawUI.WindowTitle = $prefix
$stamp = Get-Date -UFormat "%Y-%m-%d-%H-%M-%S"
Start-Transcript "$PSScriptRoot\log\$prefix-$stamp.log"
$start = Get-Date
Main
# Close Log
$end = Get-Date
$totaltime = $end - $start
Write-Host "`nTime Elapsed: $($totaltime.tostring("hh\:mm\:ss"))"
Stop-TranscriptDownload
https://github.com/spjeff/spadmin/blob/master/Practical-PowerShell-LOGs.ps1
VIDEO – Disable Quick Edit for long running PowerShell
When running PowerShell administration jobs for many hours within RDP session, we are open to risk of accidental pause. Other admins might RDP into server, click around, switch windows, and accidentally pause.
Disabling the default “Quick Edit” PowerShell window feature ensures protection for script to continue uninterrupted.
Video
Screenshot
VIDEO – Load PNP the Right Way
Quick post about detecting if PowerShell PNP is available on the local PC and installing is missing (for new systems) to make PS1 script more portable across servers.(1) Detect command (2) Install module if missing (3) Import module. Cheers
![]()
Video
Code
# Load PNP module, the right way
Add-Type -Assembly "System.IO.Compression.FileSystem" -ErrorAction SilentlyContinue | Out-Null
$pnp = Get-Command Connect-PnPOnline -ErrorAction SilentlyContinue
if (!$pnp) {
Install-Module "SharePointPnPPowerShellOnline" -Force
}
Import-Module "SharePointPnPPowerShellOnline" -ErrorAction SilentlyContinue | Out-Null
References
VIDEO – How to Add Contact for other users with MS Graph API
Wanted to share a quick demo for how to query and create Exchange Contacts for another user in Office 365 tenant with MS Graph API. Biggest challenge was permission grant in two places (1) MS Graph API in Azure Applications and (2) Exchange Folder grant Owner to user executing the MS Graph API. Updated blog post with second video to execute Graph API from PowerShell for automation and scheduling. Cheers ![]()
Video 1 – Graph Explorer Web GUI
Video 2 – Graph API from PowerShell
PowerShell – Grant Exchange Folder
$Session = New-PSSession -ConfigurationName "Microsoft.Exchange" -ConnectionUri "https://outlook.office365.com/powershell-liveid/" -Credential $UserCredential -Authentication Basic -AllowRedirection Import-PSSession $Session Add-MailboxFolderPermission -Identity "george@spjeff.com:\Contacts" –User "spjeff@spjeff.com" –AccessRights "Owner"
PowerShell – Execute Graph API
# from https://blog.mastykarz.nl/building-applications-office-365-apis-any-platform/
# Config
$clientID = "34f34d49-86b7-4437-a332-6fecaf95a244"
$tenantName = "spjeff.onmicrosoft.com"
$ClientSecret = "secret-goes-here"
$Username = "spjeff@spjeff.com"
$Password = "password-goes-here"
# Access Token
$ReqTokenBody = @{
Grant_Type = "Password"
client_Id = $clientID
Client_Secret = $clientSecret
Username = $Username
Password = $Password
Scope = "https://graph.microsoft.com/.default"
}
$TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody
$TokenResponse
# Data call - READ
$apis = @(
'https://graph.microsoft.com/v1.0/me/contacts',
'https://graph.microsoft.com/v1.0/me',
'https://graph.microsoft.com/v1.0/users',
'https://graph.microsoft.com/v1.0/users/george@spjeff.com/contacts')
$apis |% {
Write-Host $_ -Fore Yellow
Invoke-RestMethod -Headers @{Authorization = "Bearer $($Tokenresponse.access_token)"} -Uri $_ -Method GET -Body $body -ContentType "text/plain"
}
# Data call - WRITE
$newcontact = '{"givenName": "Test","surname": "Contact","emailAddresses": [{"address": "test@contact.com","name": "Pavel Bansky"}],"businessPhones": ["+1 732 555 0102"]}'
$api = 'https://graph.microsoft.com/v1.0/users/george@spjeff.com/contacts'
Invoke-RestMethod -Headers @{Authorization = "Bearer $($Tokenresponse.access_token)"} -Uri $api -Method "POST" -Body $newcontact -ContentType "application/json"Screenshots
References
- https://developer.microsoft.com/en-us/graph/graph-explorer
- https://stackoverflow.com/questions/57259684/microsoft-graph-api-list-contacts-for-user-users-does-not-work
- https://www.tecknowledgebase.com/6403/how-to-connect-to-exchange-online-using-remote-powershell/
- https://www.tecknowledgebase.com/6419/how-to-add-set-calendar-access-rights-in-exchange/
- https://blog.mastykarz.nl/building-applications-office-365-apis-any-platform
- https://stackoverflow.com/questions/35254559/client-credentials-dont-work-for-powerbi-rest-api
VIDEO – Toggle Required Fields OFF
When making bulk data changes to SharePoint List or Library you may need to temporarily turn off Required fields. Opening this can allow us to import records, apply changes, and modify metadata without prompting that required fields are missing. PowerShell below can help disable all Required fields OFF. Script generates a CSV snapshot of prior configuration during run. CSV can be used to restore original configuration for which fields are Required.
VIDEO
PowerShell Code
[CmdletBinding()]
param (
[bool]$required,
[string]$restoreFilename
)
# Module
Import-Module "SharePointPnPPowerShellOnline" -ErrorAction "SilentlyContinue" | Out-Null
# Config
$appid = "APP ID HERE"
$appsecret = "APP SECRET HERE"
function Main() {
# Connect
Connect-PnPOnline -Url "https://tenant.sharepoint.com/" -AppId $appid -AppSecret $appsecret
$ctx = Get-PnPContext
$list = Get-PnPList "Test"
$list
if ($restoreFilename) {
# ENABLE Required Fields
$csv = Import-Csv $restoreFilename
$fields = Get-PnPField -List $list
foreach ($row in $csv) {
$row.Guid
$f = $fields | ? { $_.Id -eq $row.Guid }
$f.Required = $true
$f.Update()
}
$ctx.ExecuteQuery()
}
else {
# DISABLE Required Fields
$coll = @()
$guid = (New-Guid).ToString()
$fields = Get-PnPField -List $list
foreach ($f in $fields) {
if ($f.Required) {
Write-Host "CHANGED FIELD $($f.Title) NOT REQUIRED"
$f.Required = $false
$f.Update()
$coll += $f.Id
}
}
$ctx.ExecuteQuery()
$coll | Export-Csv "PNPToggleRequiredField-$guid.csv"
}
}
# Main
Main
