Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CsvImporter tool #17

Merged
merged 18 commits into from
Jan 12, 2024
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
12 changes: 12 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ updates:
assignees:
- "Smalls1652"

# NuGet config for 'EntraMfaPrefillinator.Tools.CsvImporter'
- package-ecosystem: "nuget"
directory: "/src/Tools/CsvImporter"
target-branch: "main"
ignore:
- dependency-name: "Azure.Storage.Queues"
- dependency-name: "Microsoft.Identity.Client"
schedule:
interval: "daily"
assignees:
- "Smalls1652"

# GitHub Actions config for the repo
- package-ecosystem: "github-actions"
directory: "/"
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ jobs:
matrix:
projectPath: [
"./src/FunctionApp/",
"./src/Lib/"
"./src/Lib/",
"./src/Tools/CsvImporter/"
]
env:
DOTNET_NOLOGO: true
Expand Down
49 changes: 49 additions & 0 deletions .github/workflows/csvimporter-create-artifacts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow
name: CsvImporter / Create artifacts
on:
workflow_dispatch:

permissions:
packages: read

jobs:
create-artifacts:
name: Create artifacts
runs-on: [ ubuntu-latest, windows-latest ]
env:
DOTNET_NOLOGO: true

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
global-json-file: global.json

- name: Install .NET tools
run: dotnet tool restore

- name: Update project files with GitVersion
run: dotnet tool run dotnet-gitversion /updateprojectfiles

- name: Compile project (Windows)
if: ${{ runner.os == 'Windows' }}
run: |
dotnet restore ./src/Tools/CsvImporter/
dotnet publish ./src/Tools/CsvImporter/ --configuration "Release" --runtime "win-x64" --output "../../../artifacts/CsvImporter"

- name: Compile project (Linux)
if: ${{ runner.os == 'Linux' }}
run: |
dotnet restore ./src/Tools/CsvImporter/
dotnet publish ./src/Tools/CsvImporter/ --configuration "Release" --runtime "linux-x64" --output "../../../artifacts/CsvImporter"

- name: Create artifact
uses: actions/upload-artifact@v4
with:
name: "CsvImporter_${{ runner.os }}_${{ github.sha }}"
path: artifacts/CsvImporter
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
local-files/
build/

