Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/dev-publish-function.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: DEV - Deploy DotNet project to Azure Function App

on:
push:
branches:
- dev

# CONFIGURATION
# For help, go to https://github.com/Azure/Actions
#
# 1. Set up the following secrets in your repository:
# AZURE_FUNCTIONAPP_PUBLISH_PROFILE
#
# 2. Change these variables for your configuration:
env:
AZURE_FUNCTIONAPP_NAME: 'IntuneTLSAuthDotNetDev' # set this to your function app name on Azure
AZURE_FUNCTIONAPP_PACKAGE_PATH: '.' # set this to the path to your function app project, defaults to the repository root
DOTNET_VERSION: '9.0.x' # set this to the dotnet version to use (e.g. '2.1.x', '3.1.x', '5.0.x')

jobs:
build-and-deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC
contents: read # Required for actions/checkout
environment: dev
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@v3

- name: Setup DotNet ${{ env.DOTNET_VERSION }} Environment
uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: 'Resolve Project Dependencies Using Dotnet'
shell: bash
run: |
pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}'
dotnet build --configuration Release --output ./output
popd

- name: 'Log in to Azure with AZ CLI'
uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }} # Required to log in with OIDC
tenant-id: ${{ vars.AZURE_TENANT_ID }} # Required to log in with OIDC
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} # Required to log in with OIDC

- name: 'Run Azure Functions Action'
uses: Azure/functions-action@v1
id: deploy-to-function-app
with:
app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
package: '${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/output'
# For more samples to get started with GitHub Action workflows to deploy to Azure, refer to https://github.com/Azure/actions-workflow-samples
56 changes: 56 additions & 0 deletions .github/workflows/prod-publish-function.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: PROD - Deploy DotNet project to Azure Function App

on:
push:
branches:
- main

# CONFIGURATION
# For help, go to https://github.com/Azure/Actions
#
# 1. Set up the following secrets in your repository:
# AZURE_FUNCTIONAPP_PUBLISH_PROFILE
#
# 2. Change these variables for your configuration:
env:
AZURE_FUNCTIONAPP_NAME: 'IntuneTLSAuthDotNet' # set this to your function app name on Azure
AZURE_FUNCTIONAPP_PACKAGE_PATH: '.' # set this to the path to your function app project, defaults to the repository root
DOTNET_VERSION: '9.0.x' # set this to the dotnet version to use (e.g. '2.1.x', '3.1.x', '5.0.x')

jobs:
build-and-deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC
contents: read # Required for actions/checkout
environment: prod
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@v3

- name: Setup DotNet ${{ env.DOTNET_VERSION }} Environment
uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: 'Resolve Project Dependencies Using Dotnet'
shell: bash
run: |
pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}'
dotnet build --configuration Release --output ./output
popd

- name: 'Log in to Azure with AZ CLI'
uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }} # Required to log in with OIDC
tenant-id: ${{ vars.AZURE_TENANT_ID }} # Required to log in with OIDC
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} # Required to log in with OIDC

- name: 'Run Azure Functions Action'
uses: Azure/functions-action@v1
id: deploy-to-function-app
with:
app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
package: '${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/output'
# For more samples to get started with GitHub Action workflows to deploy to Azure, refer to https://github.com/Azure/actions-workflow-samples
51 changes: 51 additions & 0 deletions AdminVerify.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using IntuneTLSDotNet.Services;

