Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Create Azure AD service principals
#Requires -Modules 'AzureAD'
#Requires -RunAsAdministrator
<#
============================================
AUTHOR: Tao Yang
DATE: 01/03/2019
Version: 1.0
Comment: Create Azure AD service principals
============================================
#>
<#
.SYNOPSIS
New Azure AD Application for an Automation Account
.DESCRIPTION
Creates an Azure AD Application as well as a Service Principal
.PARAMETER AADAppName
A string containing the name of the Application that will be registerd in Azure AD e.g. my-aad-app
.PARAMETER KeyType
Service Principal key type. Possible values are: 'cert', 'secret' and 'both'. Default value is 'key'
.EXAMPLE
.\New-AADServicePrinciple.ps1 -AADAppName my-aad-app
#>
[CmdLetBinding()]
Param (
[Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$AADAppName,
[Parameter(Mandatory = $false)][ValidateSet('secret','cert', 'both')][String]$KeyType='key'
)
#region functions
Function New-Password
{
param(
[UInt32][ValidateScript({$_ -ge 8 -and $_ -le 128})] $Length=10,
[Switch] $LowerCase=$TRUE,
[Switch] $UpperCase=$FALSE,
[Switch] $Numbers=$FALSE,
[Switch] $Symbols=$FALSE
)
if (-not ($LowerCase -or $UpperCase -or $Numbers -or $Symbols)) {
throw "You must specify one of: -LowerCase -UpperCase -Numbers -Symbols"
return $null
}
# Specifies bitmap values for character sets selected.
$CHARSET_LOWER = 1
$CHARSET_UPPER = 2
$CHARSET_NUMBER = 4
$CHARSET_SYMBOL = 8
# Creates character arrays for the different character classes,
# based on ASCII character values.
$charsLower = 97..122 | foreach-object { [Char] $_ }
$charsUpper = 65..90 | foreach-object { [Char] $_ }
$charsNumber = 48..57 | foreach-object { [Char] $_ }
$charsSymbol = 35,36,42,43,44,45,46,47,58,59,61,63,64,
91,92,93,95,123,125,126 | foreach-object { [Char] $_ }
# Contains the array of characters to use.
$charList = @()
# Contains bitmap of the character sets selected.
$charSets = 0
if ($LowerCase) {
$charList += $charsLower
$charSets = $charSets -bor $CHARSET_LOWER
}
if ($UpperCase) {
$charList += $charsUpper
$charSets = $charSets -bor $CHARSET_UPPER
}
if ($Numbers) {
$charList += $charsNumber
$charSets = $charSets -bor $CHARSET_NUMBER
}
if ($Symbols) {
$charList += $charsSymbol
$charSets = $charSets -bor $CHARSET_SYMBOL
}
# Returns True if the string contains at least one character
# from the array, or False otherwise.
# Loops until the string contains at least
# one character from each character class.
do {
# No character classes matched yet.
$flags = 0
$output = ""
# Create output string containing random characters.
1..$Length | foreach-object {
$output += $charList[(get-random -maximum $charList.Length)]
}
# Check if character classes match.
if ($LowerCase) {
foreach ($char in $output.ToCharArray()) {If ($charsLower -contains $char) {$flags = $flags -bor $CHARSET_LOWER; break }}
}
if ($UpperCase) {
foreach ($char in $output.ToCharArray()) {If ($charsUpper -contains $char) {$flags = $flags -bor $CHARSET_UPPER; break }}
}
if ($Numbers) {
foreach ($char in $output.ToCharArray()) {If ($charsNumber -contains $char) {$flags = $flags -bor $CHARSET_NUMBER; break }}
}
if ($Symbols) {
foreach ($char in $output.ToCharArray()) {If ($charsSymbol -contains $char) {$flags = $flags -bor $CHARSET_SYMBOL; break }}
}
}
until ($flags -eq $charSets)
# Output the string.
$output
}
Function Process-AzureADSignOn
{
Write-output '', "Sign in to Azure AD with your Global Administrator account."
$script:AADConnection = Connect-AzureAD
}
#endregion
#region Sign-in to Azure AD
Try {
$CurrentAADSession = Get-AzureADCurrentSessionInfo -ErrorAction SilentlyContinue
$TenantId = $CurrentAADSession.Tenant.Id.Tostring()
Write-output "You are currently signed to to tenant '$($CurrentAADSession.tenant.Domain)' using account '$($CurrentAADSession.Account.Id).'"
Write-Output '', "Press any key to continue using current sign-in session or Esc to login using another user account."
$KeyPress = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
If ($KeyPress.virtualKeyCode -eq 27)
{
Process-AzureADSignOn
$TenantId = $Script:AADConnection.TenantId.Tostring()
}
} Catch {
Process-AzureADSignOn
$TenantId = $Script:AADConnection.TenantId.Tostring()
}
#endregion
#region common variables
$AADAppCertValidMonth = 12
#endregion
# region main code
Write-Verbose "Checking for pre-existing Azure AD application"
$testApp = Get-AzureADApplication -SearchString $AADAppName -ErrorAction SilentlyContinue
if (!$testApp)
{
$Guid = [GUID]::NewGuid().Tostring()
$ApplicationName = "$($AADAppName)_$($Guid)"
try
{
# Create Azure AD application
Write-Output '', "Creating Azure AD Application"
$AADApp = new-AzureADApplication -DisplayName $ApplicationName -Homepage "Http://$AADAppName" -IdentifierUris "Http://$AADAppName"
# Create Azure AD Service Principal
Write-Output '', "Creating Azure AD Service Principal"
$sp = New-AzureADServicePrincipal -AppId $AADApp.AppId -DisplayName $ApplicationName
$StartDate = ([Datetime]::Now).AddHours(-25)
$EndDate = $StartDate.AddMonths($AADAppCertValidMonth)
$SPKeyEndDate = $EndDate.AddDays(-1)
If ($KeyType -ieq 'cert' -or $KeyType -ieq 'both')
{
Write-Output '', 'Creating certificate based key for the Service Principal'
$CertPath = Join-Path -Path $env:TEMP -ChildPath ($ApplicationName + '.pfx')
$Cert = New-SelfSignedCertificate -DnsName $ApplicationName -CertStoreLocation cert:\LocalMachine\My -KeyExportPolicy Exportable -Provider 'Microsoft Enhanced RSA and AES Cryptographic Provider' -NotAfter $EndDate -NotBefore $StartDate
$CertThumbprint = $Cert.Thumbprint
$CertPlainPassword = New-Password -Length 12 -LowerCase -UpperCase -Numbers -Symbols
$CertPassword = ConvertTo-SecureString -String $CertPlainPassword -AsPlainText -Force
Export-PfxCertificate -Cert ('Cert:\localmachine\my\' + $CertThumbprint) -FilePath $CertPath -Password $CertPassword -Force | out-null
#Creating AAD application key credential
Write-Output " - Self signed certificated created. It is temporarily located at '$CertPath'."
$PFXCert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @($CertPath, $CertPlainPassword)
$KeyValue = [System.Convert]::ToBase64String($PFXCert.GetRawCertData())
$SpKeyCred = New-AzureADApplicationKeyCredential -ObjectId $AADApp.ObjectId -Type AsymmetricX509Cert -Usage Verify -Value $KeyValue -StartDate $cert.GetEffectiveDateString() -EndDate $SPKeyEndDate
$DeleteCert = Remove-Item (Join-Path Cert:\LocalMachine\My $CertThumbprint) -Force
}
if ($KeyType -ieq 'secret' -or $KeyType -ieq 'both') {
Write-Output '', 'Creating secret based key for the Service Principal'
$AppPassword = New-Password -Length 44 -LowerCase -UpperCase -Numbers -Symbols
$SPPasswordCred = New-AzureADApplicationPasswordCredential -ObjectId $AADApp.objectId -StartDate $StartDate -EndDate $SPKeyEndDate -Value $AppPassword
}
} catch {
throw $_.exception
}
} else
{
Write-output ""
Write-Error "ApplicationID '$($testApp.DisplayName)' already exists. No need to create it again."
Exit
}
if ($Application.ApplicationId.guid)
{
$ApplicationId = $Application.ApplicationId.guid
} else
{
$ApplicationId = $Application.ApplicationId
}
# Outputs
Write-Output '', "The Azure AD Application and Service Principal has been created:"
Write-Output " - Azure AD Tenant Id: '$($TenantId)'"
Write-Output " - Application name: '$($AADApp.DisplayName)'"
Write-Output " - Application Id: '$($AADApp.AppId)'"
If ($KeyType -ieq 'cert' -or $KeyType -ieq 'both')
{
Write-output " - Cert File: $CertPath"
Write-output " - Cert Thumbprint: $CertThumbprint"
Write-output " - Cert Password: $CertPlainPassword"
}
if ($KeyType -ieq 'secret' -or $KeyType -ieq 'both') {
Write-Output " - Client Secret: $AppPassword"
}
Write-output " - Credential Start date: $($StartDate.tostring())"
Write-output " - Credential Expiry date: $($SPKeyEndDate.tostring())"
Write-Output '', "Done!"
# endregion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.