在上一篇([faq]Azure DevOps如何透過設定Service Connection連到非同帳號的Azure 訂閲 - GUI篇)介紹了如何透過GUI的方式在Azure建立出Service Principal用來在Azure DevOps設定Service Connections的時候用來作爲登入的驗證方式。
不過GUI雖然直覺,但是Azure如果有改版那麽很有可能資訊的位置會換,這個時候要在產生Service Principal又可能要找一下。
更好的方式其實可以透過用script的方式得到結果 - 這樣不管誰來做,得到的結果都會是一樣的 - 並且需要的資訊也可以一目瞭然,不用切來切去找不同訊息的位置。
這篇來看一下,如何透過powershell的方式,快速建立出Service Principal。
使用Powershell的環境準備
在實際介紹script内容之前,要先説明一下需要先準備什麽。
用script方式操作Azure有幾種方式:
- az commandline - 這個是一個用python寫的跨平臺的工具 - 能夠直接操作Azure
- AzureRM Powershell module - 這個是一個powershell module用來操作Azure - 屬於比較舊的一個powershell module
- Az Powershell module - 這個是目前最新操作Azure的module,用來取代AzureRM - 最大差異是支援Powershell Core - 換句話説可以跨平臺
在接下來的script用的是AzureRM版本 - 要轉換成爲另外兩個版本不難。
如果在裝Visual Studio的時候有選擇Azure的話,那麽AzureRM已經有裝在電腦裡面了,如果沒有可以透過PowershellGet去安裝:
Install-Module -Name AzureRM -AllowClobber
接下來就實際看看完整的script。
一鍵建立Azure Service Principal
#Initialize
$environmentName = "AzureCloud"
$spnRole = "Contributor"
$ErrorActionPreference = "Stop"
$VerbosePreference = "SilentlyContinue"
$userName = ($env:USERNAME).Replace(' ', '')
$curentDateTime = Get-Date -Format "yyyyMMddTHHmmss"
$displayName = [String]::Format("AzureDevOps.{0}.{1}", $userName, $curentDateTime)
$homePage = "http://" + $displayName
$identifierUri = $homePage
function Get-AzureCmdletsVersion
{
$module = Get-Module AzureRM -ListAvailable
if($module)
{
return ($module).Version
}
return (Get-Module Azure -ListAvailable).Version
}
function Get-Password
{
Add-Type -AssemblyName System.Web
$password = [System.Web.Security.Membership]::GeneratePassword(40, 3)
$currentAzurePSVersion = Get-AzureCmdletsVersion
$azureVersion511 = New-Object System.Version(5, 1, 1)
$script:plainTextPassword = $password
if($currentAzurePSVersion -and $currentAzurePSVersion -ge $azureVersion511)
{
$password = ConvertTo-SecureString $password -AsPlainText -Force
}
return $password
}
#Import AzureRM
$isAzureModulePresent = Get-Module -Name AzureRM* -ListAvailable
if ([String]::IsNullOrEmpty($isAzureModulePresent) -eq $true)
{
Write-Output "Script requires AzureRM modules to be present. Obtain AzureRM from https://github.com/Azure/azure-powershell/releases. Please refer https://github.com/Microsoft/azure-pipelines-tasks/blob/master/Tasks/AzureResourceGroupDeploymentV2/README.md for recommended AzureRM versions." -Verbose
return
}
Import-Module -Name AzureRM.Profile
# login
Write-Output "Provide your credentials to access Azure subscription" -Verbose
Connect-AzureRmAccount -EnvironmentName $environmentName
# select subscription
$azureSubscription = Get-AzureRmSubscription | Out-GridView -PassThru
$azureSubscription | Select-AzureRmSubscription
$connectionName = $azureSubscription.Name
$tenantId = $azureSubscription.TenantId
$id = $azureSubscription.SubscriptionId
#Create a new AD Application
Write-Output "Creating a new Application in AAD (App URI - $identifierUri)" -Verbose
$servicePrincipalKey = Get-Password
$azureAdApplication = New-AzureRmADApplication -DisplayName $displayName `
-HomePage $homePage -IdentifierUris $identifierUri -Password $servicePrincipalKey -Verbose
$appId = $azureAdApplication.ApplicationId
Write-Output "Azure AAD Application creation completed successfully (Application Id: $appId)" -Verbose
#Create new SPN
Write-Output "Creating a new SPN" -Verbose
$spn = New-AzureRmADServicePrincipal -ApplicationId $appId
$spnName = $spn.ServicePrincipalNames
Write-Output "SPN creation completed successfully (SPN Name: $spnName)" -Verbose
#Assign role to SPN
Write-Output "Waiting for SPN creation to reflect in Directory before Role assignment"
Start-Sleep 30
Write-Output "Assigning role ($spnRole) to SPN App ($appId)" -Verbose
New-AzureRmRoleAssignment -RoleDefinitionName $spnRole -ServicePrincipalName $appId
Write-Output "SPN role assignment completed successfully" -Verbose
#Print the values
Write-Output "`nCopy and Paste below values for Service Connection" -Verbose
Write-Output "***************************************************************************"
Write-Output "Connection Name: $connectionName(SPN)"
Write-Output "Environment: $environmentName"
Write-Output "Scope Level: Subscription"
Write-Output "Subscription Id: $id"
Write-Output "Subscription Name: $connectionName"
Write-Output "Service Principal Id: $appId"
Write-Output "Service Principal key: $script:plainTextPassword"
Write-Output "Tenant Id: $tenantId"
Write-Output "***************************************************************************"
本來我想要分段介紹,但是後來想一下感覺還滿直覺,並且每一段都有註解,就留給大家研究啦。
如果有不清楚的歡迎留言一起討論
測試結果
要使用上面的script很簡單,只需要開啓一個powershell或者ISE,然後把整段貼進去然後執行就可以了。
- 執行script並且輸入Azure登入的帳號以及密碼
這邊使用的範例是用ISE來執行,第一步是要先登入Azure有權限的帳號以及密碼:
- 選擇您要建立Service Principal在那個訂閲
登入成功之後,會跳出所有能夠使用的訂閲給您選擇。
以我的例子,有兩個訂閲,我選擇使用第二個訂閲:
- 複製需要的訊息在Azure DevOps建立Service Connection
接下來就稍等一下,讓他跑完,最後跑完會把所有訊息匯整,方便建立Azure DevOps的Service Connection。
結語
用GUI雖然從操作面來説相對容易(至少不用懂如何寫Powershell),但是每一個人操作可能結果都不一樣,有可能因爲漏掉一步或者少記錄什麽需要來來回回的確認訊息。
反過來説,如果有人寫好了script,雖然寫script比較花費時間,但是一寫好了,接下來不管是誰操作結果都會一樣,並且速度很快。
希望這篇能夠讓大家快速建立出需要的Service Principal。
參考資料
- Script參考來源
這個powerhsell script是我基於這個:SPNCreation.ps1修改而成。
原始比較完整,包含當不是建立在subscription而是在Management Group,以及支援Azure Stack。
- AzureRM安裝方式
雖然AzureRM快要被淘汰了,但是很多script還是用AzureRM寫的,因此知道怎麽安裝還是很方便: