Skip to content

Commit f93af7c

Browse files
committed
add automatic secret rotation
1 parent bc71243 commit f93af7c

File tree

1 file changed

+50
-10
lines changed

1 file changed

+50
-10
lines changed

Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-UpdateTokensTimer.ps1

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,63 @@ function Start-UpdateTokensTimer {
3737
}
3838
}
3939
} catch {
40+
Write-Warning "Error updating refresh token $($_.Exception.Message)."
41+
Write-Information ($_.InvocationInfo.PositionMessage)
4042
Write-LogMessage -API 'Update Tokens' -message 'Error updating refresh token, see Log Data for details. Will try again in 7 days.' -sev 'CRITICAL' -LogData (Get-CippException -Exception $_)
4143
}
4244

45+
# Check application secret expiration for $env:ApplicationId and generate a new application secret if expiration is within 30 days.
46+
try {
47+
$AppId = $env:ApplicationID
48+
$PasswordCredentials = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/applications(appId='$AppId')?`$select=id,passwordCredentials" -NoAuthCheck $true -AsApp $true -ErrorAction Stop
49+
# sort by latest expiration date and get the first one
50+
$LastPasswordCredential = $PasswordCredentials.passwordCredentials | Sort-Object -Property endDateTime -Descending | Select-Object -First 1
51+
if ($LastPasswordCredential.endDateTime -lt (Get-Date).AddDays(30).ToUniversalTime()) {
52+
Write-Information "Application secret for $AppId is expiring soon. Generating a new application secret."
53+
$AppSecret = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/applications/$($PasswordCredentials.id)/addPassword" -Body '{"passwordCredential":{"displayName":"UpdateTokens"}}' -NoAuthCheck $true -AsApp $true -ErrorAction Stop
54+
Write-Information "New application secret generated for $AppId. Expiration date: $($AppSecret.endDateTime)."
55+
} else {
56+
Write-Information "Application secret for $AppId is valid until $($LastPasswordCredential.endDateTime). No need to generate a new application secret."
57+
}
58+
59+
if ($AppSecret) {
60+
if ($env:AzureWebJobsStorage -eq 'UseDevelopmentStorage=true') {
61+
$Table = Get-CIPPTable -tablename 'DevSecrets'
62+
$Secret = Get-CIPPAzDataTableEntity @Table -Filter "PartitionKey eq 'Secret' and RowKey eq 'Secret'"
63+
$Secret.ApplicationSecret = $AppSecret.secretText
64+
Add-AzDataTableEntity @Table -Entity $Secret -Force
65+
} else {
66+
Set-AzKeyVaultSecret -VaultName $KV -Name 'ApplicationSecret' -SecretValue (ConvertTo-SecureString -String $AppSecret.secretText -AsPlainText -Force)
67+
}
68+
}
69+
70+
# Clean up expired application secrets
71+
$ExpiredSecrets = $PasswordCredentials.passwordCredentials | Where-Object { $_.endDateTime -lt (Get-Date).ToUniversalTime() }
72+
if ($ExpiredSecrets.Count -gt 0) {
73+
Write-Information "Found $($ExpiredSecrets.Count) expired application secrets for $AppId. Removing them."
74+
foreach ($Secret in $ExpiredSecrets) {
75+
try {
76+
New-GraphPostRequest -type DELETE -uri "https://graph.microsoft.com/v1.0/applications/$($PasswordCredentials.id)/removePassword" -Body "{`"keyId`":`"$($Secret.keyId)`"}" -NoAuthCheck $true -AsApp $true -ErrorAction Stop
77+
Write-Information "Removed expired application secret with keyId $($Secret.keyId)."
78+
} catch {
79+
Write-LogMessage -API 'Update Tokens' -message "Error removing expired application secret with keyId $($Secret.keyId), see Log Data for details." -sev 'CRITICAL' -LogData (Get-CippException -Exception $_)
80+
}
81+
}
82+
} else {
83+
Write-Information "No expired application secrets found for $AppId."
84+
}
85+
} catch {
86+
Write-Warning "Error updating application secret $($_.Exception.Message)."
87+
Write-Information ($_.InvocationInfo.PositionMessage)
88+
Write-LogMessage -API 'Update Tokens' -message 'Error updating application secret, will try again in 7 days' -sev 'CRITICAL' -LogData (Get-CippException -Exception $_)
89+
}
90+
4391
# Get new refresh token for each direct added tenant
4492
$TenantList = Get-Tenants -IncludeAll | Where-Object { $_.Excluded -eq $false -and $_.delegatedPrivilegeStatus -eq 'directTenant' }
4593
if ($TenantList.Count -eq 0) {
4694
Write-Information 'No direct tenants found for refresh token update.'
4795
} else {
48-
Write-Information "Found $($TenantList.Count) direct tenants for refresh token update."
96+
Write-Information "Found $($TenantList.Count) direct tenant(s) for refresh token update."
4997
foreach ($Tenant in $TenantList) {
5098
try {
5199
Write-Information "Updating refresh token for tenant $($Tenant.displayName) - $($Tenant.customerId)"
@@ -62,13 +110,6 @@ function Start-UpdateTokensTimer {
62110
Write-LogMessage -API 'Update Tokens' -tenant $Tenant.defaultDomainName -tenantid $Tenant.customerId -message "Could not update refresh token for tenant $($Tenant.displayName). Will try again in 7 days." -sev 'CRITICAL'
63111
}
64112
} else {
65-
if ($env:MSI_SECRET) {
66-
Disable-AzContextAutosave -Scope Process | Out-Null
67-
$null = Connect-AzAccount -Identity
68-
$SubscriptionId = $env:WEBSITE_OWNER_NAME -split '\+' | Select-Object -First 1
69-
$null = Set-AzContext -SubscriptionId $SubscriptionId
70-
}
71-
$KV = ($env:WEBSITE_DEPLOYMENT_ID -split '-')[0]
72113
if ($Refreshtoken) {
73114
$name = $Tenant.customerId
74115
Set-AzKeyVaultSecret -VaultName $KV -Name $name -SecretValue (ConvertTo-SecureString -String $Refreshtoken -AsPlainText -Force)
@@ -84,7 +125,6 @@ function Start-UpdateTokensTimer {
84125
}
85126

86127
# Write an information log with the current time.
87-
Write-Information "PowerShell timer trigger function ran! TIME: $currentUTCtime"
88-
128+
Write-Information "UpdateTokens completed: $currentUTCtime"
89129
}
90130
}

0 commit comments

Comments
 (0)