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