1
1
using System ;
2
+ using System . Collections . Generic ;
2
3
using System . Diagnostics ;
3
4
using System . IO ;
4
5
using System . Linq ;
7
8
using Azure . Identity ;
8
9
using Azure . Security . KeyVault . Secrets ;
9
10
using DataPipelineTools . Tests . Common ;
10
- using Microsoft . VisualBasic . FileIO ;
11
+ using Flurl . Util ;
12
+ using Microsoft . VisualStudio . TestPlatform . ObjectModel ;
11
13
using NUnit . Framework ;
12
14
using SearchOption = System . IO . SearchOption ;
13
15
@@ -43,6 +45,8 @@ protected bool UseFunctionsEmulator
43
45
protected string ServicePrincipalName => TestContext . Parameters [ "ServicePrincipalName" ] ;
44
46
protected string ApplicationInsightsName => TestContext . Parameters [ "ApplicationInsightsName" ] ;
45
47
48
+ protected abstract string FunctionUri { get ; }
49
+
46
50
47
51
// The properties that we get from Azure Key Vault are cached for reuse
48
52
private string _functionsAppKey ;
@@ -151,6 +155,39 @@ protected bool IsRunningOnCIServer
151
155
}
152
156
153
157
protected string GetKeyVaultSecretValue ( string secretName )
158
+ {
159
+ var client = GetKeyVaultClient ( ) ;
160
+
161
+ try
162
+ {
163
+ var result = client . GetSecretAsync ( secretName ) . Result ;
164
+
165
+ return result ? . Value ? . Value ;
166
+ }
167
+ catch ( Exception ex )
168
+ {
169
+ throw new SettingsException (
170
+ $ "The key vault { KeyVaultName } is inaccessible or has been deleted. Check your run settings file.\n \n Inner Exception Message:\n { ex . Message . Split ( '\n ' ) . First ( ) } ") ;
171
+ }
172
+ }
173
+
174
+
175
+ protected IEnumerable < string > GetKeyVaultSecretNames ( )
176
+ {
177
+ var client = GetKeyVaultClient ( ) ;
178
+ try
179
+ {
180
+ var results = client . GetPropertiesOfSecrets ( ) ;
181
+ return results . Select ( x => x . Name ) ;
182
+ }
183
+ catch ( Exception ex )
184
+ {
185
+ throw new SettingsException (
186
+ $ "The key vault { KeyVaultName } is inaccessible or has been deleted. Check your run settings file.\n \n Inner Exception Message:\n { ex . Message . Split ( '\n ' ) . First ( ) } ") ;
187
+ }
188
+ }
189
+
190
+ private SecretClient GetKeyVaultClient ( )
154
191
{
155
192
/* For some reason the DefaultAzureCredential (SharedTokenCacheCredential / VisualStudioCredential) returns a 403 trying to access the key vault, even when access policies are configured correctly
156
193
* We either use one of the following to authenticate:
@@ -159,25 +196,21 @@ protected string GetKeyVaultSecretValue(string secretName)
159
196
*
160
197
* See here for more info: https://docs.microsoft.com/en-us/answers/questions/74848/access-denied-to-first-party-service.html
161
198
*/
162
- var credential = new DefaultAzureCredential ( new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true , ExcludeVisualStudioCredential = true } ) ;
199
+ var credential = new DefaultAzureCredential ( new DefaultAzureCredentialOptions
200
+ { ExcludeSharedTokenCacheCredential = true , ExcludeVisualStudioCredential = true } ) ;
163
201
164
202
if ( string . IsNullOrWhiteSpace ( KeyVaultName ) )
165
203
throw new ArgumentException ( "The run setting file does not have a value for 'KeyVaultName'" ) ;
166
204
167
205
var keyVaultUri = $ "https://{ KeyVaultName } .vault.azure.net";
168
- var client = new SecretClient ( new Uri ( keyVaultUri ) , credential ) ;
169
- var result = client . GetSecretAsync ( secretName ) . Result ;
206
+ return new SecretClient ( new Uri ( keyVaultUri ) , credential ) ;
170
207
171
- return result ? . Value ? . Value ;
172
208
}
173
209
174
210
175
211
176
212
177
213
178
-
179
-
180
-
181
214
#region Azure Functions Local Host
182
215
// We use one time setup and teardown to generate a single instance of the emulator across all classes that implement this base class
183
216
@@ -186,6 +219,13 @@ protected string GetKeyVaultSecretValue(string secretName)
186
219
[ OneTimeSetUp ]
187
220
public void StartFunctionsEmulator ( )
188
221
{
222
+ // If the run settings is referencing secrets via key vault, make sure we can connect
223
+ if ( TestContext . Parameters . Names . Any ( x => x . StartsWith ( "KeyVaultSecret" ) && ! string . IsNullOrWhiteSpace ( TestContext . Parameters [ x ] . ToString ( ) ) ) )
224
+ GetKeyVaultSecretNames ( ) ;
225
+
226
+
227
+ // Start the local functions emulator if required. Use a lock so that multiple test classes inheriting from this base class share a
228
+ // functions emulator instance
189
229
lock ( _functionsProcessLock )
190
230
{
191
231
if ( UseFunctionsEmulator )
@@ -201,6 +241,7 @@ public void StartFunctionsEmulator()
201
241
[ OneTimeTearDown ]
202
242
public void StopFunctionsEmulator ( )
203
243
{
244
+ // Once the last instance finishes, stop the local emulator instance if we're using it.
204
245
lock ( _functionsProcessLock )
205
246
{
206
247
if ( UseFunctionsEmulator )
0 commit comments