Skip to content

Commit

Permalink
Add GCCH support (#36)
Browse files Browse the repository at this point in the history
* Add GCCH support

* chore: Update Kusto cluster configurations for GovCloud support

* chore: Explicit Dependency on Function Key Vault Access

* feat: Add SCM_COMMAND_IDLE_TIMEOUT configuration for function app

* chore: Create SPN for Microsoft Graph Change Tracking app

* Enhance Azure credential configuration

Enhanced the configuration of DefaultAzureCredentialOptions in ServiceCollectionExtensions.cs within the CallRecordInsights.Extensions namespace. Added a conditional check to set the AuthorityHost property to a custom URI if identityOptions.Authority is not null. Also, updated the AdditionallyAllowedTenants collection by adding a wildcard character, "*", to allow credentials to be used across multiple tenants for increased flexibility.

* Enhance GraphServiceClient registration

* feat: update deployFunction template for GCCH/DoD

* improve deployment logging output, fix managedidentity deployment

* feat: Add support for user agent in GraphRequestBuilderAppOnlyExtensions

* Remove outdated documentation files and update README.md
  • Loading branch information
adthom committed Aug 20, 2024
1 parent f2085fc commit 3677053
Show file tree
Hide file tree
Showing 46 changed files with 382 additions and 986 deletions.
18 changes: 1 addition & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,7 @@ Because this solution uses Cosmos DB it is highly scalable and because it uses K

The solution is fully deployed within your own tenant. All data processing and data storage is done in whichever tenant you deploy the application.

1. [High Level Architecture](./docs/high-level-architecture.md)

1. [Requirements](./docs/requirements.md)

1. [Deployment](./docs/deployment.md)

1. [Validation & Verification](./docs/deployment.md#deployment-validation--verification)

1. [Admin Functions](./docs/admin-functions.md)

1. [CosmosDB, Kusto Functions, Views and Queries](./docs/data-explorer-functions-views-queries.md)

1. [Configuration Settings](./docs/function-configuration-settings.md)

1. [Multi Tenant](./docs/multi-tenant-deployment.md)

1. [Troubleshooting](./docs/troubleshooting.md)
[Documentation](../../wiki)

## Contributing

Expand Down
56 changes: 53 additions & 3 deletions deploy/bicep/GraphChangeNotificationSubnets.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,97 @@
"52.159.23.209/32",
"52.159.17.84/32",
"13.78.204.0/32",
"52.148.24.136/32",
"52.148.27.39/32",
"52.147.213.251/32",
"52.147.213.181/32",
"20.127.53.125/32",
"40.76.162.99/32",
"40.76.162.42/32",
"70.37.95.92/32",
"70.37.95.11/32",
"70.37.92.195/32",
"70.37.93.191/32",
"70.37.90.219/32",
"20.9.36.45/32",
"20.9.35.166/32",
"20.9.36.128/32",
"20.9.37.73/32",
"20.9.37.76/32",
"20.96.21.67/32",
"20.69.245.215/32",
"104.46.117.15/32",
"20.96.21.98/32",
"20.96.21.115/32",
"137.135.11.161/32",
"137.135.11.116/32",
"20.253.156.113/32",
"137.135.11.222/32",
"137.135.11.250/32",
"52.159.107.50/32",
"52.159.107.4/32",
"52.159.124.33/32",
"52.159.109.205/32",
"52.159.102.72/32",
"20.98.68.182/32",
"20.98.68.57/32",
"20.98.68.200/32",
"20.98.68.203/32",
"20.98.68.218/32",
"20.171.81.121/32",
"20.25.189.138/32",
"20.171.82.192/32",
"20.171.83.146/32",
"20.171.83.157/32",
"52.142.114.29/32",
"52.142.115.31/32",
"20.223.139.245/32",
"51.104.159.213/32",
"51.104.159.181/32",
"51.124.75.43/32",
"51.124.73.177/32",
"104.40.209.182/32",
"51.138.90.7/32",
"51.138.90.52/32",
"20.199.102.157/32",
"20.199.102.73/32",
"20.216.150.67/32",
"20.111.9.46/32",
"20.111.9.77/32",
"13.87.81.123/32",
"13.87.81.35/32",
"20.90.99.1/32",
"13.87.81.133/32",
"13.87.81.141/32",
"20.91.212.211/32",
"20.91.212.136/32",
"20.91.213.57/32",
"20.91.208.88/32",
"20.91.209.147/32",
"20.44.210.83/32",
"20.44.210.146/32",
"20.212.153.162/32",
"52.148.115.48/32",
"52.148.114.238/32",
"40.80.232.177/32",
"40.80.232.118/32",
"52.231.196.24/32",
"40.80.233.14/32",
"40.80.239.196/32",
"20.48.12.75/32",
"20.48.11.201/32",
"20.89.108.161/32",
"20.48.14.35/32",
"20.48.15.147/32",
"104.215.13.23/32",
"104.215.6.169/32",
"20.89.240.165/32"
"20.89.240.165/32",
"104.215.18.55/32",
"104.215.12.254/32",
"20.20.32.0/19",
"20.190.128.0/18",
"20.231.128.0/19",
"40.126.0.0/18"
],
"AzureUSGovernment": [
"52.244.33.45/32",
Expand All @@ -69,7 +112,9 @@
"52.182.32.51/32",
"52.182.32.143/32",
"52.181.24.199/32",
"52.181.24.220/32"
"52.181.24.220/32",
"20.140.232.0/23",
"52.126.194.0/23"
],
"AzureChinaCloud": [
"42.159.72.35/32",
Expand All @@ -79,6 +124,11 @@
"40.125.138.23/32",
"40.125.136.69/32",
"40.72.155.199/32",
"40.72.155.216/32"
"40.72.155.216/32",
"40.72.70.0/23",
"52.130.2.32/27",
"52.130.3.64/27",
"52.130.17.192/27",
"52.130.18.32/27"
]
}
51 changes: 38 additions & 13 deletions deploy/bicep/deployFunction.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -296,14 +296,14 @@ var graphChangeNotificationSubnets = loadJsonContent('GraphChangeNotificationSub

// Event Hub
resource eventHubNamespace 'Microsoft.EventHub/namespaces@2022-10-01-preview' = {
name: baseResourceName
name: length(baseResourceName) >= 6 ? baseResourceName : '${baseResourceName}${substring(uniqueString(baseResourceName),0, 6 - length(baseResourceName))}'
location: location
sku: configurations[deploymentType].eventHub.sku
properties: configurations[deploymentType].eventHub.properties
resource graphEventHub 'eventhubs' = {
name: 'graphevents'
properties: configurations[deploymentType].eventHub.eventhubs.properties
resource senderAuthorizationRule 'authorizationRules' = if (!useGraphEventHubManagedIdentity) {
resource senderAuthorizationRule 'authorizationRules' = {
name: 'sender'
properties: { rights: [ 'Send' ] }
}
Expand Down Expand Up @@ -338,12 +338,22 @@ resource serverfarm 'Microsoft.Web/serverfarms@2022-09-01' = {
sku: configurations[deploymentType].serverfarm.sku
}

var eventHubFQDN = split(split(eventHubNamespace.properties.serviceBusEndpoint,'://')[1],':')[0]

var GraphNotificationUrl = useGraphEventHubManagedIdentity /*
*/ ? 'EventHub:${eventHubNamespace.properties.serviceBusEndpoint}/eventhubname/${eventHubNamespace::graphEventHub.name}' /*
*/ ? 'EventHub:https://${eventHubFQDN}/eventhubname/${eventHubNamespace::graphEventHub.name}' /*
*/ : useSeparateKeyVaultForGraph /*
*/ ? 'EventHub:${graphKeyVault::graphEventHubConnectionString.properties.secretUri}' /*
*/ : 'EventHub:${keyvault::graphEventHubConnectionString.properties.secretUri}'

var GraphEndpoints = {
usgovvirginia : 'graph.microsoft.us'
usgovarizona : 'graph.microsoft.us'
usgovtexas : 'graph.microsoft.us'
usdodcentral : 'dod-graph.microsoft.us'
usdodeast : 'dod-graph.microsoft.us'
}

resource functionApp 'Microsoft.Web/sites@2022-09-01' = {
name: '${baseResourceName}-function'
location: location
Expand All @@ -352,34 +362,49 @@ resource functionApp 'Microsoft.Web/sites@2022-09-01' = {
properties: configurations[deploymentType].functionApp.properties
resource appSettings 'config' = {
name: 'appsettings'
properties: toObject([
properties: toObject(flatten([
[
{ key: 'RenewSubscriptionScheduleCron', value: '0 0 */2 * * *' }
// CallRecords Queue Configuration
{ key: 'CallRecordsQueueConnection__queueServiceUri', value: storageAccount.properties.primaryEndpoints.queue }
{ key: 'CallRecordsQueueConnection__credential', value: 'managedidentity' }
{ key: 'CallRecordsToDownloadQueueName', value: storageAccount::queues::download.name }

// Graph Subscription Manager Configuration
{ key: 'GraphSubscription__NotificationUrl', value: GraphNotificationUrl }
{ key: 'GraphSubscription__Tenants', value: tenantDomain }

{ key: 'CallRecordInsightsDb__EndpointUri', value: cosmosAccount.properties.documentEndpoint }
{ key: 'CallRecordInsightsDb__DatabaseName', value: cosmosAccount::database.properties.resource.id }
{ key: 'CallRecordInsightsDb__ProcessedContainerName', value: cosmosAccount::database::container.properties.resource.id }

{ key: 'GraphNotificationEventHubName', value: eventHubNamespace::graphEventHub.name }

{ key: 'EventHubConnection__fullyQualifiedNamespace', value: split(split(eventHubNamespace.properties.serviceBusEndpoint,'://')[1],':')[0] }
{ key: 'EventHubConnection__fullyQualifiedNamespace', value: eventHubFQDN }
{ key: 'EventHubConnection__credential', value: 'managedidentity' }

{ key: 'AzureWebJobsStorage__accountName', value: storageAccount.name }

{ key: 'AzureWebJobsSecretStorageType', value: 'keyvault' }
{ key: 'AzureWebJobsSecretStorageKeyVaultUri', value: keyvault.properties.vaultUri }
{ key: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' }
{ key: 'FUNCTIONS_WORKER_RUNTIME', value: 'dotnet' }
{ key: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING', value: '@Microsoft.KeyVault(VaultName=${keyvault.name};SecretName=${keyvault::storageAccountConnectionString.name})' }
{ key: 'WEBSITE_CONTENTSHARE', value: toLower(functionApp.name) }
], o => o.key, o => o.value)
{ key: 'SCM_COMMAND_IDLE_TIMEOUT', value: '1800' }
]
contains(GraphEndpoints, location) ? [ // GCCH/DoD Configuration
{ key: 'GraphSubscription__Endpoint', value: GraphEndpoints[location] }
{ key: 'AzureAd__Instance', value: environment().authentication.loginEndpoint }
{ key: 'AzureWebJobsStorage__blobServiceUri', value: storageAccount.properties.primaryEndpoints.blob }
{ key: 'AzureWebJobsStorage__queueServiceUri', value: storageAccount.properties.primaryEndpoints.queue }
{ key: 'AzureWebJobsStorage__tableServiceUri', value: storageAccount.properties.primaryEndpoints.table }
] : [ // Non-GCCH/DoD Configuration
{ key: 'AzureWebJobsStorage__accountName', value: storageAccount.name }
]]), o => o.key, o => o.value)
dependsOn: [
functionAppKeyVaultRoleAssignment // Ensure the function app has access to the key vault before reading referenced secrets
functionAppEventHubsRoleAssignment
functionAppStorageRoleAssignment
]
}
}

Expand All @@ -397,7 +422,7 @@ resource keyvault 'Microsoft.KeyVault/vaults@2023-02-01' = {
}
}

resource graphEventHubConnectionString 'secrets@2023-02-01' = if (!useGraphEventHubManagedIdentity && !useSeparateKeyVaultForGraph) {
resource graphEventHubConnectionString 'secrets' = if (!useGraphEventHubManagedIdentity && !useSeparateKeyVaultForGraph) {
name: 'GraphEventHubConnectionString'
properties: {
value: eventHubNamespace::graphEventHub::senderAuthorizationRule.listkeys().primaryConnectionString
Expand All @@ -413,7 +438,7 @@ resource graphKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' = if (!useGraphEve
location: location
properties: configurations[deploymentType].keyvault.properties

resource graphEventHubConnectionString 'secrets' = if (!useGraphEventHubManagedIdentity && useSeparateKeyVaultForGraph) {
resource graphEventHubConnectionString 'secrets' = {
name: 'GraphEventHubConnectionString'
properties: {
value: eventHubNamespace::graphEventHub::senderAuthorizationRule.listkeys().primaryConnectionString
Expand Down
10 changes: 6 additions & 4 deletions deploy/bicep/deployKusto.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ param existingKustoClusterName string = 'NOEXISTINGKUSTOCLUSTER'
@description('The type of identity to assign to the cluster. SystemAssigned is the default. None will not assign an identity. UserAssigned will assign the identity specified in the identity parameter.')
param identity string = 'SystemAssigned'

var isGovCloud = startsWith(location, 'usgov') || startsWith(location, 'usdod')

// T-Shirt sizing
var clusterConfigurations = {
DevTest: {
cluster: {
sku: {
name: 'Dev(No SLA)_Standard_E2a_v4'
name: isGovCloud ? 'Dev(No SLA)_Standard_D11_v2' : 'Dev(No SLA)_Standard_E2a_v4'
tier: 'Basic'
capacity: 1
}
Expand All @@ -45,7 +47,7 @@ var clusterConfigurations = {
Production: {
cluster: {
sku: {
name: 'Standard_E2ads_v5'
name: isGovCloud ? 'Standard_D11_v2' : 'Standard_E2ads_v5'
tier: 'Standard'
capacity: 2
}
Expand All @@ -58,11 +60,11 @@ var clusterConfigurations = {
}
}
}
// this will eventually be locked down netowrk wise, but for now, just using the same as production
// this will eventually be locked down network wise, but for now, just using the same as production
RestrictedProduction: {
cluster: {
sku: {
name: 'Standard_E2ads_v5'
name: isGovCloud ? 'Standard_D11_v2' : 'Standard_E2ads_v5'
tier: 'Standard'
capacity: 2
}
Expand Down
Loading

0 comments on commit 3677053

Please sign in to comment.