namespace IntuneTLSDotNet
{
// Admin-only function endpoints secured by function key.
public class AdminVerify(IUnifiService unifiService)
{
[Function("AdminstuffAddIp")] // POST with body raw IP string
public async Task<IActionResult> AddIp([
HttpTrigger(AuthorizationLevel.Function, "post", Route = "ip")] HttpRequest req,
FunctionContext ctx)
{
var logger = ctx.GetLogger("AdminstuffAddIp");
var body = await new StreamReader(req.Body).ReadToEndAsync();
if (string.IsNullOrWhiteSpace(body)) return new BadRequestObjectResult("Body must contain IP");
var ip = body.Trim();
var success = await unifiService.AppendManualIpAsync(ip);
return success ? new OkObjectResult($"Added {ip}") : new BadRequestObjectResult($"Invalid or duplicate {ip}");
}

[Function("AdminstuffListIps")] // GET returns JSON array
public async Task<IActionResult> ListIps([
HttpTrigger(AuthorizationLevel.Function, "get", Route = "ips")] HttpRequest req,
FunctionContext ctx)
{
var logger = ctx.GetLogger("AdminstuffListIps");
var list = await unifiService.GetAuthorizedIpListAsync();
return new OkObjectResult(list);
}

[Function("AdminstuffRefreshIps")] // POST triggers a forced refresh from Unifi API then returns list
public async Task<IActionResult> RefreshIps([
HttpTrigger(AuthorizationLevel.Function, "post", Route = "ips/refresh")] HttpRequest req,
FunctionContext ctx)
{
var logger = ctx.GetLogger("AdminstuffRefreshIps");
if (unifiService is UnifiService concrete)
{
var list = await concrete.RefreshAuthorizedIpCacheAsync();
logger.LogInformation("Cache refresh complete. {Count} IPs now in list.", list.Count);
return new OkObjectResult(list);
}
logger.LogError("Unable to refresh IP cache: service instance is not concrete UnifiService");
return new StatusCodeResult(500);
}
}
}
27 changes: 15 additions & 12 deletions IntuneTLSDotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,21 @@
<ItemGroup>
<Content Include="local.settings.json" />
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Azure.Monitor.OpenTelemetry.AspNetCore" Version="1.2.0" />
<!-- Application Insights isn't enabled by default. See https://aka.ms/AAt8mw4. -->
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.23.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="2.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.1" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.OpenTelemetry" Version="1.1.0-preview6" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Azure.Identity" Version="1.17.0" />

<!-- Add this traditional App Insights integration -->
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.23.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="2.0.0" />

<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.1" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.1" />
<PackageReference Include="Microsoft.Azure.StackExchangeRedis" Version="3.2.1" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.10" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
Expand Down
5 changes: 3 additions & 2 deletions IntuneTLSDotNet.sln
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35818.85 d17.13
# Visual Studio Version 18
VisualStudioVersion = 18.0.11116.177 d18.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntuneTLSDotNet", "IntuneTLSDotNet.csproj", "{94FD06A9-2432-4461-B08F-3A4B64D5AB9C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
ProjectSection(SolutionItems) = preProject
ApplicationInsights.config = ApplicationInsights.config
..\..\..\AppData\Roaming\JetBrains\Rider2025.2\scratches\scratch.json = ..\..\..\AppData\Roaming\JetBrains\Rider2025.2\scratches\scratch.json
EndProjectSection
EndProject
Global
Expand Down
38 changes: 27 additions & 11 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,41 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using IntuneTLSDotNet.Services;
using Azure.Monitor.OpenTelemetry.AspNetCore;
using Microsoft.Azure.Functions.Worker.OpenTelemetry;

using Azure.Identity;
using StackExchange.Redis;

// Create the function app builder
var builder = FunctionsApplication.CreateBuilder(args);

// Ensure configuration is properly loaded from all sources
builder.Configuration.AddEnvironmentVariables();

builder.ConfigureFunctionsWebApplication();

// Register HttpClient and Unifi service
// Configure Azure Redis with Managed Identity
var redisConnectionString = builder.Configuration.GetValue<string>("REDIS_CONNECTION_STRING");
if (!string.IsNullOrEmpty(redisConnectionString))
{
var configurationOptions = ConfigurationOptions.Parse($"{redisConnectionString}");

// Use Azure Managed Identity for authentication
await configurationOptions.ConfigureForAzureWithTokenCredentialAsync(new DefaultAzureCredential());

builder.Services.AddStackExchangeRedisCache(options =>
{
options.ConfigurationOptions = configurationOptions;
});
}
else
{
throw new InvalidOperationException("REDIS_CONNECTION_STRING is required for distributed caching");
}

// Register HttpClient and Unifi service with simplified logging
builder.Services
.AddHttpClient()
.AddSingleton<IConfiguration>(builder.Configuration) // Explicitly register IConfiguration
.AddSingleton<IConfiguration>(builder.Configuration)
.AddSingleton<IUnifiService, UnifiService>()
.AddOpenTelemetry()
.UseAzureMonitor()
.UseFunctionsWorkerDefaults();
.AddApplicationInsightsTelemetryWorkerService(); // Traditional App Insights integration

// Remove OpenTelemetry completely
// builder.Services.AddOpenTelemetry().UseAzureMonitor().UseFunctionsWorkerDefaults();

builder.Build().Run();
Loading
Loading