Skip to content

Commit 0e1b9e9

Browse files
authored
feat: add config az func secret store extension (#333)
* feat: add config az func secret store extension * pr-fix: use net6.0 az func * pr-fix: use correct docker file * pr-fix: use correct file paths in docker file * pr-fix: update with token replacement in app settings * pr-fix: correct az func routes * pr-add: az func docker test to release build
1 parent b56570c commit 0e1b9e9

File tree

16 files changed

+539
-19
lines changed

16 files changed

+539
-19
lines changed

build/ci-build.yml

+2-8
Original file line numberDiff line numberDiff line change
@@ -104,20 +104,14 @@ stages:
104104
inputs:
105105
artifact: 'Build'
106106
path: '$(Build.SourcesDirectory)'
107-
- task: UseDotNet@2
108-
displayName: 'Import .NET Core SDK ($(DotNet.Sdk.VersionBC))'
109-
inputs:
110-
packageType: 'sdk'
111-
version: '$(DotNet.Sdk.VersionBC)'
112107
- template: 'templates/download-hashicorp-vault.yml'
113108
parameters:
114109
targetFolder: '$(Build.SourcesDirectory)'
115110
version: $(HashiCorp.Vault.Version)
116111
vaultBinVariableName: 'Arcus.HashiCorp.VaultBin'
117-
- template: test/run-integration-tests.yml@templates
112+
- template: templates/run-integration-tests.yml
118113
parameters:
119-
dotnetSdkVersion: '$(DotNet.Sdk.Version)'
120-
projectName: '$(Project).Tests.Integration'
114+
dockerProjectName: '$(Project).Tests.Runtimes.AzureFunctions'
121115

122116
- stage: ReleaseToMyget
123117
displayName: 'Release to MyGet'

build/nuget-release.yml

+2-8
Original file line numberDiff line numberDiff line change
@@ -90,20 +90,14 @@ stages:
9090
inputs:
9191
artifact: 'Build'
9292
path: '$(Build.SourcesDirectory)'
93-
- task: UseDotNet@2
94-
displayName: 'Import .NET Core SDK ($(DotNet.Sdk.VersionBC))'
95-
inputs:
96-
packageType: 'sdk'
97-
version: '$(DotNet.Sdk.VersionBC)'
9893
- template: 'templates/download-hashicorp-vault.yml'
9994
parameters:
10095
targetFolder: '$(Build.SourcesDirectory)'
10196
version: $(HashiCorp.Vault.Version)
10297
vaultBinVariableName: 'Arcus.HashiCorp.VaultBin'
103-
- template: test/run-integration-tests.yml@templates
98+
- template: templates/run-integration-tests.yml
10499
parameters:
105-
dotnetSdkVersion: '$(DotNet.Sdk.Version)'
106-
projectName: '$(Project).Tests.Integration'
100+
dockerProjectName: '$(Project).Tests.Runtimes.AzureFunctions'
107101

108102
- stage: Release
109103
displayName: 'Release to NuGet.org'
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
parameters:
2+
dockerProjectName: ''
3+
4+
steps:
5+
- bash: |
6+
if [ -z "$PROJECT_NAME" ]; then
7+
echo "##vso[task.logissue type=error;]Missing template parameter \"dockerProjectName\""
8+
echo "##vso[task.complete result=Failed;]"
9+
fi
10+
env:
11+
PROJECT_NAME: ${{ parameters.dockerProjectName }}
12+
- task: UseDotNet@2
13+
displayName: 'Import .NET Core SDK ($(DotNet.Sdk.VersionBC))'
14+
inputs:
15+
packageType: 'sdk'
16+
version: '$(DotNet.Sdk.VersionBC)'
17+
- task: Docker@1
18+
displayName: 'Build Docker image from ${{ parameters.dockerProjectName }}'
19+
inputs:
20+
dockerFile: src/${{ parameters.dockerProjectName }}/Dockerfile
21+
imageName: '${{ parameters.dockerProjectName }}:$(Build.BuildId)'
22+
useDefaultContext: false
23+
buildContext: src
24+
- task: Docker@1
25+
displayName: 'Run new project Docker image from ${{ parameters.dockerProjectName }}'
26+
inputs:
27+
command: 'Run an image'
28+
imageName: '${{ parameters.dockerProjectName }}:$(Build.BuildId)'
29+
containerName: '${{ parameters.dockerProjectName }}'
30+
ports: '$(Arcus.AzureFunctions.HttpPort):80'
31+
- template: test/run-integration-tests.yml@templates
32+
parameters:
33+
dotnetSdkVersion: '$(DotNet.Sdk.Version)'
34+
projectName: '$(Project).Tests.Integration'
35+
- task: Bash@3
36+
inputs:
37+
targetType: 'inline'
38+
script: |
39+
docker logs ${{ parameters.dockerProjectName }}
40+
failOnStderr: true
41+
displayName: Show ${{ parameters.dockerProjectName }} logs
42+
condition: always()

build/variables/test.yml

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
variables:
22
HashiCorp.Vault.Version: 1.5.0
3+
Arcus.AzureFunctions.HttpPort: "5000"

docs/preview/features/secret-store/azure-functions.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ namespace MyHttpAzureFunction
2828
{
2929
public override void Configure(IFunctionsHostBuilder builder)
3030
{
31-
IConfiguration config = builder.GetContext().Configuration;
32-
builder.ConfigureSecretStore(stores =>
31+
builder.ConfigureSecretStore((FunctionsHostBuilderContext context, IConfiguration config, SecretStoreBuilder stores) =>
3332
{
3433
var keyVaultName = config["KeyVault_Name"];
3534
stores.AddEnvironmentVariables()

src/Arcus.Security.AzureFunctions/Extensions/IFunctionHostBuilderExtensions.cs

+19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using Arcus.Security.Core;
33
using GuardNet;
4+
using Microsoft.Extensions.Configuration;
45
using Microsoft.Extensions.DependencyInjection;
56
using Microsoft.Extensions.Hosting;
67

@@ -27,5 +28,23 @@ public static IFunctionsHostBuilder ConfigureSecretStore(this IFunctionsHostBuil
2728
functionsHostBuilder.Services.AddSecretStore(configureSecretStores);
2829
return functionsHostBuilder;
2930
}
31+
32+
/// <summary>
33+
/// Configure an <see cref="ISecretProvider"/> in the application with a given set of stores configured in the given <paramref name="configureSecretStores"/>.
34+
/// </summary>
35+
/// <param name="functionsHostBuilder">The builder to append the secret store configuration to.</param>
36+
/// <param name="configureSecretStores">The customization of the different target secret store sources to include in the final <see cref="ISecretProvider"/>.</param>
37+
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="functionsHostBuilder"/> or <paramref name="configureSecretStores"/> is <c>null</c>.</exception>
38+
public static IFunctionsHostBuilder ConfigureSecretStore(
39+
this IFunctionsHostBuilder functionsHostBuilder,
40+
Action<FunctionsHostBuilderContext, IConfiguration, SecretStoreBuilder> configureSecretStores)
41+
{
42+
Guard.NotNull(functionsHostBuilder, nameof(functionsHostBuilder), "Requires a functions host builder to add the secret store");
43+
Guard.NotNull(configureSecretStores, nameof(configureSecretStores), "Requires a function to configure the secret store with potential secret providers");
44+
45+
FunctionsHostBuilderContext context = functionsHostBuilder.GetContext();
46+
functionsHostBuilder.Services.AddSecretStore(stores => configureSecretStores(context, context.Configuration, stores));
47+
return functionsHostBuilder;
48+
}
3049
}
3150
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System.Net.Http;
2+
using System.Threading.Tasks;
3+
using Arcus.Security.Tests.Integration.Fixture;
4+
using Arcus.Testing.Logging;
5+
using Microsoft.Extensions.Configuration;
6+
using Microsoft.Extensions.Logging;
7+
using Xunit;
8+
using Xunit.Abstractions;
9+
10+
namespace Arcus.Security.Tests.Integration.AzureFunctions
11+
{
12+
[Collection("Azure Functions")]
13+
public class SecretStoreBuilderTests
14+
{
15+
private readonly string _defaultRoute;
16+
private readonly ILogger _logger;
17+
18+
private static readonly HttpClient HttpClient = new HttpClient();
19+
20+
/// <summary>
21+
/// Initializes a new instance of the <see cref="SecretStoreBuilderTests" /> class.
22+
/// </summary>
23+
public SecretStoreBuilderTests(ITestOutputHelper outputWriter)
24+
{
25+
var config = TestConfig.Create();
26+
var httpPort = config.GetValue<int>("Arcus:AzureFunctions:HttpPort");
27+
_defaultRoute = $"http://localhost:{httpPort}/api/order";
28+
29+
_logger = new XunitTestLogger(outputWriter);
30+
}
31+
32+
[Fact]
33+
public async Task ConfigureSecretStore_WithConfiguration_ReturnsConfigurationSecret()
34+
{
35+
// Act
36+
_logger.LogInformation("GET -> '{Uri}'", _defaultRoute);
37+
using (HttpResponseMessage response = await HttpClient.GetAsync(_defaultRoute))
38+
{
39+
// Assert
40+
_logger.LogInformation("{StatusCode} <- {Uri}", response.StatusCode, _defaultRoute);
41+
string contents = await response.Content.ReadAsStringAsync();
42+
Assert.Equal("TestSecret", contents);
43+
}
44+
}
45+
}
46+
}

src/Arcus.Security.Tests.Integration/appsettings.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
"VaultBin": "#{Arcus.HashiCorp.VaultBin}#"
2323
},
2424
"ApplicationInsights": {
25-
"InstrumentationKey": "#{Arcus.ApplicationInsights.InstrumentationKey}#"
25+
"InstrumentationKey": "#{Arcus.ApplicationInsights.InstrumentationKey}#"
26+
},
27+
"AzureFunctions": {
28+
"HttpPort": "#{Arcus.AzureFunctions.HttpPort}#"
2629
}
2730
}
2831
}

0 commit comments

Comments
 (0)