The world of Microsoft Authentication is all changing with stricter AD policies been forced out like Multi Factor Authentication. That’s a good thing.
For people that do all their Microsoft services manipulation through the UI, that’s OK. The Microsoft Online sign in process is (after many years of being average), finally pretty solid and can handle multiple identifications at the same time logged in.
But in the PowerShell world, its all changed on how you can connect and run processes and scripts unattended. You can’t save passwords or be on the ready to accept an MFA prompt if you want a process to run periodically, or on demand through a provisioning system.
Microsoft’s Partner Center API is also the modern way you are given delegated access to modify settings and services for your CSP Customers.
The process these days appears to be to register an Azure AD Application in to the CSP tenant, and authenticate against this application for delegated access to your sub customers.
Simple commands like Get-MSOLUsers can be run in this context, but you specify -tenantID ‘your o365 tenant id’ to work in the context you need to.
These clever guys have some great blogs about how to register a Partner Center API Secure Authentication application and authenticate with a token so you can make a connection to Office 365, Azure AD, Exchange etc.
https://www.cyberdrain.com/using-the-secure-app-model-to-connect-to-microsoft-partner-resources/
https://www.cyberdrain.com/using-the-secure-application-model-with-partnercenter-2-0-for-office365/
https://www.cyberdrain.com/connect-to-exchange-online-automated-when-mfa-is-enabled-using-the-secureapp-model/
These methods work if you are a Microsoft Partner and have a Partner Center where you manage your customers.
But, if you want to run your PowerShell directly against an Office 365 tenant that you don’t have delegated access to, that’s a different process.
I looked for ages to try and find some simple example that would allow me to register a Secure App in the customer’s tenant I want to connect to and manage and then use this app to authenticate from PowerShell with no MFA, passwords etc.
I ended up working with Elliot Munro from GCITS from the first link above, and with his clever reverse engineering skills, we figured out the below script, that’s essentially an adaption of the Partner Center Secure App script from the above examples, but it targets the end-customer’s tenant instead.
This is the code to create the Secure App in the Tenant.
Note, you still need the Partner Center API to do this, even though you won’t be making a connection to Partner Center. This gives your scripted login the rights to do what it needs to do.
Make sure you have these modules installed:
Install-Module PartnerCenter
Install-Module MSOnline
Install-Module AzureAD
Code to create Secure App:
$adAppAccess = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]@{
ResourceAppId = "00000002-0000-0000-c000-000000000000";
ResourceAccess =
[Microsoft.Open.AzureAD.Model.ResourceAccess]@{
Id = "5778995a-e1bf-45b8-affa-663a9f3f4d04";
Type = "Role"},
[Microsoft.Open.AzureAD.Model.ResourceAccess]@{
Id = "a42657d6-7f20-40e3-b6f0-cee03008a62a";
Type = "Scope"},
[Microsoft.Open.AzureAD.Model.ResourceAccess]@{
Id = "311a71cc-e848-46a1-bdf8-97ff7156d8e6";
Type = "Scope"}
}
$graphAppAccess = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]@{
ResourceAppId = "00000003-0000-0000-c000-000000000000";
ResourceAccess =
[Microsoft.Open.AzureAD.Model.ResourceAccess]@{
Id = "bf394140-e372-4bf9-a898-299cfc7564e5";
Type = "Role"},
[Microsoft.Open.AzureAD.Model.ResourceAccess]@{
Id = "7ab1d382-f21e-4acd-a863-ba3e13f7da61";
Type = "Role"}
}
$SessionInfo = Connect-AzureAD
$DisplayName = "Test Auth"
$app = New-AzureADApplication -AvailableToOtherTenants $true -DisplayName $DisplayName -IdentifierUris "https://$($SessionInfo.TenantDomain)/$((New-Guid).ToString())" -RequiredResourceAccess $adAppAccess, $graphAppAccess -ReplyUrls @("urn:ietf:wg:oauth:2.0:oob","https://localhost","http://localhost","http://localhost:8400")
$password = New-AzureADApplicationPasswordCredential -ObjectId $app.ObjectId
$spn = New-AzureADServicePrincipal -AppId $app.AppId -DisplayName $DisplayName
$PasswordToSecureString = $password.value | ConvertTo-SecureString -asPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($($app.AppId),$PasswordToSecureString)
$token = New-PartnerAccessToken -ApplicationId $app.AppId -Scopes 'Directory.AccessAsUser.All offline_access openid profile User.Read' -ServicePrincipal -Credential $credential -Tenant $spn.AppOwnerTenantID -UseAuthorizationCode
Write-Host "ApplicationId = $($app.AppId)"
Write-Host "ApplicationSecret = $($password.Value)"
Write-Host "TenantId = $($SessionInfo.TenantId)"
Write-Host "Refresh Token = $($token.RefreshToken)"
When you run the above, a browser window will pop up and get you to authenticate with a user that has the rights to create the Secure App in that O365 tenant.

This will output in the PowerShell window App Id, App Secret and Refresh Token. These are what you use to authenticate in your PowerShell scripts when you need to.
NOTE: With all of this, you’re going to be dealing with Security tokens that have some power. Make sure to save them appropriately in something like the Azure Key Vault.
The code you put in your PowerShell scripts to authenticate is the same as the example links above connecting to Partner Center.
$refreshToken = "<refresh_token>"
$app_id = "<app_id>"
$app_secret = "<app_secret>"
$tenantId = "<tenant_id>"
function Get-GCITSAccessTokenByResource($AppCredential, $tenantid, $Resource) {
$authority = "https://login.microsoftonline.com/$tenantid"
$tokenEndpointUri = "$authority/oauth2/token"
$content = @{
grant_type = "refresh_token"
client_id = $appCredential.appID
client_secret = $appCredential.secret
resource = $resource
refresh_token = $appCredential.refreshToken
}
$tokenEndpointUri = "$authority/oauth2/token"
$response = Invoke-RestMethod -Uri $tokenEndpointUri -Body $content -Method Post -UseBasicParsing
$access_token = $response.access_token
return $access_token
}
$AppCredential = @{
appId = $app_id
secret = $app_secret
refreshToken = $refreshToken
}
try
{
$MSGraphToken = Get-GCITSAccessTokenByResource -Resource "https://graph.microsoft.com" -tenantid $tenantId -AppCredential $AppCredential
$AadGraphToken = Get-GCITSAccessTokenByResource -Resource "https://graph.windows.net" -tenantid $tenantId -AppCredential $AppCredential
}
catch
{
$errorMessage = $_.Exception.Message
throw "Error refreshing tokens - $($errorMessage)"
}
try
{
#Connect Office 365
Connect-MsolService -MsGraphAccessToken $MSGraphToken -AdGraphAccessToken $AadGraphToken
#Connect Azure AD
Connect-AzureAD -MsAccessToken $MSGraphToken -AadAccessToken $AadGraphToken -AccountId $tenantId -TenantId $cspAuth_tenantId
return $true
}
catch
{
$errorMessage = $_.Exception.Message
throw "Error connecting to MSOL - $($errorMessage)"
}
Kelvin Tegelaar from the article on connecting to Exchange also demonstrates how you can connect without Partner Center to a ‘well known’ app to authenticate the modern way with Exchange.
Note in his article, the Non-Partner Center way to authenticate and give consent and then use this to connect to Exchange.
https://www.cyberdrain.com/automating-with-powershell-using-the-secure-application-model-updates/
Working next on how to connect to the Skype for Business Online module using this similar method. Will post an update when I figure it out..