Merge pull request #11 from 1iveowl/main
New deployment paradigm extended to Permissions API, Admin-API and Sign-up Administration Web App
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
name: ASDK Administration Service API - Deploy to Azure Web Services
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
env:
|
||||
AZURE_WEBAPP_NAME: 'admin-api-asdk-test-fd4k' # set this to your application's name
|
||||
AZURE_WEBAPP_PACKAGE_PATH: . # set this to the path to your web app project, defaults to the repository root
|
||||
DOTNET_VERSION: 7.x.x
|
||||
PROJECT_DIR: ./src/Saas.Admin/Saas.Admin.Service
|
||||
PROJECT_PATH: ./src/Saas.Admin/Saas.Admin.Service/Saas.Admin.Service.csproj
|
||||
OUTPUT_PATH: ./publish/admin-api
|
||||
BUILD_CONFIGURATION: Release # setting the configuration manager build configuration value for our workflow.
|
||||
|
||||
APP_NAME: admin-api
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
#azure login
|
||||
- uses: azure/login@v1
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
|
||||
# checkout the repo specifying the branch name in 'ref:'
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: main #IMPORTANT we're checking out and deploying the 'main' branch here
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Setup .NET Core SDK
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
# Run dotnet build and publish
|
||||
- name: dotnet build and publish
|
||||
run: |
|
||||
dotnet restore ${{ env.PROJECT_DIR }}
|
||||
|
||||
dotnet build ${{ env.PROJECT_PATH }} \
|
||||
--configuration ${{ env.BUILD_CONFIGURATION }}
|
||||
|
||||
dotnet publish ${{ env.PROJECT_PATH }} \
|
||||
--configuration ${{ env.BUILD_CONFIGURATION }} \
|
||||
--output '${{ env.OUTPUT_PATH }}/{{ env.AZURE_WEBAPP_NAME }}'
|
||||
|
||||
# Deploy to Azure Web apps
|
||||
- name: Run Azure webapp deploy action using publish profile credentials
|
||||
uses: azure/webapps-deploy@v2
|
||||
with:
|
||||
app-name: ${{ env.AZURE_WEBAPP_NAME }} # Replace with your app name
|
||||
package: ${{ env.OUTPUT_PATH }}/{{ env.AZURE_WEBAPP_NAME }}
|
||||
# slot-name: 'PermissionsApi-Staging'
|
||||
|
||||
# Azure logout
|
||||
- name: logout
|
||||
run: |
|
||||
az logout
|
|
@ -9,13 +9,15 @@ permissions:
|
|||
contents: read
|
||||
|
||||
env:
|
||||
AZURE_WEBAPP_NAME: 'api-permission-asdk-test-b3yf' # set this to your application's name
|
||||
AZURE_WEBAPP_NAME: 'api-permission-asdk-test-fd4k' # set this to your application's name
|
||||
AZURE_WEBAPP_PACKAGE_PATH: . # set this to the path to your web app project, defaults to the repository root
|
||||
DOTNET_VERSION: 7.x.x
|
||||
PROJECT_DIR: ./src/Saas.Identity/Saas.Permissions/Saas.Permissions.Service_v1.1
|
||||
PROJECT_PATH: ./src/Saas.Identity/Saas.Permissions/Saas.Permissions.Service_v1.1/Saas.Permissions.Service.csproj
|
||||
OUTPUT_PATH: ./publish
|
||||
OUTPUT_PATH: ./publish/api-permission
|
||||
BUILD_CONFIGURATION: Release # setting the configuration manager build configuration value for our workflow.
|
||||
|
||||
APP_NAME: permissions-api
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -30,7 +32,7 @@ jobs:
|
|||
# checkout the repo specifying the branch name in 'ref:'
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: main #IMPORTAT we're checking out and deploying the 'main' branch here
|
||||
ref: main #IMPORTANT we're checking out and deploying the 'main' branch here
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Setup .NET Core SDK
|
||||
|
@ -45,10 +47,10 @@ jobs:
|
|||
dotnet restore ${{ env.PROJECT_DIR }}
|
||||
|
||||
dotnet build ${{ env.PROJECT_PATH }} \
|
||||
--configuration Release
|
||||
--configuration ${{ env.BUILD_CONFIGURATION }}
|
||||
|
||||
dotnet publish ${{ env.PROJECT_PATH }} \
|
||||
--configuration Release \
|
||||
--configuration ${{ env.BUILD_CONFIGURATION }} \
|
||||
--output '${{ env.OUTPUT_PATH }}/{{ env.AZURE_WEBAPP_NAME }}'
|
||||
|
||||
# Deploy to Azure Web apps
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
name: ASDK Sign-up Administration Web App - Deploy to Azure Web Services
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
env:
|
||||
AZURE_WEBAPP_NAME: 'signupadmin-app-asdk-test-fd4k' # set this to your application's name
|
||||
AZURE_WEBAPP_PACKAGE_PATH: . # set this to the path to your web app project, defaults to the repository root
|
||||
DOTNET_VERSION: 7.x.x
|
||||
PROJECT_DIR: ./src/Saas.SignupAdministration/Saas.SignupAdministration.Web
|
||||
PROJECT_PATH: ./src/Saas.SignupAdministration/Saas.SignupAdministration.Web/Saas.SignupAdministration.Web.csproj
|
||||
OUTPUT_PATH: ./publish/signupadmin
|
||||
BUILD_CONFIGURATION: Release # setting the configuration manager build configuration value for our workflow.
|
||||
|
||||
APP_NAME: signupadmin-app
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
#azure login
|
||||
- uses: azure/login@v1
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
|
||||
# checkout the repo specifying the branch name in 'ref:'
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: main #IMPORTANT we're checking out and deploying the 'main' branch here
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Setup .NET Core SDK
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
# Run dotnet build and publish
|
||||
- name: dotnet build and publish
|
||||
run: |
|
||||
dotnet restore ${{ env.PROJECT_DIR }}
|
||||
|
||||
dotnet build ${{ env.PROJECT_PATH }} \
|
||||
--configuration ${{ env.BUILD_CONFIGURATION }}
|
||||
|
||||
dotnet publish ${{ env.PROJECT_PATH }} \
|
||||
--configuration ${{ env.BUILD_CONFIGURATION }} \
|
||||
--output '${{ env.OUTPUT_PATH }}/{{ env.AZURE_WEBAPP_NAME }}'
|
||||
|
||||
# Deploy to Azure Web apps
|
||||
- name: Run Azure webapp deploy action using publish profile credentials
|
||||
uses: azure/webapps-deploy@v2
|
||||
with:
|
||||
app-name: ${{ env.AZURE_WEBAPP_NAME }} # Replace with your app name
|
||||
package: ${{ env.OUTPUT_PATH }}/{{ env.AZURE_WEBAPP_NAME }}
|
||||
# slot-name: 'PermissionsApi-Staging'
|
||||
|
||||
# Azure logout
|
||||
- name: logout
|
||||
run: |
|
||||
az logout
|
|
@ -1,78 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32014.148
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "src\TestUtilities\TestUtilities.csproj", "{02F6CFF4-70ED-4C9D-AF21-4EE3AD668AA4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saas.SignupAdministration.Web", "src\Saas.SignupAdministration\Saas.SignupAdministration.Web\Saas.SignupAdministration.Web.csproj", "{71694EE0-1D10-4DC3-B4EA-30C45A9FF31F}"
|
||||
EndProject
|
||||
Project("{151D2E53-A2C4-4D7D-83FE-D05416EBD58E}") = "Saas.SignupAdministration.Web.Deployment", "src\Saas.SignupAdministration\Saas.SignupAdministration.Web.Deployment\Saas.SignupAdministration.Web.Deployment.deployproj", "{6A0836ED-483E-4F7E-8248-AC00FB52E778}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saas.Admin.Service.Tests", "src\Saas.Admin\Saas.Admin.Service.Tests\Saas.Admin.Service.Tests.csproj", "{7AAD364B-DEFB-4C66-AEBA-D0A10DBDE45B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saas.Admin.Service", "src\Saas.Admin\Saas.Admin.Service\Saas.Admin.Service.csproj", "{B75AFCC1-A92D-4122-9FB2-6C478C67132F}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7037F896-6D8A-49AD-964C-6A0FA9AE09DC}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saas.Application.Web", "src\Saas.Application\Saas.Application.Web\Saas.Application.Web.csproj", "{29BBEAD7-1043-41EC-A89D-766E1402B701}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saas.AspNetCore.Authorization", "src\Saas.Authorization\Saas.AspNetCore.Authorization\Saas.AspNetCore.Authorization.csproj", "{6B9F75FC-4739-4CA1-9347-2F4092A794B1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saas.Permissions.Service", "src\Saas.Identity\Saas.Permissions\Saas.Permissions.Service\Saas.Permissions.Service.csproj", "{E8F9B31E-E2E7-45F7-AE9B-68409630C82E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saas.AspNetCore.Authorization.Tests", "src\Saas.Authorization\Saas.AspNetCore.Authorization.Tests\Saas.AspNetCore.Authorization.Tests.csproj", "{AEDE788C-35EF-4C03-AA2F-1D1D787001FA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{02F6CFF4-70ED-4C9D-AF21-4EE3AD668AA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{02F6CFF4-70ED-4C9D-AF21-4EE3AD668AA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{02F6CFF4-70ED-4C9D-AF21-4EE3AD668AA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{02F6CFF4-70ED-4C9D-AF21-4EE3AD668AA4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{71694EE0-1D10-4DC3-B4EA-30C45A9FF31F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{71694EE0-1D10-4DC3-B4EA-30C45A9FF31F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{71694EE0-1D10-4DC3-B4EA-30C45A9FF31F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{71694EE0-1D10-4DC3-B4EA-30C45A9FF31F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6A0836ED-483E-4F7E-8248-AC00FB52E778}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6A0836ED-483E-4F7E-8248-AC00FB52E778}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6A0836ED-483E-4F7E-8248-AC00FB52E778}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6A0836ED-483E-4F7E-8248-AC00FB52E778}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7AAD364B-DEFB-4C66-AEBA-D0A10DBDE45B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7AAD364B-DEFB-4C66-AEBA-D0A10DBDE45B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7AAD364B-DEFB-4C66-AEBA-D0A10DBDE45B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7AAD364B-DEFB-4C66-AEBA-D0A10DBDE45B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B75AFCC1-A92D-4122-9FB2-6C478C67132F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B75AFCC1-A92D-4122-9FB2-6C478C67132F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B75AFCC1-A92D-4122-9FB2-6C478C67132F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B75AFCC1-A92D-4122-9FB2-6C478C67132F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{29BBEAD7-1043-41EC-A89D-766E1402B701}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{29BBEAD7-1043-41EC-A89D-766E1402B701}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{29BBEAD7-1043-41EC-A89D-766E1402B701}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{29BBEAD7-1043-41EC-A89D-766E1402B701}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6B9F75FC-4739-4CA1-9347-2F4092A794B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6B9F75FC-4739-4CA1-9347-2F4092A794B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6B9F75FC-4739-4CA1-9347-2F4092A794B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6B9F75FC-4739-4CA1-9347-2F4092A794B1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E8F9B31E-E2E7-45F7-AE9B-68409630C82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E8F9B31E-E2E7-45F7-AE9B-68409630C82E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E8F9B31E-E2E7-45F7-AE9B-68409630C82E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E8F9B31E-E2E7-45F7-AE9B-68409630C82E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AEDE788C-35EF-4C03-AA2F-1D1D787001FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AEDE788C-35EF-4C03-AA2F-1D1D787001FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AEDE788C-35EF-4C03-AA2F-1D1D787001FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AEDE788C-35EF-4C03-AA2F-1D1D787001FA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {FD825DD0-F9E6-41F6-AA7B-33A4ECC441F2}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -1,51 +0,0 @@
|
|||
version: "3.8"
|
||||
|
||||
services:
|
||||
asdk-admin:
|
||||
image: asdk-admin
|
||||
container_name: asdk-admin
|
||||
build:
|
||||
context: ./src/
|
||||
dockerfile: ./Saas.Admin/Saas.Admin.Service/Dockerfile
|
||||
expose:
|
||||
- "80"
|
||||
ports:
|
||||
- "8080:80"
|
||||
asdk-web:
|
||||
image: asdk-web
|
||||
container_name: asdk-web
|
||||
build:
|
||||
context: ./src/
|
||||
dockerfile: ./Saas.Application/Saas.Application.Web/Dockerfile
|
||||
expose:
|
||||
- "80"
|
||||
ports:
|
||||
- "8081:80"
|
||||
asdk-signup:
|
||||
image: asdk-signup
|
||||
container_name: asdk-signup
|
||||
build:
|
||||
context: ./src/
|
||||
dockerfile: ./Saas.SignupAdministration/Saas.SignupAdministration.Web/Dockerfile
|
||||
expose:
|
||||
- "80"
|
||||
ports:
|
||||
- "8082:80"
|
||||
|
||||
asdk-permissions:
|
||||
image: asdk-permissions
|
||||
container_name: asdk-permissions
|
||||
build:
|
||||
context: ./src/
|
||||
dockerfile: ./Saas.Identity/Saas.Permissions/Saas.Permissions.Service/Dockerfile
|
||||
expose:
|
||||
- "80"
|
||||
ports:
|
||||
- "8083:80"
|
||||
asdk-identity-setup:
|
||||
image: asdk-identity-setup
|
||||
container_name: asdk-identity-setup
|
||||
build:
|
||||
context: ./src/Saas.Identity
|
||||
dockerfile: ./Saas.IdentityProvider/scripts/Dockerfile
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
#Requires -Version 3.0
|
||||
|
||||
Param(
|
||||
[string] [Parameter(Mandatory=$true)] $ResourceGroupLocation,
|
||||
[string] $ResourceGroupName = 'Saas.Admin',
|
||||
[switch] $UploadArtifacts,
|
||||
[string] $StorageAccountName,
|
||||
[string] $StorageContainerName = $ResourceGroupName.ToLowerInvariant() + '-stageartifacts',
|
||||
[string] $TemplateFile = 'azuredeploy.json',
|
||||
[string] $TemplateParametersFile = 'azuredeploy.parameters.json',
|
||||
[string] $ArtifactStagingDirectory = '.',
|
||||
[string] $DSCSourceFolder = 'DSC',
|
||||
[switch] $ValidateOnly
|
||||
)
|
||||
|
||||
try {
|
||||
[Microsoft.Azure.Common.Authentication.AzureSession]::ClientFactory.AddUserAgent("VSAzureTools-$UI$($host.name)".replace(' ','_'), '3.0.0')
|
||||
} catch { }
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Set-StrictMode -Version 3
|
||||
|
||||
function Format-ValidationOutput {
|
||||
param ($ValidationOutput, [int] $Depth = 0)
|
||||
Set-StrictMode -Off
|
||||
return @($ValidationOutput | Where-Object { $_ -ne $null } | ForEach-Object { @(' ' * $Depth + ': ' + $_.Message) + @(Format-ValidationOutput @($_.Details) ($Depth + 1)) })
|
||||
}
|
||||
|
||||
$OptionalParameters = New-Object -TypeName Hashtable
|
||||
$TemplateFile = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, $TemplateFile))
|
||||
$TemplateParametersFile = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, $TemplateParametersFile))
|
||||
|
||||
if ($UploadArtifacts) {
|
||||
# Convert relative paths to absolute paths if needed
|
||||
$ArtifactStagingDirectory = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, $ArtifactStagingDirectory))
|
||||
$DSCSourceFolder = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, $DSCSourceFolder))
|
||||
|
||||
# Parse the parameter file and update the values of artifacts location and artifacts location SAS token if they are present
|
||||
$JsonParameters = Get-Content $TemplateParametersFile -Raw | ConvertFrom-Json
|
||||
if (($JsonParameters | Get-Member -Type NoteProperty 'parameters') -ne $null) {
|
||||
$JsonParameters = $JsonParameters.parameters
|
||||
}
|
||||
$ArtifactsLocationName = '_artifactsLocation'
|
||||
$ArtifactsLocationSasTokenName = '_artifactsLocationSasToken'
|
||||
$OptionalParameters[$ArtifactsLocationName] = $JsonParameters | Select -Expand $ArtifactsLocationName -ErrorAction Ignore | Select -Expand 'value' -ErrorAction Ignore
|
||||
$OptionalParameters[$ArtifactsLocationSasTokenName] = $JsonParameters | Select -Expand $ArtifactsLocationSasTokenName -ErrorAction Ignore | Select -Expand 'value' -ErrorAction Ignore
|
||||
|
||||
# Create DSC configuration archive
|
||||
if (Test-Path $DSCSourceFolder) {
|
||||
$DSCSourceFilePaths = @(Get-ChildItem $DSCSourceFolder -File -Filter '*.ps1' | ForEach-Object -Process {$_.FullName})
|
||||
foreach ($DSCSourceFilePath in $DSCSourceFilePaths) {
|
||||
$DSCArchiveFilePath = $DSCSourceFilePath.Substring(0, $DSCSourceFilePath.Length - 4) + '.zip'
|
||||
Publish-AzureRmVMDscConfiguration $DSCSourceFilePath -OutputArchivePath $DSCArchiveFilePath -Force -Verbose
|
||||
}
|
||||
}
|
||||
|
||||
# Create a storage account name if none was provided
|
||||
if ($StorageAccountName -eq '') {
|
||||
$StorageAccountName = 'stage' + ((Get-AzureRmContext).Subscription.SubscriptionId).Replace('-', '').substring(0, 19)
|
||||
}
|
||||
|
||||
$StorageAccount = (Get-AzureRmStorageAccount | Where-Object{$_.StorageAccountName -eq $StorageAccountName})
|
||||
|
||||
# Create the storage account if it doesn't already exist
|
||||
if ($StorageAccount -eq $null) {
|
||||
$StorageResourceGroupName = 'ARM_Deploy_Staging'
|
||||
New-AzureRmResourceGroup -Location "$ResourceGroupLocation" -Name $StorageResourceGroupName -Force
|
||||
$StorageAccount = New-AzureRmStorageAccount -StorageAccountName $StorageAccountName -Type 'Standard_LRS' -ResourceGroupName $StorageResourceGroupName -Location "$ResourceGroupLocation"
|
||||
}
|
||||
|
||||
# Generate the value for artifacts location if it is not provided in the parameter file
|
||||
if ($OptionalParameters[$ArtifactsLocationName] -eq $null) {
|
||||
$OptionalParameters[$ArtifactsLocationName] = $StorageAccount.Context.BlobEndPoint + $StorageContainerName + '/'
|
||||
}
|
||||
|
||||
# Copy files from the local storage staging location to the storage account container
|
||||
New-AzureStorageContainer -Name $StorageContainerName -Context $StorageAccount.Context -ErrorAction SilentlyContinue *>&1
|
||||
|
||||
$ArtifactFilePaths = Get-ChildItem $ArtifactStagingDirectory -Recurse -File | ForEach-Object -Process {$_.FullName}
|
||||
foreach ($SourcePath in $ArtifactFilePaths) {
|
||||
Set-AzureStorageBlobContent -File $SourcePath -Blob $SourcePath.Substring($ArtifactStagingDirectory.length + 1) `
|
||||
-Container $StorageContainerName -Context $StorageAccount.Context -Force
|
||||
}
|
||||
|
||||
# Generate a 4 hour SAS token for the artifacts location if one was not provided in the parameters file
|
||||
if ($OptionalParameters[$ArtifactsLocationSasTokenName] -eq $null) {
|
||||
$OptionalParameters[$ArtifactsLocationSasTokenName] = ConvertTo-SecureString -AsPlainText -Force `
|
||||
(New-AzureStorageContainerSASToken -Container $StorageContainerName -Context $StorageAccount.Context -Permission r -ExpiryTime (Get-Date).AddHours(4))
|
||||
}
|
||||
}
|
||||
|
||||
# Create the resource group only when it doesn't already exist
|
||||
if ((Get-AzureRmResourceGroup -Name $ResourceGroupName -Location $ResourceGroupLocation -Verbose -ErrorAction SilentlyContinue) -eq $null) {
|
||||
New-AzureRmResourceGroup -Name $ResourceGroupName -Location $ResourceGroupLocation -Verbose -Force -ErrorAction Stop
|
||||
}
|
||||
|
||||
if ($ValidateOnly) {
|
||||
$ErrorMessages = Format-ValidationOutput (Test-AzureRmResourceGroupDeployment -ResourceGroupName $ResourceGroupName `
|
||||
-TemplateFile $TemplateFile `
|
||||
-TemplateParameterFile $TemplateParametersFile `
|
||||
@OptionalParameters)
|
||||
if ($ErrorMessages) {
|
||||
Write-Output '', 'Validation returned the following errors:', @($ErrorMessages), '', 'Template is invalid.'
|
||||
}
|
||||
else {
|
||||
Write-Output '', 'Template is valid.'
|
||||
}
|
||||
}
|
||||
else {
|
||||
New-AzureRmResourceGroupDeployment -Name ((Get-ChildItem $TemplateFile).BaseName + '-' + ((Get-Date).ToUniversalTime()).ToString('MMdd-HHmm')) `
|
||||
-ResourceGroupName $ResourceGroupName `
|
||||
-TemplateFile $TemplateFile `
|
||||
-TemplateParameterFile $TemplateParametersFile `
|
||||
@OptionalParameters `
|
||||
-Force -Verbose `
|
||||
-ErrorVariable ErrorMessages
|
||||
if ($ErrorMessages) {
|
||||
Write-Output '', 'Template deployment returned the following errors:', @(@($ErrorMessages) | ForEach-Object { $_.Exception.Message.TrimEnd("`r`n") })
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<SkipCopyBuildProduct>true</SkipCopyBuildProduct>
|
||||
<AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>
|
||||
<TargetRuntime>None</TargetRuntime>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">obj\</BaseIntermediateOutputPath>
|
||||
<BaseIntermediateOutputPath Condition=" !HasTrailingSlash('$(BaseIntermediateOutputPath)') ">$(BaseIntermediateOutputPath)\</BaseIntermediateOutputPath>
|
||||
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\</IntermediateOutputPath>
|
||||
<ProjectReferencesOutputPath Condition=" '$(ProjectReferencesOutputPath)' == '' ">$(IntermediateOutputPath)ProjectReferences</ProjectReferencesOutputPath>
|
||||
<ProjectReferencesOutputPath Condition=" !HasTrailingSlash('$(ProjectReferencesOutputPath)') ">$(ProjectReferencesOutputPath)\</ProjectReferencesOutputPath>
|
||||
<StageArtifacts Condition=" '$(StageArtifacts)' == '' ">true</StageArtifacts>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<DefineCommonItemSchemas>false</DefineCommonItemSchemas>
|
||||
<DefineCommonCapabilities>false</DefineCommonCapabilities>
|
||||
</PropertyGroup>
|
||||
|
||||
<ProjectExtensions>
|
||||
<ProjectCapabilities>
|
||||
<DeploymentProject />
|
||||
</ProjectCapabilities>
|
||||
</ProjectExtensions>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<Content>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None>
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</None>
|
||||
<ProjectReference>
|
||||
<Private>false</Private>
|
||||
<Targets>Build</Targets>
|
||||
</ProjectReference>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<Target Name="CreateManifestResourceNames" />
|
||||
|
||||
<PropertyGroup>
|
||||
<StageArtifactsDependsOn>
|
||||
_GetDeploymentProjectContent;
|
||||
_CalculateContentOutputRelativePaths;
|
||||
_GetReferencedProjectsOutput;
|
||||
_CalculateArtifactStagingDirectory;
|
||||
_CopyOutputToArtifactStagingDirectory;
|
||||
</StageArtifactsDependsOn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="_CopyOutputToArtifactStagingDirectory">
|
||||
<Copy SourceFiles="@(DeploymentProjectContentOutput)" DestinationFiles="$(ArtifactStagingDirectory)\$(MSBuildProjectName)%(RelativePath)" />
|
||||
<Copy SourceFiles="@(BuildProjectReferencesOutput)" DestinationFiles="$(ArtifactStagingDirectory)\$(MSBuildProjectName)\%(ProjectName)\%(RecursiveDir)%(FileName)%(Extension)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="_GetDeploymentProjectContent">
|
||||
<MSBuild Projects="$(MSBuildProjectFile)" Targets="ContentFilesProjectOutputGroup">
|
||||
<Output TaskParameter="TargetOutputs" ItemName="DeploymentProjectContentOutput" />
|
||||
</MSBuild>
|
||||
</Target>
|
||||
|
||||
<Target Name="_GetReferencedProjectsOutput">
|
||||
<PropertyGroup>
|
||||
<MsBuildProperties>Configuration=$(Configuration);Platform=$(Platform)</MsBuildProperties>
|
||||
</PropertyGroup>
|
||||
|
||||
<MSBuild Projects="@(ProjectReference)"
|
||||
BuildInParallel="$(BuildInParallel)"
|
||||
Properties="$(MsBuildProperties)"
|
||||
Targets="%(ProjectReference.Targets)" />
|
||||
|
||||
<ItemGroup>
|
||||
<BuildProjectReferencesOutput Include="%(ProjectReference.IncludeFilePath)">
|
||||
<ProjectName>$([System.IO.Path]::GetFileNameWithoutExtension('%(ProjectReference.Identity)'))</ProjectName>
|
||||
</BuildProjectReferencesOutput>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="_CalculateArtifactStagingDirectory" Condition=" '$(ArtifactStagingDirectory)'=='' ">
|
||||
<PropertyGroup>
|
||||
<ArtifactStagingDirectory Condition=" '$(OutDir)'!='' ">$(OutDir)</ArtifactStagingDirectory>
|
||||
<ArtifactStagingDirectory Condition=" '$(ArtifactStagingDirectory)'=='' ">$(OutputPath)</ArtifactStagingDirectory>
|
||||
<ArtifactStagingDirectory Condition=" !HasTrailingSlash('$(ArtifactStagingDirectory)') ">$(ArtifactStagingDirectory)\</ArtifactStagingDirectory>
|
||||
<ArtifactStagingDirectory>$(ArtifactStagingDirectory)staging\</ArtifactStagingDirectory>
|
||||
<ArtifactStagingDirectory Condition=" '$(Build_StagingDirectory)'!='' AND '$(TF_Build)'=='True' ">$(Build_StagingDirectory)</ArtifactStagingDirectory>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
<!-- Appends each of the deployment project's content output files with metadata indicating its relative path from the deployment project's folder. -->
|
||||
<Target Name="_CalculateContentOutputRelativePaths"
|
||||
Outputs="%(DeploymentProjectContentOutput.Identity)">
|
||||
<PropertyGroup>
|
||||
<_OriginalIdentity>%(DeploymentProjectContentOutput.Identity)</_OriginalIdentity>
|
||||
<_RelativePath>$(_OriginalIdentity.Replace('$(MSBuildProjectDirectory)', ''))</_RelativePath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<DeploymentProjectContentOutput>
|
||||
<RelativePath>$(_RelativePath)</RelativePath>
|
||||
</DeploymentProjectContentOutput>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="CoreCompile" />
|
||||
|
||||
<PropertyGroup>
|
||||
<StageArtifactsAfterTargets Condition=" '$(StageArtifacts)' == 'true' ">
|
||||
PrepareForRun
|
||||
</StageArtifactsAfterTargets>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="StageArtifacts" DependsOnTargets="$(StageArtifactsDependsOn)" AfterTargets="$(StageArtifactsAfterTargets)"/>
|
||||
|
||||
<!-- Custom target to clean up local deployment staging files -->
|
||||
<Target Name="DeleteBinObjFolders" BeforeTargets="Clean">
|
||||
<RemoveDir Directories="$(OutputPath)" />
|
||||
<RemoveDir Directories="$(BaseIntermediateOutputPath)" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -1,34 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|AnyCPU">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>AnyCPU</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|AnyCPU">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>AnyCPU</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>d52c84e3-a85c-4bde-9550-35e6c075381c</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PrepareForBuildDependsOn>
|
||||
</PrepareForBuildDependsOn>
|
||||
</PropertyGroup>
|
||||
<Import Condition=" Exists('Deployment.targets') " Project="Deployment.targets" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" />
|
||||
<!-- vertag<:>start tokens<:>maj.min -->
|
||||
<Import Condition=" Exists('$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Deployment\1.1\DeploymentProject.targets') " Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Deployment\1.1\DeploymentProject.targets" />
|
||||
<!-- vertag<:>end -->
|
||||
<ItemGroup>
|
||||
<Content Include="azuredeploy.json" />
|
||||
<Content Include="azuredeploy.parameters.json" />
|
||||
<None Include="Deployment.targets">
|
||||
<Visible>False</Visible>
|
||||
</None>
|
||||
<Content Include="Deploy-AzureResourceGroup.ps1" />
|
||||
</ItemGroup>
|
||||
<Target Name="GetReferenceAssemblyPaths" />
|
||||
</Project>
|
|
@ -1,161 +0,0 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"SaasProviderName": {
|
||||
"type": "string",
|
||||
"defaultValue": "contoso"
|
||||
},
|
||||
"SaasEnvironment": {
|
||||
"type": "string",
|
||||
"defaultValue": "dev",
|
||||
"allowedValues": [
|
||||
"dev",
|
||||
"staging",
|
||||
"test",
|
||||
"prod"
|
||||
]
|
||||
},
|
||||
"SaasLocation": {
|
||||
"type": "string",
|
||||
"defaultValue": "[resourceGroup().location]",
|
||||
"metadata": {
|
||||
"description": "Location for the Cosmos DB account."
|
||||
}
|
||||
},
|
||||
"SaasInstanceNumber": {
|
||||
"type": "string",
|
||||
"defaultValue": "001"
|
||||
},
|
||||
"CosmosDbEndpoint": {
|
||||
"type": "string"
|
||||
},
|
||||
"CosmosDbAccountKey": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The account key output of CosmosDB"
|
||||
}
|
||||
},
|
||||
"CosmosDbAccountName": {
|
||||
"type": "string",
|
||||
"defaultValue": "[concat('cosmos-', parameters('SaasProviderName'), '-', parameters('SaasEnvironment'), '-', parameters('SaasInstanceNumber'))]",
|
||||
"metadata": {
|
||||
"description": "Cosmos DB account name"
|
||||
}
|
||||
},
|
||||
"CosmosDbDatabaseName": {
|
||||
"type": "string",
|
||||
"defaultValue": "azuresaas",
|
||||
"metadata": {
|
||||
"description": "The name for the Core (SQL) database"
|
||||
}
|
||||
},
|
||||
"CosmosDbConnectionString": {
|
||||
"type": "string"
|
||||
},
|
||||
"IdentityDbConnectionString": {
|
||||
"type": "string"
|
||||
},
|
||||
"CatalogDbConnectionString": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"appServicePlanName": "[concat('app-', parameters('SaasProviderName'), '-admin-', parameters('SaasEnvironment'), '-', parameters('SaasInstanceNumber'))]",
|
||||
"adminWebAppName": "[concat('app-', parameters('SaasProviderName'), '-admin-', parameters('SaasEnvironment'), '-', parameters('SaasInstanceNumber'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"location": "[resourceGroup().location]",
|
||||
"apiVersion": "2015-08-01",
|
||||
"sku": {
|
||||
"name": "F1"
|
||||
},
|
||||
"dependsOn": [],
|
||||
"tags": {
|
||||
"displayName": "SaaS Provider App Service Plan"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"numberOfWorkers": 1
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "[variables('adminWebAppName')]",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"location": "[resourceGroup().location]",
|
||||
"apiVersion": "2015-08-01",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Web/serverFarms/', variables('appServicePlanName'))]"
|
||||
],
|
||||
"tags": {
|
||||
"displayName": "SaaS Administration Web App"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[variables('adminWebAppName')]",
|
||||
"serverFarmId": "[resourceId(resourceGroup().name, 'Microsoft.Web/serverFarms', variables('appServicePlanName'))]",
|
||||
"siteConfig": {
|
||||
"netFrameworkVersion": "v6.0",
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "ASPNETCORE_ENVIRONMENT",
|
||||
"value": "Production"
|
||||
},
|
||||
{
|
||||
"name": "AppSettings:CosmosDb:Account",
|
||||
"value": "[parameters('CosmosDbEndpoint')]"
|
||||
},
|
||||
{
|
||||
"name": "AppSettings:CosmosDb:Key",
|
||||
"value": "[parameters('CosmosDbAccountKey')]"
|
||||
},
|
||||
{
|
||||
"name": "AppSettings:CosmosDb:DatabaseName",
|
||||
"value": "[parameters('CosmosDbDatabaseName')]"
|
||||
},
|
||||
{
|
||||
"name": "AppSettings:CosmosDb:ContainerName",
|
||||
"value": "OnboardingFlow"
|
||||
}
|
||||
],
|
||||
"connectionStrings": [
|
||||
{
|
||||
"name": "CosmosDb",
|
||||
"connectionString": "[parameters('CosmosDbConnectionString')]",
|
||||
"type": "Custom"
|
||||
},
|
||||
{
|
||||
"name": "IdentityDbConnection",
|
||||
"connectionString": "[parameters('IdentityDbConnectionString')]",
|
||||
"type": "SQLAzure"
|
||||
},
|
||||
{
|
||||
"name": "CatalogDbConnection",
|
||||
"connectionString": "[parameters('CatalogDbConnectionString')]",
|
||||
"type": "SQLAzure"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "MSDeploy",
|
||||
"type": "extensions",
|
||||
"location": "[resourceGroup().location]",
|
||||
"apiVersion": "2015-08-01",
|
||||
"dependsOn": [ "[resourceId('Microsoft.Web/sites', variables('adminWebAppName'))]" ],
|
||||
"tags": { "displayName": "Deploy" },
|
||||
"properties": {
|
||||
"packageUri": "https://stsaasdev001.blob.core.windows.net/artifacts/saas-admin/Saas.Admin.Service.zip?sv=2020-04-08&st=2021-06-07T19%3A23%3A20Z&se=2022-06-08T19%3A23%3A00Z&sr=c&sp=rl&sig=kNf0qwTfaCJg02xYeUHlfmHOJvI1bGU1HftjUJ5hl5o%3D"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
}
|
||||
}
|
|
@ -1,15 +1,12 @@
|
|||
namespace Saas.Admin.Service.Tests
|
||||
namespace Saas.Admin.Service.Tests;
|
||||
|
||||
public class NewTenantRequestTests
|
||||
{
|
||||
using Xunit;
|
||||
|
||||
public class NewTenantRequestTests
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public void All_Values_Are_Copied_To_Tenant(NewTenantRequest tenantRequest)
|
||||
{
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public void All_Values_Are_Copied_To_Tenant(NewTenantRequest tenantRequest)
|
||||
{
|
||||
Tenant tenant = tenantRequest.ToTenant();
|
||||
Tenant tenant = tenantRequest.ToTenant();
|
||||
|
||||
AssertAdditions.AllPropertiesAreEqual(tenant, tenantRequest, nameof(tenant.ConcurrencyToken), nameof(tenant.CreatedTime), nameof(tenant.Id));
|
||||
}
|
||||
AssertAdditions.AllPropertiesAreEqual(tenant, tenantRequest, nameof(tenant.ConcurrencyToken), nameof(tenant.CreatedTime), nameof(tenant.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.5" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<PackageReference Include="coverlet.collector" Version="3.2.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
|
|
@ -1,109 +1,98 @@
|
|||
namespace Saas.Admin.Service.Tests
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoFixture.Xunit2;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Saas.Admin.Service.Exceptions;
|
||||
using Saas.Admin.Service.Services;
|
||||
|
||||
namespace Saas.Admin.Service.Tests;
|
||||
|
||||
public class TenantServiceTests
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using AutoFixture.Xunit2;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using Saas.Admin.Service.Data;
|
||||
using Saas.Admin.Service.Exceptions;
|
||||
using Saas.Admin.Service.Services;
|
||||
|
||||
using TestUtilities;
|
||||
|
||||
using Xunit;
|
||||
|
||||
public class TenantServiceTests
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task Will_Not_Return_Null_When_No_Tenants(TenantService tenantService)
|
||||
{
|
||||
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task Will_Not_Return_Null_When_No_Tenants(TenantService tenantService)
|
||||
{
|
||||
IEnumerable<TenantDTO>? result = await tenantService.GetAllTenantsAsync();
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result.ToList());
|
||||
}
|
||||
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task Will_throw_if_tenenent_Not_Found([Frozen] TenantsContext tenantsContext, TenantService tenantService, Guid tenantId)
|
||||
{
|
||||
Assert.Null(await tenantsContext.Tenants.FindAsync(tenantId));
|
||||
|
||||
await Assert.ThrowsAsync<ItemNotFoundExcepton>(() => tenantService.GetTenantAsync(tenantId));
|
||||
}
|
||||
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task Will_throw_if_tenenent_Not_Found2([Frozen] TenantsContext tenantsContext, TenantService tenantService, Tenant[] tenants)
|
||||
{
|
||||
await tenantsContext.Tenants.AddRangeAsync(tenants);
|
||||
|
||||
Guid id = tenants[^1].Id;
|
||||
TenantDTO? tenant = await tenantService.GetTenantAsync(id);
|
||||
|
||||
AssertAdditions.AllPropertiesAreEqual(tenant, tenants[^1], nameof(tenant.CreatedTime), nameof(tenant.Version));
|
||||
await Assert.ThrowsAsync<ItemNotFoundExcepton>(() => tenantService.GetTenantAsync(Guid.NewGuid()));
|
||||
}
|
||||
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task can_find_tenent([Frozen] TenantsContext tenantsContext, TenantService tenantService, Tenant[] tenants)
|
||||
{
|
||||
await tenantsContext.Tenants.AddRangeAsync(tenants);
|
||||
|
||||
Guid id = tenants[^1].Id;
|
||||
TenantDTO? tenant = await tenantService.GetTenantAsync(id);
|
||||
|
||||
AssertAdditions.AllPropertiesAreEqual(tenant, tenants[^1], nameof(tenant.CreatedTime), nameof(tenant.Version));
|
||||
}
|
||||
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task Can_update_tenant([Frozen] TenantsContext tenantsContext, TenantService tenantService, Tenant[] originalTenants, TenantDTO updatedDto)
|
||||
{
|
||||
await tenantsContext.Tenants.AddRangeAsync(originalTenants);
|
||||
await tenantsContext.SaveChangesAsync();
|
||||
|
||||
Tenant tenant = await tenantsContext.Tenants.FirstAsync();
|
||||
DateTime originalCreated = tenant.CreatedTime!.Value;
|
||||
|
||||
updatedDto.Id = tenant.Id;
|
||||
updatedDto.Version = null;
|
||||
|
||||
TenantDTO? updatedTenant = await tenantService.UpdateTenantAsync(updatedDto);
|
||||
|
||||
AssertAdditions.AllPropertiesAreEqual(updatedDto, updatedTenant, nameof(updatedTenant.Version), nameof(updatedTenant.CreatedTime));
|
||||
Assert.Equal(originalCreated, updatedTenant.CreatedTime);
|
||||
}
|
||||
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task Can_delete_tenant([Frozen] TenantsContext tenantsContext, TenantService tenantService, Tenant[] originalTenants)
|
||||
{
|
||||
await tenantsContext.Tenants.AddRangeAsync(originalTenants);
|
||||
await tenantsContext.SaveChangesAsync();
|
||||
|
||||
Guid idToDelete = originalTenants[0].Id;
|
||||
|
||||
Assert.NotNull(await tenantService.GetTenantAsync(idToDelete));
|
||||
|
||||
await tenantService.DeleteTenantAsync(idToDelete);
|
||||
|
||||
await Assert.ThrowsAsync<ItemNotFoundExcepton>(() => tenantService.GetTenantAsync(idToDelete));
|
||||
}
|
||||
|
||||
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task Check_For_Existing_Route([Frozen] TenantsContext tenantsContext, TenantService tenantService, Tenant[] originalTenants, string notInDBRoute)
|
||||
{
|
||||
await tenantsContext.Tenants.AddRangeAsync(originalTenants);
|
||||
await tenantsContext.SaveChangesAsync();
|
||||
|
||||
bool exists = await tenantService.CheckPathExists(originalTenants[0].Route);
|
||||
Assert.True(exists);
|
||||
|
||||
bool notExists = await tenantService.CheckPathExists(notInDBRoute);
|
||||
Assert.False(notExists);
|
||||
}
|
||||
IEnumerable<TenantDTO>? result = await tenantService.GetAllTenantsAsync();
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result.ToList());
|
||||
}
|
||||
}
|
||||
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task Will_throw_if_tenenent_Not_Found([Frozen] TenantsContext tenantsContext, TenantService tenantService, Guid tenantId)
|
||||
{
|
||||
Assert.Null(await tenantsContext.Tenants.FindAsync(tenantId));
|
||||
|
||||
await Assert.ThrowsAsync<ItemNotFoundExcepton>(() => tenantService.GetTenantAsync(tenantId));
|
||||
}
|
||||
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task Will_throw_if_tenenent_Not_Found2([Frozen] TenantsContext tenantsContext, TenantService tenantService, Tenant[] tenants)
|
||||
{
|
||||
await tenantsContext.Tenants.AddRangeAsync(tenants);
|
||||
|
||||
Guid id = tenants[^1].Id;
|
||||
TenantDTO? tenant = await tenantService.GetTenantAsync(id);
|
||||
|
||||
AssertAdditions.AllPropertiesAreEqual(tenant, tenants[^1], nameof(tenant.CreatedTime), nameof(tenant.Version));
|
||||
await Assert.ThrowsAsync<ItemNotFoundExcepton>(() => tenantService.GetTenantAsync(Guid.NewGuid()));
|
||||
}
|
||||
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task can_find_tenent([Frozen] TenantsContext tenantsContext, TenantService tenantService, Tenant[] tenants)
|
||||
{
|
||||
await tenantsContext.Tenants.AddRangeAsync(tenants);
|
||||
|
||||
Guid id = tenants[^1].Id;
|
||||
TenantDTO? tenant = await tenantService.GetTenantAsync(id);
|
||||
|
||||
AssertAdditions.AllPropertiesAreEqual(tenant, tenants[^1], nameof(tenant.CreatedTime), nameof(tenant.Version));
|
||||
}
|
||||
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task Can_update_tenant([Frozen] TenantsContext tenantsContext, TenantService tenantService, Tenant[] originalTenants, TenantDTO updatedDto)
|
||||
{
|
||||
await tenantsContext.Tenants.AddRangeAsync(originalTenants);
|
||||
await tenantsContext.SaveChangesAsync();
|
||||
|
||||
Tenant tenant = await tenantsContext.Tenants.FirstAsync();
|
||||
DateTime originalCreated = tenant.CreatedTime!.Value;
|
||||
|
||||
updatedDto.Id = tenant.Id;
|
||||
updatedDto.Version = null;
|
||||
|
||||
TenantDTO? updatedTenant = await tenantService.UpdateTenantAsync(updatedDto);
|
||||
|
||||
AssertAdditions.AllPropertiesAreEqual(updatedDto, updatedTenant, nameof(updatedTenant.Version), nameof(updatedTenant.CreatedTime));
|
||||
Assert.Equal(originalCreated, updatedTenant.CreatedTime);
|
||||
}
|
||||
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task Can_delete_tenant([Frozen] TenantsContext tenantsContext, TenantService tenantService, Tenant[] originalTenants)
|
||||
{
|
||||
await tenantsContext.Tenants.AddRangeAsync(originalTenants);
|
||||
await tenantsContext.SaveChangesAsync();
|
||||
|
||||
Guid idToDelete = originalTenants[0].Id;
|
||||
|
||||
Assert.NotNull(await tenantService.GetTenantAsync(idToDelete));
|
||||
|
||||
await tenantService.DeleteTenantAsync(idToDelete);
|
||||
|
||||
await Assert.ThrowsAsync<ItemNotFoundExcepton>(() => tenantService.GetTenantAsync(idToDelete));
|
||||
}
|
||||
|
||||
|
||||
[Theory, AutoDataNSubstitute]
|
||||
public async Task Check_For_Existing_Route([Frozen] TenantsContext tenantsContext, TenantService tenantService, Tenant[] originalTenants, string notInDBRoute)
|
||||
{
|
||||
await tenantsContext.Tenants.AddRangeAsync(originalTenants);
|
||||
await tenantsContext.SaveChangesAsync();
|
||||
|
||||
bool exists = await tenantService.CheckPathExists(originalTenants[0].Route);
|
||||
Assert.True(exists);
|
||||
|
||||
bool notExists = await tenantService.CheckPathExists(notInDBRoute);
|
||||
Assert.False(notExists);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,9 @@ public class TenantDTO
|
|||
ProductTierId = tenant.ProductTierId;
|
||||
CategoryId = tenant.CategoryId;
|
||||
|
||||
Version = tenant.ConcurrencyToken != null ? Convert.ToBase64String(tenant.ConcurrencyToken) : null;
|
||||
Version = tenant.ConcurrencyToken is not null
|
||||
? Convert.ToBase64String(tenant.ConcurrencyToken)
|
||||
: null;
|
||||
}
|
||||
|
||||
public Tenant ToTenant()
|
||||
|
|
|
@ -1,37 +1,78 @@
|
|||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
using Azure.Identity;
|
||||
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Configuration.AzureAppConfiguration;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Saas.Admin.Service;
|
||||
using Saas.Admin.Service.Data;
|
||||
using Saas.Admin.Service.Utilities;
|
||||
using Saas.AspNetCore.Authorization.AuthHandlers;
|
||||
using Saas.AspNetCore.Authorization.ClaimTransformers;
|
||||
using Saas.Shared.Options;
|
||||
using Saas.Swagger;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Services.AddApplicationInsightsTelemetry();
|
||||
|
||||
X509Certificate2 permissionsApiCertificate;
|
||||
string projectName = Assembly.GetCallingAssembly().GetName().Name
|
||||
?? throw new NullReferenceException("Project name cannot be null.");
|
||||
|
||||
if (builder.Environment.IsProduction())
|
||||
var logger = LoggerFactory.Create(config => config.AddConsole()).CreateLogger(projectName);
|
||||
|
||||
logger.LogInformation("001");
|
||||
|
||||
/* IMPORTANT
|
||||
In the configuration pattern used here, we're seeking to minimize the use of appsettings.json,
|
||||
as well as eliminate the need for storing local secrets.
|
||||
|
||||
Instead we're utilizing the Azure App Configuration service for storing settings and the Azure Key Vault to store secrets.
|
||||
Azure App Configuration still hold references to the secret, but not the secret themselves.
|
||||
|
||||
This approach is more secure and allows us to have a single source of truth
|
||||
for all settings and secrets.
|
||||
|
||||
The settings and secrets are provisioned by the deployment script made available for deploying this service.
|
||||
Please see the readme for the project for details.
|
||||
|
||||
For local development, please see the ASDK Permission Service readme.md for more
|
||||
on how to set up and run this service in a local development environment - i.e., a local dev machine.
|
||||
*/
|
||||
|
||||
if (builder.Environment.IsDevelopment())
|
||||
{
|
||||
// Get Secrets From Azure Key Vault if in production. If not in production, secrets are automatically loaded in from the .NET secrets manager
|
||||
// https://docs.microsoft.com/en-us/aspnet/core/security/key-vault-configuration?view=aspnetcore-6.0
|
||||
builder.Configuration.AddAzureKeyVault(
|
||||
new Uri(builder.Configuration["KeyVault:Url"]),
|
||||
new DefaultAzureCredential(),
|
||||
new CustomPrefixKeyVaultSecretManager("admin"));
|
||||
|
||||
InitializeDevEnvironment();
|
||||
}
|
||||
else
|
||||
{
|
||||
InitializeProdEnvironment();
|
||||
}
|
||||
|
||||
builder.Services.Configure<AzureB2CAdminApiOptions>(
|
||||
builder.Configuration.GetRequiredSection(AzureB2CAdminApiOptions.SectionName));
|
||||
|
||||
builder.Services.Configure<ClaimToRoleTransformerOptions>(
|
||||
builder.Configuration.GetRequiredSection(ClaimToRoleTransformerOptions.SectionName));
|
||||
|
||||
builder.Services.Configure<AzureB2CPermissionsApiOptions>(
|
||||
builder.Configuration.GetRequiredSection(AzureB2CPermissionsApiOptions.SectionName));
|
||||
|
||||
builder.Services.Configure<PermissionsApiOptions>(
|
||||
builder.Configuration.GetRequiredSection(PermissionsApiOptions.SectionName));
|
||||
|
||||
builder.Services.Configure<SqlOptions>(
|
||||
builder.Configuration.GetRequiredSection(SqlOptions.SectionName));
|
||||
|
||||
// Using Entity Framework for accessing permission data stored in the Permissions Db.
|
||||
builder.Services.AddDbContext<TenantsContext>(options =>
|
||||
options.UseSqlServer(builder.Configuration.GetConnectionString("TenantsContext")));
|
||||
{
|
||||
var sqlConnectionString = builder.Configuration.GetRequiredSection(SqlOptions.SectionName)
|
||||
.Get<SqlOptions>()?.SQLConnectionString
|
||||
?? throw new NullReferenceException("SQL Connection string cannot be null.");
|
||||
});
|
||||
|
||||
// Add authentication for incoming requests
|
||||
builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration, "AzureAdB2C");
|
||||
builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration, AzureB2CAdminApiOptions.SectionName);
|
||||
|
||||
builder.Services.AddTransient<IClaimsTransformation, ClaimToRoleTransformer>();
|
||||
|
||||
|
||||
builder.Services.AddClaimToRoleTransformer(builder.Configuration, "ClaimToRoleTransformer");
|
||||
builder.Services.AddRouteBasedRoleHandler("tenantId");
|
||||
builder.Services.AddRouteBasedRoleHandler("userId");
|
||||
|
||||
|
@ -39,7 +80,6 @@ builder.Services.AddRouteBasedRoleHandler("userId");
|
|||
|
||||
builder.Services.AddAuthorization(options =>
|
||||
{
|
||||
|
||||
options.AddPolicy(AppConstants.Policies.Authenticated, policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireAuthenticatedUser();
|
||||
|
@ -93,26 +133,25 @@ builder.Services.AddAuthorization(options =>
|
|||
|
||||
});
|
||||
|
||||
|
||||
builder.Services.AddControllers();
|
||||
|
||||
builder.Services.AddScoped<ITenantService, TenantService>();
|
||||
builder.Services.AddScoped<IPermissionService, PermissionService>();
|
||||
|
||||
builder.Services.AddHttpClient<IPermissionServiceClient, PermissionServiceClient>()
|
||||
.ConfigureHttpClient(options =>
|
||||
.ConfigureHttpClient((serviceProvider, client) =>
|
||||
{
|
||||
options.BaseAddress = new Uri(builder.Configuration["PermissionsApi:BaseUrl"]);
|
||||
options.DefaultRequestHeaders.Add("x-api-key", builder.Configuration["PermissionsApi:ApiKey"]);
|
||||
});
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen(options =>
|
||||
{
|
||||
string? xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
||||
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
|
||||
});
|
||||
var baseUrl = scope.ServiceProvider.GetRequiredService<IOptions<AzureB2CPermissionsApiOptions>>().Value.BaseUrl
|
||||
?? throw new NullReferenceException("Permissions Base Url cannot be null");
|
||||
|
||||
var apiKey = scope.ServiceProvider.GetRequiredService<IOptions<PermissionsApiOptions>>().Value.ApiKey
|
||||
?? throw new NullReferenceException("Permissions Base Api Key cannot be null");
|
||||
|
||||
client.BaseAddress = new Uri(baseUrl);
|
||||
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
@ -134,4 +173,78 @@ app.UseAuthorization();
|
|||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
app.Run();
|
||||
|
||||
|
||||
/*---------------
|
||||
local methods
|
||||
----------------*/
|
||||
|
||||
void InitializeDevEnvironment()
|
||||
{
|
||||
// IMPORTANT
|
||||
// The current version.
|
||||
// Must correspond exactly to the version string of our deployment as specificed in the deployment config.json.
|
||||
var version = "ver0.8.0";
|
||||
|
||||
logger.LogInformation("Version: {version}", version);
|
||||
logger.LogInformation($"Is Development.");
|
||||
|
||||
// For local development, use the Secret Manager feature of .NET to store a connection string
|
||||
// and likewise for storing a secret for the permission-api app.
|
||||
// https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-7.0&tabs=windows
|
||||
|
||||
var appConfigurationconnectionString = builder.Configuration.GetConnectionString("AppConfig")
|
||||
?? throw new NullReferenceException("App config missing.");
|
||||
|
||||
// Use the connection string to access Azure App Configuration to get access to app settings stored there.
|
||||
// To gain access to Azure Key Vault use 'Azure Cli: az login' to log into Azure.
|
||||
// This login on will also now provide valid access tokens to the local development environment.
|
||||
// For more details and the option to chain and combine multiple credential options with `ChainedTokenCredential`
|
||||
// please see: https://learn.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#define-a-custom-authentication-flow-with-chainedtokencredential
|
||||
|
||||
AzureCliCredential credential = new();
|
||||
|
||||
builder.Configuration.AddAzureAppConfiguration(options =>
|
||||
options.Connect(appConfigurationconnectionString)
|
||||
.ConfigureKeyVault(kv => kv.SetCredential(new ChainedTokenCredential(credential)))
|
||||
.Select(KeyFilter.Any, version)); // <-- Important: since we're using labels in our Azure App Configuration store
|
||||
|
||||
// Configuring Swagger.
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
|
||||
// Enabling to option for add the 'x-api-key' header to swagger UI.
|
||||
builder.Services.AddSwaggerGen(option =>
|
||||
{
|
||||
option.SwaggerDoc("v1", new() { Title = "Admin API", Version = "v1.1" });
|
||||
option.OperationFilter<SwagCustomHeaderFilter>();
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeProdEnvironment()
|
||||
{
|
||||
// For procution environment, we'll configured Managed Identities for managing access Azure App Services
|
||||
// and Key Vault. The Azure App Services endpoint is stored in an environment variable for the web app.
|
||||
|
||||
var version = builder.Configuration.GetRequiredSection("Version")?.Value
|
||||
?? throw new NullReferenceException("The Version value cannot be found. Has the 'Version' environment variable been set correctly for the Web App?");
|
||||
|
||||
logger.LogInformation("Version: {version}", version);
|
||||
logger.LogInformation($"Is Production.");
|
||||
|
||||
var appConfigurationEndpoint = builder.Configuration.GetRequiredSection("AppConfiguration:Endpoint")?.Value
|
||||
?? throw new NullReferenceException("The Azure App Configuration Endpoint cannot be found. Has the endpoint environment variable been set correctly for the Web App?");
|
||||
|
||||
// Get the ClientId of the UserAssignedIdentity
|
||||
// If we don't set this ClientID in the ManagedIdentityCredential constructor, it doesn't know it should use the user assigned managed id.
|
||||
var managedIdentityClientId = builder.Configuration.GetRequiredSection("UserAssignedManagedIdentityClientId")?.Value
|
||||
?? throw new NullReferenceException("The Environment Variable 'UserAssignedManagedIdentityClientId' cannot be null. Check the App Service Configuration.");
|
||||
|
||||
ManagedIdentityCredential userAssignedManagedCredentials = new(managedIdentityClientId);
|
||||
|
||||
builder.Configuration.AddAzureAppConfiguration(options =>
|
||||
options.Connect(new Uri(appConfigurationEndpoint), userAssignedManagedCredentials)
|
||||
.ConfigureKeyVault(kv => kv.SetCredential(userAssignedManagedCredentials))
|
||||
.Select(KeyFilter.Any, version)); // <-- Important since we're using labels in our Azure App Configuration store
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>aspnet-Saas.Admin.Service-5358E0C3-EA51-44EA-B381-CA2F9D9710D3</UserSecretsId>
|
||||
|
@ -11,20 +11,22 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.2.2" />
|
||||
<PackageReference Include="Azure.Identity" Version="1.6.0" />
|
||||
<PackageReference Include="Azure.Security.KeyVault.Certificates" Version="4.3.0" />
|
||||
<PackageReference Include="Azure.Identity" Version="1.8.1" />
|
||||
<PackageReference Include="Azure.Security.KeyVault.Certificates" Version="4.4.0" />
|
||||
<PackageReference Include="Dawn.Guard" Version="1.12.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.5">
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.AzureAppConfiguration" Version="5.2.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Identity.Web" Version="1.24.1" />
|
||||
<PackageReference Include="Microsoft.Identity.Web.UI" Version="1.24.1" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.4" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.1" />
|
||||
<PackageReference Include="Microsoft.Identity.Web" Version="1.25.10" />
|
||||
<PackageReference Include="Microsoft.Identity.Web.UI" Version="1.25.10" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
|
@ -32,6 +34,7 @@
|
|||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Saas.Authorization\Saas.AspNetCore.Authorization\Saas.AspNetCore.Authorization.csproj" />
|
||||
<ProjectReference Include="..\..\Saas.Lib\Saas.Authorization\Saas.AspNetCore.Authorization\Saas.AspNetCore.Authorization.csproj" />
|
||||
<ProjectReference Include="..\..\Saas.Lib\Saas.Shared\Saas.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
{
|
||||
"AzureAdB2C": {
|
||||
"SignedOutCallbackPath": "/signout/B2C_1A_SIGNUP_SIGNIN",
|
||||
"SignUpSignInPolicyId": "B2C_1A_SIGNUP_SIGNIN"
|
||||
},
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
|
@ -13,9 +9,9 @@
|
|||
"Url": "",
|
||||
"PermissionsApiCertName": "devenvcert"
|
||||
},
|
||||
"PermissionsApi": {
|
||||
"BaseUrl": "https://localhost:7023"
|
||||
},
|
||||
//"PermissionsApi": {
|
||||
// "BaseUrl": "https://localhost:7023"
|
||||
//},
|
||||
"ClaimToRoleTransformer": {
|
||||
"SourceClaimType": "permissions", //Name of the claim custom roles are in
|
||||
"RoleClaimtype": "MyCustomRoles", //Type of the claim to use in the new Identity (works along side of built in)
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30114.105
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.4.33213.308
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Saas.Admin.Service", "Saas.Admin.Service\Saas.Admin.Service.csproj", "{A6134452-BA8E-4C84-8879-C8EEF82ED5C9}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saas.Admin.Service", "Saas.Admin.Service\Saas.Admin.Service.csproj", "{A6134452-BA8E-4C84-8879-C8EEF82ED5C9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Saas.Admin.Service.Tests", "Saas.Admin.Service.Tests\Saas.Admin.Service.Tests.csproj", "{5974515A-43DF-43E7-A1E6-FC97E47E480F}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saas.Admin.Service.Tests", "Saas.Admin.Service.Tests\Saas.Admin.Service.Tests.csproj", "{5974515A-43DF-43E7-A1E6-FC97E47E480F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtilities", "..\TestUtilities\TestUtilities.csproj", "{5A220537-38DA-495B-8CCF-723FA65310F1}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\TestUtilities\TestUtilities.csproj", "{5A220537-38DA-495B-8CCF-723FA65310F1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saas.AspNetCore.Authorization", "..\Saas.Lib\Saas.Authorization\Saas.AspNetCore.Authorization\Saas.AspNetCore.Authorization.csproj", "{9B4C4680-3BBA-419B-AF79-CB80E1422B1A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saas.Shared", "..\Saas.Lib\Saas.Shared\Saas.Shared.csproj", "{E584B588-FBB6-4D30-9EA2-6B6E531F82EC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A6134452-BA8E-4C84-8879-C8EEF82ED5C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A6134452-BA8E-4C84-8879-C8EEF82ED5C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
|
@ -30,5 +31,19 @@ Global
|
|||
{5A220537-38DA-495B-8CCF-723FA65310F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5A220537-38DA-495B-8CCF-723FA65310F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5A220537-38DA-495B-8CCF-723FA65310F1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9B4C4680-3BBA-419B-AF79-CB80E1422B1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9B4C4680-3BBA-419B-AF79-CB80E1422B1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9B4C4680-3BBA-419B-AF79-CB80E1422B1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9B4C4680-3BBA-419B-AF79-CB80E1422B1A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E584B588-FBB6-4D30-9EA2-6B6E531F82EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E584B588-FBB6-4D30-9EA2-6B6E531F82EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E584B588-FBB6-4D30-9EA2-6B6E531F82EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E584B588-FBB6-4D30-9EA2-6B6E531F82EC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {503B7AA4-0DAC-4A3D-95CE-B1D2DCAE55B0}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# nektos/act
|
||||
.secret
|
||||
.secrets
|
||||
secret
|
||||
secrets
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# repo base
|
||||
repo_base="$( git rev-parse --show-toplevel )"
|
||||
REPO_BASE="${repo_base}"
|
||||
|
||||
host_deployment_dir="${repo_base}/src/Saas.Admin/deployment"
|
||||
container_deployment_dir="/asdk/src/Saas.Admin/deployment"
|
||||
|
||||
# running the './act/script/clean-credentials' script using our ASDK deployment script container - i.e., not the act container
|
||||
docker run \
|
||||
--interactive \
|
||||
--tty \
|
||||
--rm \
|
||||
--volume "${host_deployment_dir}":"${container_deployment_dir}":ro \
|
||||
--volume "${REPO_BASE}/src/Saas.Lib/Deployment.Script.Modules/":/asdk/src/Saas.Lib/Deployment.Script.Modules:ro \
|
||||
--volume "${REPO_BASE}/src/Saas.Identity/Saas.IdentityProvider/deployment/config/":/asdk/src/Saas.Identity/Saas.IdentityProvider/deployment/config:ro \
|
||||
--volume "${REPO_BASE}/.git/":/asdk/.git:ro \
|
||||
--volume "${HOME}/.azure/":/asdk/.azure:ro \
|
||||
--volume "${HOME}/asdk/act/.secret":/asdk/act/.secret \
|
||||
--env "ASDK_DEPLOYMENT_SCRIPT_PROJECT_BASE=/asdk/src/Saas.Admin/deployment" \
|
||||
"${DEPLOYMENT_CONTAINER_NAME}" \
|
||||
bash /asdk/src/Saas.Lib/Deployment.Script.Modules/clean-credentials.sh
|
||||
|
||||
./setup.sh -s
|
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -u -e -o pipefail
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
{
|
||||
source "./../constants.sh"
|
||||
source "$SHARED_MODULE_DIR/config-module.sh"
|
||||
}
|
||||
|
||||
repo_base="$(git rev-parse --show-toplevel)"
|
||||
REPO_BASE="${repo_base}"
|
||||
|
||||
host_act_secrets_dir="${HOME}/asdk/act/.secret"
|
||||
host_deployment_dir="${repo_base}/src/Saas.Admin/deployment"
|
||||
container_deployment_dir="/asdk/src/Saas.Admin/deployment"
|
||||
|
||||
# running the './act/script/patch-app-name.sh' script using our ASDK deployment script container - i.e., not the act container
|
||||
docker run \
|
||||
--interactive \
|
||||
--tty \
|
||||
--rm \
|
||||
--volume "${host_deployment_dir}":"${container_deployment_dir}":ro \
|
||||
--volume "${host_deployment_dir}/act/workflows/":"${container_deployment_dir}/act/workflows" \
|
||||
--volume "${REPO_BASE}/src/Saas.Lib/Deployment.Script.Modules/":/asdk/src/Saas.Lib/Deployment.Script.Modules:ro \
|
||||
--volume "${REPO_BASE}/src/Saas.Identity/Saas.IdentityProvider/deployment/config/":/asdk/src/Saas.Identity/Saas.IdentityProvider/deployment/config:ro \
|
||||
--volume "${REPO_BASE}/.git/":/asdk/.git:ro \
|
||||
--volume "${HOME}/.azure/":/asdk/.azure:ro \
|
||||
--volume "${host_act_secrets_dir}":/asdk/act/.secret \
|
||||
--env "ASDK_DEPLOYMENT_SCRIPT_PROJECT_BASE"="${container_deployment_dir}" \
|
||||
"${DEPLOYMENT_CONTAINER_NAME}" \
|
||||
bash /asdk/src/Saas.Lib/Deployment.Script.Modules/deploy-debug.sh
|
||||
|
||||
# --artifact-server-path=./src/publish \
|
||||
|
||||
# run act container to run github action locally, using local workflow file and local code base.
|
||||
gh act workflow_dispatch \
|
||||
--rm \
|
||||
--bind \
|
||||
--pull=false \
|
||||
--secret-file "${host_act_secrets_dir}/secret" \
|
||||
--directory "${REPO_BASE}" \
|
||||
--workflows "${ACT_LOCAL_WORKFLOW_DEBUG_FILE}" \
|
||||
--platform "ubuntu-latest=${ACT_CONTAINER_NAME}"
|
|
@ -0,0 +1,9 @@
|
|||
# Saving Time Running Local GitHub Actions
|
||||
|
||||
GitHub actions are terrific for CI/CD automated deployment. It the *inner loop* for getting GitHub actions right can be a tedious affair - i.e., having to commit, push and run when testing and troubleshoot.
|
||||
|
||||
Luckily, there a solution for this called [act](https://github.com/nektos/act). Act lets you run the a GitHub running locally in a container that mimics what is running in GitHub. You still have to commit your latest code to GitHub, as act will pull it from there when it runs. However, you don't have to commit and push every time you make a change to the GitHub action workflow. This last part can save a lot of time and avoid all this *testing*, *wip* etc. commit and pushes to you main branch. It also allows you to have a slightly different `workflow.yml` file that pulls from for instance your dev branch rather than your main branch.
|
||||
|
||||
|
||||
|
||||
... bla. bla.
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
skip_docker_build=false
|
||||
force_update=false
|
||||
|
||||
while getopts 'sf' flag; do
|
||||
case "${flag}" in
|
||||
s) skip_docker_build=true ;;
|
||||
f) force_update=true ;;
|
||||
*) skip_docker_build=false ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "../constants.sh"
|
||||
|
||||
echo "Setting up the SaaS Admin Service API Act deployment environment."
|
||||
echo "Settings execute permissions on necessary scripts files."
|
||||
|
||||
sudo mkdir -p "${ACT_SECRETS_DIR}"
|
||||
|
||||
sudo chmod +x ${ACT_DIR}/*.sh
|
||||
sudo chmod +x ${SCRIPT_DIR}/*.sh >/dev/null 2>&1
|
||||
sudo touch ${ACT_SECRETS_FILE}
|
||||
sudo chown "${USER}" ${ACT_SECRETS_FILE}
|
||||
sudo touch ${ACT_SECRETS_FILE_RG}
|
||||
sudo chown "${USER}" ${ACT_SECRETS_FILE_RG}
|
||||
|
||||
if [ "${skip_docker_build}" = false ]; then
|
||||
echo "Building the deployment container."
|
||||
|
||||
if [[ "${force_update}" == false ]]; then
|
||||
"${ACT_CONTAINER_DIR}"/build.sh -n "${ACT_CONTAINER_NAME}"
|
||||
else
|
||||
"${ACT_CONTAINER_DIR}"/build.sh -n "${ACT_CONTAINER_NAME}" -f
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "SaaS SaaS Admin Service API Act environment setup complete. You can now run the local deployment script using the command './deploy.sh'."
|
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
name: ASDK Administration Service API - Deploy to Azure Web Services
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
env:
|
||||
APP_NAME: admin-api
|
||||
AZURE_WEBAPP_NAME: admin-api-asdk-test-fd4k # set this to your application's name
|
||||
AZURE_WEBAPP_PACKAGE_PATH: . # set this to the path to your web app project, defaults to the repository root
|
||||
DOTNET_VERSION: 7.x.x
|
||||
PROJECT_DIR: ./src/Saas.Admin/Saas.Admin.Service
|
||||
PROJECT_PATH: ${{ env.PROJECT_DIR }}/Saas.Admin.Service.csproj
|
||||
PUBLISH_PATH: ./publish
|
||||
OUTPUT_PATH: ${{ env.PUBLISH_PATH }}/${{ env.APP_NAME }}/package
|
||||
SYMBOLS_PATH: ${{ env.PUBLISH_PATH }}/symbols
|
||||
BUILD_CONFIGURATION: Debug # setting the configuration manager build configuration value for our workflow.
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
##################################################################
|
||||
# this section is specifically changed for for local deployment. #
|
||||
##################################################################
|
||||
# Azure login
|
||||
- uses: azure/login@v1
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
||||
|
||||
# checkout the _local_ repository
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
##################################################################
|
||||
# end of local deployment specific section. #
|
||||
##################################################################
|
||||
|
||||
# Setup .NET Core SDK
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
# Run dotnet build and publish
|
||||
- name: dotnet build and publish
|
||||
run: |
|
||||
dotnet restore ${{ env.PROJECT_DIR }}
|
||||
|
||||
dotnet build ${{ env.PROJECT_PATH }} \
|
||||
--configuration ${{ env.BUILD_CONFIGURATION }}
|
||||
|
||||
dotnet publish ${{ env.PROJECT_PATH }} \
|
||||
--configuration ${{ env.BUILD_CONFIGURATION }} \
|
||||
--output ${{ env.OUTPUT_PATH }}
|
||||
|
||||
# Deploy to Azure Web apps
|
||||
- name: Run Azure webapp deploy action using publish profile credentials
|
||||
uses: azure/webapps-deploy@v2
|
||||
with:
|
||||
app-name: ${{ env.AZURE_WEBAPP_NAME }} # Replace with your app name
|
||||
package: ${{ env.OUTPUT_PATH }}
|
||||
|
||||
######################
|
||||
# *** Debug only *** #
|
||||
######################
|
||||
# Copy symbols files (*.pdb)) to local publish folder # rm -rf ${{ env.OUTPUT_PATH }}/${{ env.AZURE_WEBAPP_NAME }}
|
||||
- name: copy symbols files (*.pdb)) to local publish folder
|
||||
run: |
|
||||
mkdir -p ${{ env.PUBLISH_PATH }}/symbols
|
||||
echo "Copying symbols files to '${{ env.SYMBOLS_PATH }}'"
|
||||
cp -r ${{ env.OUTPUT_PATH }}/*.pdb ${{ env.SYMBOLS_PATH }}
|
||||
######################
|
||||
# *** End *** #
|
||||
######################
|
||||
|
||||
# Azure logout
|
||||
- name: logout
|
||||
run: |
|
||||
az logout
|
|
@ -0,0 +1,2 @@
|
|||
*-parameters.json
|
||||
identity-foundation-outputs.json
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
@description('The SaaS Signup Administration web site name.')
|
||||
param adminapi string
|
||||
|
||||
@description('Version')
|
||||
param version string
|
||||
|
||||
@description('Environment')
|
||||
@allowed([
|
||||
'Development'
|
||||
'Staging'
|
||||
'Production'
|
||||
])
|
||||
param environment string
|
||||
|
||||
@description('The App Service Plan ID.')
|
||||
param appServicePlanName string
|
||||
|
||||
@description('The Uri of the Key Vault.')
|
||||
param keyVaultUri string
|
||||
|
||||
@description('The location for all resources.')
|
||||
param location string
|
||||
|
||||
@description('Azure App Configuration User Assigned Identity Name.')
|
||||
param userAssignedIdentityName string
|
||||
|
||||
@description('The name of the Azure App Configuration.')
|
||||
param appConfigurationName string
|
||||
|
||||
@description('The name of the Log Analytics Workspace used by Application Insigths.')
|
||||
param logAnalyticsWorkspaceName string
|
||||
|
||||
@description('The name of Application Insights.')
|
||||
param applicationInsightsName string
|
||||
|
||||
module signupAdministrationWebApp './../../../Saas.Lib/Saas.Bicep.Module/appServiceModuleWithObservability.bicep' = {
|
||||
name: 'AdminServiceApi'
|
||||
params: {
|
||||
appServiceName: adminapi
|
||||
version: version
|
||||
environment: environment
|
||||
appServicePlanName: appServicePlanName
|
||||
keyVaultUri: keyVaultUri
|
||||
location: location
|
||||
userAssignedIdentityName: userAssignedIdentityName
|
||||
appConfigurationName: appConfigurationName
|
||||
logAnalyticsWorkspaceName: logAnalyticsWorkspaceName
|
||||
applicationInsightsName: applicationInsightsName
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
@description('Version')
|
||||
param version string
|
||||
|
||||
@description('The name of the key vault')
|
||||
param keyVaultName string
|
||||
|
||||
@description('Azure B2C Domain Name.')
|
||||
param azureB2CDomain string
|
||||
|
||||
@description('Azure B2C Tenant Id.')
|
||||
param azureB2cTenantId string
|
||||
|
||||
@description('Azure AD Instance')
|
||||
param azureAdInstance string
|
||||
|
||||
@description('The Azure B2C Signed Out Call Back Path.')
|
||||
param signedOutCallBackPath string
|
||||
|
||||
@description('The Azure B2C Sign up/in Policy Id.')
|
||||
param signUpSignInPolicyId string
|
||||
|
||||
@description('The Azure B2C Permissions API base Url.')
|
||||
param baseUrl string
|
||||
|
||||
@description('The Client Id found on registered Permissions API app page.')
|
||||
param clientId string
|
||||
|
||||
@description('User Identity Name')
|
||||
param userAssignedIdentityName string
|
||||
|
||||
@description('App Configuration Name')
|
||||
param appConfigurationName string
|
||||
|
||||
@description('Indicates the Authentication type for new identity')
|
||||
param authenticationType string
|
||||
|
||||
@description('Type of the claim to use in the new Identity, works alongside built-in')
|
||||
param roleClaimType string
|
||||
|
||||
@description('Name of the claim custom roles are in')
|
||||
param sourceClaimType string
|
||||
|
||||
@description('Application ID URI for the exposed Admin API scopes.')
|
||||
param applicationIdUri string
|
||||
|
||||
@description('Admin API scopes.')
|
||||
param adminApiScopes string
|
||||
|
||||
resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' existing = {
|
||||
name: userAssignedIdentityName
|
||||
}
|
||||
|
||||
// Create object with array of objects containing the kayname and value to be stored in Azure App Configuration store.
|
||||
|
||||
var azureB2CKeyName = 'AzureB2C'
|
||||
var adminApiKeyName = 'AdminApi'
|
||||
var claimToRoleTransformerKeyName = 'ClaimToRoleTransformer'
|
||||
|
||||
var appConfigStore = {
|
||||
appConfigurationName: appConfigurationName
|
||||
keyVaultName: keyVaultName
|
||||
userAssignedIdentityName: userAssignedIdentity.name
|
||||
label: version
|
||||
entries: [
|
||||
{
|
||||
key: '${adminApiKeyName}:${azureB2CKeyName}:BaseUrl'
|
||||
value: baseUrl
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${adminApiKeyName}:${azureB2CKeyName}:ClientId'
|
||||
value: clientId
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${adminApiKeyName}:${azureB2CKeyName}:TenantId'
|
||||
value: azureB2cTenantId
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${adminApiKeyName}:${azureB2CKeyName}:Domain'
|
||||
value: azureB2CDomain
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${adminApiKeyName}:${azureB2CKeyName}:Instance'
|
||||
value: azureAdInstance
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${adminApiKeyName}:${azureB2CKeyName}:Audience'
|
||||
value: clientId
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${adminApiKeyName}:${azureB2CKeyName}:SignedOutCallbackPath'
|
||||
value: signedOutCallBackPath
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${adminApiKeyName}:${azureB2CKeyName}:SignUpSignInPolicyId'
|
||||
value: signUpSignInPolicyId
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${claimToRoleTransformerKeyName}:AuthenticationType'
|
||||
value: authenticationType
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${claimToRoleTransformerKeyName}:RoleClaimType'
|
||||
value: roleClaimType
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${claimToRoleTransformerKeyName}:SourceClaimType'
|
||||
value: sourceClaimType
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${adminApiKeyName}:ApplicationIdUri'
|
||||
value: applicationIdUri
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${adminApiKeyName}:Scopes'
|
||||
value: ' ${string(adminApiScopes)}' // notice the space before the string, this is a necessary hack. https://github.com/Azure/bicep/issues/6167
|
||||
isSecret: false
|
||||
contentType: 'application/json'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Adding App Configuration entries
|
||||
module appConfigurationSettings './../../../Saas.Lib/Saas.Bicep.Module/addConfigEntry.bicep' = [ for entry in appConfigStore.entries: {
|
||||
name: replace('Entry-${entry.key}', ':', '-')
|
||||
params: {
|
||||
appConfigurationName: appConfigStore.appConfigurationName
|
||||
userAssignedIdentityName: appConfigStore.userAssignedIdentityName
|
||||
keyVaultName: keyVaultName
|
||||
value: entry.value
|
||||
contentType: entry.contentType
|
||||
keyName: entry.key
|
||||
label: appConfigStore.label
|
||||
isSecret: entry.isSecret
|
||||
}
|
||||
}]
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
force_update=false
|
||||
|
||||
while getopts f flag
|
||||
do
|
||||
case "${flag}" in
|
||||
f) force_update=true;;
|
||||
*) force_update=false;;
|
||||
esac
|
||||
done
|
||||
|
||||
repo_base="$( git rev-parse --show-toplevel )"
|
||||
docker_file_folder="${repo_base}/src/Saas.lib/Deployment.Container"
|
||||
|
||||
|
||||
# redirect to build.sh in the Deployment.Container folder
|
||||
if [[ "${force_update}" == false ]]; then
|
||||
"${docker_file_folder}/build.sh"
|
||||
else
|
||||
"${docker_file_folder}/build.sh" -f
|
||||
fi
|
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# disable unused variable warning https://www.shellcheck.net/wiki/SC2034
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
# app naming
|
||||
APP_NAME="admin-api"
|
||||
APP_DEPLOYMENT_NAME="adminServiceApi"
|
||||
|
||||
# repo base
|
||||
repo_base="$(git rev-parse --show-toplevel)"
|
||||
REPO_BASE="${repo_base}"
|
||||
|
||||
# project base directory
|
||||
BASE_DIR="${REPO_BASE}/src/Saas.Admin/deployment"
|
||||
|
||||
# local script directory
|
||||
SCRIPT_DIR="${BASE_DIR}/script"
|
||||
|
||||
#local log directory
|
||||
LOG_FILE_DIR="${BASE_DIR}/log"
|
||||
|
||||
# act directory
|
||||
ACT_DIR="${BASE_DIR}/act"
|
||||
|
||||
# GitHub workflows
|
||||
WORKFLOW_BASE="${REPO_BASE}/.github/workflows"
|
||||
GITHUB_ACTION_WORKFLOW_FILE="${WORKFLOW_BASE}/admin-service-api-deploy.yml"
|
||||
ACT_LOCAL_WORKFLOW_DEBUG_FILE="${ACT_DIR}/workflows/admin-service-api-deploy-debug.yml"
|
||||
|
||||
# global script directory
|
||||
SHARED_MODULE_DIR="${REPO_BASE}/src/Saas.Lib/Deployment.Script.Modules"
|
||||
|
||||
# adding app service global constants
|
||||
source "${SHARED_MODULE_DIR}/app-service-constants.sh"
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source "./constants.sh"
|
||||
|
||||
repo_base="$(git rev-parse --show-toplevel)"
|
||||
host_act_secrets_dir="${HOME}/asdk/act/.secret"
|
||||
|
||||
host_deployment_dir="${repo_base}/src/Saas.Admin/deployment"
|
||||
container_deployment_dir="/asdk/src/Saas.Admin/deployment"
|
||||
|
||||
# using volumes '--volume' to mount only the needed directories to the container.
|
||||
# using ':ro' to make scrip directories etc. read-only. Only config and log directories are writable.
|
||||
docker run \
|
||||
--interactive \
|
||||
--tty \
|
||||
--rm \
|
||||
--volume "${host_deployment_dir}":"${container_deployment_dir}":ro \
|
||||
--volume "${host_deployment_dir}/log":"${container_deployment_dir}/log" \
|
||||
--volume "${host_deployment_dir}/Bicep/Parameters":"${container_deployment_dir}"/Bicep/Parameters \
|
||||
--volume "${repo_base}/src/Saas.Identity/Saas.IdentityProvider/deployment/config/":/asdk/src/Saas.Identity/Saas.IdentityProvider/deployment/config:ro \
|
||||
--volume "${repo_base}/src/Saas.Lib/Deployment.Script.Modules/":/asdk/src/Saas.Lib/Deployment.Script.Modules:ro \
|
||||
--volume "${repo_base}/src/Saas.Lib/Saas.Bicep.Module":/asdk/src/Saas.Lib/Saas.Bicep.Module:ro \
|
||||
--volume "${repo_base}/.github/workflows":/asdk/.github/workflows \
|
||||
--volume "${repo_base}/.git/":/asdk/.git:ro \
|
||||
--volume "${HOME}/.azure/":/asdk/.azure:ro \
|
||||
--volume "${host_act_secrets_dir}":/asdk/act/.secret \
|
||||
--env "ASDK_DEPLOYMENT_SCRIPT_PROJECT_BASE"="${container_deployment_dir}" \
|
||||
"${DEPLOYMENT_CONTAINER_NAME}" \
|
||||
bash ${container_deployment_dir}/start.sh
|
|
@ -0,0 +1,132 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import sys
|
||||
import re
|
||||
|
||||
def get_b2c_value(
|
||||
config: dict,
|
||||
key: str,
|
||||
keyName: str) -> 'dict[str, dict[str, str]]':
|
||||
|
||||
value = config['azureb2c'][key]
|
||||
|
||||
return {
|
||||
keyName: {
|
||||
'value': value
|
||||
}
|
||||
}
|
||||
|
||||
def get_claimTransformer_value(
|
||||
config: dict,
|
||||
key: str,
|
||||
keyName: str) -> 'dict[str, dict[str, str]]':
|
||||
|
||||
value = config['claimToRoleTransformer'][key]
|
||||
return {
|
||||
keyName: {
|
||||
'value': value
|
||||
}
|
||||
}
|
||||
|
||||
def get_deploy_b2c_value(
|
||||
config: dict,
|
||||
key: str,
|
||||
keyName: str) -> 'dict[str, dict[str, str]]':
|
||||
|
||||
value = config['deployment']['azureb2c'][key]
|
||||
return {
|
||||
keyName: {
|
||||
'value': value
|
||||
}
|
||||
}
|
||||
|
||||
def get_app_value(
|
||||
config: dict,
|
||||
app_name: str,
|
||||
key: str,
|
||||
keyName: str) -> 'dict[str, dict[str, str]]':
|
||||
|
||||
for app in config['appRegistrations']:
|
||||
if app['name'] == app_name:
|
||||
return {
|
||||
keyName: {
|
||||
'value': app[key]
|
||||
}
|
||||
}
|
||||
|
||||
def get_app_scopes(
|
||||
config: dict,
|
||||
app_name: str,
|
||||
keyName: str) -> 'dict[str, dict[str, str]]':
|
||||
|
||||
scopes = []
|
||||
|
||||
for app in config['appRegistrations']:
|
||||
if app['name'] == app_name:
|
||||
for scope in app['scopes']:
|
||||
scopes.append(scope['name'])
|
||||
|
||||
return {
|
||||
keyName: {
|
||||
'value': json.dumps(scopes)
|
||||
}
|
||||
}
|
||||
|
||||
def get_output_value(outputs: dict, output_name: str) -> 'dict[str, dict[str, str]]':
|
||||
item = outputs[output_name]
|
||||
if item : return {
|
||||
output_name : {
|
||||
'value': item['value']
|
||||
}
|
||||
}
|
||||
|
||||
def patch_paramenters_file(
|
||||
app_name: str,
|
||||
identity_outputs: str,
|
||||
paramenter_file: str,
|
||||
config_file: str) -> None:
|
||||
|
||||
with open(config_file, 'r') as f:
|
||||
config = json.load(f)
|
||||
|
||||
with open(identity_outputs, 'r') as f:
|
||||
identity_outputs = json.load(f)
|
||||
|
||||
with open(paramenter_file, 'r') as f:
|
||||
parameters = json.load(f)
|
||||
|
||||
parameters['parameters'].update(get_output_value(identity_outputs, 'version'))
|
||||
parameters['parameters'].update(get_output_value(identity_outputs, 'keyVaultName'))
|
||||
|
||||
parameters['parameters'].update(get_deploy_b2c_value(config, 'domainName', 'azureB2CDomain'))
|
||||
parameters['parameters'].update(get_deploy_b2c_value(config, 'tenantId', 'azureB2cTenantId'))
|
||||
parameters['parameters'].update(get_deploy_b2c_value(config, 'instance', 'azureAdInstance'))
|
||||
|
||||
parameters['parameters'].update(get_b2c_value(config, 'signedOutCallBackPath', 'signedOutCallBackPath'))
|
||||
parameters['parameters'].update(get_b2c_value(config, 'signUpSignInPolicyId', 'signUpSignInPolicyId'))
|
||||
|
||||
parameters['parameters'].update(get_app_value(config, app_name, 'appId', 'clientId'))
|
||||
parameters['parameters'].update(get_app_value(config, app_name, 'baseUrl', 'baseUrl'))
|
||||
|
||||
parameters['parameters'].update(get_app_value(config, app_name, 'applicationIdUri', 'applicationIdUri'))
|
||||
parameters['parameters'].update(get_app_scopes(config, app_name, 'adminApiScopes'))
|
||||
|
||||
parameters['parameters'].update(get_output_value(identity_outputs, 'userAssignedIdentityName'))
|
||||
parameters['parameters'].update(get_output_value(identity_outputs, 'appConfigurationName'))
|
||||
|
||||
parameters['parameters'].update(get_claimTransformer_value(config, 'authenticationType', 'authenticationType'))
|
||||
parameters['parameters'].update(get_claimTransformer_value(config, 'roleClaimType', 'roleClaimType'))
|
||||
parameters['parameters'].update(get_claimTransformer_value(config, 'sourceClaimType', 'sourceClaimType'))
|
||||
|
||||
with open(paramenter_file, 'w') as f:
|
||||
f.write(json.dumps(parameters, indent=4))
|
||||
|
||||
# Main entry point for the script
|
||||
if __name__ == "__main__":
|
||||
app_name = sys.argv[1]
|
||||
identity_outputs = sys.argv[2]
|
||||
paramenter_file = sys.argv[3]
|
||||
config_file = sys.argv[4]
|
||||
|
||||
patch_paramenters_file(app_name, identity_outputs, paramenter_file, config_file)
|
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "./constants.sh"
|
||||
|
||||
echo "Setting up the deployment environment."
|
||||
echo "Settings execute permissions on necessary scripts files."
|
||||
|
||||
(
|
||||
sudo chmod +x ./*.sh
|
||||
sudo chmod +x ./script/*.sh >/dev/null 2>&1
|
||||
sudo chmod +x ./script/*.py
|
||||
) ||
|
||||
{
|
||||
echo "Failed to set execute permissions on the necessary scripts."
|
||||
exit 1
|
||||
}
|
||||
|
||||
repo_base="$(git rev-parse --show-toplevel)" ||
|
||||
{
|
||||
echo "Failed to get the root of the repository."
|
||||
exit 1
|
||||
}
|
||||
|
||||
docker_file_folder="${repo_base}/src/Saas.lib/Deployment.Container"
|
||||
|
||||
# redirect to build.sh in the Deployment.Container folder
|
||||
sudo chmod +x "${docker_file_folder}/build.sh" ||
|
||||
{
|
||||
echo "Failed to set execute permissions on the 'build.sh' script."
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Building the deployment container."
|
||||
./build.sh ||
|
||||
{
|
||||
echo "Failed to build the deployment container. Please ensure that Docker is installed and running."
|
||||
exit 1
|
||||
}
|
||||
|
||||
(
|
||||
echo "Setting up log folder..."
|
||||
mkdir -p "$LOG_FILE_DIR"
|
||||
sudo chown "${USER}" "$LOG_FILE_DIR"
|
||||
) ||
|
||||
{
|
||||
echo "Failed to set up log folder."
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo
|
||||
echo "Setup complete. You can now run the deployment script using the command './run.sh'."
|
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# if not running in a container
|
||||
if ! [ -f /.dockerenv ]; then
|
||||
echo "Running outside of a container us not supported. Please run the script using './run.sh'."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
{
|
||||
source "${ASDK_DEPLOYMENT_SCRIPT_PROJECT_BASE}/constants.sh"
|
||||
source "$SHARED_MODULE_DIR/config-module.sh"
|
||||
source "$SHARED_MODULE_DIR/log-module.sh"
|
||||
source "$SHARED_MODULE_DIR/user-module.sh"
|
||||
}
|
||||
|
||||
# set bash options to exit on unset variables and errors (exit 1) including pipefail
|
||||
set -u -e -o pipefail
|
||||
|
||||
if ! [[ -f $CONFIG_FILE ]]; then
|
||||
echo "The ASDK Identity Foundation has not completed or 'config.json' file from it's deployment is missing. Please run the Identity Foundation deployment script first."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# get now date and time for backup file name
|
||||
now=$(date '+%Y-%m-%d--%H-%M-%S')
|
||||
|
||||
# set run time for deployment script instance
|
||||
export ASDK_DEPLOYMENT_SCRIPT_RUN_TIME="${now}"
|
||||
|
||||
# using the az cli settings and cache from the host machine
|
||||
initialize-az-cli "$HOME/.azure"
|
||||
|
||||
echo "Provisioning the SaaS Administration Service API..." |
|
||||
log-output \
|
||||
--level info \
|
||||
--header "SaaS Administration Service API"
|
||||
|
||||
"${SHARED_MODULE_DIR}/"deploy-app-service.sh
|
||||
|
||||
"${SHARED_MODULE_DIR}/"deploy-config-entries.sh
|
||||
|
||||
echo "Patching '${APP_NAME}' GitHub Action workflow file." |
|
||||
log-output \
|
||||
--level info \
|
||||
--header "SaaS Administration Service API"
|
||||
|
||||
"${SHARED_MODULE_DIR}/patch-github-workflow.py" \
|
||||
"${APP_NAME}" \
|
||||
"${CONFIG_FILE}" \
|
||||
"${GITHUB_ACTION_WORKFLOW_FILE}" ||
|
||||
echo "Failed to patch ${APP_NAME} GitHub Action workflow file" |
|
||||
log-output \
|
||||
--level error \
|
||||
--header "Critical Error" ||
|
||||
exit 1
|
||||
|
||||
git_repo_origin="$(git config --get remote.origin.url)"
|
||||
|
||||
echo "'${APP_NAME}' is ready to be deployed. You have two options:"
|
||||
echo " a) To deploy to production, use the GitHub Action: ${git_repo_origin::-4}/actions"
|
||||
echo
|
||||
echo " b) To deploy for live debugging in Azure; navigate to the act directory ('cd act') and run './setup.sh' and then run './deploy.sh' to deploy for remote debugging."
|
|
@ -1,76 +1,64 @@
|
|||
# Saas.Admin.Service
|
||||
# SaaS Admin Service API
|
||||
|
||||
The SaaS Admin Service is an API that is reponsible for tenant management operations. Within this folder, you will find 3 sections:
|
||||
The SaaS Admin Service is an API that is reponsible for tenant management operations.
|
||||
|
||||
1. Saas.Admin.Service - The .NET Web API project containing the code for the API
|
||||
This project hosts a service API which serves as a gateway to administrate the SaaS ecosystem of Tenants.
|
||||
|
||||
2. Saas.Admin.Service.Deployment - The bicep module for deploying the infrastructure required to host the API in Azure
|
||||
## Overview
|
||||
|
||||
3. Saas.Admin.Service.Tests - Unit tests for the service
|
||||
Within this folder you will find two subfolders:
|
||||
|
||||
## 1. Module Overview
|
||||
- **Saas.Permissions.Service** - the C# project for the API
|
||||
- **deployment** - a set of tools for deploying the API for production
|
||||
- The sub-subfolder **[act](./deployment/act)** is for deploying the API for remote debugging
|
||||
- Saas.Admin.Service.Tests - Unit tests for the API.
|
||||
|
||||
This project hosts a service api which serves as a gateway to administrate the SaaS ecosystem of Tenants. It is fully self-contained such that it includes complete copies of all necessary classes for operation. Since it contains no direct references to the other projects, it can be extracted to launch in isolation. However, keep in mind that some functionality within the API does have [dependencies](https://azure.github.io/azure-saas/components/admin-service/#dependencies) on other services.
|
||||
## Dependencies
|
||||
|
||||
The service depends on:
|
||||
|
||||
- The **Identity Foundation** that was deployed a spart of the Identity Foundation and on the [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/use-the-api).
|
||||
- The **[SaaS Permissions Services API](./../Saas.Identity/Saas.Permissions/readme.md)**.
|
||||
- The [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/overview).
|
||||
|
||||
For a complete overview, please see the [SaaS.Admin.Service](https://azure.github.io/azure-saas/components/admin-service/) page in our documentation site.
|
||||
|
||||
## 2. How to Run Locally
|
||||
## Provisioning the API
|
||||
|
||||
Once configured, this app presents an api service which exposes endpoints to perform CRUD operations on application tenant data. It may be run locally during development of service logic and for regenerating its included NSwag api client. (An NSwag file is included in the Admin project to generate its client.)
|
||||
To work with the SaaS Admin Services API it must first be provisions to your Azure ASDK resource group. This is true even if you initially is planning to run the API in your local development environment. The provisioning ensure that configuration and settings to be correctly added to your Azure App Configuration store and readies the API for later deployment to Azure.
|
||||
|
||||
### i. Requirements
|
||||
Provisioning is easy:
|
||||
|
||||
To run the web api, you must have the following installed on your machine:
|
||||
1. Navigate to the sub folder `deployment`.
|
||||
|
||||
- [.NET 6.0](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
|
||||
- [ASP.NET Core 6.0](https://docs.microsoft.com/en-us/aspnet/core/introduction-to-aspnet-core?view=aspnetcore-6.0)
|
||||
- (Recommended) [Visual Studio](https://visualstudio.microsoft.com/downloads/) or [Visual Studio Code](https://code.visualstudio.com/download)
|
||||
- A connection string to a running, empty SQL Server Database.
|
||||
- [Local DB](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-express-localdb?view=sql-server-ver15) (Windows Only) - See `Additional Resources` below for basic config secret
|
||||
- [SQL Server Docker Container](https://hub.docker.com/_/microsoft-mssql-server)
|
||||
- [SQL Server Developer Edition](https://www.microsoft.com/en-us/sql-server/sql-server-downloads)
|
||||
- A deployed [Identity Framework](https://azure.github.io/azure-saas/quick-start/) instance
|
||||
- [Azure AD B2C](https://azure.microsoft.com/en-us/services/active-directory/external-identities/b2c/) - created automatically with Bicep deployment
|
||||
2. Run these commands:
|
||||
|
||||
### ii. Development Tools
|
||||
```bash
|
||||
sudo chmod +x setup.sh
|
||||
./setup.sh
|
||||
./run.sh
|
||||
```
|
||||
|
||||
- [NSwag](https://github.com/RicoSuter/NSwag) - An NSwag configuration file has been included to generate an appropriate client from the included Admin project.
|
||||
*Consumes Clients:*
|
||||
- [permissions-service-client-generator.nswag](Saas.Admin.Service/permissions-service-client-generator.nswag)
|
||||
*Consumed By:*
|
||||
- [Saas.SignupAdministration](../Saas.SignupAdministration)
|
||||
- [Saas.Application](../Saas.Application)
|
||||
Now you're ready to move on.
|
||||
|
||||
### iii. App Settings
|
||||
## How to Run Locally
|
||||
|
||||
In order to run the project locally, the App Settings marked as `secret: true` must be set using the [.NET secrets manager](https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-6.0&tabs=windows). When deployed to azure using the bicep deployments, these secrets are [loaded from Azure Key Vault](https://docs.microsoft.com/en-us/aspnet/core/security/key-vault-configuration?view=aspnetcore-6.0#secret-storage-in-the-development-environment) instead.
|
||||
Guidelines for getting up and running with SaaS Signup Administration in your local development, are identical to the guidelines found the *[Requirements](./../Saas.Identity/Saas.Permissions/readme.md#Requirements)* and the *[Configuration, settings and secrets when running locally](./../Saas.Identity/Saas.Permissions/readme.md#running-the-saas-permissions-service-api-locally)* section in the [SaaS Permissions Service readme](./../Saas.Identity/Saas.Permissions/readme.md).
|
||||
|
||||
Default values for non secret app settings can be found in [appsettings.json](Saas.Admin.Service/appsettings.json)
|
||||
## Running the SaaS Administration Service API Locally
|
||||
|
||||
| AppSetting Key | Description | Secret | Default Value |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------------------------------------- |
|
||||
| AzureAdB2C:ClientId | The service client corresponding to the Signup Admin application | true | |
|
||||
| AzureAdB2C:Domain | Domain name for the Azure AD B2C instance | true | |
|
||||
| AzureAdB2C:Instance | URL for the root of the Azure AD B2C instance | true | |
|
||||
| AzureAdB2C:SignedOutCallbackPath | Callback path (not full url) contacted after signout | false | /signout/B2C_1A_SIGNUP_SIGNIN |
|
||||
| AzureAdB2C:SignUpSignInPolicyId | Name of signup/signin policy | false | B2C_1A_SIGNUP_SIGNIN |
|
||||
| AzureAdB2C:TenantId | Identifier for the overall Azure AD B2C tenant for the overall SaaS ecosystem | true | |
|
||||
| ClaimToRoleTransformer:AuthenticationType | Indicates the Authentication type for new identity | false | MyCustomRoleAuth |
|
||||
| ClaimToRoleTransformer:RoleClaimtype | Type of the claim to use in the new Identity, works alongside built-in | false | MyCustomRoles |
|
||||
| ClaimToRoleTransformer:SourceClaimType | Name of the claim custom roles are in | false | permissions |
|
||||
| ConnectionStrings:TenantsContext | Connection String to SQL server database used to store permission data. | true | (localdb connnection string) |
|
||||
| KeyVault:Url | KeyVault URL to pull secret values from in production | false | |
|
||||
| Logging:LogLevel:Default | Logging level when no configured provider is matched | false | Information |
|
||||
| Logging:LogLevel:Microsoft.AspNetCore | Logging level for AspNetCore logging | false | Warning |
|
||||
| PermissionsApi:BaseUrl | URL for downstream [Permissions API](../Saas.Identity/Saas.Permissions/readme.md) | false | |
|
||||
| PermissionsApi:ApiKey | API Key to use for authentication with the downstream [Permissions API](../Saas.Identity/Saas.Permissions/readme.md) | true | |
|
||||
--- TODO BEGIN ---
|
||||
|
||||
### iv. Starting the App
|
||||
*Add some guidelines about how to create valid JWT tokens to test the API locally etc...*
|
||||
|
||||
1. Insert secrets marked as required for running locally into your secrets manager (such as by using provided script).
|
||||
1. Start app. Service will launch as presented Swagger API.
|
||||
---TODO END ---
|
||||
|
||||
## 3. Additional Resources
|
||||
## How to Deploy the SaaS Administration Service API to Azure
|
||||
|
||||
### i. LocalDB
|
||||
If using the LocalDB persistance for local development, tables and data can be interacted with directly through Visual Studio. Under the `View` menu, find `SQL Server Object Explorer`. Additional documentation is available [here](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-express-localdb?view=sql-server-ver16)
|
||||
The guidelines are identity to *[How to Deploy SaaS Permissions Service API to Azure](./../Saas.Identity/Saas.Permissions/readme.md#how--to-deploy-saas-permissions-service-api-to-azure)*.
|
||||
|
||||
## Debugging in Azure
|
||||
|
||||
The guidelines are identity to *[Debugging in Azure](./../Saas.Identity/Saas.Permissions/readme.md#debugging-in-azure)* for the SaaS Permissions Service API.
|
||||
|
||||
Happy debugging!
|
|
@ -1,42 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace Saas.AspNetCore.Authorization.AuthHandlers
|
||||
{
|
||||
public class RouteBasedRoleCusomizer : IRoleCustomizer
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
public RouteBasedRoleCusomizer(IHttpContextAccessor httpContextAccessor, string routeName, bool includeOriginals = false)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
IncludeOriginals = includeOriginals;
|
||||
RouteName = routeName;
|
||||
}
|
||||
|
||||
public bool IncludeOriginals { get; internal set; }
|
||||
public string RouteName { get; protected set; }
|
||||
|
||||
public IEnumerable<string> CustomizeRoles(IEnumerable<string> allowedRoles)
|
||||
{
|
||||
HttpContext httpContext = _httpContextAccessor.HttpContext;
|
||||
string context = httpContext.GetRouteValue(RouteName) as string;
|
||||
|
||||
if (context != null && allowedRoles != null)
|
||||
{
|
||||
foreach (string role in allowedRoles)
|
||||
{
|
||||
yield return string.Format("{0}.{1}", context, role);
|
||||
}
|
||||
if (IncludeOriginals)
|
||||
{
|
||||
foreach (string role in allowedRoles)
|
||||
{
|
||||
yield return role;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Saas.AspNetCore.Authorization.ClaimTransformers
|
||||
{
|
||||
/// <summary>
|
||||
/// Transforms a custom claim in space delimited format to roles
|
||||
/// The user principal will factor in the custom roles when IsInRole is called
|
||||
/// </summary>
|
||||
public class ClaimToRoleTransformer : IClaimsTransformation
|
||||
{
|
||||
private readonly string _sourceClaimType;
|
||||
private readonly string _roleType;
|
||||
private readonly string _authType;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="sourceClaimType">Name of the space delimited claim to transform</param>
|
||||
/// <param name="roleClaimType">Type of the individual role claims generated</param>
|
||||
/// <param name="authType">Authentication type to set the new identity to</param>
|
||||
public ClaimToRoleTransformer(string sourceClaimType, string roleClaimType, string authType)
|
||||
{
|
||||
_sourceClaimType = sourceClaimType;
|
||||
_roleType = roleClaimType;
|
||||
_authType = authType;
|
||||
}
|
||||
|
||||
|
||||
public ClaimToRoleTransformer(IOptions<ClaimToRoleTransformerOptions> options)
|
||||
: this(options.Value.SourceClaimType, options.Value.RoleClaimType, options.Value.AuthenticationType)
|
||||
{
|
||||
}
|
||||
|
||||
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
|
||||
{
|
||||
System.Collections.Generic.IEnumerable<Claim> customClaims = principal.Claims.Where(c => _sourceClaimType.Equals(c.Type, StringComparison.OrdinalIgnoreCase));
|
||||
System.Collections.Generic.IEnumerable<Claim> roleClaims = customClaims.SelectMany(c =>
|
||||
{
|
||||
return c.Value.Split(' ').Select(s => new Claim(_roleType, s));
|
||||
});
|
||||
|
||||
if (!roleClaims.Any())
|
||||
{
|
||||
return Task.FromResult(principal);
|
||||
}
|
||||
|
||||
|
||||
ClaimsPrincipal transformed = new ClaimsPrincipal(principal);
|
||||
ClaimsIdentity rolesIdentity = new ClaimsIdentity(roleClaims, _authType, null, _roleType);
|
||||
transformed.AddIdentity(rolesIdentity);
|
||||
return Task.FromResult(transformed);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Saas.AspNetCore.Authorization.ClaimTransformers
|
||||
{
|
||||
public static class ClaimToRoleTransformerExtensions
|
||||
{
|
||||
public static IServiceCollection AddClaimToRoleTransformer(this IServiceCollection services, string sourceClaimType,
|
||||
string roleClaimType = ClaimToRoleTransformerOptions.DefaultRoleClaimType,
|
||||
string authenticationType = ClaimToRoleTransformerOptions.DefaultAuthenticationType)
|
||||
{
|
||||
services.AddOptions<ClaimToRoleTransformerOptions>()
|
||||
.Configure(options =>
|
||||
{
|
||||
options.RoleClaimType = roleClaimType;
|
||||
options.SourceClaimType = sourceClaimType;
|
||||
options.AuthenticationType = authenticationType;
|
||||
});
|
||||
|
||||
services.RegisterTransformer();
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddClaimToRoleTransformer(this IServiceCollection services,
|
||||
IConfiguration configurationSection, Action<ClaimToRoleTransformerOptions> configure = null)
|
||||
{
|
||||
services.Configure<ClaimToRoleTransformerOptions>(configurationSection);
|
||||
|
||||
if (configure != null)
|
||||
{
|
||||
services.Configure<ClaimToRoleTransformerOptions>(configure);
|
||||
}
|
||||
|
||||
services.RegisterTransformer();
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddClaimToRoleTransformer(this IServiceCollection services,
|
||||
IConfiguration configuration, string configSectionName,
|
||||
Action<ClaimToRoleTransformerOptions> configure = null)
|
||||
{
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentException("configuration");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(configSectionName))
|
||||
{
|
||||
throw new ArgumentException("configSectionName");
|
||||
}
|
||||
|
||||
IConfigurationSection section = configuration.GetSection(configSectionName);
|
||||
|
||||
AddClaimToRoleTransformer(services, section, configure);
|
||||
return services;
|
||||
}
|
||||
|
||||
private static void RegisterTransformer(this IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<IClaimsTransformation, ClaimToRoleTransformer>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
namespace Saas.AspNetCore.Authorization.ClaimTransformers
|
||||
{
|
||||
public class ClaimToRoleTransformerOptions
|
||||
{
|
||||
public const string ConfigSectionName = "ClaimToRoleTransformer";
|
||||
public const string DefaultRoleClaimType = "CustomRole";
|
||||
public const string DefaultAuthenticationType = "CustomRoleAuthentication";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Name of the space delimited claim to transform
|
||||
/// </summary>
|
||||
public string SourceClaimType { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Type of the individual role claims generated
|
||||
/// </summary>
|
||||
public string RoleClaimType { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Authentication type to set the new identity to
|
||||
/// </summary>
|
||||
public string AuthenticationType { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
|
@ -1,29 +1,41 @@
|
|||
@description('Version')
|
||||
param version string
|
||||
|
||||
@description('URL for downstream admin service.')
|
||||
param appSettingsAdminServiceBaseUrl string
|
||||
|
||||
@description('The name of the key vault')
|
||||
param keyVaultName string
|
||||
|
||||
@description('URL for downstream admin service.')
|
||||
param azureB2CDomain string
|
||||
// @description('URL for downstream admin service.')
|
||||
// param azureB2CInstance string
|
||||
|
||||
@description('The B2C login endpoint in format of https://(Tenant Name).b2clogin.com.')
|
||||
param azureB2CLoginEndpoint string
|
||||
// @description('URL for downstream admin service.')
|
||||
// param azureB2CClientId string
|
||||
|
||||
@description('Tenant Id found on your AD B2C dashboard.')
|
||||
param azureB2CTenantId string
|
||||
// @description('URL for downstream admin service.')
|
||||
// param azureB2CDomain string
|
||||
|
||||
@description('The Client Id found on registered Permissions API app page.')
|
||||
param permissionApiClientId string
|
||||
// @description('The B2C login endpoint in format of https://(Tenant Name).b2clogin.com.')
|
||||
// param azureB2CLoginEndpoint string
|
||||
|
||||
@description('Permissions API Certificate Name')
|
||||
param permissionCertificateName string
|
||||
// @description('Tenant Id found on your AD B2C dashboard.')
|
||||
// param azureB2CTenantId string
|
||||
|
||||
@description('Permissions API Instance')
|
||||
param permissionInstance string
|
||||
// @description('The Azure B2C Signed Out Call Back Path.')
|
||||
// param signedOutCallBackPath string
|
||||
|
||||
// @description('The Azure B2C Sign up/in Policy Id.')
|
||||
// param signUpSignInPolicyId string
|
||||
|
||||
// @description('The Azure B2C Permissions API base Url.')
|
||||
// param permissionsBaseUrl string
|
||||
|
||||
// @description('The Client Id found on registered Permissions API app page.')
|
||||
// param permissionApiClientId string
|
||||
|
||||
// @description('Permissions API Certificate Name')
|
||||
// param permissionCertificateName string
|
||||
|
||||
// @description('Permissions API Instance')
|
||||
// param permissionInstance string
|
||||
|
||||
@description('Select an admin account name used for resource creation.')
|
||||
param sqlAdministratorLogin string
|
||||
|
@ -31,8 +43,8 @@ param sqlAdministratorLogin string
|
|||
@description('User Identity Name')
|
||||
param userAssignedIdentityName string
|
||||
|
||||
@description('Key Vault Url')
|
||||
param keyVaultUrl string
|
||||
// @description('Key Vault Url')
|
||||
// param keyVaultUrl string
|
||||
|
||||
@description('App Configuration Name')
|
||||
param appConfigurationName string
|
||||
|
@ -47,24 +59,17 @@ param permissionsSqlServerFQDN string
|
|||
@secure()
|
||||
param sqlAdministratorLoginPassword string
|
||||
|
||||
// @description('PermissionsAPI key name')
|
||||
// param permissionsApiKeyName string
|
||||
|
||||
resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' existing = {
|
||||
name: userAssignedIdentityName
|
||||
}
|
||||
|
||||
// Create object with array of objects containing the kayname and value to be stored in Azure App Configuration store.
|
||||
|
||||
var azureB2C = 'AzureB2C'
|
||||
var permissionApi = 'PermissionApi'
|
||||
var msGraph = 'MsGraph'
|
||||
var sql = 'Sql'
|
||||
|
||||
var permissionCertificates = [
|
||||
{
|
||||
SourceType: keyVaultName
|
||||
KeyVaultUrl: keyVaultUrl
|
||||
KeyVaultCertificateName: permissionCertificateName
|
||||
}
|
||||
]
|
||||
var msGraphKeyName = 'MsGraph'
|
||||
var sqlKeyName = 'Sql'
|
||||
|
||||
var appConfigStore = {
|
||||
appConfigurationName: appConfigurationName
|
||||
|
@ -73,103 +78,31 @@ var appConfigStore = {
|
|||
label: version
|
||||
entries: [
|
||||
{
|
||||
key: '${azureB2C}:AdminServiceBaseUrl'
|
||||
value: appSettingsAdminServiceBaseUrl
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${azureB2C}:Domain'
|
||||
value: azureB2CDomain
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${azureB2C}:LoginEndpoint'
|
||||
value: azureB2CLoginEndpoint
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${azureB2C}:TenantId'
|
||||
value: azureB2CTenantId
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionApi}:ClientId'
|
||||
value: permissionApiClientId
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionApi}:TenantId'
|
||||
value: azureB2CTenantId
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionApi}:Domain'
|
||||
value: azureB2CDomain
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionApi}:Instance'
|
||||
value: permissionInstance
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionApi}:Audience'
|
||||
value: permissionApiClientId
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionApi}:CallbackPath'
|
||||
value: '/signin-oidc'
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionApi}:SignedOutCallbackPath'
|
||||
value: '/signout-oidc'
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionApi}:Certificates'
|
||||
value: replace('${permissionCertificates}', '\'','"') // replace single quotes with double quotes in the json string
|
||||
isSecret: false
|
||||
contentType: 'application/json'
|
||||
}
|
||||
{
|
||||
key: '${sql}:SQLAdministratorLoginName'
|
||||
key: '${sqlKeyName}:SQLAdministratorLoginName'
|
||||
value: sqlAdministratorLogin
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${sql}:SQLAdministratorLoginPassword'
|
||||
key: '${sqlKeyName}:SQLAdministratorLoginPassword'
|
||||
value: sqlAdministratorLoginPassword
|
||||
isSecret: true
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${sql}:SQLConnectionString'
|
||||
key: '${sqlKeyName}:SQLConnectionString'
|
||||
value: 'Data Source=tcp:${permissionsSqlServerFQDN},1433;Initial Catalog=${permissionsSqlDatabaseName};User Id=${sqlAdministratorLogin}@${permissionsSqlServerFQDN};Password=${sqlAdministratorLoginPassword};'
|
||||
isSecret: true
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${msGraph}:BaseUrl'
|
||||
key: '${msGraphKeyName}:BaseUrl'
|
||||
value: 'https://graph.microsoft.com/v1.0'
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${msGraph}:Scopes'
|
||||
key: '${msGraphKeyName}:Scopes'
|
||||
value: 'https://graph.microsoft.com/.default'
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
|
@ -178,8 +111,8 @@ var appConfigStore = {
|
|||
}
|
||||
|
||||
// Adding App Configuration entries
|
||||
module appConfigurationSettings 'addConfigEntry.bicep' = [ for entry in appConfigStore.entries: {
|
||||
name: replace('AppConfigurationSettings-${entry.key}', ':', '-')
|
||||
module appConfigurationSettings './../../../../../../Saas.Lib/Saas.Bicep.Module/addConfigEntry.bicep' = [ for entry in appConfigStore.entries: {
|
||||
name: replace('Entry-${entry.key}', ':', '-')
|
||||
params: {
|
||||
appConfigurationName: appConfigStore.appConfigurationName
|
||||
userAssignedIdentityName: appConfigStore.userAssignedIdentityName
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
@description('The App Service Plan ID.')
|
||||
param appServicePlanName string
|
||||
|
||||
@description('The location for all resources.')
|
||||
param location string
|
||||
|
||||
@description('Azure App Configuration User Assigned Identity Name.')
|
||||
param userAssignedIdentityName string
|
||||
|
||||
|
||||
@description('The name of the Log Analytics Workspace used by Application Insigths.')
|
||||
param logAnalyticsWorkspaceName string
|
||||
|
||||
@description('The name of the Automation Account.')
|
||||
param automationAccountName string
|
||||
|
||||
@description('The name of Application Insights.')
|
||||
param applicationInsightsName string
|
||||
|
||||
@description('App Service Plan OS')
|
||||
@allowed([
|
||||
'linux'
|
||||
'windows'
|
||||
])
|
||||
param appServicePlanOS string
|
||||
|
||||
resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' existing = {
|
||||
name: userAssignedIdentityName
|
||||
}
|
||||
|
||||
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
|
||||
name: logAnalyticsWorkspaceName
|
||||
location: location
|
||||
properties: {
|
||||
sku: {
|
||||
name: 'PerGB2018'
|
||||
}
|
||||
retentionInDays: 30
|
||||
features: {
|
||||
enableLogAccessUsingOnlyResourcePermissions: true
|
||||
}
|
||||
}
|
||||
identity: {
|
||||
type: 'UserAssigned'
|
||||
userAssignedIdentities: {
|
||||
'${userAssignedIdentity.id}': {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' = {
|
||||
name: automationAccountName
|
||||
location: location
|
||||
properties: {
|
||||
sku: {
|
||||
name: 'Basic'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var automationAccountLinkedWorkspaceName = 'Automation'
|
||||
|
||||
resource automationAccountLinkedWorkspace 'Microsoft.OperationalInsights/workspaces/linkedServices@2020-08-01' = {
|
||||
name: '${logAnalyticsWorkspace.name}/${automationAccountLinkedWorkspaceName}'
|
||||
properties: {
|
||||
resourceId: automationAccount.id
|
||||
}
|
||||
}
|
||||
|
||||
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
|
||||
name: applicationInsightsName
|
||||
location: location
|
||||
kind: 'web'
|
||||
properties: {
|
||||
Application_Type: 'web'
|
||||
publicNetworkAccessForIngestion: 'Enabled'
|
||||
publicNetworkAccessForQuery: 'Enabled'
|
||||
WorkspaceResourceId: logAnalyticsWorkspace.id
|
||||
}
|
||||
}
|
||||
|
||||
resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
|
||||
name: appServicePlanName
|
||||
location: location
|
||||
kind: appServicePlanOS
|
||||
sku: {
|
||||
name: 'S1'
|
||||
}
|
||||
properties: {
|
||||
reserved: ((appServicePlanOS == 'linux') ? true : false)
|
||||
}
|
||||
}
|
||||
|
||||
// resource appConfig 'Microsoft.AppConfiguration/configurationStores@2022-05-01' existing = {
|
||||
// name: appConfigurationName
|
||||
// }
|
||||
|
||||
// // metadata :[
|
||||
// // {
|
||||
// // name:'CURRENT_STACK'
|
||||
// // value:'dotnetcode'
|
||||
// // }
|
||||
// // ]
|
||||
|
||||
// resource permissionsApi 'Microsoft.Web/sites@2022-03-01' = {
|
||||
// name: permissionsApiName
|
||||
// location: location
|
||||
// kind: 'app,windows'
|
||||
// properties: {
|
||||
// serverFarmId: appServicePlan.name
|
||||
// httpsOnly: true
|
||||
// // clientCertEnabled: true // https://learn.microsoft.com/en-us/azure/app-service/app-service-web-configure-tls-mutual-auth?tabs=bicep
|
||||
// clientCertMode: 'Required'
|
||||
// siteConfig: {
|
||||
// ftpsState: 'FtpsOnly'
|
||||
// alwaysOn: true
|
||||
// http20Enabled: true
|
||||
// keyVaultReferenceIdentity: userAssignedIdentity.id // Must specify this when using User Assigned Managed Identity. Read here: https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references?tabs=azure-cli#access-vaults-with-a-user-assigned-identity
|
||||
// detailedErrorLoggingEnabled: true
|
||||
// netFrameworkVersion: 'v7.0'
|
||||
// // linuxFxVersion: 'DOTNETCORE|7.0'
|
||||
// }
|
||||
// }
|
||||
// identity: {
|
||||
// type: 'UserAssigned'
|
||||
// userAssignedIdentities: {
|
||||
// '${userAssignedIdentity.id}': {}
|
||||
// }
|
||||
// }
|
||||
// resource appsettings 'config@2022-03-01' = {
|
||||
// name: 'appsettings'
|
||||
// properties: {
|
||||
// Version: 'ver${version}'
|
||||
// Logging__LogLevel__Default: 'Information'
|
||||
// Logging__LogLevel__Microsoft__AspNetCore: 'Warning'
|
||||
// KeyVault__Url: keyVaultUri
|
||||
// ASPNETCORE_ENVIRONMENT: environment
|
||||
// UserAssignedManagedIdentityClientId: userAssignedIdentity.properties.clientId
|
||||
// AppConfiguration__Endpoint : appConfig.properties.endpoint
|
||||
// APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString // https://learn.microsoft.com/en-us/azure/azure-monitor/app/migrate-from-instrumentation-keys-to-connection-strings
|
||||
// ApplicationInsightsAgent_EXTENSION_VERSION: '~2'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// resource diagnosticsSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
|
||||
// name: 'string'
|
||||
// scope: permissionsApi
|
||||
// properties: {
|
||||
// logs: [
|
||||
// {
|
||||
// categoryGroup: 'allLogs'
|
||||
// enabled: true
|
||||
// retentionPolicy: {
|
||||
// days: 7
|
||||
// enabled: true
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// metrics: [
|
||||
// {
|
||||
// category: 'AllMetrics'
|
||||
// enabled: true
|
||||
// }
|
||||
// ]
|
||||
// workspaceId: logAnalyticsWorkspace.id
|
||||
// }
|
||||
// }
|
||||
|
||||
// // resource permissionsApiStagingSlot 'Microsoft.Web/sites/slots@2022-03-01' = {
|
||||
// // name: 'PermissionsApi-Staging'
|
||||
// // parent: permissionsApi
|
||||
// // location: location
|
||||
// // kind: 'app,linux'
|
||||
// // properties: {
|
||||
// // serverFarmId: appServicePlan.name
|
||||
// // httpsOnly: true
|
||||
// // siteConfig: {
|
||||
// // alwaysOn: true
|
||||
// // linuxFxVersion: 'DOTNETCORE|7.0'
|
||||
// // http20Enabled: true
|
||||
// // }
|
||||
// // }
|
||||
// // identity: {
|
||||
// // type: 'UserAssigned'
|
||||
// // userAssignedIdentities: { '${userAssignedIdentity.id}': {} }
|
||||
// // }
|
||||
// // resource appsettings 'config@2022-03-01' = {
|
||||
// // name: 'appsettings'
|
||||
// // properties: {
|
||||
// // Version: version
|
||||
// // Logging__LogLevel__Default: 'Information'
|
||||
// // Logging__LogLevel__Microsoft__AspNetCore: 'Warning'
|
||||
// // KeyVault__Url: keyVaultUri
|
||||
// // ASPNETCORE_ENVIRONMENT: 'Development'
|
||||
// // UserAssignedManagedIdentityClientId: userAssignedIdentity.properties.clientId
|
||||
// // AppConfiguration__Endpoint : appConfig.properties.endpoint
|
||||
// // }
|
||||
// // }
|
||||
// // resource metadata 'config@2022-03-01' = {
|
||||
// // name: 'metadata'
|
||||
// // properties: {
|
||||
// // CURRENT_STACK: 'dotnet'
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // Resource - Permissions Api - Deployment
|
||||
// //////////////////////////////////////////////////
|
||||
// // resource permissionsApiDeployment 'Microsoft.Web/sites/extensions@2021-03-01' = {
|
||||
// // parent: permissionsApi
|
||||
// // name: 'MSDeploy'
|
||||
// // properties: {
|
||||
// // packageUri: 'https://stsaasdev001.blob.${environment().suffixes.storage}/artifacts/saas-provider/Saas.Provider.Web.zip?sv=2020-04-08&st=2021-06-07T19%3A23%3A20Z&se=2022-06-08T19%3A23%3A00Z&sr=c&sp=rl&sig=kNf0qwTfaCJg02xYeUHlfmHOJvI1bGU1HftjUJ5hl5o%3D'
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // Outputs
|
||||
// //////////////////////////////////////////////////
|
||||
// output permissionsApiHostName string = permissionsApi.properties.defaultHostName
|
||||
output appServicePlanName string = appServicePlan.name
|
|
@ -1,219 +0,0 @@
|
|||
// Parameters
|
||||
//////////////////////////////////////////////////
|
||||
@description('Version')
|
||||
param version string
|
||||
|
||||
@description('The App Service Plan ID.')
|
||||
param appServicePlanName string
|
||||
|
||||
@description('The Uri of the Key Vault.')
|
||||
param keyVaultUri string
|
||||
|
||||
@description('The location for all resources.')
|
||||
param location string
|
||||
|
||||
@description('The Permissions Api name.')
|
||||
param permissionsApiName string
|
||||
|
||||
@description('Azure App Configuration User Assigned Identity Name.')
|
||||
param userAssignedIdentityName string
|
||||
|
||||
@description('The name of the Azure App Configuration.')
|
||||
param appConfigurationName string
|
||||
|
||||
@description('The name of the Log Analytics Workspace used by Application Insigths.')
|
||||
param logAnalyticsWorkspaceName string
|
||||
|
||||
@description('The name of the Automation Account.')
|
||||
param automationAccountName string
|
||||
|
||||
@description('The name of Application Insights.')
|
||||
param applicationInsightsName string
|
||||
|
||||
resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' existing = {
|
||||
name: userAssignedIdentityName
|
||||
}
|
||||
|
||||
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
|
||||
name: logAnalyticsWorkspaceName
|
||||
location: location
|
||||
properties: {
|
||||
sku: {
|
||||
name: 'PerGB2018'
|
||||
}
|
||||
retentionInDays: 30
|
||||
features: {
|
||||
enableLogAccessUsingOnlyResourcePermissions: true
|
||||
}
|
||||
}
|
||||
identity: {
|
||||
type: 'UserAssigned'
|
||||
userAssignedIdentities: {
|
||||
'${userAssignedIdentity.id}': {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' = {
|
||||
name: automationAccountName
|
||||
location: location
|
||||
properties: {
|
||||
sku: {
|
||||
name: 'Basic'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var automationAccountLinkedWorkspaceName = 'Automation'
|
||||
|
||||
resource automationAccountLinkedWorkspace 'Microsoft.OperationalInsights/workspaces/linkedServices@2020-08-01' = {
|
||||
name: '${logAnalyticsWorkspace.name}/${automationAccountLinkedWorkspaceName}'
|
||||
properties: {
|
||||
resourceId: automationAccount.id
|
||||
}
|
||||
}
|
||||
|
||||
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
|
||||
name: applicationInsightsName
|
||||
location: location
|
||||
kind: 'web'
|
||||
properties: {
|
||||
Application_Type: 'web'
|
||||
publicNetworkAccessForIngestion: 'Enabled'
|
||||
publicNetworkAccessForQuery: 'Enabled'
|
||||
WorkspaceResourceId: logAnalyticsWorkspace.id
|
||||
}
|
||||
}
|
||||
|
||||
resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
|
||||
name: appServicePlanName
|
||||
location: location
|
||||
kind: 'windows'
|
||||
sku: {
|
||||
name: 'S1'
|
||||
}
|
||||
properties: {
|
||||
reserved: false // change to true to enable request Linux rather than Windows. Go figure :)
|
||||
}
|
||||
}
|
||||
|
||||
resource appConfig 'Microsoft.AppConfiguration/configurationStores@2022-05-01' existing = {
|
||||
name: appConfigurationName
|
||||
}
|
||||
|
||||
resource permissionsApi 'Microsoft.Web/sites@2022-03-01' = {
|
||||
name: permissionsApiName
|
||||
location: location
|
||||
kind: 'app,windows'
|
||||
properties: {
|
||||
serverFarmId: appServicePlan.name
|
||||
httpsOnly: true
|
||||
// clientCertEnabled: true // https://learn.microsoft.com/en-us/azure/app-service/app-service-web-configure-tls-mutual-auth?tabs=bicep
|
||||
clientCertMode: 'Required'
|
||||
siteConfig: {
|
||||
ftpsState: 'FtpsOnly'
|
||||
alwaysOn: true
|
||||
// linuxFxVersion: 'DOTNETCORE|7.0'
|
||||
http20Enabled: true
|
||||
keyVaultReferenceIdentity: userAssignedIdentity.id // Must specify this when using User Assigned Managed Identity. Read here: https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references?tabs=azure-cli#access-vaults-with-a-user-assigned-identity
|
||||
detailedErrorLoggingEnabled: true
|
||||
}
|
||||
}
|
||||
identity: {
|
||||
type: 'UserAssigned'
|
||||
userAssignedIdentities: {
|
||||
'${userAssignedIdentity.id}': {}
|
||||
}
|
||||
}
|
||||
resource appsettings 'config@2022-03-01' = {
|
||||
name: 'appsettings'
|
||||
properties: {
|
||||
Version: 'ver${version}'
|
||||
Logging__LogLevel__Default: 'Information'
|
||||
Logging__LogLevel__Microsoft__AspNetCore: 'Warning'
|
||||
KeyVault__Url: keyVaultUri
|
||||
ASPNETCORE_ENVIRONMENT: 'Production'
|
||||
UserAssignedManagedIdentityClientId: userAssignedIdentity.properties.clientId
|
||||
AppConfiguration__Endpoint : appConfig.properties.endpoint
|
||||
APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString // https://learn.microsoft.com/en-us/azure/azure-monitor/app/migrate-from-instrumentation-keys-to-connection-strings
|
||||
ApplicationInsightsAgent_EXTENSION_VERSION: '~2'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource diagnosticsSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
|
||||
name: 'string'
|
||||
scope: permissionsApi
|
||||
properties: {
|
||||
logs: [
|
||||
{
|
||||
categoryGroup: 'allLogs'
|
||||
enabled: true
|
||||
retentionPolicy: {
|
||||
days: 7
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
]
|
||||
metrics: [
|
||||
{
|
||||
category: 'AllMetrics'
|
||||
enabled: true
|
||||
}
|
||||
]
|
||||
workspaceId: logAnalyticsWorkspace.id
|
||||
}
|
||||
}
|
||||
|
||||
// resource permissionsApiStagingSlot 'Microsoft.Web/sites/slots@2022-03-01' = {
|
||||
// name: 'PermissionsApi-Staging'
|
||||
// parent: permissionsApi
|
||||
// location: location
|
||||
// kind: 'app,linux'
|
||||
// properties: {
|
||||
// serverFarmId: appServicePlan.name
|
||||
// httpsOnly: true
|
||||
// siteConfig: {
|
||||
// alwaysOn: true
|
||||
// linuxFxVersion: 'DOTNETCORE|7.0'
|
||||
// http20Enabled: true
|
||||
// }
|
||||
// }
|
||||
// identity: {
|
||||
// type: 'UserAssigned'
|
||||
// userAssignedIdentities: { '${userAssignedIdentity.id}': {} }
|
||||
// }
|
||||
// resource appsettings 'config@2022-03-01' = {
|
||||
// name: 'appsettings'
|
||||
// properties: {
|
||||
// Version: version
|
||||
// Logging__LogLevel__Default: 'Information'
|
||||
// Logging__LogLevel__Microsoft__AspNetCore: 'Warning'
|
||||
// KeyVault__Url: keyVaultUri
|
||||
// ASPNETCORE_ENVIRONMENT: 'Development'
|
||||
// UserAssignedManagedIdentityClientId: userAssignedIdentity.properties.clientId
|
||||
// AppConfiguration__Endpoint : appConfig.properties.endpoint
|
||||
// }
|
||||
// }
|
||||
// resource metadata 'config@2022-03-01' = {
|
||||
// name: 'metadata'
|
||||
// properties: {
|
||||
// CURRENT_STACK: 'dotnet'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Resource - Permissions Api - Deployment
|
||||
//////////////////////////////////////////////////
|
||||
// resource permissionsApiDeployment 'Microsoft.Web/sites/extensions@2021-03-01' = {
|
||||
// parent: permissionsApi
|
||||
// name: 'MSDeploy'
|
||||
// properties: {
|
||||
// packageUri: 'https://stsaasdev001.blob.${environment().suffixes.storage}/artifacts/saas-provider/Saas.Provider.Web.zip?sv=2020-04-08&st=2021-06-07T19%3A23%3A20Z&se=2022-06-08T19%3A23%3A00Z&sr=c&sp=rl&sig=kNf0qwTfaCJg02xYeUHlfmHOJvI1bGU1HftjUJ5hl5o%3D'
|
||||
// }
|
||||
// }
|
||||
|
||||
// Outputs
|
||||
//////////////////////////////////////////////////
|
||||
output permissionsApiHostName string = permissionsApi.properties.defaultHostName
|
||||
output appServicePlanName string = appServicePlan.name
|
|
@ -1,11 +1,17 @@
|
|||
@description('Version')
|
||||
param version string
|
||||
|
||||
@description('Environment')
|
||||
@allowed([
|
||||
'Development'
|
||||
'Staging'
|
||||
'Production'
|
||||
])
|
||||
param environment string
|
||||
|
||||
@description('The ip address of the dev machine')
|
||||
param devMachineIp string
|
||||
|
||||
@description('URL for downstream admin service.')
|
||||
param appSettingsAdminServiceBaseUrl string
|
||||
|
||||
@description('postfix')
|
||||
param solutionPostfix string
|
||||
|
@ -19,27 +25,6 @@ param solutionName string
|
|||
@description('The name of the key vault')
|
||||
param keyVaultName string
|
||||
|
||||
@description('URL for downstream admin service.')
|
||||
param azureB2CDomain string
|
||||
|
||||
@description('The B2C login endpoint in format of https://(Tenant Name).b2clogin.com.')
|
||||
param azureB2CLoginEndpoint string
|
||||
|
||||
@description('Tenant Id found on your AD B2C dashboard.')
|
||||
param azureB2CTenantId string
|
||||
|
||||
@description('The Client Id found on registered Permissions API app page.')
|
||||
param permissionApiClientId string
|
||||
|
||||
@description('Permissions API Certificate Name')
|
||||
param permissionCertificateName string
|
||||
|
||||
@description('Permissions API Instance')
|
||||
param permissionInstance string
|
||||
|
||||
@description('Permission API Name')
|
||||
param permissionsApiName string
|
||||
|
||||
@description('Permissions API Secret key')
|
||||
param permissionApiKey string
|
||||
|
||||
|
@ -49,6 +34,7 @@ param sqlAdministratorLogin string
|
|||
@description('The location for all resources.')
|
||||
param location string = resourceGroup().location
|
||||
|
||||
var appServicePlanOS = 'windows'
|
||||
var appServicePlanName = 'plan-${solutionPrefix}-${solutionName}-${solutionPostfix}'
|
||||
var appConfigurationName = 'appconfig-${solutionPrefix}-${solutionName}-${solutionPostfix}'
|
||||
var permissionsSqlDatabaseName = 'sqldb-permissions-${solutionPrefix}-${solutionName}-${solutionPostfix}'
|
||||
|
@ -85,8 +71,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
|
|||
name: keyVaultName
|
||||
}
|
||||
|
||||
// Create object w/ array of objects containing the kayname and value to be stored in Azure App Configuration store.
|
||||
var permissionApi = 'PermissionApi'
|
||||
|
||||
|
||||
module appConfigurationModule './Module/appConfigurationStore.bicep' = {
|
||||
name: 'AppConfigurationDeployment'
|
||||
|
@ -106,6 +91,9 @@ module keyVaultAccessPolicyModule 'Module/keyVaultAccessRBAC.bicep' = {
|
|||
keyVault
|
||||
]
|
||||
}
|
||||
|
||||
// Create object w/ array of objects containing the kayname and value to be stored in Azure App Configuration store.
|
||||
var permissionsApiKeyName = 'PermissionsApi'
|
||||
module restApiKeyModule './Module/linkToExistingKeyVaultSecret.bicep' = {
|
||||
name: 'PermissionApiKeyDeployment'
|
||||
params: {
|
||||
|
@ -114,7 +102,7 @@ module restApiKeyModule './Module/linkToExistingKeyVaultSecret.bicep' = {
|
|||
appConfigurationName: appConfigurationName
|
||||
userAssignedIdentityName: userAssignedIdentity.name
|
||||
keyVaultKeyName: permissionApiKey
|
||||
keyName: '${permissionApi}:apiKey'
|
||||
keyName: '${permissionsApiKeyName}:ApiKey'
|
||||
}
|
||||
dependsOn: [
|
||||
keyVaultAccessPolicyModule
|
||||
|
@ -127,16 +115,13 @@ resource appConfigurationStore 'Microsoft.AppConfiguration/configurationStores@2
|
|||
name: appConfigurationName
|
||||
}
|
||||
|
||||
module permissionsApiModule './Module/permissionsApi.bicep' = {
|
||||
module appPlanModule './Module/appPlan.bicep' = {
|
||||
name: 'PermissionsApiDeployment'
|
||||
params: {
|
||||
version: version
|
||||
appServicePlanOS: appServicePlanOS
|
||||
appServicePlanName: appServicePlanName
|
||||
keyVaultUri: keyVault.properties.vaultUri
|
||||
location: location
|
||||
permissionsApiName: permissionsApiName
|
||||
userAssignedIdentityName: userAssignedIdentity.name
|
||||
appConfigurationName: appConfigurationStore.name
|
||||
applicationInsightsName: applicationInsightsName
|
||||
logAnalyticsWorkspaceName: logAnalyticsWorkspaceName
|
||||
automationAccountName: automationAccountName
|
||||
|
@ -153,17 +138,9 @@ module configurationEntriesModule './Module/addConfigEntries.bicep' = {
|
|||
name: 'ConfigurationEntriesDeployment'
|
||||
params: {
|
||||
version: version
|
||||
appSettingsAdminServiceBaseUrl: appSettingsAdminServiceBaseUrl
|
||||
keyVaultName: keyVault.name
|
||||
azureB2CDomain: azureB2CDomain
|
||||
azureB2CLoginEndpoint: azureB2CLoginEndpoint
|
||||
azureB2CTenantId: azureB2CTenantId
|
||||
permissionApiClientId: permissionApiClientId
|
||||
permissionCertificateName: permissionCertificateName
|
||||
permissionInstance: permissionInstance
|
||||
sqlAdministratorLogin: sqlAdministratorLogin
|
||||
userAssignedIdentityName: userAssignedIdentity.name
|
||||
keyVaultUrl: keyVault.properties.vaultUri
|
||||
appConfigurationName: appConfigurationStore.name
|
||||
permissionsSqlDatabaseName: permissionsSqlDatabaseName
|
||||
permissionsSqlServerFQDN: permissionsSqlModule.outputs.permissionsSqlServerFQDN
|
||||
|
@ -174,21 +151,21 @@ module configurationEntriesModule './Module/addConfigEntries.bicep' = {
|
|||
keyVaultAccessPolicyModule
|
||||
keyVault
|
||||
restApiKeyModule
|
||||
permissionsApiModule
|
||||
appPlanModule
|
||||
]
|
||||
}
|
||||
|
||||
output version string = version
|
||||
output location string = location
|
||||
output environment string = environment
|
||||
output appConfigurationName string = appConfigurationName
|
||||
output keyVaultName string = keyVault.name
|
||||
output keyVaultUri string = keyVault.properties.vaultUri
|
||||
output appServicePlanName string = permissionsApiModule.outputs.appServicePlanName
|
||||
output appServicePlanName string = appPlanModule.outputs.appServicePlanName
|
||||
output permissionsSqlServerName string = permissionsSqlModule.outputs.permissionsSqlServerName
|
||||
output userAssignedIdentityName string = userAssignedIdentity.name
|
||||
output userAssignedIdentityId string = userAssignedIdentity.id
|
||||
output permissionsSqlServerFQDN string = permissionsSqlModule.outputs.permissionsSqlServerFQDN
|
||||
output permissionsApiHostName string = permissionsApiModule.outputs.permissionsApiHostName
|
||||
output applicationInsightsName string = applicationInsightsName
|
||||
output logAnalyticsWorkspaceName string = logAnalyticsWorkspaceName
|
||||
output automationAccountName string = automationAccountName
|
||||
|
|
|
@ -1,7 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
force_update=false
|
||||
|
||||
while getopts f flag
|
||||
do
|
||||
case "${flag}" in
|
||||
f) force_update=true;;
|
||||
*) force_update=false;;
|
||||
esac
|
||||
done
|
||||
|
||||
repo_base="$( git rev-parse --show-toplevel )"
|
||||
docker_file_folder="${repo_base}/src/Saas.lib/Deployment.Container"
|
||||
|
||||
|
||||
# redirect to build.sh in the Deployment.Container folder
|
||||
"${docker_file_folder}/build.sh"
|
||||
if [[ "${force_update}" == false ]]; then
|
||||
"${docker_file_folder}/build.sh"
|
||||
else
|
||||
"${docker_file_folder}/build.sh" -f
|
||||
fi
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
}
|
||||
},
|
||||
"version": "0.8.0",
|
||||
"environment": "development",
|
||||
"production": false,
|
||||
"environment": "Production",
|
||||
"git": {
|
||||
"branch": "main"
|
||||
},
|
||||
|
@ -42,6 +41,8 @@
|
|||
}
|
||||
},
|
||||
"azureb2c": {
|
||||
"signedOutCallBackPath": "/signout/B2C_1A_SIGNUP_SIGNIN",
|
||||
"signUpSignInPolicyId": "B2C_1A_SIGNUP_SIGNIN",
|
||||
"policyKeys": [
|
||||
{
|
||||
"name": "TokenSigningKeyContainer",
|
||||
|
@ -62,10 +63,16 @@
|
|||
"keyUsage": "Signature"
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
"claimToRoleTransformer": {
|
||||
"authenticationType": "MyCustomRoleAuth",
|
||||
"roleClaimType": "MyCustomRole",
|
||||
"sourceClaimType": "permissions"
|
||||
},
|
||||
"appRegistrations": [
|
||||
{
|
||||
"name": "admin-api",
|
||||
"appServiceName": null,
|
||||
"certificate": false,
|
||||
"redirectUri": null,
|
||||
"applicationIdUri": null,
|
||||
|
@ -117,6 +124,7 @@
|
|||
},
|
||||
{
|
||||
"name": "signupadmin-app",
|
||||
"appServiceName": null,
|
||||
"certificate": true,
|
||||
"redirectUri": null,
|
||||
"appId": null,
|
||||
|
@ -148,6 +156,7 @@
|
|||
},
|
||||
{
|
||||
"name": "saas-app",
|
||||
"appServiceName": null,
|
||||
"certificate": true,
|
||||
"redirectUri": null,
|
||||
"appId": null,
|
||||
|
@ -174,6 +183,8 @@
|
|||
},
|
||||
{
|
||||
"name": "permissions-api",
|
||||
"baseUrl": null,
|
||||
"appServiceName": null,
|
||||
"apiName": null,
|
||||
"appId": null,
|
||||
"objectId": null,
|
||||
|
@ -182,7 +193,6 @@
|
|||
"redirectUri": null,
|
||||
"permissionsApiUrl": null,
|
||||
"rolesApiUrl": null,
|
||||
"instance": "https://login.microsoftonline.com/",
|
||||
"publicKeyPath": null,
|
||||
"certificateKeyName": null,
|
||||
"scopes": null,
|
||||
|
@ -195,15 +205,8 @@
|
|||
"offline_access"
|
||||
],
|
||||
"appRoles": [
|
||||
"User.Read.All"
|
||||
]
|
||||
},
|
||||
{
|
||||
"grantAdminConsent": true,
|
||||
"resourceId": "00000003-0000-0000-c000-000000000000",
|
||||
"scopes": [
|
||||
"openid",
|
||||
"offline_access"
|
||||
"User.Read.All",
|
||||
"Application.ReadWrite.OwnedBy"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -266,6 +269,18 @@
|
|||
"sqlAdminLoginName": "sqlAdmin"
|
||||
},
|
||||
"deployment": {
|
||||
"identityFoundation": {
|
||||
"name": "IdentityFoundationDeployment"
|
||||
},
|
||||
"adminServiceApi": {
|
||||
"name": "AdminServiceAPIDeployment"
|
||||
},
|
||||
"signupAdministration": {
|
||||
"name": "SignupAdmininstrationDeployment"
|
||||
},
|
||||
"permissionApi": {
|
||||
"name": "PermissionApiDeployment"
|
||||
},
|
||||
"users": [],
|
||||
"azureCli": {
|
||||
"configDir": null
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
"parameters": {
|
||||
"version": {
|
||||
"value": null
|
||||
},
|
||||
"devMachineIp": {
|
||||
},
|
||||
"environment": {
|
||||
"value": null
|
||||
},
|
||||
"appSettingsAdminServiceBaseUrl": {
|
||||
"devMachineIp": {
|
||||
"value": null
|
||||
},
|
||||
"solutionPostfix": {
|
||||
|
@ -23,27 +23,6 @@
|
|||
"keyVaultName": {
|
||||
"value": null
|
||||
},
|
||||
"azureB2CDomain": {
|
||||
"value": null
|
||||
},
|
||||
"azureB2CLoginEndpoint": {
|
||||
"value": null
|
||||
},
|
||||
"azureB2CTenantId": {
|
||||
"value": null
|
||||
},
|
||||
"permissionsApiName": {
|
||||
"value": null
|
||||
},
|
||||
"permissionApiClientId": {
|
||||
"value": null
|
||||
},
|
||||
"permissionInstance": {
|
||||
"value": null
|
||||
},
|
||||
"permissionCertificateName": {
|
||||
"value": null
|
||||
},
|
||||
"permissionApiKey": {
|
||||
"value": null
|
||||
},
|
||||
|
|
|
@ -16,9 +16,11 @@ The purpose of the Azure SaaS Dev Kit is to boost your SaaS journey by providing
|
|||
|
||||
*Answer*: Yes, and no. In fact Bicep is used when ever possible as part of the deployment script. Yet, the ASDK Identity Foundation relies on [Azure Active Directory B2C](https://learn.microsoft.com/en-us/azure/active-directory-b2c/overview) as well as defining [Azure Active Directory App Registrations](https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-how-applications-are-added). At the time of writing those resources and their configurations cannot be automated by ARM and Bicep.
|
||||
|
||||
## Running the Deployment Script in a Container, Using Docker (recommended)
|
||||
## Running the Deployment Script
|
||||
|
||||
Running the deployment script utilizing a container is highly recommend. Using a container will ensure that you have all the required dependencies in their correct configurations etc. In short; when the script runs and that you are running in a controlled environment. It will also minimize the chances that some other properties of your existing environment interferes with the script or that the script inadvertently interferes with your existing environment.
|
||||
Running the deployment script requires utilizing a container and Docker.
|
||||
|
||||
Using a container will ensure that you have all the required dependencies in their correct configurations in a controlled environment. This will also minimize the chances that some other properties of your existing environment might with the script, or that the script inadvertently will interferes with your existing environment.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
|
@ -43,7 +45,7 @@ No matter the operating system you're using, you will need these tools to be ins
|
|||
- [**Docker Desktop**](https://docs.docker.com/get-docker/).
|
||||
- If you have Docker already, make sure to get the latest updates before you begin. If you have Docker installed but haven't used it for a while. Reinstalling will often solve potential issues.
|
||||
- [Azure Command Line Interface (**az cli**)](https://learn.microsoft.com/en-us/cli/azure/what-is-azure-cli) from the terminal: [How to install the Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli).
|
||||
- [GitHub’s official command line tool (**gh**)]([GitHub CLI | Take GitHub to the command line](https://cli.github.com/)). For more on installation see [here](https://github.com/cli/cli#installation).
|
||||
- [GitHub’s official command line tool (**gh**)](https://cli.github.com/). For more on installation see [here](https://github.com/cli/cli#installation).
|
||||
- **Zip** which can be installed with the command: `sudo apt install zip` .
|
||||
- Note: **zip** is already installed on MacOS per default.
|
||||
|
||||
|
@ -72,7 +74,9 @@ chmod +x setup.sh # only needed the first time to set execute permissions on set
|
|||
|
||||
This will take a few minutes to complete and you will only need to do it once. The container will be named `asdk-script-deployment`.
|
||||
|
||||
> Tip: If you make changes to `Dockerfile`, defining the container, you can update the container by running `./build.sh`.
|
||||
> *Tip #1*: If you make changes to `Dockerfile`, defining the container, you can update the container by running `./build.sh`.
|
||||
>
|
||||
> Tip #2: If you want to force a rebuild of the container, please us `./build.sh -f`. This can be handy if there's a new version of az cli or GitHub cli that you want to update the container with.
|
||||
|
||||
### Running the deployment script using the container
|
||||
|
||||
|
@ -195,34 +199,6 @@ While running the script the second time, you will be asked to log in once, and
|
|||
|
||||
> Info: The script will cache this login session too, so that if you need to run the script multiple times, you will not be asked to log in to your Azure AD B2C tenant again. The login session for Azure B2C is cached here: `$HOME/asdk/.cache/`.
|
||||
|
||||
## Running Deployment Script on Your Computer Without Docker (not recommended)
|
||||
|
||||
While not recommended, you can also run the deployment script *bare-bone* on you computing without using a container. It will generally run slower. More importantly, since the run environment is not controlled, there is a higher risk for something going off the rails. That said, the script is tested for this and will work in many circumstances.
|
||||
|
||||
The script have been tested on:
|
||||
|
||||
- Windows 10/11 running in WSL with a Ubuntu 22.04 distro.
|
||||
- Ubuntu 22.04.
|
||||
- MacOS Ventura. 13.1+, including MacOS running on Apple Silicon.
|
||||
- While not tested on other configurations, it will likely run recent Linux distros and versions as well as and earlier and recent versions of MacOS too.
|
||||
|
||||
Make sure that you have all the tools mentioned above as well as the following installed on your machine before running the script:
|
||||
|
||||
- [JQ v1.6+](https://linuxhint.com/bash_jq_command/) for Bash.
|
||||
- [GitHub’s official command line tool (gh)](https://github.com/cli/cli#installation)
|
||||
- Specifically on MacOS, you'll need a more recent of `bash` as the default version is rather old.
|
||||
- To do this you can use homebrew: [`brew install bash`](https://formulae.brew.sh/formula/bash).
|
||||
|
||||
|
||||
When these requirements are met, the script can be run using the following command:
|
||||
|
||||
```bash
|
||||
chmod +x start.sh
|
||||
./start.sh
|
||||
```
|
||||
|
||||
From there on everything else is virtually identical to running the script from inside a container, as described above.
|
||||
|
||||
## What If Something Goes Wrong?
|
||||
|
||||
It shouldn't happen, but we all know that it does - thank you [Murphy](https://en.wikipedia.org/wiki/Murphy%27s_law)! In most cases, when something goes wrong along the way, all you'll need to do is to run the script once again. The deployment script will skip the parts that have already been completed and re-try the parts that have not.
|
||||
|
|
|
@ -18,11 +18,12 @@ docker run \
|
|||
--interactive \
|
||||
--tty \
|
||||
--rm \
|
||||
--volume "${repo_base}/src/Saas.Identity/Saas.IdentityProvider/deployment/":/asdk/src/Saas.Identity/Saas.IdentityProvider/deployment:ro \
|
||||
--volume "${repo_base}/src/Saas.Identity/Saas.IdentityProvider/deployment/config/":/asdk/src/Saas.Identity/Saas.IdentityProvider/deployment/config \
|
||||
--volume "${repo_base}/src/Saas.Identity/Saas.IdentityProvider/deployment/log/":/asdk/src/Saas.Identity/Saas.IdentityProvider/deployment/log \
|
||||
--volume "${repo_base}/src/Saas.Identity/Saas.IdentityProvider/policies/":/asdk/src/Saas.Identity/Saas.IdentityProvider/policies \
|
||||
--volume "${repo_base}/src/Saas.Lib/Deployment.Script.Modules/":/asdk/src/Saas.Lib/Deployment.Script.Modules:ro \
|
||||
--volume "${repo_base}/src/Saas.Identity/Saas.IdentityProvider/deployment":/asdk/src/Saas.Identity/Saas.IdentityProvider/deployment:ro \
|
||||
--volume "${repo_base}/src/Saas.Identity/Saas.IdentityProvider/deployment/config":/asdk/src/Saas.Identity/Saas.IdentityProvider/deployment/config \
|
||||
--volume "${repo_base}/src/Saas.Identity/Saas.IdentityProvider/deployment/log":/asdk/src/Saas.Identity/Saas.IdentityProvider/deployment/log \
|
||||
--volume "${repo_base}/src/Saas.Identity/Saas.IdentityProvider/policies":/asdk/src/Saas.Identity/Saas.IdentityProvider/policies \
|
||||
--volume "${repo_base}/src/Saas.Lib/Deployment.Script.Modules":/asdk/src/Saas.Lib/Deployment.Script.Modules:ro \
|
||||
--volume "${repo_base}/src/Saas.Lib/Saas.Bicep.Module":/asdk/src/Saas.Lib/Saas.Bicep.Module:ro \
|
||||
--volume "${repo_base}/.git/":/asdk/.git:ro \
|
||||
--volume "${HOME}/.azure/":/asdk/.azure:ro \
|
||||
--volume "${HOME}/asdk/.cache/":/asdk/.cache \
|
||||
|
|
|
@ -41,14 +41,6 @@ echo "Adding app registrations to Azure B2C tenant." \
|
|||
declare -i scopes_length
|
||||
declare -i permissions_length
|
||||
|
||||
# b2c_name="$( get-value ".deployment.azureb2c.name" )"
|
||||
# prefix="$( get-value ".initConfig.naming.solutionPrefix" )"
|
||||
# postfix="$( get-value ".deployment.postfix" )"
|
||||
# solution_name="$( get-value ".initConfig.naming.solutionName" )"
|
||||
# app_id_uri="api://${b2c_name}/${prefix}-${solution_name}-${postfix}"
|
||||
|
||||
# put-value ".deployment.azureb2c.applicationIdUri" "${app_id_uri}"
|
||||
|
||||
# read each item in the JSON array to an item in the Bash array
|
||||
readarray -t app_reg_array < <( jq --compact-output '.appRegistrations[]' "${CONFIG_FILE}")
|
||||
|
||||
|
@ -56,6 +48,20 @@ readarray -t app_reg_array < <( jq --compact-output '.appRegistrations[]' "${CON
|
|||
# declare -i i
|
||||
# i=1
|
||||
|
||||
b2c_tenant_name="$(get-value ".deployment.azureb2c.name" )" \
|
||||
|| echo "Azure B2C tenant namenot found." \
|
||||
| log-output \
|
||||
--level error \
|
||||
--header "Critical error" \
|
||||
|| exit 1
|
||||
|
||||
echo "Setting instance ${b2c_tenant_name}.b2clogin.com" \
|
||||
| log-output \
|
||||
--level info \
|
||||
--header "Azure B2C Instance"
|
||||
|
||||
put-value ".deployment.azureb2c.instance" "https://${b2c_tenant_name}.b2clogin.com"
|
||||
|
||||
# iterate through the Bash array of app registrations
|
||||
for app in "${app_reg_array[@]}"; do
|
||||
app_name=$( jq --raw-output '.name' <<< "${app}" )
|
||||
|
@ -74,9 +80,9 @@ for app in "${app_reg_array[@]}"; do
|
|||
display_name="${app_name}"
|
||||
|
||||
echo "Provisioning app registration for: ${display_name}..." \
|
||||
| log-output \
|
||||
--level info \
|
||||
--header "${display_name}"
|
||||
| log-output \
|
||||
--level info \
|
||||
--header "${display_name}"
|
||||
|
||||
if app-exist "${app_id}"; then
|
||||
echo "App registration for ${app_name} already exist. If you made changes or updated the certificate, you will have to delete the app registration to use this script to update it. " \
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
set -u -e -o pipefail
|
||||
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
{
|
||||
# include script modules into current shell
|
||||
|
@ -42,13 +41,18 @@ if ! resource-exist "${b2c_type_name}" "${b2c_name}" ; then
|
|||
name="${b2c_name}" \
|
||||
skuName="${b2c_sku_name}" \
|
||||
tier="${b2c_tier}" \
|
||||
|| echo "Azure B2C deployment failed." | log-output \
|
||||
--level error \
|
||||
--header "Critical error" \
|
||||
|| echo "Azure B2C deployment failed." \
|
||||
| log-output \
|
||||
--level error \
|
||||
--header "Critical error" \
|
||||
|| exit 1
|
||||
|
||||
echo "Provisionning of Azure B2C tenant Successful." | log-output --level success
|
||||
echo "Provisionning of Azure B2C tenant Successful." |
|
||||
log-output \
|
||||
--level success
|
||||
|
||||
else
|
||||
echo "Existing Azure B2C tenant found and will be used." | log-output --level success
|
||||
echo "Existing Azure B2C tenant found and will be used." |
|
||||
log-output \
|
||||
--level success
|
||||
fi
|
|
@ -112,9 +112,7 @@ subject="repo:${git_org_project_name}:ref:refs/heads/main"
|
|||
|
||||
put-value ".oidc.credentials.subject" "${subject}"
|
||||
|
||||
federation_id="$( get-value ".oidc.federation.id" )"
|
||||
|
||||
if ! federation-exist "${oidc_app_id}" "${federation_id}"; then
|
||||
if ! federation-exist "${oidc_app_id}" "${subject}" ; then
|
||||
echo "Creating OIDC Connect Workflow federation..." \
|
||||
| log-output \
|
||||
--level info
|
||||
|
|
|
@ -10,44 +10,47 @@ set -e -o pipefail
|
|||
source "$SHARED_MODULE_DIR/config-module.sh"
|
||||
}
|
||||
|
||||
if ! [[ -f "${IDENTITY_FOUNDATION_BICEP_PARAMETERS_FILE}" ]]; then
|
||||
echo "The file ${IDENTITY_FOUNDATION_BICEP_PARAMETERS_FILE} does not exist, creating it now" \
|
||||
| log-output \
|
||||
if [[ ! -s "${IDENTITY_FOUNDATION_BICEP_PARAMETERS_FILE}" ||
|
||||
! -f "${IDENTITY_FOUNDATION_BICEP_PARAMETERS_FILE}" ]]; then
|
||||
|
||||
echo "The file ${IDENTITY_FOUNDATION_BICEP_PARAMETERS_FILE} does not exist or is empty, creating it now" |
|
||||
log-output \
|
||||
--level info
|
||||
cp "${IDENTITY_FOUNDATION_BICEP_PARAMETERS_TEMPLATE_FILE}" "${IDENTITY_FOUNDATION_BICEP_PARAMETERS_FILE}"
|
||||
fi
|
||||
|
||||
set -u
|
||||
|
||||
"${SCRIPT_DIR}/map-identity-paramenters.py" "${CONFIG_FILE}" "${IDENTITY_FOUNDATION_BICEP_PARAMETERS_FILE}" \
|
||||
| log-output \
|
||||
"${SCRIPT_DIR}/map-identity-paramenters.py" "${CONFIG_FILE}" "${IDENTITY_FOUNDATION_BICEP_PARAMETERS_FILE}" |
|
||||
log-output \
|
||||
--level info \
|
||||
--header "Generating Identity Foundation services parameters..." \
|
||||
|| echo "Failed to map Identity Foundation services parameters" \
|
||||
| log-output \
|
||||
--level error \
|
||||
--header "Critical Error" \
|
||||
|| exit 1
|
||||
--header "Generating Identity Foundation services parameters..." ||
|
||||
echo "Failed to map Identity Foundation services parameters" |
|
||||
log-output \
|
||||
--level error \
|
||||
--header "Critical Error" ||
|
||||
exit 1
|
||||
|
||||
resource_group="$( get-value ".deployment.resourceGroup.name" )"
|
||||
resource_group="$(get-value ".deployment.resourceGroup.name")"
|
||||
deployment_name="$(get-value ".deployment.identityFoundation.name")"
|
||||
|
||||
echo "Provisioning Identity Foundation services in resource group ${resource_group}..." \
|
||||
| log-output \
|
||||
echo "Provisioning '${deployment_name}' to resource group ${resource_group}..." |
|
||||
log-output \
|
||||
--level info
|
||||
|
||||
az deployment group create \
|
||||
--resource-group "${resource_group}" \
|
||||
--name "IdentityBicepDeployment" \
|
||||
--name "${deployment_name}" \
|
||||
--template-file "${DEPLOY_IDENTITY_FOUNDATION_FILE}" \
|
||||
--parameters "${IDENTITY_FOUNDATION_BICEP_PARAMETERS_FILE}" \
|
||||
| log-output \
|
||||
--level info \
|
||||
|| echo "Failed to deploy Identity Foundation services" \
|
||||
| log-output \
|
||||
--level error \
|
||||
--header "Critical Error" \
|
||||
|| exit 1
|
||||
--parameters "${IDENTITY_FOUNDATION_BICEP_PARAMETERS_FILE}" |
|
||||
log-output \
|
||||
--level info ||
|
||||
echo "Failed to deploy Identity Foundation services" |
|
||||
log-output \
|
||||
--level error \
|
||||
--header "Critical Error" ||
|
||||
exit 1
|
||||
|
||||
echo "Indentity Foundation services successfully provisioned in resource group ${resource_group}..." \
|
||||
| log-output \
|
||||
--level success
|
||||
echo "'${deployment_name}' was successfully provisioned to resource group ${resource_group}..." |
|
||||
log-output \
|
||||
--level success
|
||||
|
|
|
@ -19,7 +19,7 @@ def create_appsettings_file(config_file: str, app_settings_file: str) -> None:
|
|||
|
||||
# get the values to be added to the appsettings file
|
||||
name = config['environment']
|
||||
production = config['production']
|
||||
production = config['environment'] == 'Production'
|
||||
tenant = config['deployment']['azureb2c']['domainName']
|
||||
identityExperienceFrameworkAppId = get_app_value(config, "IdentityExperienceFramework", "appId")
|
||||
proxyIdentityExperienceFrameworkAppId = get_app_value(config, "ProxyIdentityExperienceFramework", "appId")
|
||||
|
|
|
@ -58,7 +58,7 @@ function final-state() {
|
|||
| log-output \
|
||||
--level error \
|
||||
--header "Deployment script completion" \
|
||||
|| echo "Please review the log file for more details: ${LOG_FILE_DIR}/${ASDK_ID_PROVIDER_DEPLOYMENT_RUN_TIME}" \
|
||||
|| echo "Please review the log file for more details: ${LOG_FILE_DIR}/${ASDK_DEPLOYMENT_SCRIPT_RUN_TIME}" \
|
||||
| log-output \
|
||||
--level warn
|
||||
fi
|
||||
|
@ -246,27 +246,48 @@ function populate-configuration-manifest() {
|
|||
service_principal_name="${solution_prefix}-usr-sp-${postfix}"
|
||||
put-value ".deployment.azureb2c.servicePrincipal.username" "${service_principal_name}"
|
||||
|
||||
admin_api_name="admin-api-${long_solution_name}"
|
||||
|
||||
put-app-value \
|
||||
"admin-api" \
|
||||
"appServiceName" \
|
||||
"${admin_api_name}"
|
||||
|
||||
put-app-value \
|
||||
"admin-api" \
|
||||
"baseUrl" \
|
||||
"api-admin-${long_solution_name}"
|
||||
"https://${admin_api_name}.azurewebsites.net"
|
||||
|
||||
put-app-value \
|
||||
"admin-api" \
|
||||
"applicationIdUri" \
|
||||
"api://${b2c_name}/${long_solution_name}/admin-api"
|
||||
|
||||
signup_admin_app_name="signupadmin-app-${long_solution_name}"
|
||||
|
||||
put-app-value \
|
||||
"signupadmin-app" \
|
||||
"appServiceName" \
|
||||
"${signup_admin_app_name}"
|
||||
|
||||
# adding redirecturl to signupadmin-app
|
||||
put-app-value \
|
||||
"signupadmin-app" \
|
||||
"redirectUri" \
|
||||
"https://appsignup-${long_solution_name}.azurewebsites.net/signin-oidc"
|
||||
"https://signupadmin-app-${long_solution_name}.azurewebsites.net/signin-oidc"
|
||||
|
||||
saas_app_name="saas-app-${long_solution_name}"
|
||||
|
||||
put-app-value \
|
||||
"saas-app" \
|
||||
"appServiceName" \
|
||||
"${saas_app_name}"
|
||||
|
||||
# adding redirecturl to saas-app
|
||||
put-app-value \
|
||||
"saas-app" \
|
||||
"redirectUri" \
|
||||
"https://saasapp-${long_solution_name}.azurewebsites.net/signin-oidc"
|
||||
"https://saas-app-${long_solution_name}.azurewebsites.net/signin-oidc"
|
||||
|
||||
permission_api_name="api-permission-${long_solution_name}"
|
||||
|
||||
|
@ -276,6 +297,16 @@ function populate-configuration-manifest() {
|
|||
"apiName" \
|
||||
"${permission_api_name}"
|
||||
|
||||
put-app-value \
|
||||
"permissions-api" \
|
||||
"appServiceName" \
|
||||
"${permission_api_name}"
|
||||
|
||||
put-app-value \
|
||||
"permissions-api" \
|
||||
"baseUrl" \
|
||||
"https://${permission_api_name}.azurewebsites.net"
|
||||
|
||||
# adding permission API Url to permissions-api
|
||||
put-app-value \
|
||||
"permissions-api" \
|
||||
|
|
|
@ -14,55 +14,55 @@ set -u -e -o pipefail
|
|||
|
||||
function check-prerequisites() {
|
||||
|
||||
echo "Checking prerequisites..." \
|
||||
| log-output \
|
||||
echo "Checking prerequisites..." |
|
||||
log-output \
|
||||
--level info \
|
||||
--header "Checking prerequisites"
|
||||
|
||||
what_os="$( get-os )" \
|
||||
|| echo "Unsupported OS: ${what_os}. This script support linux and macos." \
|
||||
| log-output \
|
||||
--level error \
|
||||
--header "Critical Error" \
|
||||
|| exit 1
|
||||
what_os="$(get-os)" ||
|
||||
echo "Unsupported OS: ${what_os}. This script support linux and macos." |
|
||||
log-output \
|
||||
--level error \
|
||||
--header "Critical Error" ||
|
||||
exit 1
|
||||
|
||||
echo "Supported operating system: ${what_os}" | \
|
||||
log-output \
|
||||
--level success \
|
||||
echo "Supported operating system: ${what_os}" |
|
||||
log-output \
|
||||
--level success
|
||||
|
||||
# check if bash version is supported
|
||||
is-valid-bash "5.0.0" \
|
||||
| log-output \
|
||||
is-valid-bash "5.0.0" |
|
||||
log-output \
|
||||
--level info \
|
||||
--header "Checking bash version" \
|
||||
|| echo "The version of bash is not supported." \
|
||||
| log-output \
|
||||
--level error \
|
||||
--header "Critical error" \
|
||||
|| exit 1
|
||||
--header "Checking bash version" ||
|
||||
echo "The version of bash is not supported." |
|
||||
log-output \
|
||||
--level error \
|
||||
--header "Critical error" ||
|
||||
exit 1
|
||||
|
||||
# check if az cli version is supported
|
||||
is-valid-az-cli "2.42.0" \
|
||||
| log-output \
|
||||
is-valid-az-cli "2.42.0" |
|
||||
log-output \
|
||||
--level info \
|
||||
--header "Checking az cli version" \
|
||||
|| echo "The version of az cli is not supported." \
|
||||
| log-output \
|
||||
--level error \
|
||||
--header "Critical error" \
|
||||
|| exit 1
|
||||
--header "Checking az cli version" ||
|
||||
echo "The version of az cli is not supported." |
|
||||
log-output \
|
||||
--level error \
|
||||
--header "Critical error" ||
|
||||
exit 1
|
||||
|
||||
is-valid-jq "1.5" \
|
||||
| log-output \
|
||||
is-valid-jq "1.5" |
|
||||
log-output \
|
||||
--level info \
|
||||
--header "Checking jq version" \
|
||||
|| echo "The version of jq is not supported." \
|
||||
| log-output \
|
||||
--level error \
|
||||
--header "Critical error" \
|
||||
|| exit 1
|
||||
|
||||
# if running in a container copy the msal token cache
|
||||
--header "Checking jq version" ||
|
||||
echo "The version of jq is not supported." |
|
||||
log-output \
|
||||
--level error \
|
||||
--header "Critical error" ||
|
||||
exit 1
|
||||
|
||||
# if running in a container copy the msal token cache
|
||||
# so that user may not have to log in again to main tenant.
|
||||
if [ -f /.dockerenv ]; then
|
||||
cp -f /asdk/.azure/msal_token_cache.* /root/.azure/
|
||||
|
@ -77,58 +77,56 @@ function initialize-shell-scripts() {
|
|||
sudo chmod +x ${SCRIPT_DIR}/*.sh
|
||||
sudo chmod +x ${SHARED_MODULE_DIR}/*.py
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
function initialize-configuration-manifest-file()
|
||||
{
|
||||
if [[ ! -f "${CONFIG_FILE}" ]]; then
|
||||
function initialize-configuration-manifest-file() {
|
||||
|
||||
echo "It looks like this is the first time you're running this script. Setting things up..." \
|
||||
| log-output \
|
||||
if [[ ! -s "${CONFIG_FILE}" || ! -f "${CONFIG_FILE}" ]]; then
|
||||
echo "It looks like this is the first time you're running this script. Setting things up..." |
|
||||
log-output \
|
||||
--level info
|
||||
|
||||
echo
|
||||
|
||||
echo "Creating new './config/config.json' from 'config-template.json.'" \
|
||||
| log-output \
|
||||
echo "Creating new './config/config.json' from 'config-template.json.'" |
|
||||
log-output \
|
||||
--level info
|
||||
|
||||
|
||||
cp "${CONFIG_TEMPLATE_FILE}" "${CONFIG_FILE}"
|
||||
sudo chown -R 666 "${CONFIG_DIR}"
|
||||
|
||||
echo
|
||||
|
||||
echo "Before beginning deployment you must specify initial configuration in the 'initConfig' object:" \
|
||||
| log-output \
|
||||
echo "Before beginning deployment you must specify initial configuration in the 'initConfig' object:" |
|
||||
log-output \
|
||||
--level warning
|
||||
|
||||
init_config="$( get-value ".initConfig" )"
|
||||
init_config="$(get-value ".initConfig")"
|
||||
|
||||
echo "${init_config}" \
|
||||
| log-output \
|
||||
--level msg;
|
||||
echo "${init_config}" |
|
||||
log-output \
|
||||
--level msg
|
||||
|
||||
echo
|
||||
|
||||
echo "Please add required initial settings to the initConfig object in ./config/config.json and run this script again." \
|
||||
| log-output \
|
||||
echo "Please add required initial settings to the initConfig object in ./config/config.json and run this script again." |
|
||||
log-output \
|
||||
--level warning
|
||||
exit 2
|
||||
|
||||
else
|
||||
|
||||
# Setting configuration variables
|
||||
echo "Initializing Configuration" \
|
||||
| log-output \
|
||||
echo "Initializing Configuration" |
|
||||
log-output \
|
||||
--level info \
|
||||
--header "Configation Settings"
|
||||
|
||||
backup-config-beginning
|
||||
fi
|
||||
|
||||
echo "Configuration settings: $CONFIG_FILE." \
|
||||
| log-output \
|
||||
echo "Configuration settings: $CONFIG_FILE." |
|
||||
log-output \
|
||||
--level success
|
||||
}
|
||||
|
||||
|
@ -142,4 +140,4 @@ initialize-shell-scripts
|
|||
initialize-configuration-manifest-file
|
||||
|
||||
# set to install az cli extensions without prompting
|
||||
az config set extension.use_dynamic_install=yes_without_prompt &> /dev/null
|
||||
az config set extension.use_dynamic_install=yes_without_prompt &>/dev/null
|
||||
|
|
|
@ -17,11 +17,11 @@ def patch_paramenters_file(config_file: str, paramenter_file: str) -> None:
|
|||
parameters['parameters']['version']['value'] \
|
||||
= config['version']
|
||||
|
||||
parameters['parameters']['environment']['value'] \
|
||||
= config['environment']
|
||||
|
||||
parameters['parameters']['devMachineIp']['value'] \
|
||||
= config['deployment']['devMachine']['ip']
|
||||
|
||||
parameters['parameters']['appSettingsAdminServiceBaseUrl']['value'] \
|
||||
= get_app_value(config, "admin-api", "baseUrl")
|
||||
|
||||
parameters['parameters']['solutionPostfix']['value'] \
|
||||
= config['deployment']['postfix']
|
||||
|
@ -35,27 +35,6 @@ def patch_paramenters_file(config_file: str, paramenter_file: str) -> None:
|
|||
parameters['parameters']['keyVaultName']['value'] \
|
||||
= config['deployment']['keyVault']['name']
|
||||
|
||||
parameters['parameters']['azureB2CDomain']['value'] \
|
||||
= config['deployment']['azureb2c']['domainName']
|
||||
|
||||
parameters['parameters']['azureB2CLoginEndpoint']['value'] \
|
||||
= f"https://{config['deployment']['azureb2c']['name']}.b2clogin.com"
|
||||
|
||||
parameters['parameters']['azureB2CTenantId']['value'] \
|
||||
= config['deployment']['azureb2c']['tenantId']
|
||||
|
||||
parameters['parameters']['permissionsApiName']['value'] \
|
||||
= get_app_value(config, "permissions-api", "apiName")
|
||||
|
||||
parameters['parameters']['permissionApiClientId']['value'] \
|
||||
= get_app_value(config, "permissions-api", "appId")
|
||||
|
||||
parameters['parameters']['permissionInstance']['value'] \
|
||||
= get_app_value(config, "permissions-api", "instance")
|
||||
|
||||
parameters['parameters']['permissionCertificateName']['value'] \
|
||||
= get_app_value(config, "permissions-api", "certificateKeyName")
|
||||
|
||||
parameters['parameters']['permissionApiKey']['value'] \
|
||||
= 'RestApiKey'
|
||||
|
||||
|
|
|
@ -6,15 +6,30 @@ source "./constants.sh"
|
|||
echo "Setting up the deployment environment."
|
||||
echo "Settings execute permissions on necessary scripts files."
|
||||
|
||||
sudo chmod +x ./*.sh
|
||||
sudo chmod +x ./script/*.sh
|
||||
sudo chmod +x ./script/*.py
|
||||
(
|
||||
sudo chmod +x ./*.sh || exit 1
|
||||
sudo chmod +x ./script/*.sh || exit 1
|
||||
sudo chmod +x ./script/*.py || exit 1
|
||||
) ||
|
||||
{
|
||||
echo "Failed to set execute permissions on the necessary scripts."
|
||||
exit 1
|
||||
}
|
||||
|
||||
repo_base="$(git rev-parse --show-toplevel)" ||
|
||||
{
|
||||
echo "Failed to get the root of the repository."
|
||||
exit 1
|
||||
}
|
||||
|
||||
repo_base="$( git rev-parse --show-toplevel )"
|
||||
docker_file_folder="${repo_base}/src/Saas.lib/Deployment.Container"
|
||||
|
||||
# redirect to build.sh in the Deployment.Container folder
|
||||
sudo chmod +x "${docker_file_folder}/build.sh"
|
||||
sudo chmod +x "${docker_file_folder}/build.sh" ||
|
||||
{
|
||||
echo "Failed to set execute permissions on the 'build.sh' script."
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Building the deployment container."
|
||||
./build.sh ||
|
||||
|
@ -23,20 +38,28 @@ echo "Building the deployment container."
|
|||
exit 1
|
||||
}
|
||||
|
||||
echo "Setting up log folder..."
|
||||
mkdir -p "$LOG_FILE_DIR"
|
||||
sudo chown "${USER}" "$LOG_FILE_DIR"
|
||||
(
|
||||
echo "Setting up log folder..."
|
||||
mkdir -p "$LOG_FILE_DIR" || exit 1
|
||||
sudo chown "${USER}" "$LOG_FILE_DIR" || exit 1
|
||||
|
||||
echo "Setting up config folder..."
|
||||
mkdir -p "${CONFIG_DIR}"
|
||||
sudo chown "${USER}" "${CONFIG_DIR}"
|
||||
sudo chown "${USER}" "${CONFIG_FILE}"
|
||||
sudo chown "${USER}" "${IDENTITY_FOUNDATION_BICEP_PARAMETERS_FILE}"
|
||||
echo "Setting up config folder..."
|
||||
mkdir -p "${CONFIG_DIR}" || exit 1
|
||||
sudo chown "${USER}" "${CONFIG_DIR}" || exit 1
|
||||
touch "${CONFIG_FILE}" || exit 1
|
||||
sudo chown "${USER}" "${CONFIG_FILE}" || exit 1
|
||||
touch "${IDENTITY_FOUNDATION_BICEP_PARAMETERS_FILE}" || exit 1
|
||||
sudo chown "${USER}" "${IDENTITY_FOUNDATION_BICEP_PARAMETERS_FILE}" || exit 1
|
||||
|
||||
echo "Setting up policy folder..."
|
||||
mkdir -p "${IDENTITY_EXPERIENCE_FRAMEWORK_POLICY_ENVIRONMENT_DIR}"
|
||||
sudo chown "${USER}" "${IDENTITY_EXPERIENCE_FRAMEWORK_POLICY_ENVIRONMENT_DIR}"
|
||||
sudo chown "${USER}" "${IDENTITY_EXPERIENCE_FRAMEWORK_POLICY_APP_SETTINGS_FILE}"
|
||||
echo "Setting up policy folder..."
|
||||
mkdir -p "${IDENTITY_EXPERIENCE_FRAMEWORK_POLICY_ENVIRONMENT_DIR}" || exit 1
|
||||
sudo chown "${USER}" "${IDENTITY_EXPERIENCE_FRAMEWORK_POLICY_ENVIRONMENT_DIR}" || exit 1
|
||||
sudo chown "${USER}" "${IDENTITY_EXPERIENCE_FRAMEWORK_POLICY_APP_SETTINGS_FILE}" || exit 1
|
||||
) ||
|
||||
{
|
||||
echo "Failed to setting up folders with permissions."
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo
|
||||
echo "Setup complete. You can now run the deployment script using the command './run.sh'."
|
||||
echo "Setup complete. You can now run the deployment script using the command './run.sh'."
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
|
||||
export ASDK_CACHE_AZ_CLI_SESSIONS=true
|
||||
|
||||
# if not running in a container
|
||||
if ! [ -f /.dockerenv ]; then
|
||||
echo "Running outside of a container us not supported. Please run the deployment script using './run.sh'."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -z $ASDK_DEPLOYMENT_SCRIPT_PROJECT_BASE ]]; then
|
||||
# repo base
|
||||
echo "ASDK_DEPLOYMENT_SCRIPT_PROJECT_BASE is not set. Setting it to default value."
|
||||
|
@ -31,7 +37,7 @@ set -u -e -o pipefail
|
|||
now=$(date '+%Y-%m-%d--%H-%M-%S')
|
||||
|
||||
# set run time for deployment script instance
|
||||
export ASDK_ID_PROVIDER_DEPLOYMENT_RUN_TIME="${now}"
|
||||
export ASDK_DEPLOYMENT_SCRIPT_RUN_TIME="${now}"
|
||||
|
||||
# create log file directory if it does not exist
|
||||
if ! [ -f /.dockerenv ] && [[ ! -d "${LOG_FILE_DIR}" ]]; then
|
||||
|
@ -40,7 +46,7 @@ if ! [ -f /.dockerenv ] && [[ ! -d "${LOG_FILE_DIR}" ]]; then
|
|||
fi
|
||||
|
||||
# create log file for this deployment script instance
|
||||
touch "${LOG_FILE_DIR}/deploy-${ASDK_ID_PROVIDER_DEPLOYMENT_RUN_TIME}.log"
|
||||
touch "${LOG_FILE_DIR}/deploy-${ASDK_DEPLOYMENT_SCRIPT_RUN_TIME}.log"
|
||||
|
||||
echo "Welcome to the Azure SaaS Dev Kit - Azure B2C Identity Provider deployment script." \
|
||||
| log-output \
|
||||
|
@ -70,6 +76,7 @@ if ! [ -f /.dockerenv ]; then
|
|||
# make sure that the init script is executable
|
||||
chmod +x "$SCRIPT_DIR/init.sh"
|
||||
fi
|
||||
|
||||
# initialize deployment environment
|
||||
"${SCRIPT_DIR}/init.sh" \
|
||||
|| if [[ $? -eq 2 ]]; then exit 0; fi
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
Environments/
|
||||
appsettings.json
|
||||
appsettings.json
|
||||
.vscode
|
До Ширина: | Высота: | Размер: 73 KiB После Ширина: | Высота: | Размер: 73 KiB |
До Ширина: | Высота: | Размер: 129 KiB После Ширина: | Высота: | Размер: 129 KiB |
До Ширина: | Высота: | Размер: 68 KiB После Ширина: | Высота: | Размер: 68 KiB |
До Ширина: | Высота: | Размер: 60 KiB После Ширина: | Высота: | Размер: 60 KiB |
До Ширина: | Высота: | Размер: 60 KiB После Ширина: | Высота: | Размер: 60 KiB |
До Ширина: | Высота: | Размер: 150 KiB После Ширина: | Высота: | Размер: 150 KiB |
До Ширина: | Высота: | Размер: 171 KiB После Ширина: | Высота: | Размер: 171 KiB |
Двоичные данные
src/Saas.Identity/Saas.Permissions/.assets/readme/image-20230126203229729.png
Normal file
После Ширина: | Высота: | Размер: 103 KiB |
Двоичные данные
src/Saas.Identity/Saas.Permissions/.assets/readme/image-20230126230446548.png
Normal file
После Ширина: | Высота: | Размер: 31 KiB |
Двоичные данные
src/Saas.Identity/Saas.Permissions/.assets/readme/image-20230127151459605.png
Normal file
После Ширина: | Высота: | Размер: 25 KiB |
Двоичные данные
src/Saas.Identity/Saas.Permissions/.assets/readme/image-20230128153624642.png
Normal file
После Ширина: | Высота: | Размер: 51 KiB |
Двоичные данные
src/Saas.Identity/Saas.Permissions/.assets/readme/image-20230128153659108.png
Normal file
После Ширина: | Высота: | Размер: 52 KiB |
Двоичные данные
src/Saas.Identity/Saas.Permissions/.assets/readme/image-20230128154233104.png
Normal file
После Ширина: | Высота: | Размер: 42 KiB |
Двоичные данные
src/Saas.Identity/Saas.Permissions/.assets/readme/image-20230128154523352.png
Normal file
После Ширина: | Высота: | Размер: 61 KiB |
Двоичные данные
src/Saas.Identity/Saas.Permissions/.assets/readme/image-20230128154735945.png
Normal file
После Ширина: | Высота: | Размер: 29 KiB |
Двоичные данные
src/Saas.Identity/Saas.Permissions/.assets/readme/image-20230128160051561.png
Normal file
После Ширина: | Высота: | Размер: 16 KiB |
Двоичные данные
src/Saas.Identity/Saas.Permissions/.assets/readme/image-20230128161036500.png
Normal file
После Ширина: | Высота: | Размер: 147 KiB |
Двоичные данные
src/Saas.Identity/Saas.Permissions/.assets/readme/image-20230128161825544.png
Normal file
После Ширина: | Высота: | Размер: 77 KiB |
Двоичные данные
src/Saas.Identity/Saas.Permissions/.assets/readme/image-20230128163318970.png
Normal file
После Ширина: | Высота: | Размер: 109 KiB |
|
@ -1,6 +1,6 @@
|
|||
using Microsoft.Extensions.Options;
|
||||
using Saas.Permissions.Service.Models;
|
||||
using Saas.Permissions.Service.Options;
|
||||
using Saas.Shared.Options;
|
||||
|
||||
namespace Saas.Permissions.Service.Middleware;
|
||||
|
||||
|
@ -8,9 +8,9 @@ public class ApiKeyMiddleware {
|
|||
|
||||
private readonly RequestDelegate _next;
|
||||
private const string API_KEY = "x-api-key";
|
||||
private readonly PermissionApiOptions _permissionOptions;
|
||||
private readonly PermissionsApiOptions _permissionOptions;
|
||||
|
||||
public ApiKeyMiddleware(IOptions<PermissionApiOptions> permissionOptions, RequestDelegate next) {
|
||||
public ApiKeyMiddleware(IOptions<PermissionsApiOptions> permissionOptions, RequestDelegate next) {
|
||||
_next = next;
|
||||
_permissionOptions = permissionOptions.Value;
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
using ClientAssertionWithKeyVault.Interface;
|
||||
|
||||
namespace Saas.Permissions.Service.Options;
|
||||
|
||||
public record PermissionApiOptions
|
||||
{
|
||||
public const string SectionName = "PermissionApi";
|
||||
|
||||
public string? Audience { get; init; }
|
||||
public string? ApiKey { get; init; }
|
||||
public string? ClientId { get; init; }
|
||||
public Certificate[]? Certificates { get; init; }
|
||||
public string? TenantId { get; init; }
|
||||
public string? Domain { get; init; }
|
||||
|
||||
}
|
||||
|
||||
public record Certificate : IKeyInfo
|
||||
{
|
||||
public string? SourceType { get; init; }
|
||||
public string? KeyVaultUrl { get; init; }
|
||||
public string? KeyVaultCertificateName { get; init; }
|
||||
}
|
|
@ -1,14 +1,15 @@
|
|||
using Azure.Identity;
|
||||
using Saas.Permissions.Service.Data;
|
||||
using Saas.Permissions.Service.Interfaces;
|
||||
using Saas.Permissions.Service.Options;
|
||||
using Saas.Shared.Options;
|
||||
using Saas.Permissions.Service.Services;
|
||||
using Saas.Permissions.Service.Swagger;
|
||||
using Saas.Swagger;
|
||||
using ClientAssertionWithKeyVault.Interface;
|
||||
using ClientAssertionWithKeyVault;
|
||||
using Saas.Permissions.Service.Middleware;
|
||||
using Microsoft.Extensions.Configuration.AzureAppConfiguration;
|
||||
using Polly;
|
||||
using System.Reflection;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Services.AddApplicationInsightsTelemetry();
|
||||
|
@ -20,7 +21,7 @@ builder.Services.AddApplicationInsightsTelemetry();
|
|||
Instead we're utilizing the Azure App Configuration service for storing settings and the Azure Key Vault to store secrets.
|
||||
Azure App Configuration still hold references to the secret, but not the secret themselves.
|
||||
|
||||
This approach is more secure, and allows us to have a single source of truth
|
||||
This approach is more secure and allows us to have a single source of truth
|
||||
for all settings and secrets.
|
||||
|
||||
The settings and secrets were provisioned to Azure App Configuration and Azure Key Vault
|
||||
|
@ -30,7 +31,12 @@ builder.Services.AddApplicationInsightsTelemetry();
|
|||
on how to set up and run this service in a local development environment - i.e., a local dev machine.
|
||||
*/
|
||||
|
||||
var logger = LoggerFactory.Create(config => config.AddConsole()).CreateLogger("Saas.Permissions.API");
|
||||
string projectName = Assembly.GetCallingAssembly().GetName().Name
|
||||
?? throw new NullReferenceException("Project name cannot be null");
|
||||
|
||||
var logger = LoggerFactory.Create(config => config.AddConsole()).CreateLogger(projectName);
|
||||
|
||||
logger.LogInformation("001");
|
||||
|
||||
if (builder.Environment.IsDevelopment())
|
||||
{
|
||||
|
@ -43,8 +49,11 @@ else
|
|||
|
||||
// Add configuration settings data using Options Pattern.
|
||||
// For more see: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-7.0
|
||||
builder.Services.Configure<PermissionApiOptions>(
|
||||
builder.Configuration.GetRequiredSection(PermissionApiOptions.SectionName));
|
||||
builder.Services.Configure<PermissionsApiOptions>(
|
||||
builder.Configuration.GetRequiredSection(PermissionsApiOptions.SectionName));
|
||||
|
||||
builder.Services.Configure<AzureB2CPermissionsApiOptions>(
|
||||
builder.Configuration.GetRequiredSection(AzureB2CPermissionsApiOptions.SectionName));
|
||||
|
||||
builder.Services.Configure<SqlOptions>(
|
||||
builder.Configuration.GetRequiredSection(SqlOptions.SectionName));
|
||||
|
@ -140,7 +149,7 @@ void InitializeDevEnvironment()
|
|||
{
|
||||
// IMPORTANT
|
||||
// The current version.
|
||||
// Must corresspond exactly to the version string of our deployment as specificed in the deployment config.json.
|
||||
// Must correspond exactly to the version string of our deployment as specificed in the deployment config.json.
|
||||
var version = "ver0.8.0";
|
||||
|
||||
logger.LogInformation("Version: {version}", version);
|
||||
|
@ -167,18 +176,16 @@ void InitializeDevEnvironment()
|
|||
.ConfigureKeyVault(kv => kv.SetCredential(new ChainedTokenCredential(credential)))
|
||||
.Select(KeyFilter.Any, version)); // <-- Important: since we're using labels in our Azure App Configuration store
|
||||
|
||||
// Configuring Swagger.
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
|
||||
// Enabling to option for add the 'x-api-key' header to swagger UI.
|
||||
builder.Services.AddSwaggerGen(option =>
|
||||
{
|
||||
option.SwaggerDoc("v1", new() { Title = "Permissions API", Version = "v1.1" });
|
||||
option.OperationFilter<SwagCustomHeaderFilter>();
|
||||
});
|
||||
|
||||
// Configuring Swagger.
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
}
|
||||
|
||||
void InitializeProdEnvironment()
|
||||
|
|
|
@ -26,11 +26,16 @@
|
|||
<PackageReference Include="Microsoft.Identity.Web.MicrosoftGraph" Version="1.25.10" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Saas.Lib\ClientAssertionWithKeyVault\ClientAssertionWithKeyVault.csproj" />
|
||||
<ProjectReference Include="..\..\..\Saas.Lib\Saas.Shared\Saas.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Options\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -8,7 +8,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saas.Permissions.Service",
|
|||
{5FF9E406-A16E-48B4-B6E2-E04B64F7BF28} = {5FF9E406-A16E-48B4-B6E2-E04B64F7BF28}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientAssertionWithKeyVault", "..\..\..\Saas.Lib\ClientAssertionWithKeyVault\ClientAssertionWithKeyVault.csproj", "{5FF9E406-A16E-48B4-B6E2-E04B64F7BF28}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClientAssertionWithKeyVault", "..\..\..\Saas.Lib\ClientAssertionWithKeyVault\ClientAssertionWithKeyVault.csproj", "{5FF9E406-A16E-48B4-B6E2-E04B64F7BF28}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Saas.Shared", "..\..\..\Saas.Lib\Saas.Shared\Saas.Shared.csproj", "{D8A87153-45CB-4212-8231-EA1D0FA71554}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -24,6 +26,10 @@ Global
|
|||
{5FF9E406-A16E-48B4-B6E2-E04B64F7BF28}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5FF9E406-A16E-48B4-B6E2-E04B64F7BF28}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5FF9E406-A16E-48B4-B6E2-E04B64F7BF28}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D8A87153-45CB-4212-8231-EA1D0FA71554}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D8A87153-45CB-4212-8231-EA1D0FA71554}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D8A87153-45CB-4212-8231-EA1D0FA71554}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D8A87153-45CB-4212-8231-EA1D0FA71554}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -3,8 +3,8 @@ using Microsoft.Graph;
|
|||
using Saas.Permissions.Service.Exceptions;
|
||||
using Saas.Permissions.Service.Interfaces;
|
||||
using Saas.Permissions.Service.Models;
|
||||
using Saas.Permissions.Service.Options;
|
||||
using System.Text;
|
||||
using Saas.Shared.Options;
|
||||
|
||||
namespace Saas.Permissions.Service.Services;
|
||||
|
||||
|
@ -19,10 +19,10 @@ public class GraphAPIService : IGraphAPIService
|
|||
"Client Assertion Signing Provider");
|
||||
|
||||
private readonly GraphServiceClient _graphServiceClient;
|
||||
private readonly PermissionApiOptions _permissionOptions;
|
||||
private readonly AzureB2CPermissionsApiOptions _permissionOptions;
|
||||
|
||||
public GraphAPIService(
|
||||
IOptions<PermissionApiOptions> permissionApiOptions,
|
||||
IOptions<AzureB2CPermissionsApiOptions> permissionApiOptions,
|
||||
IGraphApiClientFactory graphClientFactory,
|
||||
ILogger<GraphAPIService> logger)
|
||||
{
|
||||
|
@ -121,8 +121,8 @@ public class GraphAPIService : IGraphAPIService
|
|||
try
|
||||
{
|
||||
var servicePrincipal = await _graphServiceClient.ServicePrincipals.Request()
|
||||
.Filter($"appId eq '{clientId}'")
|
||||
.GetAsync();
|
||||
.Filter($"appId eq '{clientId}'")
|
||||
.GetAsync();
|
||||
|
||||
return servicePrincipal.SingleOrDefault();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Graph;
|
||||
using Saas.Permissions.Service.Options;
|
||||
using Saas.Shared.Options;
|
||||
using Saas.Permissions.Service.Interfaces;
|
||||
|
||||
namespace Saas.Permissions.Service.Services;
|
||||
|
@ -24,7 +24,7 @@ public class GraphApiClientFactory : IGraphApiClientFactory
|
|||
public GraphServiceClient Create() =>
|
||||
new(_httpClient, _msGraphOptions.BaseUrl)
|
||||
{
|
||||
AuthenticationProvider = _authenticationProvider,
|
||||
AuthenticationProvider = _authenticationProvider
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using Microsoft.Graph;
|
||||
using Microsoft.Identity.Client;
|
||||
using Saas.Permissions.Service.Interfaces;
|
||||
using Saas.Permissions.Service.Options;
|
||||
using Saas.Shared.Options;
|
||||
using System.Net.Http.Headers;
|
||||
using ClientAssertionWithKeyVault.Interface;
|
||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||
|
@ -25,7 +25,7 @@ public class KeyVaultSigningCredentialsAuthProvider : IAuthenticationProvider
|
|||
|
||||
public KeyVaultSigningCredentialsAuthProvider(
|
||||
IOptions<MSGraphOptions> msGraphOptions,
|
||||
IOptions<PermissionApiOptions> permissionApiOptions,
|
||||
IOptions<AzureB2CPermissionsApiOptions> azureAdB2COptions,
|
||||
IClientAssertionSigningProvider clientAssertionSigningProvider,
|
||||
IKeyVaultCredentialService credentialService,
|
||||
ILogger<KeyVaultSigningCredentialsAuthProvider> logger)
|
||||
|
@ -34,19 +34,19 @@ public class KeyVaultSigningCredentialsAuthProvider : IAuthenticationProvider
|
|||
_msGraphOptions = msGraphOptions.Value;
|
||||
_clientAssertionSigningProvider = clientAssertionSigningProvider;
|
||||
|
||||
if (permissionApiOptions?.Value?.Certificates?[0] is null)
|
||||
if (azureAdB2COptions?.Value?.ClientCertificates?[0] is null)
|
||||
{
|
||||
logger.LogError("Certificate cannot be null.");
|
||||
throw new NullReferenceException("Certificate cannot be null.");
|
||||
}
|
||||
|
||||
_msalClient = ConfidentialClientApplicationBuilder
|
||||
.Create(permissionApiOptions.Value.ClientId)
|
||||
.WithAuthority(AzureCloudInstance.AzurePublic, permissionApiOptions.Value.TenantId)
|
||||
.Create(azureAdB2COptions.Value.ClientId)
|
||||
.WithAuthority(AzureCloudInstance.AzurePublic, azureAdB2COptions.Value.TenantId)
|
||||
.WithClientAssertion(
|
||||
(AssertionRequestOptions options) =>
|
||||
_clientAssertionSigningProvider.GetClientAssertion(
|
||||
permissionApiOptions.Value.Certificates[0],
|
||||
azureAdB2COptions.Value.ClientCertificates[0],
|
||||
options.TokenEndpoint,
|
||||
options.ClientID,
|
||||
credentialService.GetCredential(),
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# nektos/act
|
||||
.secret
|
||||
.secrets
|
||||
.secrets
|
||||
secret
|
||||
secrets
|
|
@ -1,6 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
repo_base="$( git rev-parse --show-toplevel )"
|
||||
docker_file_folder="${repo_base}/src/Saas.Identity/Saas.Permissions/deployment/act"
|
||||
|
||||
docker build --file "${docker_file_folder}/Dockerfile" --tag act-container:latest .
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# repo base
|
||||
repo_base="$(git rev-parse --show-toplevel)"
|
||||
REPO_BASE="${repo_base}"
|
||||
|
||||
host_deployment_dir="${repo_base}/src/Saas.Identity/Saas.Permissions/deployment"
|
||||
container_deployment_dir="/asdk/src/Saas.Identity/Saas.Permissions/deployment"
|
||||
|
||||
# running the './act/script/clean-credentials' script using our ASDK deployment script container - i.e., not the act container
|
||||
docker run \
|
||||
--interactive \
|
||||
--tty \
|
||||
--rm \
|
||||
--volume "${host_deployment_dir}":"${container_deployment_dir}":ro \
|
||||
--volume "${REPO_BASE}/src/Saas.Lib/Deployment.Script.Modules/":/asdk/src/Saas.Lib/Deployment.Script.Modules:ro \
|
||||
--volume "${REPO_BASE}/src/Saas.Identity/Saas.IdentityProvider/deployment/config/":/asdk/src/Saas.Identity/Saas.IdentityProvider/deployment/config:ro \
|
||||
--volume "${REPO_BASE}/.git/":/asdk/.git:ro \
|
||||
--volume "${HOME}/.azure/":/asdk/.azure:ro \
|
||||
--volume "${HOME}/asdk/act/.secret":/asdk/act/.secret \
|
||||
--env "ASDK_DEPLOYMENT_SCRIPT_PROJECT_BASE"="${container_deployment_dir}" \
|
||||
"${DEPLOYMENT_CONTAINER_NAME}" \
|
||||
bash /asdk/src/Saas.Lib/Deployment.Script.Modules/clean-credentials.sh
|
||||
|
||||
./setup.sh -s
|
|
@ -0,0 +1,42 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -u -e -o pipefail
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
{
|
||||
source "./../constants.sh"
|
||||
source "$SHARED_MODULE_DIR/config-module.sh"
|
||||
}
|
||||
|
||||
repo_base="$(git rev-parse --show-toplevel)"
|
||||
REPO_BASE="${repo_base}"
|
||||
|
||||
host_act_secrets_dir="${HOME}/asdk/act/.secret"
|
||||
host_deployment_dir="${repo_base}/src/Saas.Identity/Saas.Permissions/deployment"
|
||||
container_deployment_dir="/asdk/src/Saas.Identity/Saas.Permissions/deployment"
|
||||
|
||||
# running the './act/script/patch-app-name.sh' script using our ASDK deployment script container - i.e., not the act container
|
||||
docker run \
|
||||
--interactive \
|
||||
--tty \
|
||||
--rm \
|
||||
--volume "${host_deployment_dir}":"${container_deployment_dir}":ro \
|
||||
--volume "${host_deployment_dir}/act/workflows/":"${container_deployment_dir}/act/workflows" \
|
||||
--volume "${REPO_BASE}/src/Saas.Lib/Deployment.Script.Modules/":/asdk/src/Saas.Lib/Deployment.Script.Modules:ro \
|
||||
--volume "${REPO_BASE}/src/Saas.Identity/Saas.IdentityProvider/deployment/config/":/asdk/src/Saas.Identity/Saas.IdentityProvider/deployment/config:ro \
|
||||
--volume "${REPO_BASE}/.git/":/asdk/.git:ro \
|
||||
--volume "${HOME}/.azure/":/asdk/.azure:ro \
|
||||
--volume "${host_act_secrets_dir}":/asdk/act/.secret \
|
||||
--env "ASDK_DEPLOYMENT_SCRIPT_PROJECT_BASE"="${container_deployment_dir}" \
|
||||
"${DEPLOYMENT_CONTAINER_NAME}" \
|
||||
bash /asdk/src/Saas.Lib/Deployment.Script.Modules/deploy-debug.sh
|
||||
|
||||
# run act container to run github action locally, using local workflow file and local code base.
|
||||
gh act workflow_dispatch \
|
||||
--rm \
|
||||
--bind \
|
||||
--pull=false \
|
||||
--secret-file "${host_act_secrets_dir}/secret" \
|
||||
--directory "${REPO_BASE}" \
|
||||
--workflows "${ACT_LOCAL_WORKFLOW_DEBUG_FILE}" \
|
||||
--platform "ubuntu-latest=${ACT_CONTAINER_NAME}"
|
|
@ -1,3 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
gh act workflow_dispatch --secret-file .secrets -W ./workflows/permissions-api-deploy-debug.yml -P ubuntu-latest=act-container:latest
|
|
@ -1 +1,39 @@
|
|||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
skip_docker_build=false
|
||||
force_update=false
|
||||
|
||||
while getopts 'sf' flag; do
|
||||
case "${flag}" in
|
||||
s) skip_docker_build=true ;;
|
||||
f) force_update=true ;;
|
||||
*) skip_docker_build=false ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "../constants.sh"
|
||||
|
||||
echo "Setting up the SaaS Permissions Serivce API Act deployment environment."
|
||||
echo "Settings execute permissions on necessary scripts files."
|
||||
|
||||
sudo mkdir -p "${ACT_SECRETS_DIR}"
|
||||
|
||||
sudo chmod +x ${ACT_DIR}/*.sh
|
||||
sudo chmod +x ${SCRIPT_DIR}/*.sh >/dev/null 2>&1
|
||||
sudo touch ${ACT_SECRETS_FILE}
|
||||
sudo chown "${USER}" ${ACT_SECRETS_FILE}
|
||||
sudo touch ${ACT_SECRETS_FILE_RG}
|
||||
sudo chown "${USER}" ${ACT_SECRETS_FILE_RG}
|
||||
|
||||
if [ "${skip_docker_build}" = false ]; then
|
||||
echo "Building the deployment container."
|
||||
|
||||
if [[ "${force_update}" == false ]]; then
|
||||
"${ACT_CONTAINER_DIR}"/build.sh -n "${ACT_CONTAINER_NAME}"
|
||||
else
|
||||
"${ACT_CONTAINER_DIR}"/build.sh -n "${ACT_CONTAINER_NAME}" -f
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "SaaS Permissions Service API Act environment setup complete. You can now run the local deployment script using the command './deploy.sh'."
|
||||
|
|
|
@ -1,42 +1,44 @@
|
|||
---
|
||||
name: Deploy Permission API to Azure Web Services
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# inputs:
|
||||
# logLevel:
|
||||
# description: 'Log level'
|
||||
# required: true
|
||||
# default: 'warning'
|
||||
# tags:
|
||||
# description: 'Test scenario tags'
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
env:
|
||||
AZURE_WEBAPP_NAME: 'api-permission-asdk-test-b3yf' # set this to your application's name
|
||||
APP_NAME: permissions-api
|
||||
AZURE_WEBAPP_NAME: api-permission-asdk-test-fd4k # set this to your application's name
|
||||
AZURE_WEBAPP_PACKAGE_PATH: "." # set this to the path to your web app project, defaults to the repository root
|
||||
DOTNET_VERSION: 7.x.x
|
||||
PROJECT_DIR: ./src/Saas.Identity/Saas.Permissions/Saas.Permissions.Service_v1.1
|
||||
PROJECT_PATH: ./src/Saas.Identity/Saas.Permissions/Saas.Permissions.Service_v1.1/Saas.Permissions.Service.csproj
|
||||
OUTPUT_PATH: ./publish
|
||||
PROJECT_PATH: ${{ env.PROJECT_DIR }}/Saas.Permissions.Service.csproj
|
||||
PUBLISH_PATH: ./publish
|
||||
OUTPUT_PATH: ${{ env.PUBLISH_PATH }}/${{ env.APP_NAME }}/package
|
||||
SYMBOLS_PATH: ${{ env.PUBLISH_PATH }}/symbols
|
||||
BUILD_CONFIGURATION: Debug # setting the configuration manager build configuration value for our workflow.
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
##################################################
|
||||
# this section is specific for local deployment. #
|
||||
##################################################
|
||||
# Azure login
|
||||
- uses: azure/login@v1
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
||||
|
||||
# checkout the repo specifying the branch name in 'ref:'
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: dev #IMPORTANT we're checking out and deploying the 'dev' branch here, not 'main'.
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# checkout the _local_ repository
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
#################################################
|
||||
# end of local deployment specific section. #
|
||||
#################################################
|
||||
|
||||
# Setup .NET Core SDK
|
||||
- name: Setup .NET Core
|
||||
|
@ -50,19 +52,31 @@ jobs:
|
|||
dotnet restore ${{ env.PROJECT_DIR }}
|
||||
|
||||
dotnet build ${{ env.PROJECT_PATH }} \
|
||||
--configuration Debug
|
||||
--configuration ${{ env.BUILD_CONFIGURATION }}
|
||||
|
||||
dotnet publish ${{ env.PROJECT_PATH }} \
|
||||
--configuration Debug \
|
||||
--output '${{ env.OUTPUT_PATH }}/{{ env.AZURE_WEBAPP_NAME }}'
|
||||
--configuration ${{ env.BUILD_CONFIGURATION }} \
|
||||
--output ${{ env.OUTPUT_PATH }}
|
||||
|
||||
# Deploy to Azure Web apps
|
||||
- name: "Run Azure webapp deploy action using publish profile credentials"
|
||||
uses: azure/webapps-deploy@v2
|
||||
with:
|
||||
app-name: ${{ env.AZURE_WEBAPP_NAME }} # Replace with your app name
|
||||
package: ${{ env.OUTPUT_PATH }}/{{ env.AZURE_WEBAPP_NAME }}
|
||||
package: ${{ env.OUTPUT_PATH }}
|
||||
# slot-name: 'PermissionsApi-Staging'
|
||||
######################
|
||||
# *** Debug only *** #
|
||||
######################
|
||||
# Copy symbols files (*.pdb)) to local publish folder # rm -rf ${{ env.OUTPUT_PATH }}/${{ env.AZURE_WEBAPP_NAME }}
|
||||
- name: copy symbols files (*.pdb)) to local publish folder
|
||||
run: |
|
||||
mkdir -p ${{ env.PUBLISH_PATH }}/symbols
|
||||
echo "Copying symbols files to '${{ env.SYMBOLS_PATH }}'"
|
||||
cp -r ${{ env.OUTPUT_PATH }}/*.pdb ${{ env.SYMBOLS_PATH }}
|
||||
######################
|
||||
# *** End *** #
|
||||
######################
|
||||
|
||||
# Azure logout
|
||||
- name: logout
|
||||
|
|
2
src/Saas.Identity/Saas.Permissions/deployment/bicep/Parameters/.gitignore
поставляемый
Normal file
|
@ -0,0 +1,2 @@
|
|||
*-parameters.json
|
||||
identity-foundation-outputs.json
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
@description('The SaaS Permission API.')
|
||||
param permissionsapi string
|
||||
|
||||
@description('Version')
|
||||
param version string
|
||||
|
||||
@description('Environment')
|
||||
@allowed([
|
||||
'Development'
|
||||
'Staging'
|
||||
'Production'
|
||||
])
|
||||
param environment string
|
||||
|
||||
@description('The App Service Plan ID.')
|
||||
param appServicePlanName string
|
||||
|
||||
@description('The Uri of the Key Vault.')
|
||||
param keyVaultUri string
|
||||
|
||||
@description('The location for all resources.')
|
||||
param location string
|
||||
|
||||
@description('Azure App Configuration User Assigned Identity Name.')
|
||||
param userAssignedIdentityName string
|
||||
|
||||
@description('The name of the Azure App Configuration.')
|
||||
param appConfigurationName string
|
||||
|
||||
@description('The name of the Log Analytics Workspace used by Application Insigths.')
|
||||
param logAnalyticsWorkspaceName string
|
||||
|
||||
@description('The name of Application Insights.')
|
||||
param applicationInsightsName string
|
||||
|
||||
module signupAdministrationWebApp './../../../../Saas.Lib/Saas.Bicep.Module/appServiceModuleWithObservability.bicep' = {
|
||||
name: 'PermissionApi'
|
||||
params: {
|
||||
appServiceName: permissionsapi
|
||||
version: version
|
||||
environment: environment
|
||||
appServicePlanName: appServicePlanName
|
||||
keyVaultUri: keyVaultUri
|
||||
location: location
|
||||
userAssignedIdentityName: userAssignedIdentityName
|
||||
appConfigurationName: appConfigurationName
|
||||
logAnalyticsWorkspaceName: logAnalyticsWorkspaceName
|
||||
applicationInsightsName: applicationInsightsName
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
@description('Version')
|
||||
param version string
|
||||
|
||||
@description('The name of the key vault')
|
||||
param keyVaultName string
|
||||
|
||||
@description('The URI of the key vault.')
|
||||
param keyVaultUri string
|
||||
|
||||
@description('Azure B2C Domain Name.')
|
||||
param azureB2CDomain string
|
||||
|
||||
@description('Azure B2C Tenant Id.')
|
||||
param azureB2cTenantId string
|
||||
|
||||
@description('Azure AD Instance')
|
||||
param azureAdInstance string
|
||||
|
||||
@description('The Azure B2C Signed Out Call Back Path.')
|
||||
param signedOutCallBackPath string
|
||||
|
||||
@description('The Azure B2C Sign up/in Policy Id.')
|
||||
param signUpSignInPolicyId string
|
||||
|
||||
@description('The Azure B2C Permissions API base Url.')
|
||||
param baseUrl string
|
||||
|
||||
@description('The Client Id found on registered Permissions API app page.')
|
||||
param clientId string
|
||||
|
||||
@description('User Identity Name')
|
||||
param userAssignedIdentityName string
|
||||
|
||||
@description('App Configuration Name')
|
||||
param appConfigurationName string
|
||||
|
||||
@description('The name of the certificate key.')
|
||||
param certificateKeyName string
|
||||
|
||||
// Create object with array of objects containing the kayname and value to be stored in Azure App Configuration store.
|
||||
|
||||
resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' existing = {
|
||||
name: userAssignedIdentityName
|
||||
}
|
||||
|
||||
var azureB2CKeyName = 'AzureB2C'
|
||||
var permissionsApiKeyName = 'permissionsApi'
|
||||
|
||||
var certificates = [
|
||||
{
|
||||
SourceType: keyVaultName
|
||||
KeyVaultUrl: keyVaultUri
|
||||
KeyVaultCertificateName: certificateKeyName
|
||||
}
|
||||
]
|
||||
|
||||
var appConfigStore = {
|
||||
appConfigurationName: appConfigurationName
|
||||
keyVaultName: keyVaultName
|
||||
userAssignedIdentityName: userAssignedIdentity.name
|
||||
label: version
|
||||
entries: [
|
||||
{
|
||||
key: '${permissionsApiKeyName}:${azureB2CKeyName}:ClientCertificates'
|
||||
value: ' ${string(certificates)}' // notice the space before the string, this is a necessary hack. https://github.com/Azure/bicep/issues/6167
|
||||
isSecret: false
|
||||
contentType: 'application/json'
|
||||
}
|
||||
{
|
||||
key: '${permissionsApiKeyName}:${azureB2CKeyName}:BaseUrl'
|
||||
value: baseUrl
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionsApiKeyName}:${azureB2CKeyName}:ClientId'
|
||||
value: clientId
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionsApiKeyName}:${azureB2CKeyName}:TenantId'
|
||||
value: azureB2cTenantId
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionsApiKeyName}:${azureB2CKeyName}:Domain'
|
||||
value: azureB2CDomain
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionsApiKeyName}:${azureB2CKeyName}:Instance'
|
||||
value: azureAdInstance
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionsApiKeyName}:${azureB2CKeyName}:Audience'
|
||||
value: clientId
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionsApiKeyName}:${azureB2CKeyName}:SignedOutCallbackPath'
|
||||
value: signedOutCallBackPath
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
{
|
||||
key: '${permissionsApiKeyName}:${azureB2CKeyName}:SignUpSignInPolicyId'
|
||||
value: signUpSignInPolicyId
|
||||
isSecret: false
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Adding App Configuration entries
|
||||
module appConfigurationSettings './../../../../Saas.Lib/Saas.Bicep.Module/addConfigEntry.bicep' = [ for entry in appConfigStore.entries: {
|
||||
name: replace('Entry-${entry.key}', ':', '-')
|
||||
params: {
|
||||
appConfigurationName: appConfigStore.appConfigurationName
|
||||
userAssignedIdentityName: appConfigStore.userAssignedIdentityName
|
||||
keyVaultName: keyVaultName
|
||||
value: entry.value
|
||||
contentType: entry.contentType
|
||||
keyName: entry.key
|
||||
label: appConfigStore.label
|
||||
isSecret: entry.isSecret
|
||||
}
|
||||
}]
|
|
@ -1,7 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
force_update=false
|
||||
|
||||
while getopts f flag
|
||||
do
|
||||
case "${flag}" in
|
||||
f) force_update=true;;
|
||||
*) force_update=false;;
|
||||
esac
|
||||
done
|
||||
|
||||
repo_base="$( git rev-parse --show-toplevel )"
|
||||
docker_file_folder="${repo_base}/src/Saas.lib/Deployment.Container"
|
||||
|
||||
|
||||
# redirect to build.sh in the Deployment.Container folder
|
||||
"${docker_file_folder}/build.sh"
|
||||
if [[ "${force_update}" == false ]]; then
|
||||
"${docker_file_folder}/build.sh"
|
||||
else
|
||||
"${docker_file_folder}/build.sh" -f
|
||||
fi
|
||||
|
|
|
@ -3,23 +3,16 @@
|
|||
# disable unused variable warning https://www.shellcheck.net/wiki/SC2034
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
# user directories
|
||||
BASE_AZURE_CONFIG_DIR="$HOME/.azure"
|
||||
B2C_USR_AZURE_CONFIG_DIR="${HOME}/b2c/.azure"
|
||||
SP_USR_AZURE_CONFIG_DIR="${HOME}/sp/.azure"
|
||||
# app naming
|
||||
APP_NAME="permissions-api"
|
||||
APP_DEPLOYMENT_NAME="permissionApi"
|
||||
|
||||
# repo base
|
||||
repo_base="$( git rev-parse --show-toplevel )"
|
||||
repo_base="$(git rev-parse --show-toplevel)"
|
||||
REPO_BASE="${repo_base}"
|
||||
|
||||
WORKFLOW_BASE="${REPO_BASE}/.github/workflows"
|
||||
PERMISSIONS_DEPLOYMENT_WORKFLOW="${WORKFLOW_BASE}/permissions-api-deploy.yml"
|
||||
|
||||
# script directories
|
||||
BASE_DIR="${ASDK_PERMISSIONS_API_DEPLOYMENT_BASE_DIR}"
|
||||
|
||||
# global script directory
|
||||
SHARED_MODULE_DIR="${REPO_BASE}/src/Saas.Lib/Deployment.Script.Modules"
|
||||
# project base directory
|
||||
BASE_DIR="${REPO_BASE}/src/Saas.Identity/Saas.Permissions/deployment"
|
||||
|
||||
# local script directory
|
||||
SCRIPT_DIR="${BASE_DIR}/script"
|
||||
|
@ -27,6 +20,16 @@ SCRIPT_DIR="${BASE_DIR}/script"
|
|||
#local log directory
|
||||
LOG_FILE_DIR="${BASE_DIR}/log"
|
||||
|
||||
# configuration manifest for the Identity Foundation deployment, run previously
|
||||
CONFIG_DIR="${REPO_BASE}/src/Saas.Identity/Saas.IdentityProvider/deployment/config"
|
||||
CONFIG_FILE="${REPO_BASE}/src/Saas.Identity/Saas.IdentityProvider/deployment/config/config.json"
|
||||
# act directory
|
||||
ACT_DIR="${BASE_DIR}/act"
|
||||
|
||||
# GitHub workflows
|
||||
WORKFLOW_BASE="${REPO_BASE}/.github/workflows"
|
||||
GITHUB_ACTION_WORKFLOW_FILE="${WORKFLOW_BASE}/permissions-api-deploy.yml"
|
||||
ACT_LOCAL_WORKFLOW_DEBUG_FILE="${ACT_DIR}/workflows/permissions-api-deploy-debug.yml"
|
||||
|
||||
# global script directory
|
||||
SHARED_MODULE_DIR="${REPO_BASE}/src/Saas.Lib/Deployment.Script.Modules"
|
||||
|
||||
# adding app service global constants
|
||||
source "${SHARED_MODULE_DIR}/app-service-constants.sh"
|
||||
|
|