Add OIDC auth for GenerateResourcesAndImage.ps1 (#13765)
This commit is contained in:
@@ -66,6 +66,35 @@ Function Show-LatestCommit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Get-GitHubActionsOidcIdToken {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $True)]
|
||||||
|
[string] $RequestUrl,
|
||||||
|
[Parameter(Mandatory = $True)]
|
||||||
|
[string] $RequestToken,
|
||||||
|
[Parameter(Mandatory = $False)]
|
||||||
|
[string] $Audience = 'api://AzureADTokenExchange'
|
||||||
|
)
|
||||||
|
|
||||||
|
$separator = if ($RequestUrl -match '\?') { '&' } else { '?' }
|
||||||
|
$urlWithAudience = "${RequestUrl}${separator}audience=$([System.Uri]::EscapeDataString($Audience))"
|
||||||
|
$headers = @{ Authorization = "Bearer $RequestToken" }
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = Invoke-RestMethod -Method Get -Uri $urlWithAudience -Headers $headers
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "Failed to request GitHub Actions OIDC ID token. Ensure workflow permissions include 'id-token: write'. Details: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([string]::IsNullOrEmpty($response.value)) {
|
||||||
|
throw "GitHub Actions OIDC token response did not contain a 'value' field."
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response.value
|
||||||
|
}
|
||||||
|
|
||||||
function Start-Sleep($seconds) {
|
function Start-Sleep($seconds) {
|
||||||
$doneDT = (Get-Date).AddSeconds($seconds)
|
$doneDT = (Get-Date).AddSeconds($seconds)
|
||||||
while ($doneDT -gt (Get-Date)) {
|
while ($doneDT -gt (Get-Date)) {
|
||||||
@@ -103,6 +132,13 @@ Function GenerateResourcesAndImage {
|
|||||||
The Azure client secret to use to authenticate with Azure. If not specified, the current user's credentials will be used.
|
The Azure client secret to use to authenticate with Azure. If not specified, the current user's credentials will be used.
|
||||||
.PARAMETER AzureTenantId
|
.PARAMETER AzureTenantId
|
||||||
The Azure tenant id to use to authenticate with Azure. If not specified, the current user's credentials will be used.
|
The Azure tenant id to use to authenticate with Azure. If not specified, the current user's credentials will be used.
|
||||||
|
.PARAMETER UseOidc
|
||||||
|
If set, authenticate using GitHub Actions OIDC (federated credentials) instead of a client secret.
|
||||||
|
Requires AzureClientId and AzureTenantId, and OidcRequestToken/OidcRequestUrl parameters.
|
||||||
|
.PARAMETER OidcRequestToken
|
||||||
|
GitHub Actions OIDC request token.
|
||||||
|
.PARAMETER OidcRequestUrl
|
||||||
|
GitHub Actions OIDC request URL.
|
||||||
.PARAMETER RestrictToAgentIpAddress
|
.PARAMETER RestrictToAgentIpAddress
|
||||||
If set, access to the VM used by packer to generate the image is restricted to the public IP address this script is run from.
|
If set, access to the VM used by packer to generate the image is restricted to the public IP address this script is run from.
|
||||||
This parameter cannot be used in combination with the virtual_network_name packer parameter.
|
This parameter cannot be used in combination with the virtual_network_name packer parameter.
|
||||||
@@ -143,6 +179,14 @@ Function GenerateResourcesAndImage {
|
|||||||
[Parameter(Mandatory = $False)]
|
[Parameter(Mandatory = $False)]
|
||||||
[string] $AzureTenantId,
|
[string] $AzureTenantId,
|
||||||
[Parameter(Mandatory = $False)]
|
[Parameter(Mandatory = $False)]
|
||||||
|
[switch] $UseOidc,
|
||||||
|
[Parameter(Mandatory = $False)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string] $OidcRequestToken,
|
||||||
|
[Parameter(Mandatory = $False)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string] $OidcRequestUrl,
|
||||||
|
[Parameter(Mandatory = $False)]
|
||||||
[string] $PluginVersion = "2.2.1",
|
[string] $PluginVersion = "2.2.1",
|
||||||
[Parameter(Mandatory = $False)]
|
[Parameter(Mandatory = $False)]
|
||||||
[switch] $RestrictToAgentIpAddress,
|
[switch] $RestrictToAgentIpAddress,
|
||||||
@@ -214,10 +258,17 @@ Function GenerateResourcesAndImage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Validating packer template..."
|
Write-Host "Validating packer template..."
|
||||||
|
$validateClientSecret = "fake"
|
||||||
|
if ($UseOidc) {
|
||||||
|
$validateClientSecret = ""
|
||||||
|
}
|
||||||
|
|
||||||
& $PackerBinary validate `
|
& $PackerBinary validate `
|
||||||
"-only=$($PackerTemplate.BuildName).*" `
|
"-only=$($PackerTemplate.BuildName).*" `
|
||||||
"-var=client_id=fake" `
|
"-var=client_id=fake" `
|
||||||
"-var=client_secret=fake" `
|
"-var=client_secret=$($validateClientSecret)" `
|
||||||
|
"-var=oidc_request_token=fake" `
|
||||||
|
"-var=oidc_request_url=fake" `
|
||||||
"-var=subscription_id=$($SubscriptionId)" `
|
"-var=subscription_id=$($SubscriptionId)" `
|
||||||
"-var=tenant_id=fake" `
|
"-var=tenant_id=fake" `
|
||||||
"-var=location=$($AzureLocation)" `
|
"-var=location=$($AzureLocation)" `
|
||||||
@@ -239,10 +290,23 @@ Function GenerateResourcesAndImage {
|
|||||||
Write-Verbose "No AzureClientId was provided, will use interactive login."
|
Write-Verbose "No AzureClientId was provided, will use interactive login."
|
||||||
az login --output none
|
az login --output none
|
||||||
}
|
}
|
||||||
|
elseif ($UseOidc) {
|
||||||
|
if ([string]::IsNullOrEmpty($AzureTenantId)) {
|
||||||
|
throw "AzureTenantId is required for OIDC authentication."
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Verbose "Using OIDC service principal login (federated credentials)."
|
||||||
|
$idToken = Get-GitHubActionsOidcIdToken -RequestUrl $OidcRequestUrl -RequestToken $OidcRequestToken
|
||||||
|
az login --service-principal --username $AzureClientId --tenant $AzureTenantId --federated-token $idToken --output none
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
Write-Verbose "AzureClientId was provided, will use service principal login."
|
if ([string]::IsNullOrEmpty($AzureClientSecret) -or [string]::IsNullOrEmpty($AzureTenantId)) {
|
||||||
|
throw "AzureClientSecret and AzureTenantId are required for service principal login unless -UseOidc is specified."
|
||||||
|
}
|
||||||
|
Write-Verbose "AzureClientId was provided, will use service principal login (client secret)."
|
||||||
az login --service-principal --username $AzureClientId --password=$AzureClientSecret --tenant $AzureTenantId --output none
|
az login --service-principal --username $AzureClientId --password=$AzureClientSecret --tenant $AzureTenantId --output none
|
||||||
}
|
}
|
||||||
|
|
||||||
az account set --subscription $SubscriptionId
|
az account set --subscription $SubscriptionId
|
||||||
if ($LastExitCode -ne 0) {
|
if ($LastExitCode -ne 0) {
|
||||||
throw "Failed to login to Azure subscription '$SubscriptionId'."
|
throw "Failed to login to Azure subscription '$SubscriptionId'."
|
||||||
@@ -257,7 +321,7 @@ Function GenerateResourcesAndImage {
|
|||||||
throw "Resource group '$ResourceGroupName' does not exist."
|
throw "Resource group '$ResourceGroupName' does not exist."
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create service principal
|
# Create / choose authentication for packer
|
||||||
if ([string]::IsNullOrEmpty($AzureClientId)) {
|
if ([string]::IsNullOrEmpty($AzureClientId)) {
|
||||||
Write-Host "Creating service principal for packer..."
|
Write-Host "Creating service principal for packer..."
|
||||||
$ADCleanupRequired = $true
|
$ADCleanupRequired = $true
|
||||||
@@ -277,9 +341,26 @@ Function GenerateResourcesAndImage {
|
|||||||
Write-Host "Service principal created with id '$ServicePrincipalAppId'. It will be deleted after the build."
|
Write-Host "Service principal created with id '$ServicePrincipalAppId'. It will be deleted after the build."
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$ServicePrincipalAppId = $AzureClientId
|
if ($UseOidc) {
|
||||||
$ServicePrincipalPassword = $AzureClientSecret
|
if ([string]::IsNullOrEmpty($AzureTenantId)) {
|
||||||
$TenantId = $AzureTenantId
|
throw "AzureTenantId is required for OIDC authentication."
|
||||||
|
}
|
||||||
|
|
||||||
|
$ServicePrincipalAppId = $AzureClientId
|
||||||
|
$ServicePrincipalPassword = ""
|
||||||
|
$TenantId = $AzureTenantId
|
||||||
|
# Avoid leaking OIDC request values via command line arguments.
|
||||||
|
$env:PKR_VAR_oidc_request_token = $OidcRequestToken
|
||||||
|
$env:PKR_VAR_oidc_request_url = $OidcRequestUrl
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ([string]::IsNullOrEmpty($AzureClientSecret) -or [string]::IsNullOrEmpty($AzureTenantId)) {
|
||||||
|
throw "AzureClientSecret and AzureTenantId are required for service principal authentication unless -UseOidc is specified."
|
||||||
|
}
|
||||||
|
$ServicePrincipalAppId = $AzureClientId
|
||||||
|
$ServicePrincipalPassword = $AzureClientSecret
|
||||||
|
$TenantId = $AzureTenantId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Write-Debug "Service principal app id: $ServicePrincipalAppId."
|
Write-Debug "Service principal app id: $ServicePrincipalAppId."
|
||||||
Write-Debug "Tenant id: $TenantId."
|
Write-Debug "Tenant id: $TenantId."
|
||||||
@@ -288,6 +369,8 @@ Function GenerateResourcesAndImage {
|
|||||||
-only "$($PackerTemplate.BuildName).*" `
|
-only "$($PackerTemplate.BuildName).*" `
|
||||||
-var "client_id=$($ServicePrincipalAppId)" `
|
-var "client_id=$($ServicePrincipalAppId)" `
|
||||||
-var "client_secret=$($ServicePrincipalPassword)" `
|
-var "client_secret=$($ServicePrincipalPassword)" `
|
||||||
|
-var "oidc_request_token=$($env:PKR_VAR_oidc_request_token)" `
|
||||||
|
-var "oidc_request_url=$($env:PKR_VAR_oidc_request_url)" `
|
||||||
-var "subscription_id=$($SubscriptionId)" `
|
-var "subscription_id=$($SubscriptionId)" `
|
||||||
-var "tenant_id=$($TenantId)" `
|
-var "tenant_id=$($TenantId)" `
|
||||||
-var "location=$($AzureLocation)" `
|
-var "location=$($AzureLocation)" `
|
||||||
|
|||||||
Reference in New Issue
Block a user