Microsoft Secure App Authentication

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://gcits.com/knowledge-base/how-to-connect-to-delegated-office-365-tenants-using-the-secure-app-model/

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..

NodeMCU Pin Map (and what ones you can actually use for stuff)

I found this picture on the internet, and it’s gold.. thank you to whoever made it – I always struggle with which pins you can actually use for stuff, and which pins will stop the thing from booting or flashing if you try and use or do weird stuff (like SD2)

For translation – Pins D10 and D9 are RX/TX for the onboard USB Serial port – so if you are using the USB Serial, you can’t use these pins

D1/D2 – normally I2c (GPIO 4,5) – tested this as well with a Servo Controller Board and I2C Scan tool

So D3 and D4 can only be used for Digital Write’s

D10 and D9 are RX/TX on the USB Serial – so if you want to debug in console, you can’t really use these easily

D1 and D2 work nice for I2c but also work for Digital Read or Write

SD3 works as GPIO10

SD2 is evidently no go – I did an I2c test on GPIO 9 (SD2) and 10 (SD3) and the I2c Scan found the device connected, but trying to use just made the NodeMCU freak and reboot

D5,D6,D7,D8 are all fully usable read and write

D0 – messes with booting up/flashing so I generally stay clear of it.

A0 is Analog Pin

Dynamics 365 – Connecting your app using MFA

MFA is becoming a common thing, as joyous as it is to use 🙂

I have a C# app that connects to Dynamics CRM/365 and I had to update it to support Microsoft Azure MFA.

I couldn’t really find any definitive guide out there, i had to cobble all different things together to get a working solution.

I hope this guide helps out some other poor sucker like me.

 

1. I had to update my application to use the modern CRM Tooling method of connection.

I added the following in Nuget to my solution – the key being the CrmTooling which supports the new connection string method of connecting.

In my code, i changed the way i obtained an IOrganizationService to the below (simplified):

string conn = "my connection string";

IOrganizationService _crmService;

CrmServiceClient service = new CrmServiceClient(conn);

_crmService = (IOrganizationService)service.OrganizationWebProxyClient != null ? (IOrganizationService)service.OrganizationWebProxyClient : (IOrganizationService)service.OrganizationServiceProxy;

 

This gets me a connection using the new Tooling DLL and a CRM Connection string.

 

Next step, you need to create an application in Azure AD.  I followed this guide.

The trick is the Redirect URI – i wasn’t working with a web app – I ended up using http://localhost

https://docs.microsoft.com/en-us/dynamics365/customer-engagement/developer/walkthrough-register-dynamics-365-app-azure-active-directory

 

Finally, constructing a connection string that would work with the new registered Azure App.

AuthType=OAuth;Url=https://yourcrm.crm.dynamics.com;AppId=yournewappid;RedirectUri=http://localhost;

 

Now when you go to connect, the Microsoft Sign In assistant pops up and handles the authentication to the CRM Instance.

And, if you have MFA turned on, you are also prompted with MFA.

 

Happy Days!

 

Azure DNS via PowerShell

In my working world, I recently have discovered the relatively new feature in Azure – DNS.

From my early playing round it seems great! Very fast to update and easy to control with scripting.

You will need the Azure PowerShell provider.  Very simple to get.

You basically open up PowerShell and type:

Install-Module AzureRM

And then you wait 5 mins while it gets and installs a whole bunch of stuff..

 

These two links are handy to give you insight in to how to connect and running different commands:

https://docs.microsoft.com/en-us/powershell/azureps-cmdlets-docs/

https://docs.microsoft.com/en-us/azure/dns/dns-operations-dnszones

 

 

Below is a script i wrote that that adds an A record.  It does a few checks and creates the dependent bits if they don’t exist – basically you need a resource group to put the DNS zone in, then you need a DNS zone and then you can create a record.

Couple of things to note from the script – you will want to set your location – my closest is “australiasoutheast” – you will want to find your preferred location to create your resource groups.

Second thing, I found this out by accident but very handy.. you can sign in to AzureRM and then ‘export’ your sign in details to a JSON file where the password etc is encrypted.. then in your script you can just use the Select-AzureRmProfile command and point it at the file, and it takes care of establishing a session with AzureRM.

Export your login details:

#Login - this prompts auth crenetials
Login-AzureRmAccount

#Once you are auth'd you can export your login credentials to an encrypted JSON file
Save-AzureRmProfile -Path “..\azureprofile.json”

 

Here’s the script – you can see it uses the saved credentials above:

param (
	[string] $zone,		  #eg: paultest100.com
	[string] $hostname,	  #eg: www.paultest100.com  
	[string] $address,	  #eg: 10.20.30.40
	[int] $TTL	          #eg: 3600
)

#get record from hostname - strip off the zone
$z = $hostname.Replace($zone, '').TrimEnd('.')

#create resource group name
$rgName = "MyDNSResources"

#Login
Select-AzureRmProfile -Path "..\azureprofile.json"
        
#Get Resource Group - Create if it doesn't exist
$rg = Get-AzureRmResourceGroup -Name $rgName
if ($rg -eq $null)
{
    $rg = New-AzureRmResourceGroup -Name $rgName -location "australiasoutheast"
}

#Get DNS Zone - Create if it doesn't exist
$dnsZone = Get-AzureRmDnsZone -Name $zone -ResourceGroupName $rg.ResourceGroupName
if ($dnsZone -eq $null)
{
    $dnsZone = New-AzureRmDnsZone -Name $zone -ResourceGroupName $rg.ResourceGroupName
}

#Get A record - Create if it doesn't exist
$dnsRecord = Get-AzureRmDnsRecordSet -Name $z -RecordType A -ZoneName $dnsZone.Name -ResourceGroupName $rg.ResourceGroupName
if ($dnsRecord -eq $null)
{
    #Create A Record
    New-AzureRmDnsRecordSet -Name $z -RecordType A -ZoneName $dnsZone.Name -ResourceGroupName $rg.ResourceGroupName -Ttl $TTL -DnsRecords (New-AzureRmDnsRecordConfig -IPv4Address $Address) -Overwrite -Force
}