@@ -13,206 +13,74 @@ Param(
13
13
14
14
[Parameter (Mandatory = $true )]
15
15
[String []]
16
- $webAppName ,
16
+ $webAppNames ,
17
17
18
18
[Parameter (Mandatory = $true )]
19
19
[String ]
20
20
$webAppKeyName ,
21
21
22
22
[Parameter (Mandatory = $true )]
23
23
[hashtable ]
24
- $azureKeyVaultSecretPair ,
25
-
26
- [Parameter (Mandatory = $false )]
27
- [String ]
28
- $functionName ,
29
-
30
- [Parameter (Mandatory = $false )]
31
- [ValidateRange (1 , [int ]::MaxValue)]
32
- [Int ]
33
- $keyLength = 64 ,
34
-
35
- [Parameter (Mandatory = $false )]
36
- [ValidateRange (1 , [int ]::MaxValue)]
37
- [Int ]
38
- $maxAttempts = 15 ,
39
-
40
- [Parameter (Mandatory = $false )]
41
- [ValidateRange (1 , [int ]::MaxValue)]
42
- [Int ]
43
- $secondsToWait = 15
24
+ $azureKeyVaultSecretPair
44
25
)
45
26
46
27
# Function to Create Random Strings
47
- function Get-RandomCharacters ($length , $characters ) {
48
- $random = 1 .. $length | ForEach-Object { Get-Random - Maximum $characters.length }
49
- $private :ofs = " "
50
- return [String ]$characters [$random ]
51
- }
52
-
53
- # Function to Wait
54
- function CheckAndWait ($value , $threshold , $message , $waitTime ) {
55
- if ($value -gt $threshold )
56
- {
57
- Write-Host $message
58
- Start-Sleep - s $waitTime
59
- }
60
- }
61
-
62
- # Function to set Resource Strings
63
- function SetResourceStrings ($subscriptionId , $resourceGroup , $webAppName , $webAppKeyName , $functionName )
28
+ function Create-AppKey ()
64
29
{
65
- [hashtable ] $ResourceStrings = @ {}
66
-
67
- # Set Parameters
68
- $ResourceStrings += @ {subscriptionId = $subscriptionId }
69
- $ResourceStrings += @ {resourceGroup = $resourceGroup }
70
- $ResourceStrings += @ {webAppName = $webAppName }
71
- $ResourceStrings += @ {webAppKeyName = $webAppKeyName }
72
- $ResourceStrings += @ {functionName = $functionName }
73
-
74
- # Set Azure Function Resource String, and Alternate Key Name
75
- $ResourceStrings += @ {resourceString = " https://management.azure.com/subscriptions/$ ( $ResourceStrings [' subscriptionId' ]) /resourceGroups/$ ( $ResourceStrings [' resourceGroup' ]) /providers/Microsoft.Web/sites/$ ( $ResourceStrings [' webAppName' ]) " }
76
- $ResourceStrings += @ {webAppKeyNameAlt = $webAppKeyName + " Alt" }
77
-
78
- # Set Azure Function Resource String, and Alternate Key Name
79
- if ([string ]::IsNullOrEmpty($ ($ResourceStrings [' functionName' ]))){
80
- # Set URIs
81
- $ResourceStrings += @ {primaryUri = " $ ( $ResourceStrings [' resourceString' ]) /host/default/functionkeys/$ ( $ ($ResourceStrings [' webAppKeyName' ])) ?api-version=2018-11-01" }
82
- $ResourceStrings += @ {secondaryUri = " $ ( $ResourceStrings [' resourceString' ]) /host/default/functionkeys/$ ( $ ($ResourceStrings [' webAppKeyNameAlt' ])) ?api-version=2018-11-01" }
83
- $ResourceStrings += @ {keyRequestUri = " $ ( $ResourceStrings [' resourceString' ]) /host/default/listKeys?api-version=2018-11-01" }
84
- } else {
85
- # Set URIs
86
- $ResourceStrings += @ {primaryUri = " $ ( $ResourceStrings [' resourceString' ]) /functions/$ ( $ResourceStrings [' functionName' ]) /keys/$ ( $ ($ResourceStrings [' webAppKeyName' ])) ?api-version=2018-11-01" }
87
- $ResourceStrings += @ {secondaryUri = " $ ( $ResourceStrings [' resourceString' ]) /functions/$ ( $ResourceStrings [' functionName' ]) /keys/$ ( $ ($ResourceStrings [' webAppKeyNameAlt' ])) ?api-version=2018-11-01" }
88
- $ResourceStrings += @ {keyRequestUri = " $ ( $ResourceStrings [' resourceString' ]) /functions/$ ( $ResourceStrings [' functionName' ]) /listKeys?api-version=2018-02-01" }
89
- }
30
+ $private :characters = ' abcdefghiklmnoprstuvwxyzABCDEFGHIJKLMENOPTSTUVWXYZ'
31
+ $private :randomChars = 1 .. 64 | ForEach-Object { Get-Random - Maximum $characters.length }
90
32
91
- return $ResourceStrings
33
+ # Set the output field separator to empty instead of space
34
+ $private :ofs = " "
35
+ return [String ]$characters [$randomChars ]
92
36
}
93
37
94
- Function PrintHeader ($message )
38
+ $local :newAltKeyValue = " "
39
+
40
+ Write-Host " Verifying keys of web apps"
41
+ foreach ($webApp in $webAppNames )
95
42
{
96
- Write-Host
97
- Write-Host
98
- Write-Host " *******************************************************"
99
- Write-Host $message
100
- Write-Host " *******************************************************"
101
- }
43
+ Write-Host " Getting keys of" $webApp
44
+ $private :keysJson = az functionapp keys list - g $resourceGroup - n $webApp
45
+ $private :keys = $keysJson | ConvertFrom-Json - AsHashtable
102
46
103
- # Write Parameters to Host
104
- PrintHeader - message " Rotating Keys for following Parameters"
105
- Write-Host " subscriptionId: $subscriptionId "
106
- Write-Host " resourceGroup: $resourceGroup "
107
- Write-Host " webAppName: $webAppName "
108
- Write-Host " webAppKeyName: $webAppKeyName "
109
- Write-Host " azureKeyVaultSecretPair: " + ($azureKeyVaultSecretPair | Out-String )
110
- Write-Host " functionName: $functionName "
111
- Write-Host " keyLength: $keyLength "
112
- Write-Host " maxAttempts: $maxAttempts "
113
- Write-Host " secondsToWait: $secondsToWait "
114
-
115
- # Fetch Token and Set Headers
116
- PrintHeader - message " Fetching Access Token"
117
- $token = $ (az account get-access - token -- query accessToken -- output tsv)
118
- if (! $? ) {
119
- Write-Host " Failed to get access token."
120
- exit 1
121
- }
122
- Write-Host " Creating Headers"
123
- $header = @ {Authorization = " Bearer " + $token ; Accept = ' application/json' }
124
-
125
- # Create Initial Resource Strings
126
- [hashtable ] $ResourceStrings = @ {}
127
- $ResourceStrings = SetResourceStrings - subscriptionId $subscriptionId - resourceGroup $resourceGroup - webAppName $webAppName [0 ] - webAppKeyName $webAppKeyName - functionName $functionName
128
-
129
- # Read current key
130
- PrintHeader - message " Reading Keys from First App: $ ( $ResourceStrings [' webAppName' ]) "
131
- $attempt = 0
132
- do {
133
- CheckAndWait - value $attempt - threshold 0 - message " Waiting before next attempt...." - waitTime $secondsToWait
134
- $attempt ++
135
- Write-Host " Attempting to read current key:" $attempt
136
- $secondaryKeyResponse = Invoke-WebRequest - Method Post - Uri $ResourceStrings [' keyRequestUri' ] - Headers $header
137
- Start-Sleep - s 5
138
- } while ($secondaryKeyResponse.StatusCode -ne 200 -or ! $? -and $attempt -lt $maxAttempts )
139
-
140
- # Fail if we failed to read key
141
- if ($secondaryKeyResponse.StatusCode -ne 200 ) {
142
- Write-Host " Failed to fetch current key."
143
- exit 1
47
+ if ($keys.functionKeys.ContainsKey ($webAppKeyName ))
48
+ {
49
+ if ([string ]::IsNullOrEmpty($newAltKeyValue ))
50
+ {
51
+ $newAltKeyValue = $keys.functionKeys [$webAppKeyName ]
52
+ }
53
+ elseif ($newAltKeyValue -ne $keys.functionKeys [$webAppKeyName ])
54
+ {
55
+ # Maybe eventually have a switch to overwrite, but for now let the dev figure it out manually.
56
+ throw " The value of $webAppKeyName is not the same in all web apps."
57
+ }
58
+ }
144
59
}
145
60
146
- # Parse Key
147
- $secondaryKeyParse = $secondaryKeyResponse.Content | ConvertFrom-Json
148
- if ([string ]::IsNullOrEmpty($ResourceStrings [' functionName' ])){
149
- $secondaryKey = $secondaryKeyParse.functionKeys ." $webAppKeyName "
150
- } else {
151
- $secondaryKey = $secondaryKeyParse ." $webAppKeyName "
152
- }
61
+ Write-Host " Creating new app key"
62
+ $local :newKeyValue = Create- AppKey
153
63
154
- # Create new Function Keys
155
- $primaryKey = Get-RandomCharacters - length $keyLength - characters ' abcdefghiklmnoprstuvwxyzABCDEFGHIJKLMENOPTSTUVWXYZ '
156
- if ([ string ]::IsNullOrEmpty( $secondaryKey )){
157
- $secondaryKey = Get-RandomCharacters - length $keyLength - characters ' abcdefghiklmnoprstuvwxyzABCDEFGHIJKLMENOPTSTUVWXYZ '
64
+ if ([ string ]::IsNullOrEmpty( $newAltKeyValue ))
65
+ {
66
+ Write-Warning " $webAppKeyName doesn't exist in any of the web apps. "
67
+ $newAltKeyValue = Create - AppKey
158
68
}
159
69
70
+ $local :webAppKeyNameAlt = $webAppKeyName + " Alt"
160
71
161
- # Process all Functions
162
- foreach ($app in $webAppName )
163
- {
164
- $ResourceStrings = SetResourceStrings - subscriptionId $subscriptionId - resourceGroup $resourceGroup - webAppName $app - webAppKeyName $webAppKeyName - functionName $functionName
165
- PrintHeader - message " Processing: $ ( $ResourceStrings [' webAppName' ]) "
166
-
167
- # Create Function Key Payloads
168
- $primaryPayload = (@ { properties = @ { name = $ ($ResourceStrings [' webAppKeyName' ]); value = $primaryKey } } | ConvertTo-Json - Compress)
169
- $secondaryPayload = (@ { properties = @ { name = $ ($ResourceStrings [' $webAppKeyNameAlt' ]); value = $secondaryKey } } | ConvertTo-Json - Compress)
170
-
171
- # # Rotate Keys
172
- # Set Alternate Key First
173
- $attempt = 0
174
- do {
175
- CheckAndWait - value $attempt - threshold 0 - message " Waiting before next attempt...." - waitTime $secondsToWait
176
- $attempt ++
177
- Write-Host " Attempting to set alternate key:" $attempt
178
- $alternateKeyResponse = Invoke-WebRequest - Method Put - Uri $ResourceStrings [' secondaryUri' ] - Body " $secondaryPayload " - Headers $header - ContentType " application/json"
179
- } while ($alternateKeyResponse.StatusCode -ne 200 -or ! $? -and $attempt -lt $maxAttempts )
180
- if ($alternateKeyResponse.StatusCode -ne 200 ) {
181
- Write-Host " Failed to set alternate key."
182
- exit 1
183
- }
184
-
185
- # Set Primary Key
186
- $attempt = 0
187
- do {
188
- CheckAndWait - value $attempt - threshold 0 - message " Waiting before next attempt...." - waitTime $secondsToWait
189
- $attempt ++
190
- Write-Host " Attempting to set primary key:" $attempt
191
- $primaryKeyResponse = Invoke-WebRequest - Method Put - Uri $ResourceStrings [' primaryUri' ] - Body " $primaryPayload " - Headers $header - ContentType " application/json"
192
- } while ($primaryKeyResponse.StatusCode -ne 200 -or ! $? -and $attempt -lt $maxAttempts )
193
- if ($primaryKeyResponse.StatusCode -ne 200 ) {
194
- Write-Host " Failed to set primary key."
195
- exit 1
196
- }
197
- }
72
+ foreach ($webApp in $webAppNames )
73
+ {
74
+ Write-Host " Setting keys for" $webApp
198
75
199
- $azureKeyVaultSecretPair.GetEnumerator () | ForEach-Object {
200
- $azureKeyVault = $_.Key
201
- $azureKeyVaultSecret = $_.Value
202
-
203
- # Update Key Vault
204
- PrintHeader - message " Updating Key-Vault: $azureKeyVault "
205
- $attempt = 0
206
- do {
207
- CheckAndWait - value $attempt - threshold 0 - message " Waiting before next attempt...." - waitTime $secondsToWait
208
- $attempt ++
209
- Write-Host " Attempting to update key-vault:" $attempt
210
- $_ = az keyvault secret set -- vault- name $azureKeyVault -- name $azureKeyVaultSecret -- value $primaryKey
211
- } while (! $? -and $attempt -lt $maxAttempts )
212
- if (! $? ) {
213
- Write-Host " Failed to update keyvault."
214
- exit 1
215
- }
76
+ # Always do alt first.
77
+ az functionapp keys set -- key- name $webAppKeyNameAlt -- key- type functionKeys - n $webApp - g $resourceGroup -- key- value $newAltKeyValue | Out-Null
78
+ az functionapp keys set -- key- name $webAppKeyName -- key- type functionKeys - n $webApp - g $resourceGroup -- key- value $newKeyValue | Out-Null
216
79
}
217
80
218
- PrintHeader - message " Keys Updated"
81
+ Write-Host " Setting new app key in keyvaults"
82
+ foreach ($keyVaultName in $azureKeyVaultSecretPair.keys )
83
+ {
84
+ Write-Host " Setting new app key value to $ ( $azureKeyVaultSecretPair [$keyVaultName ]) in $keyVaultName "
85
+ az keyvault secret set -- vault- name $keyVaultName -- name $azureKeyVaultSecretPair [$keyVaultName ] -- value $newKeyValue | Out-Null
86
+ }
0 commit comments