__blobstorage__/
__queuestorage__/
__azurite_*.json
Expand Down
6 changes: 6 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"recommendations": [
"ms-azuretools.vscode-azurefunctions",
"ms-dotnettools.csharp"
]
}
7 changes: 6 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,10 @@
"dotnet.server.path": "latest",
"task.quickOpen.history": 0,
"task.autoDetect": "off",
"powershell.startAutomatically": false
"powershell.startAutomatically": false,
"azureFunctions.projectSubpath": "src/FunctionApp",
"azureFunctions.deploySubpath": "src/FunctionApp/bin/Release/net8.0/publish",
"azureFunctions.projectLanguage": "C#",
"azureFunctions.projectRuntime": "~4",
"azureFunctions.preDeployTask": "publish (functions)"
}
91 changes: 88 additions & 3 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
}
},
"runOptions": {
"instanceLimit": 2,
"instanceLimit": 2
},
"presentation": {
"echo": false,
Expand Down Expand Up @@ -167,8 +167,6 @@
"Build: EntraMfaPrefillinator.FunctionApp"
]
},
// Remaining tasks are only for the VSCode launch configs
// or are supporting tasks.
{
"label": "Build: EntraMfaPrefillinator.FunctionApp",
"detail": "Build the EntraMfaPrefillinator.FunctionApp project.",
Expand All @@ -194,6 +192,89 @@
"showReuseMessage": true,
"clear": true
}
},
// Remaining tasks are only for the VSCode launch configs
// or are supporting tasks.
{
"label": "clean (functions)",
"hide": true,
"command": "dotnet",
"args": [
"clean",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"type": "process",
"problemMatcher": "$msCompile",
"options": {
"cwd": "${workspaceFolder}/src/FunctionApp"
}
},
{
"label": "build (functions)",
"hide": true,
"command": "dotnet",
"args": [
"build",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"type": "process",
"dependsOn": "clean (functions)",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": "$msCompile",
"options": {
"cwd": "${workspaceFolder}/src/FunctionApp"
}
},
{
"label": "clean release (functions)",
"hide": true,
"command": "dotnet",
"args": [
"clean",
"--configuration",
"Release",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"type": "process",
"problemMatcher": "$msCompile",
"options": {
"cwd": "${workspaceFolder}/src/FunctionApp"
}
},
{
"label": "publish (functions)",
"hide": true,
"command": "dotnet",
"args": [
"publish",
"--configuration",
"Release",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"type": "process",
"dependsOn": "clean release (functions)",
"problemMatcher": "$msCompile",
"options": {
"cwd": "${workspaceFolder}/src/FunctionApp"
}
},
{
"hide": true,
"type": "func",
"dependsOn": "build (functions)",
"options": {
"cwd": "${workspaceFolder}/src/FunctionApp/bin/Debug/net8.0"
},
"command": "host start",
"isBackground": true,
"problemMatcher": "$func-dotnet-watch"
}
],
"inputs": [
Expand Down Expand Up @@ -230,6 +311,10 @@
{
"label": "FunctionApp",
"value": "src/FunctionApp"
},
{
"label": "Tools.CsvImporter",
"value": "src/Tools/CsvImporter"
}
]
},
Expand Down
10 changes: 10 additions & 0 deletions EntraMfaPrefillinator.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lib", "src\Lib\Lib.csproj",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunctionApp", "src\FunctionApp\FunctionApp.csproj", "{6CAFB055-0347-439F-91AB-4F3BC7D37713}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{33DE40FB-A917-4A86-AA79-5BAB5CC8E66A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsvImporter", "src\Tools\CsvImporter\CsvImporter.csproj", "{B5F8BD80-AEDB-4F1B-AEA9-E53E0DF95310}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -26,9 +30,15 @@ Global
{6CAFB055-0347-439F-91AB-4F3BC7D37713}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CAFB055-0347-439F-91AB-4F3BC7D37713}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CAFB055-0347-439F-91AB-4F3BC7D37713}.Release|Any CPU.Build.0 = Release|Any CPU
{B5F8BD80-AEDB-4F1B-AEA9-E53E0DF95310}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5F8BD80-AEDB-4F1B-AEA9-E53E0DF95310}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5F8BD80-AEDB-4F1B-AEA9-E53E0DF95310}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5F8BD80-AEDB-4F1B-AEA9-E53E0DF95310}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{44026C4A-055E-421A-9763-F769130A7562} = {47D1FEFD-4DFF-4080-98FC-7B35A63D3FD5}
{6CAFB055-0347-439F-91AB-4F3BC7D37713} = {47D1FEFD-4DFF-4080-98FC-7B35A63D3FD5}
{33DE40FB-A917-4A86-AA79-5BAB5CC8E66A} = {47D1FEFD-4DFF-4080-98FC-7B35A63D3FD5}
{B5F8BD80-AEDB-4F1B-AEA9-E53E0DF95310} = {33DE40FB-A917-4A86-AA79-5BAB5CC8E66A}
EndGlobalSection
EndGlobal
59 changes: 44 additions & 15 deletions src/FunctionApp/Functions/ProcessUserAuthUpdateQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,38 @@ FunctionContext executionContext
jsonTypeInfo: QueueJsonContext.Default.UserAuthUpdateQueueItem
)!;

logger.LogInformation("Received request for {UserPrincipalName}.", queueItem.UserPrincipalName);
string userName = queueItem.UserName ?? queueItem.UserPrincipalName ?? throw new Exception("'userName' or 'userPrincipalName' must be supplied in the request.");

logger.LogInformation("Received request for '{userName}'.", userName);

User user;
try
if (queueItem.UserName is not null || queueItem.EmployeeId is not null)
{
try
{
user = await _graphClientService.GetUserByUserNameAndEmployeeNumberAsync(queueItem.UserName, queueItem.EmployeeId);
}
catch (Exception e)
{
logger.LogError(e, "Error getting user, '{userName}' [{employeeId}].", queueItem.UserName, queueItem.EmployeeId);
throw;
}
}
else if (queueItem.UserPrincipalName is not null)
{
user = await _graphClientService.GetUserAsync(queueItem.UserPrincipalName);
try
{
user = await _graphClientService.GetUserAsync(queueItem.UserPrincipalName);
}
catch (Exception e)
{
logger.LogError(e, "Error getting user, '{userPrincipalName}'.", queueItem.UserPrincipalName);
throw;
}
}
catch (Exception e)
else
{
logger.LogError(e, "Error getting user, {UserPrincipalName}.", queueItem.UserPrincipalName);
throw;
throw new Exception("'userName' and 'employeeId' or 'userPrincipalName' must be supplied in the request.");
}

