Skip to content

Commit c2bb55e

Browse files
committed
Refactoring to add new auth types for data lake
1 parent 353e7f6 commit c2bb55e

File tree

21 files changed

+482
-137
lines changed

21 files changed

+482
-137
lines changed

Azure-DevTestLab/Environments/sqlcollaborative_AzureDataPipelineTools/azuredeploy.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,10 @@
521521
{
522522
"name": "APPLICATIONINSIGHTS_CONNECTION_STRING",
523523
"value": "[reference(resourceId('microsoft.insights/components', variables('applicationInsightsName')), variables('applicationInsightsApiVersion')).ConnectionString]"
524+
},
525+
{
526+
"name": "TENANT_ID",
527+
"value": "[parameters('devopsServicePrincipalCredentials').tenant_id]"
524528
}
525529
]
526530
}

DataPipelineTools.Functions.Tests/DataLake/DataLakeFunctions/Integration/DataLakeGetItemsIntegrationTests.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ namespace DataPipelineTools.Functions.Tests.DataLake.DataLakeFunctions.Integrati
1313
[Category(nameof(TestType.IntegrationTest))]
1414
public class DataLakeGetItemsIntegrationTests: IntegrationTestBase
1515
{
16-
protected string FunctionUri => $"{FunctionsAppUrl}/api/DataLakeGetItems";
16+
protected override string FunctionUri => $"{FunctionsAppUrl}/api/DataLakeGetItems";
1717

1818
[SetUp]
1919
public void Setup()
2020
{
2121
Logger.LogInformation($"Running tests in { (IsRunningOnCIServer ? "CI" : "local") } environment using Functions App '{FunctionsAppUrl}'");
2222
Logger.LogInformation($"TestContext.Parameters.Count: { TestContext.Parameters.Count }");
23+
24+
Logger.LogInformation($"Key Vault Name: { KeyVaultName }");
2325
}
2426

2527
[Test]
@@ -28,7 +30,7 @@ public async Task Test_FunctionIsRunnable()
2830
using (var client = new HttpClient())
2931
{
3032
var queryParams = HttpUtility.ParseQueryString(string.Empty);
31-
queryParams["AccountUri"] = this.StorageAccountName;
33+
queryParams["account"] = this.StorageAccountName;
3234
queryParams["container"] = this.StorageContainerName;
3335

3436
if (!IsEmulatorRunning)

DataPipelineTools.Functions.Tests/IntegrationTestBase.cs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Diagnostics;
34
using System.IO;
45
using System.Linq;
@@ -7,7 +8,8 @@
78
using Azure.Identity;
89
using Azure.Security.KeyVault.Secrets;
910
using DataPipelineTools.Tests.Common;
10-
using Microsoft.VisualBasic.FileIO;
11+
using Flurl.Util;
12+
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
1113
using NUnit.Framework;
1214
using SearchOption = System.IO.SearchOption;
1315

@@ -43,6 +45,8 @@ protected bool UseFunctionsEmulator
4345
protected string ServicePrincipalName => TestContext.Parameters["ServicePrincipalName"];
4446
protected string ApplicationInsightsName => TestContext.Parameters["ApplicationInsightsName"];
4547

48+
protected abstract string FunctionUri { get; }
49+
4650

4751
// The properties that we get from Azure Key Vault are cached for reuse
4852
private string _functionsAppKey;
@@ -151,6 +155,39 @@ protected bool IsRunningOnCIServer
151155
}
152156

153157
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\nInner 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\nInner Exception Message:\n {ex.Message.Split('\n').First()}");
187+
}
188+
}
189+
190+
private SecretClient GetKeyVaultClient()
154191
{
155192
/* For some reason the DefaultAzureCredential (SharedTokenCacheCredential / VisualStudioCredential) returns a 403 trying to access the key vault, even when access policies are configured correctly
156193
* We either use one of the following to authenticate:
@@ -159,25 +196,21 @@ protected string GetKeyVaultSecretValue(string secretName)
159196
*
160197
* See here for more info: https://docs.microsoft.com/en-us/answers/questions/74848/access-denied-to-first-party-service.html
161198
*/
162-
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true, ExcludeVisualStudioCredential = true });
199+
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions
200+
{ExcludeSharedTokenCacheCredential = true, ExcludeVisualStudioCredential = true});
163201

164202
if (string.IsNullOrWhiteSpace(KeyVaultName))
165203
throw new ArgumentException("The run setting file does not have a value for 'KeyVaultName'");
166204

167205
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);
170207

171-
return result?.Value?.Value;
172208
}
173209

174210

175211

176212

177213

178-
179-
180-
181214
#region Azure Functions Local Host
182215
// We use one time setup and teardown to generate a single instance of the emulator across all classes that implement this base class
183216

@@ -186,6 +219,13 @@ protected string GetKeyVaultSecretValue(string secretName)
186219
[OneTimeSetUp]
187220
public void StartFunctionsEmulator()
188221
{
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
189229
lock (_functionsProcessLock)
190230
{
191231
if (UseFunctionsEmulator)
@@ -201,6 +241,7 @@ public void StartFunctionsEmulator()
201241
[OneTimeTearDown]
202242
public void StopFunctionsEmulator()
203243
{
244+
// Once the last instance finishes, stop the local emulator instance if we're using it.
204245
lock (_functionsProcessLock)
205246
{
206247
if (UseFunctionsEmulator)

DataPipelineTools.Functions/Common/FunctionsBase.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ public FunctionsBase(ILogger<FunctionsBase> logger)
1414
_logger = logger;
1515
}
1616

17-
protected JObject GetTemplateResponse(DataLakeConfig dataLakeConfig, object parameters)
17+
protected JObject GetTemplateResponse(IDataLakeConnectionConfig dataLakeConnectionConfig, object parameters)
1818
{
1919
var assemblyInfo = AssemblyHelpers.GetAssemblyVersionInfoJson();
2020

2121
var responseJson = new JObject();
2222
if (assemblyInfo.HasValues)
2323
responseJson.Add("debugInfo", assemblyInfo);
2424

25-
if (dataLakeConfig.BaseUrl != null)
26-
responseJson.Add("storageContainerUrl", dataLakeConfig.BaseUrl);
25+
if (dataLakeConnectionConfig.BaseUrl != null)
26+
responseJson.Add("storageContainerUrl", dataLakeConnectionConfig.BaseUrl);
2727

2828
var paramatersJson = JObject.FromObject(parameters);
2929
responseJson.Add("parameters", paramatersJson);

0 commit comments

Comments
 (0)