if (queueItem.EmailAddress is not null)
Expand All @@ -51,7 +72,7 @@ FunctionContext executionContext

if (emailAuthMethods is not null && emailAuthMethods.Length != 0)
{
logger.LogWarning("'{UserPrincipalName}' already has email auth methods configured. Skipping...", queueItem.UserPrincipalName);
logger.LogWarning("'{userPrincipalName}' already has email auth methods configured. Skipping...", user.UserPrincipalName);
}
else
{
Expand All @@ -62,18 +83,22 @@ await _graphClientService.AddEmailAuthenticationMethodAsync(
emailAddress: queueItem.EmailAddress
);

logger.LogInformation("Added email auth method for {UserPrincipalName}.", queueItem.UserPrincipalName);
logger.LogInformation("Added email auth method for '{userPrincipalName}'.", user.UserPrincipalName);
}
catch (GraphClientDryRunException)
{
logger.LogWarning("Dry run is enabled. Skipping adding email auth method for '{userPrincipalName}'.", user.UserPrincipalName);
}
catch (Exception e)
{
logger.LogError(e, "Error adding email auth method for {UserPrincipalName}.", queueItem.UserPrincipalName);
logger.LogError(e, "Error adding email auth method for '{userPrincipalName}'.", user.UserPrincipalName);
throw;
}
}
}
else
{
logger.LogWarning("'{UserPrincipalName}' did not have an email address supplied in the request. Skipping...", queueItem.UserPrincipalName);
logger.LogWarning("'{userPrincipalName}' did not have an email address supplied in the request. Skipping...", user.UserPrincipalName);
}

if (queueItem.PhoneNumber is not null)
Expand All @@ -82,7 +107,7 @@ await _graphClientService.AddEmailAuthenticationMethodAsync(

if (phoneAuthMethods is not null && phoneAuthMethods.Length != 0)
{
logger.LogWarning("'{UserPrincipalName}' already has phone auth methods configured. Skipping...", queueItem.UserPrincipalName);
logger.LogWarning("'{userPrincipalName}' already has phone auth methods configured. Skipping...", user.UserPrincipalName);
}
else
{
Expand All @@ -93,21 +118,25 @@ await _graphClientService.AddPhoneAuthenticationMethodAsync(
phoneNumber: queueItem.PhoneNumber
);

logger.LogInformation("Added phone auth method for {UserPrincipalName}.", queueItem.UserPrincipalName);
logger.LogInformation("Added phone auth method for '{userPrincipalName}'.", user.UserPrincipalName);
}
catch (GraphClientDryRunException)
{
logger.LogWarning("Dry run is enabled. Skipping adding phone auth method for '{userPrincipalName}'.", user.UserPrincipalName);
}
catch (Exception e)
{
logger.LogError(e, "Error adding phone auth method for {UserPrincipalName}.", queueItem.UserPrincipalName);
logger.LogError(e, "Error adding phone auth method for '{userPrincipalName}'.", user.UserPrincipalName);
throw;
}
}
}
else
{
logger.LogWarning("'{UserPrincipalName}' did not have a phone number supplied in the request. Skipping...", queueItem.UserPrincipalName);
logger.LogWarning("'{userPrincipalName}' did not have a phone number supplied in the request. Skipping...", user.UserPrincipalName);
}

stopwatch.Stop();
logger.LogInformation("Processed request for {UserPrincipalName} in {ElapsedMilliseconds}ms.", queueItem.UserPrincipalName, stopwatch.ElapsedMilliseconds);
logger.LogInformation("Processed request for '{userPrincipalName}' in {ElapsedMilliseconds}ms.", user.UserPrincipalName, stopwatch.ElapsedMilliseconds);
}
}
3 changes: 2 additions & 1 deletion src/FunctionApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
{
"https://graph.microsoft.com/.default"
}
}
},
disableUpdateMethods: bool.Parse(provider.GetRequiredService<IConfiguration>()["dryRun"])
)
);

Expand Down
1 change: 1 addition & 0 deletions src/Lib/JsonSourceGen/GraphJsonContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace EntraMfaPrefillinator.Lib;
)]
[JsonSerializable(typeof(User))]
[JsonSerializable(typeof(User[]))]
[JsonSerializable(typeof(GraphCollection<User>))]
[JsonSerializable(typeof(EmailAuthenticationMethod))]
[JsonSerializable(typeof(EmailAuthenticationMethod[]))]
[JsonSerializable(typeof(GraphCollection<EmailAuthenticationMethod>))]
Expand Down
Loading