From db45632283e6982fb095f6be33540c28ad54960a Mon Sep 17 00:00:00 2001 From: Steve Keeler Date: Sun, 9 Jul 2023 23:14:55 -0400 Subject: [PATCH] Scripts to generate config from template, support JSON config intellisense in editors, fix bugs in deployment scripts (#379) Fixes path normalization bug in deployment scripts #374 Fixes subscription filtering bug in deployment scripts #375 Adds CanadaPubSecALZ configuration JSON schema support for editors #376 Adds Scripts to generate CanadaPubSecALZ configuration files using existing environments as template #377 Adds Deploy landing zones to new Azure subscriptions in new primary tenant #378 --- .github/workflows/0-everything.yml | 2 +- .github/workflows/1-management-groups.yml | 2 +- .github/workflows/2-roles.yml | 2 +- .github/workflows/3-logging.yml | 2 +- .github/workflows/4-policy.yml | 2 +- .github/workflows/5-azure-firewall-policy.yml | 2 +- .../5-hub-network-with-azure-firewall.yml | 2 +- .github/workflows/5-hub-network-with-nva.yml | 2 +- .github/workflows/6-identity.yml | 2 +- .github/workflows/7-subscriptions.yml | 2 +- .gitignore | 1 + .../templates/jobs/trigger-subscriptions.yml | 6 +- README.md | 1 + .../CanadaESLZ-main/identity.parameters.json | 170 ----- .../identity.parameters.json | 187 +++++ .../CanadaESLZ-main/logging.parameters.json | 128 ---- .../logging.parameters.json | 149 ++++ .../azure-firewall-policy.parameters.json | 4 +- .../hub-azfw/hub-network.parameters.json | 65 +- .../hub-nva/hub-network.parameters.json | 65 +- ...c0_generic-subscription_canadacentral.json | 171 ----- ...cbed91ab29a7_healthcare_canadacentral.json | 179 ----- ...d7a360_machinelearning_canadacentral.json} | 75 +- ...10cc2b79b6c7_healthcare_canadacentral.json | 200 ++++++ ...05_generic-subscription_canadacentral.json | 192 +++++ ...9ea82c_machinelearning_canadacentral.json} | 75 +- ...3f1a61_machinelearning_canadacentral.json} | 75 +- ...e16f12_machinelearning_canadacentral.json} | 76 +- config/variables/CanadaESLZ-main.yml | 86 --- config/variables/CanadaPubSecALZ-main.yml | 78 +++ docs/archetypes/authoring-guide.md | 16 +- docs/onboarding/azure-devops-pipelines.md | 54 +- docs/onboarding/configuration-scripts.md | 663 ++++++++++++++++++ docs/policy/readme.md | 12 +- .../configuration/Connect-AlzCredential.ps1 | 70 ++ .../configuration/Get-AlzConfiguration.ps1 | 55 ++ .../configuration/Get-AlzSubscriptions.ps1 | 55 ++ .../configuration/Install-Prerequisites.ps1 | 14 + .../configuration/New-AlzConfiguration.ps1 | 475 +++++++++++++ scripts/configuration/New-AlzCredential.ps1 | 132 ++++ scripts/configuration/New-AlzDeployment.ps1 | 217 ++++++ .../configuration/Remove-AlzConfiguration.ps1 | 150 ++++ .../configuration/Remove-AlzCredential.ps1 | 123 ++++ scripts/configuration/Test-AlzCredential.ps1 | 138 ++++ .../Functions/EnvironmentContext.ps1 | 26 +- .../Functions/HubNetworkWithAzureFirewall.ps1 | 46 +- .../Functions/HubNetworkWithNVA.ps1 | 44 +- .../deployments/Functions/Subscriptions.ps1 | 30 +- scripts/deployments/RunWorkflows.ps1 | 16 +- scripts/onboarding/create-pipelines.bat | 2 +- .../set-variables.CanadaPubSecALZ.bat | 54 ++ 51 files changed, 3408 insertions(+), 987 deletions(-) delete mode 100644 config/identity/CanadaESLZ-main/identity.parameters.json create mode 100644 config/identity/CanadaPubSecALZ-main/identity.parameters.json delete mode 100644 config/logging/CanadaESLZ-main/logging.parameters.json create mode 100644 config/logging/CanadaPubSecALZ-main/logging.parameters.json rename config/networking/{CanadaESLZ-main => CanadaPubSecALZ-main}/hub-azfw-policy/azure-firewall-policy.parameters.json (78%) rename config/networking/{CanadaESLZ-main => CanadaPubSecALZ-main}/hub-azfw/hub-network.parameters.json (80%) rename config/networking/{CanadaESLZ-main => CanadaPubSecALZ-main}/hub-nva/hub-network.parameters.json (86%) delete mode 100644 config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/4f9f8765-911a-4a6d-af60-4bc0473268c0_generic-subscription_canadacentral.json delete mode 100644 config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/82f7705e-3386-427b-95b7-cbed91ab29a7_healthcare_canadacentral.json rename config/subscriptions/{CanadaESLZ-main/pubsec/LandingZones/DevTest/f08c3057-1713-4a6f-b7e6-0df355b60c30_machinelearning_canadacentral.json => CanadaPubSecALZ-main/DevTest/0a5970ed-b9d5-464a-acef-8a06f0d7a360_machinelearning_canadacentral.json} (71%) create mode 100644 config/subscriptions/CanadaPubSecALZ-main/DevTest/1f519216-5e39-4b51-a9b6-10cc2b79b6c7_healthcare_canadacentral.json create mode 100644 config/subscriptions/CanadaPubSecALZ-main/DevTest/8422552f-3840-4934-a971-6ee349ffbb05_generic-subscription_canadacentral.json rename config/subscriptions/{CanadaESLZ-main/pubsec/LandingZones/DevTest/ec6c5689-db04-4f1e-b76d-834a51dd0e27_machinelearning_canadacentral.json => CanadaPubSecALZ-main/DevTest/9d9817f5-c218-4553-b686-58be8d9ea82c_machinelearning_canadacentral.json} (71%) rename config/subscriptions/{CanadaESLZ-main/pubsec/LandingZones/DevTest/f459218a-e8bb-49c9-b768-ee6828a144aa_machinelearning_canadacentral.json => CanadaPubSecALZ-main/DevTest/bbe30f1a-fda5-4873-b51b-c838cf3f1a61_machinelearning_canadacentral.json} (72%) rename config/subscriptions/{CanadaESLZ-main/pubsec/LandingZones/DevTest/8c6e48a4-4c73-4a1f-9f95-9447804f2c98_machinelearning_canadacentral.json => CanadaPubSecALZ-main/DevTest/f881fccb-2598-4b9c-b87c-b392f5e16f12_machinelearning_canadacentral.json} (72%) delete mode 100644 config/variables/CanadaESLZ-main.yml create mode 100644 config/variables/CanadaPubSecALZ-main.yml create mode 100644 docs/onboarding/configuration-scripts.md create mode 100644 scripts/configuration/Connect-AlzCredential.ps1 create mode 100644 scripts/configuration/Get-AlzConfiguration.ps1 create mode 100644 scripts/configuration/Get-AlzSubscriptions.ps1 create mode 100644 scripts/configuration/Install-Prerequisites.ps1 create mode 100644 scripts/configuration/New-AlzConfiguration.ps1 create mode 100644 scripts/configuration/New-AlzCredential.ps1 create mode 100644 scripts/configuration/New-AlzDeployment.ps1 create mode 100644 scripts/configuration/Remove-AlzConfiguration.ps1 create mode 100644 scripts/configuration/Remove-AlzCredential.ps1 create mode 100644 scripts/configuration/Test-AlzCredential.ps1 create mode 100644 scripts/onboarding/set-variables.CanadaPubSecALZ.bat diff --git a/.github/workflows/0-everything.yml b/.github/workflows/0-everything.yml index e2f7b01a..6f58d224 100644 --- a/.github/workflows/0-everything.yml +++ b/.github/workflows/0-everything.yml @@ -31,7 +31,7 @@ on: required: false environmentName: type: string - description: Environment name (optional), e.g. CanadaESLZ-main + description: Environment name (optional), e.g. CanadaPubSecALZ-main required: false defaults: diff --git a/.github/workflows/1-management-groups.yml b/.github/workflows/1-management-groups.yml index 17ffebe1..f042e3a6 100644 --- a/.github/workflows/1-management-groups.yml +++ b/.github/workflows/1-management-groups.yml @@ -14,7 +14,7 @@ on: inputs: environmentName: type: string - description: Environment name (optional), e.g. CanadaESLZ-main + description: Environment name (optional), e.g. CanadaPubSecALZ-main required: false defaults: diff --git a/.github/workflows/2-roles.yml b/.github/workflows/2-roles.yml index 115cb15c..f034d6e4 100644 --- a/.github/workflows/2-roles.yml +++ b/.github/workflows/2-roles.yml @@ -14,7 +14,7 @@ on: inputs: environmentName: type: string - description: Environment name (optional), e.g. CanadaESLZ-main + description: Environment name (optional), e.g. CanadaPubSecALZ-main required: false defaults: diff --git a/.github/workflows/3-logging.yml b/.github/workflows/3-logging.yml index a90b118b..dd883305 100644 --- a/.github/workflows/3-logging.yml +++ b/.github/workflows/3-logging.yml @@ -14,7 +14,7 @@ on: inputs: environmentName: type: string - description: Environment name (optional), e.g. CanadaESLZ-main + description: Environment name (optional), e.g. CanadaPubSecALZ-main required: false defaults: diff --git a/.github/workflows/4-policy.yml b/.github/workflows/4-policy.yml index 6ecf8b9d..004218b2 100644 --- a/.github/workflows/4-policy.yml +++ b/.github/workflows/4-policy.yml @@ -14,7 +14,7 @@ on: inputs: environmentName: type: string - description: Environment name (optional), e.g. CanadaESLZ-main + description: Environment name (optional), e.g. CanadaPubSecALZ-main required: false defaults: diff --git a/.github/workflows/5-azure-firewall-policy.yml b/.github/workflows/5-azure-firewall-policy.yml index b2240abf..a6d73f72 100644 --- a/.github/workflows/5-azure-firewall-policy.yml +++ b/.github/workflows/5-azure-firewall-policy.yml @@ -14,7 +14,7 @@ on: inputs: environmentName: type: string - description: Environment name (optional), e.g. CanadaESLZ-main + description: Environment name (optional), e.g. CanadaPubSecALZ-main required: false defaults: diff --git a/.github/workflows/5-hub-network-with-azure-firewall.yml b/.github/workflows/5-hub-network-with-azure-firewall.yml index ae131650..61579e5c 100644 --- a/.github/workflows/5-hub-network-with-azure-firewall.yml +++ b/.github/workflows/5-hub-network-with-azure-firewall.yml @@ -14,7 +14,7 @@ on: inputs: environmentName: type: string - description: Environment name (optional), e.g. CanadaESLZ-main + description: Environment name (optional), e.g. CanadaPubSecALZ-main required: false defaults: diff --git a/.github/workflows/5-hub-network-with-nva.yml b/.github/workflows/5-hub-network-with-nva.yml index 65557837..35423d11 100644 --- a/.github/workflows/5-hub-network-with-nva.yml +++ b/.github/workflows/5-hub-network-with-nva.yml @@ -14,7 +14,7 @@ on: inputs: environmentName: type: string - description: Environment name (optional), e.g. CanadaESLZ-main + description: Environment name (optional), e.g. CanadaPubSecALZ-main required: false defaults: diff --git a/.github/workflows/6-identity.yml b/.github/workflows/6-identity.yml index 0804b1ab..5f97362e 100644 --- a/.github/workflows/6-identity.yml +++ b/.github/workflows/6-identity.yml @@ -14,7 +14,7 @@ on: inputs: environmentName: type: string - description: Environment name (optional), e.g. CanadaESLZ-main + description: Environment name (optional), e.g. CanadaPubSecALZ-main required: false defaults: diff --git a/.github/workflows/7-subscriptions.yml b/.github/workflows/7-subscriptions.yml index 0b0a0d33..ceb3bd46 100644 --- a/.github/workflows/7-subscriptions.yml +++ b/.github/workflows/7-subscriptions.yml @@ -19,7 +19,7 @@ on: required: true environmentName: type: string - description: Environment name (optional), e.g. CanadaESLZ-main + description: Environment name (optional), e.g. CanadaPubSecALZ-main required: false defaults: diff --git a/.gitignore b/.gitignore index d0b2c249..bb915074 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ experiments/* **/*.swp +**/*.diff .vscode/* /*.sh /*.ps1 diff --git a/.pipelines/templates/jobs/trigger-subscriptions.yml b/.pipelines/templates/jobs/trigger-subscriptions.yml index 679946e1..bf069428 100644 --- a/.pipelines/templates/jobs/trigger-subscriptions.yml +++ b/.pipelines/templates/jobs/trigger-subscriptions.yml @@ -89,13 +89,17 @@ jobs: { $url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/pipelines/$($env:SYSTEM_DEFINITIONID)/runs?api-version=6.0-preview.1" Write-Host "Invoking pipeline definition with URL: $url" + $paths = $env:SUBSCRIPTION_CHANGES -split ',' + $guids = $paths -replace '.*?([0-9a-f]{8}[-]?([0-9a-f]{4}[-]?){3}[0-9a-f]{12}).*', '$1' + $changes = $guids -join ',' $body = @" { "templateParameters": { - "subscriptions":"[$env:SUBSCRIPTION_CHANGES]" + "subscriptions":"[$changes]" }, } "@ + Write-Host "Invoking pipeline definition with body: $body" $headers = @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } $pipeline = Invoke-RestMethod -Uri $url -Headers $headers -Method Post -Body $body -ContentType application/json Write-Host "Pipeline invocation result = $($pipeline | ConvertTo-Json -Depth 100)" diff --git a/README.md b/README.md index f42af606..d07ef966 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ See the following onboarding guides for setup instructions: * [Azure DevOps Setup](docs/onboarding/azure-devops-setup.md) provides guidance on considerations and recommended practices when creating and configuring your Azure DevOps Services environment. * [Azure DevOps Scripts](docs/onboarding/azure-devops-scripts.md) provides guidance on the scripts available to help simplify the onboarding process to Azure Landing Zones design using Azure DevOps pipelines. * [Azure DevOps Pipelines](docs/onboarding/azure-devops-pipelines.md) provides guidance on the manual steps for onboarding to the Azure Landing Zones design using Azure DevOps Pipelines. +* [Configuration Scripts](docs/onboarding/configuration-scripts.md) provides guidance on the scripts available to help simplify the configuration process of the Azure Landing Zones design. ## Goals diff --git a/config/identity/CanadaESLZ-main/identity.parameters.json b/config/identity/CanadaESLZ-main/identity.parameters.json deleted file mode 100644 index 8c759443..00000000 --- a/config/identity/CanadaESLZ-main/identity.parameters.json +++ /dev/null @@ -1,170 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "serviceHealthAlerts": { - "value": { - "resourceGroupName": "service-health", - "incidentTypes": [ "Incident", "Security" ], - "regions": [ "Global", "Canada East", "Canada Central" ], - "receivers": { - "app": [ "alzcanadapubsec@microsoft.com" ], - "email": [ "alzcanadapubsec@microsoft.com" ], - "sms": [ { "countryCode": "1", "phoneNumber": "6045555555" } ], - "voice": [ { "countryCode": "1", "phoneNumber": "6045555555" } ] - }, - "actionGroupName": "Service health action group", - "actionGroupShortName": "health-alert", - "alertRuleName": "Incidents and Security", - "alertRuleDescription": "Service Health: Incidents and Security" - } - }, - "securityCenter": { - "value": { - "email": "alzcanadapubsec@microsoft.com", - "phone": "6045555555" - } - }, - "subscriptionRoleAssignments": { - "value": [ - { - "comments": "Built-in Owner Role", - "roleDefinitionId": "8e3af657-a8ff-443c-a75c-2fe8c4bcb635", - "securityGroupObjectIds": [ - "3a4fa072-cc14-471d-aeac-49afdbde9f7a" - ] - } - ] - }, - "subscriptionBudget": { - "value": { - "createBudget": false - } - }, - "subscriptionTags": { - "value": { - "ISSO": "isso-tbd", - "ClientOrganization": "client-organization-tag", - "CostCenter": "cost-center-tag", - "DataSensitivity": "data-sensitivity-tag", - "ProjectContact": "project-contact-tag", - "ProjectName": "project-name-tag", - "TechnicalContact": "technical-contact-tag" - } - }, - "resourceTags": { - "value": { - "ClientOrganization": "client-organization-tag", - "CostCenter": "cost-center-tag", - "DataSensitivity": "data-sensitivity-tag", - "ProjectContact": "project-contact-tag", - "ProjectName": "project-name-tag", - "TechnicalContact": "technical-contact-tag" - } - }, - "resourceGroups": { - "value": { - "automation": "automation", - "networking": "networking", - "networkWatcher": "NetworkWatcherRG", - "backupRecoveryVault": "backup", - "domainControllers": "DomainControllersRG", - "dnsResolver": "dns-resolverRG", - "dnsCondionalForwarders": "dns-CondionalForwardersRG", - "privateDnsZones": "pubsec-dns" - } - }, - "automation": { - "value": { - "name": "automation" - } - }, - "backupRecoveryVault": { - "value": { - "enabled": true, - "name": "backup-vault" - } - }, - "privateDnsZones": { - "value": { - "enabled": false, - "resourceGroupName": "pubsec-dns" - } - }, - - "privateDnsResolver": { - "value": { - "enabled": true, - "name": "dns-resolver", - "inboundEndpointName": "dns-resolver-Inbound", - "outboundEndpointName": "dns-resolver-Outbound" - } - }, - - "privateDnsResolverRuleset": { - "value": { - "enabled": true, - "name": "dns-resolver-ruleset", - "linkRuleSetToVnet": true, - "linkRuleSetToVnetName": "dns-resolver-vnet-link", - "forwardingRules": [ - { - "name": "default", - "domain": "dontMakeMeThink.local", - "state": "Enabled", - "targetDnsServers": [ - { - "ipAddress": "10.99.99.100" - }, - { - "ipAddress": "10.99.99.99" - } - ] - } - ] - } - }, - - "hubNetwork": { - "value": { - "virtualNetworkId": "/subscriptions/ed7f4eed-9010-4227-b115-2a5e37728f27/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", - "rfc1918IPRange": "10.18.0.0/22", - "rfc6598IPRange": "100.60.0.0/16", - "egressVirtualApplianceIp": "10.18.1.4" - } - }, - - "network": { - "value": { - "deployVnet": true, - "peerToHubVirtualNetwork": true, - "useRemoteGateway": false, - "name": "id-vnet", - "dnsServers": [ - "10.18.1.4" - ], - "addressPrefixes": [ - "10.15.0.0/24" - ], - "subnets": { - "domainControllers": { - "comments": "Identity Subnet for Domain Controllers and VM-Based DNS Servers", - "name": "DomainControllers", - "addressPrefix": "10.15.0.0/27" - }, - "dnsResolverInbound": { - "comments": "Azure DNS Resolver Inbound Requests subnet", - "name": "AzureDNSResolver-Inbound", - "addressPrefix": "10.15.0.32/27" - }, - "dnsResolverOutbound": { - "comments": "Azure DNS Resolver Outbound Requests subnet", - "name": "AzureDNSResolver-Outbound", - "addressPrefix": "10.15.0.64/27" - }, - "optional": [] - } - } - } - } - } \ No newline at end of file diff --git a/config/identity/CanadaPubSecALZ-main/identity.parameters.json b/config/identity/CanadaPubSecALZ-main/identity.parameters.json new file mode 100644 index 00000000..de75793c --- /dev/null +++ b/config/identity/CanadaPubSecALZ-main/identity.parameters.json @@ -0,0 +1,187 @@ +{ + "$schema": "https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-platform-identity.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "serviceHealthAlerts": { + "value": { + "alertRuleName": "Identity Alerts", + "receivers": { + "app": [ + "identity@example.com" + ], + "sms": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ], + "email": [ + "identity@example.com" + ], + "voice": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ] + }, + "regions": [ + "Global", + "Canada Central", + "Canada East" + ], + "resourceGroupName": "service-health-alerts-rg", + "actionGroupName": "Identity Alerts", + "actionGroupShortName": "identity-ag", + "incidentTypes": [ + "Incident", + "Security" + ], + "alertRuleDescription": "Identity Alerts for Incidents and Security" + } + }, + "securityCenter": { + "value": { + "email": "security@example.com", + "phone": "6135555555" + } + }, + "subscriptionRoleAssignments": { + "value": [ + { + "comments": "Built-in Contributor Role", + "securityGroupObjectIds": [ + "b4df54ba-7232-40fa-8f51-f84e8d149322" + ], + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" + } + ] + }, + "subscriptionBudget": { + "value": { + "createBudget": false + } + }, + "subscriptionTags": { + "value": { + "ISSO": "isso-tbd", + "ClientOrganization": "client-organization-tag", + "CostCenter": "cost-center-tag", + "DataSensitivity": "data-sensitivity-tag", + "ProjectContact": "project-contact-tag", + "ProjectName": "project-name-tag", + "TechnicalContact": "technical-contact-tag" + } + }, + "resourceTags": { + "value": { + "ClientOrganization": "client-organization-tag", + "CostCenter": "cost-center-tag", + "DataSensitivity": "data-sensitivity-tag", + "ProjectContact": "project-contact-tag", + "ProjectName": "project-name-tag", + "TechnicalContact": "technical-contact-tag" + } + }, + "resourceGroups": { + "value": { + "automation": "automation", + "networking": "networking", + "networkWatcher": "NetworkWatcherRG", + "backupRecoveryVault": "backup", + "domainControllers": "DomainControllersRG", + "dnsResolver": "dns-resolverRG", + "dnsCondionalForwarders": "dns-CondionalForwardersRG", + "privateDnsZones": "pubsec-dns" + } + }, + "automation": { + "value": { + "name": "automation" + } + }, + "backupRecoveryVault": { + "value": { + "enabled": true, + "name": "backup-vault" + } + }, + "privateDnsZones": { + "value": { + "enabled": false, + "resourceGroupName": "pubsec-dns" + } + }, + "privateDnsResolver": { + "value": { + "enabled": true, + "name": "dns-resolver", + "inboundEndpointName": "dns-resolver-Inbound", + "outboundEndpointName": "dns-resolver-Outbound" + } + }, + "privateDnsResolverRuleset": { + "value": { + "enabled": true, + "name": "dns-resolver-ruleset", + "linkRuleSetToVnet": true, + "linkRuleSetToVnetName": "dns-resolver-vnet-link", + "forwardingRules": [ + { + "name": "default", + "domain": "dontMakeMeThink.local", + "state": "Enabled", + "targetDnsServers": [ + { + "ipAddress": "10.99.99.100" + }, + { + "ipAddress": "10.99.99.99" + } + ] + } + ] + } + }, + "hubNetwork": { + "value": { + "virtualNetworkId": "/subscriptions/4fd845de-f6c8-4e6d-9a87-c21c4ebf7edd/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", + "rfc1918IPRange": "10.18.0.0/22", + "rfc6598IPRange": "100.60.0.0/16", + "egressVirtualApplianceIp": "10.18.1.4" + } + }, + "network": { + "value": { + "deployVnet": true, + "peerToHubVirtualNetwork": true, + "useRemoteGateway": false, + "name": "id-vnet", + "dnsServers": [ + "10.18.1.4" + ], + "addressPrefixes": [ + "10.15.0.0/24" + ], + "subnets": { + "domainControllers": { + "comments": "Identity Subnet for Domain Controllers and VM-Based DNS Servers", + "name": "DomainControllers", + "addressPrefix": "10.15.0.0/27" + }, + "dnsResolverInbound": { + "comments": "Azure DNS Resolver Inbound Requests subnet", + "name": "AzureDNSResolver-Inbound", + "addressPrefix": "10.15.0.32/27" + }, + "dnsResolverOutbound": { + "comments": "Azure DNS Resolver Outbound Requests subnet", + "name": "AzureDNSResolver-Outbound", + "addressPrefix": "10.15.0.64/27" + }, + "optional": [] + } + } + } + } +} diff --git a/config/logging/CanadaESLZ-main/logging.parameters.json b/config/logging/CanadaESLZ-main/logging.parameters.json deleted file mode 100644 index 6fdddda9..00000000 --- a/config/logging/CanadaESLZ-main/logging.parameters.json +++ /dev/null @@ -1,128 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "serviceHealthAlerts": { - "value": { - "resourceGroupName": "service-health", - "incidentTypes": [ "Incident", "Security" ], - "regions": [ "Global", "Canada East", "Canada Central" ], - "receivers": { - "app": [ "alzcanadapubsec@microsoft.com" ], - "email": [ "alzcanadapubsec@microsoft.com" ], - "sms": [ { "countryCode": "1", "phoneNumber": "6045555555" } ], - "voice": [ { "countryCode": "1", "phoneNumber": "6045555555" } ] - }, - "actionGroupName": "Service health action group", - "actionGroupShortName": "health-alert", - "alertRuleName": "Incidents and Security", - "alertRuleDescription": "Service Health: Incidents and Security" - } - }, - "securityCenter": { - "value": { - "email": "alzcanadapubsec@microsoft.com", - "phone": "6045555555" - } - }, - "subscriptionRoleAssignments": { - "value": [ - { - "comments": "Built-in Contributor Role", - "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c", - "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] - } - ] - }, - "subscriptionBudget": { - "value": { - "createBudget": false - } - }, - "subscriptionTags": { - "value": { - "ISSO": "isso-tbd" - } - }, - "resourceTags": { - "value": { - "ClientOrganization": "client-organization-tag", - "CostCenter": "cost-center-tag", - "DataSensitivity": "data-sensitivity-tag", - "ProjectContact": "project-contact-tag", - "ProjectName": "project-name-tag", - "TechnicalContact": "technical-contact-tag" - } - }, - "logAnalyticsResourceGroupName": { - "value": "pubsec-central-logging" - }, - "logAnalyticsWorkspaceName": { - "value": "log-analytics-workspace" - }, - "logAnalyticsRetentionInDays": { - "value": 730 - }, - "logAnalyticsAutomationAccountName": { - "value": "automation-account" - }, - "dataCollectionRule": { - "value": { - "enabled": true, - "name": "DCR-AzureMonitorLogs", - "windowsEventLogs": [ - { - "streams": [ - "Microsoft-Event" - ], - "xPathQueries": [ - "Application!*[System[(Level=1 or Level=2 or Level=3)]]", - "Security!*[System[(band(Keywords,13510798882111488))]]", - "System!*[System[(Level=1 or Level=2 or Level=3)]]" - ], - "name": "eventLogsDataSource" - } - ], - "syslog": [ - { - "streams": [ - "Microsoft-Syslog" - ], - "facilityNames": [ - "auth", - "authpriv", - "cron", - "daemon", - "mark", - "kern", - "local0", - "local1", - "local2", - "local3", - "local4", - "local5", - "local6", - "local7", - "lpr", - "mail", - "news", - "syslog", - "user", - "uucp" - ], - "logLevels": [ - "Warning", - "Error", - "Critical", - "Alert", - "Emergency" - ], - "name": "sysLogsDataSource" - } - ] - } - } - } -} \ No newline at end of file diff --git a/config/logging/CanadaPubSecALZ-main/logging.parameters.json b/config/logging/CanadaPubSecALZ-main/logging.parameters.json new file mode 100644 index 00000000..ee0ee242 --- /dev/null +++ b/config/logging/CanadaPubSecALZ-main/logging.parameters.json @@ -0,0 +1,149 @@ +{ + "$schema": "https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-platform-logging.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "serviceHealthAlerts": { + "value": { + "alertRuleName": "Logging Alerts", + "receivers": { + "app": [ + "logging@example.com" + ], + "sms": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ], + "email": [ + "logging@example.com" + ], + "voice": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ] + }, + "regions": [ + "Global", + "Canada Central", + "Canada East" + ], + "resourceGroupName": "service-health-alerts-rg", + "actionGroupName": "Logging Alerts", + "actionGroupShortName": "logging-ag", + "incidentTypes": [ + "Incident", + "Security" + ], + "alertRuleDescription": "Logging Alerts for Incidents and Security" + } + }, + "securityCenter": { + "value": { + "email": "security@example.com", + "phone": "6135555555" + } + }, + "subscriptionRoleAssignments": { + "value": [ + { + "comments": "Built-in Contributor Role", + "securityGroupObjectIds": [ + "9e16fb9d-7ea4-43fb-a92c-a5dbe308f921" + ], + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" + } + ] + }, + "subscriptionBudget": { + "value": { + "createBudget": false + } + }, + "subscriptionTags": { + "value": { + "ISSO": "isso-tbd" + } + }, + "resourceTags": { + "value": { + "ClientOrganization": "client-organization-tag", + "CostCenter": "cost-center-tag", + "DataSensitivity": "data-sensitivity-tag", + "ProjectContact": "project-contact-tag", + "ProjectName": "project-name-tag", + "TechnicalContact": "technical-contact-tag" + } + }, + "logAnalyticsResourceGroupName": { + "value": "pubsec-central-logging" + }, + "logAnalyticsWorkspaceName": { + "value": "log-analytics-workspace" + }, + "logAnalyticsRetentionInDays": { + "value": 730 + }, + "logAnalyticsAutomationAccountName": { + "value": "automation-account" + }, + "dataCollectionRule": { + "value": { + "enabled": false, + "name": "DCR-AzureMonitorLogs", + "windowsEventLogs": [ + { + "streams": [ + "Microsoft-Event" + ], + "xPathQueries": [ + "Application!*[System[(Level=1 or Level=2 or Level=3)]]", + "Security!*[System[(band(Keywords,13510798882111488))]]", + "System!*[System[(Level=1 or Level=2 or Level=3)]]" + ], + "name": "eventLogsDataSource" + } + ], + "syslog": [ + { + "streams": [ + "Microsoft-Syslog" + ], + "facilityNames": [ + "auth", + "authpriv", + "cron", + "daemon", + "mark", + "kern", + "local0", + "local1", + "local2", + "local3", + "local4", + "local5", + "local6", + "local7", + "lpr", + "mail", + "news", + "syslog", + "user", + "uucp" + ], + "logLevels": [ + "Warning", + "Error", + "Critical", + "Alert", + "Emergency" + ], + "name": "sysLogsDataSource" + } + ] + } + } + } +} diff --git a/config/networking/CanadaESLZ-main/hub-azfw-policy/azure-firewall-policy.parameters.json b/config/networking/CanadaPubSecALZ-main/hub-azfw-policy/azure-firewall-policy.parameters.json similarity index 78% rename from config/networking/CanadaESLZ-main/hub-azfw-policy/azure-firewall-policy.parameters.json rename to config/networking/CanadaPubSecALZ-main/hub-azfw-policy/azure-firewall-policy.parameters.json index 6bf1f18e..becb6455 100644 --- a/config/networking/CanadaESLZ-main/hub-azfw-policy/azure-firewall-policy.parameters.json +++ b/config/networking/CanadaPubSecALZ-main/hub-azfw-policy/azure-firewall-policy.parameters.json @@ -1,5 +1,5 @@ { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "$schema": "https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-platform-connectivity-hub-azfw-policy.json#", "contentVersion": "1.0.0.0", "parameters": { "resourceTags": { @@ -19,4 +19,4 @@ "value": "pubsecAzureFirewallPolicy" } } -} \ No newline at end of file +} diff --git a/config/networking/CanadaESLZ-main/hub-azfw/hub-network.parameters.json b/config/networking/CanadaPubSecALZ-main/hub-azfw/hub-network.parameters.json similarity index 80% rename from config/networking/CanadaESLZ-main/hub-azfw/hub-network.parameters.json rename to config/networking/CanadaPubSecALZ-main/hub-azfw/hub-network.parameters.json index c74a8ffa..b6ecc49b 100644 --- a/config/networking/CanadaESLZ-main/hub-azfw/hub-network.parameters.json +++ b/config/networking/CanadaPubSecALZ-main/hub-azfw/hub-network.parameters.json @@ -1,38 +1,59 @@ { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "$schema": "https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-platform-connectivity-hub-azfw.json#", "contentVersion": "1.0.0.0", "parameters": { "serviceHealthAlerts": { "value": { - "resourceGroupName": "service-health", - "incidentTypes": [ "Incident", "Security" ], - "regions": [ "Global", "Canada East", "Canada Central" ], - "receivers": { - "app": [ "alzcanadapubsec@microsoft.com" ], - "email": [ "alzcanadapubsec@microsoft.com" ], - "sms": [ { "countryCode": "1", "phoneNumber": "6045555555" } ], - "voice": [ { "countryCode": "1", "phoneNumber": "6045555555" } ] - }, - "actionGroupName": "Service health action group", - "actionGroupShortName": "health-alert", - "alertRuleName": "Incidents and Security", - "alertRuleDescription": "Service Health: Incidents and Security" + "alertRuleName": "Networking Alerts", + "receivers": { + "app": [ + "networking@example.com" + ], + "sms": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ], + "email": [ + "networking@example.com" + ], + "voice": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ] + }, + "regions": [ + "Global", + "Canada Central", + "Canada East" + ], + "resourceGroupName": "service-health-alerts-rg", + "actionGroupName": "Networking Alerts", + "actionGroupShortName": "network-ag", + "incidentTypes": [ + "Incident", + "Security" + ], + "alertRuleDescription": "Networking Alerts for Incidents and Security" } }, "securityCenter": { "value": { - "email": "alzcanadapubsec@microsoft.com", - "phone": "6045555555" + "email": "security@example.com", + "phone": "6135555555" } }, "subscriptionRoleAssignments": { "value": [ { "comments": "Built-in Contributor Role", - "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c", "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] + "12d01649-fdb7-4769-afe5-66248082a064" + ], + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" } ] }, @@ -59,13 +80,13 @@ "privateDnsZones": { "value": { "enabled": true, - "resourceGroupName": "pubsec-dns" + "resourceGroupName": "private-dns-rg" } }, "ddosStandard": { "value": { + "resourceGroupName": "ddos-rg", "enabled": false, - "resourceGroupName": "pubsec-ddos", "planName": "ddos-plan" } }, @@ -207,4 +228,4 @@ } } } -} \ No newline at end of file +} diff --git a/config/networking/CanadaESLZ-main/hub-nva/hub-network.parameters.json b/config/networking/CanadaPubSecALZ-main/hub-nva/hub-network.parameters.json similarity index 86% rename from config/networking/CanadaESLZ-main/hub-nva/hub-network.parameters.json rename to config/networking/CanadaPubSecALZ-main/hub-nva/hub-network.parameters.json index 964a8f9d..2dcd55f5 100644 --- a/config/networking/CanadaESLZ-main/hub-nva/hub-network.parameters.json +++ b/config/networking/CanadaPubSecALZ-main/hub-nva/hub-network.parameters.json @@ -1,38 +1,59 @@ { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "$schema": "https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-platform-connectivity-hub-nva.json#", "contentVersion": "1.0.0.0", "parameters": { "serviceHealthAlerts": { "value": { - "resourceGroupName": "service-health", - "incidentTypes": [ "Incident", "Security" ], - "regions": [ "Global", "Canada East", "Canada Central" ], - "receivers": { - "app": [ "alzcanadapubsec@microsoft.com" ], - "email": [ "alzcanadapubsec@microsoft.com" ], - "sms": [ { "countryCode": "1", "phoneNumber": "6045555555" } ], - "voice": [ { "countryCode": "1", "phoneNumber": "6045555555" } ] - }, - "actionGroupName": "Service health action group", - "actionGroupShortName": "health-alert", - "alertRuleName": "Incidents and Security", - "alertRuleDescription": "Service Health: Incidents and Security" + "alertRuleName": "Networking Alerts", + "receivers": { + "app": [ + "networking@example.com" + ], + "sms": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ], + "email": [ + "networking@example.com" + ], + "voice": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ] + }, + "regions": [ + "Global", + "Canada Central", + "Canada East" + ], + "resourceGroupName": "service-health-alerts-rg", + "actionGroupName": "Networking Alerts", + "actionGroupShortName": "network-ag", + "incidentTypes": [ + "Incident", + "Security" + ], + "alertRuleDescription": "Networking Alerts for Incidents and Security" } }, "securityCenter": { "value": { - "email": "alzcanadapubsec@microsoft.com", - "phone": "6045555555" + "email": "security@example.com", + "phone": "6135555555" } }, "subscriptionRoleAssignments": { "value": [ { "comments": "Built-in Contributor Role", - "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c", "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] + "12d01649-fdb7-4769-afe5-66248082a064" + ], + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" } ] }, @@ -59,13 +80,13 @@ "privateDnsZones": { "value": { "enabled": true, - "resourceGroupName": "pubsec-dns" + "resourceGroupName": "private-dns-rg" } }, "ddosStandard": { "value": { + "resourceGroupName": "ddos-rg", "enabled": false, - "resourceGroupName": "pubsec-ddos", "planName": "ddos-plan" } }, @@ -294,4 +315,4 @@ } } } -} \ No newline at end of file +} diff --git a/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/4f9f8765-911a-4a6d-af60-4bc0473268c0_generic-subscription_canadacentral.json b/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/4f9f8765-911a-4a6d-af60-4bc0473268c0_generic-subscription_canadacentral.json deleted file mode 100644 index 28484d8f..00000000 --- a/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/4f9f8765-911a-4a6d-af60-4bc0473268c0_generic-subscription_canadacentral.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "location": { - "value": "canadacentral" - }, - "serviceHealthAlerts": { - "value": { - "resourceGroupName": "service-health", - "incidentTypes": [ "Incident", "Security" ], - "regions": [ "Global", "Canada East", "Canada Central" ], - "receivers": { - "app": [ "alzcanadapubsec@microsoft.com" ], - "email": [ "alzcanadapubsec@microsoft.com" ], - "sms": [ { "countryCode": "1", "phoneNumber": "6045555555" } ], - "voice": [ { "countryCode": "1", "phoneNumber": "6045555555" } ] - }, - "actionGroupName": "Service health action group", - "actionGroupShortName": "health-alert", - "alertRuleName": "Incidents and Security", - "alertRuleDescription": "Service Health: Incidents and Security" - } - }, - "securityCenter": { - "value": { - "email": "alzcanadapubsec@microsoft.com", - "phone": "6045555555" - } - }, - "subscriptionRoleAssignments": { - "value": [ - { - "comments": "Built-in Role: Contributor", - "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c", - "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] - }, - { - "comments": "Custom Role: Landing Zone Application Owner", - "roleDefinitionId": "b4c87314-c1a1-5320-9c43-779585186bcc", - "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] - } - ] - }, - "subscriptionBudget": { - "value": { - "createBudget": false - } - }, - "subscriptionTags": { - "value": { - "ISSO": "isso-tag" - } - }, - "resourceTags": { - "value": { - "ClientOrganization": "client-organization-tag", - "CostCenter": "cost-center-tag", - "DataSensitivity": "data-sensitivity-tag", - "ProjectContact": "project-contact-tag", - "ProjectName": "project-name-tag", - "TechnicalContact": "technical-contact-tag" - } - }, - "resourceGroups": { - "value": { - "automation": "automation", - "networking": "networking", - "networkWatcher": "NetworkWatcherRG", - "backupRecoveryVault": "backup" - } - }, - "automation": { - "value": { - "name": "automation" - } - }, - "backupRecoveryVault": { - "value": { - "enabled": true, - "name": "backup-vault" - } - }, - "hubNetwork": { - "value": { - "virtualNetworkId": "/subscriptions/ed7f4eed-9010-4227-b115-2a5e37728f27/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", - "rfc1918IPRange": "10.18.0.0/22", - "rfc6598IPRange": "100.60.0.0/16", - "egressVirtualApplianceIp": "10.18.1.4" - } - }, - "network": { - "value": { - "deployVnet": true, - "peerToHubVirtualNetwork": true, - "useRemoteGateway": false, - "name": "vnet", - "dnsServers": [ - "10.18.1.4" - ], - "addressPrefixes": [ - "10.2.0.0/16" - ], - "subnets": [ - { - "comments": "App Management Zone (OZ)", - "name": "appManagement", - "addressPrefix": "10.2.1.0/25", - "nsg": { - "enabled": true - }, - "udr": { - "enabled": true - } - }, - { - "comments": "Presentation Zone (PAZ)", - "name": "web", - "addressPrefix": "10.2.2.0/25", - "nsg": { - "enabled": true - }, - "udr": { - "enabled": true - } - }, - { - "comments": "Application Zone (RZ)", - "name": "app", - "addressPrefix": "10.2.3.0/25", - "nsg": { - "enabled": true - }, - "udr": { - "enabled": true - } - }, - { - "comments": "Data Zone (HRZ)", - "name": "data", - "addressPrefix": "10.2.4.0/25", - "nsg": { - "enabled": true - }, - "udr": { - "enabled": true - } - }, - { - "comments": "App Service", - "name": "appservice", - "addressPrefix": "10.2.5.0/25", - "nsg": { - "enabled": false - }, - "udr": { - "enabled": false - }, - "delegations": { - "serviceName": "Microsoft.Web/serverFarms" - } - } - ] - } - } - } -} \ No newline at end of file diff --git a/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/82f7705e-3386-427b-95b7-cbed91ab29a7_healthcare_canadacentral.json b/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/82f7705e-3386-427b-95b7-cbed91ab29a7_healthcare_canadacentral.json deleted file mode 100644 index c2f72de0..00000000 --- a/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/82f7705e-3386-427b-95b7-cbed91ab29a7_healthcare_canadacentral.json +++ /dev/null @@ -1,179 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "serviceHealthAlerts": { - "value": { - "resourceGroupName": "service-health", - "incidentTypes": [ "Incident", "Security" ], - "regions": [ "Global", "Canada East", "Canada Central" ], - "receivers": { - "app": [ "alzcanadapubsec@microsoft.com" ], - "email": [ "alzcanadapubsec@microsoft.com" ], - "sms": [ { "countryCode": "1", "phoneNumber": "6045555555" } ], - "voice": [ { "countryCode": "1", "phoneNumber": "6045555555" } ] - }, - "actionGroupName": "Service health action group", - "actionGroupShortName": "health-alert", - "alertRuleName": "Incidents and Security", - "alertRuleDescription": "Service Health: Incidents and Security" - } - }, - "securityCenter": { - "value": { - "email": "alzcanadapubsec@microsoft.com", - "phone": "6045555555" - } - }, - "subscriptionRoleAssignments": { - "value": [ - { - "comments": "Built-in Role: Contributor", - "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c", - "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] - }, - { - "comments": "Custom Role: Landing Zone Application Owner", - "roleDefinitionId": "b4c87314-c1a1-5320-9c43-779585186bcc", - "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] - } - ] - }, - "subscriptionBudget": { - "value": { - "createBudget": false - } - }, - "subscriptionTags": { - "value": { - "ISSO": "isso-tag" - } - }, - "resourceTags": { - "value": { - "ClientOrganization": "client-organization-tag", - "CostCenter": "cost-center-tag", - "DataSensitivity": "data-sensitivity-tag", - "ProjectContact": "project-contact-tag", - "ProjectName": "project-name-tag", - "TechnicalContact": "technical-contact-tag" - } - }, - "resourceGroups": { - "value": { - "automation": "health-automation", - "compute": "health-compute", - "monitor": "health-monitor", - "networking": "health-network", - "networkWatcher": "NetworkWatcherRG", - "security": "health-security", - "storage": "health-storage" - } - }, - "useCMK": { - "value": true - }, - "keyVault": { - "value": { - "secretExpiryInDays": 3650 - } - }, - "automation": { - "value": { - "name": "automation" - } - }, - "sqldb": { - "value": { - "enabled": true, - "sqlAuthenticationUsername": "azadmin", - "aadAuthenticationOnly": false - } - }, - "synapse": { - "value": { - "aadAuthenticationOnly": true, - "aadLoginName": "az.admins", - "aadLoginObjectID": "e0357d81-55d8-44e9-9d9c-ab09dc710785", - "aadLoginType": "Group" - } - }, - "hubNetwork": { - "value": { - "virtualNetworkId": "/subscriptions/ed7f4eed-9010-4227-b115-2a5e37728f27/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", - "rfc1918IPRange": "10.18.0.0/22", - "rfc6598IPRange": "100.60.0.0/16", - "egressVirtualApplianceIp": "10.18.1.4", - "privateDnsManagedByHub": true, - "privateDnsManagedByHubSubscriptionId": "ed7f4eed-9010-4227-b115-2a5e37728f27", - "privateDnsManagedByHubResourceGroupName": "pubsec-dns" - } - }, - "network": { - "value": { - "peerToHubVirtualNetwork": true, - "useRemoteGateway": false, - "name": "health-vnet", - "dnsServers": [ - "10.18.1.4" - ], - "addressPrefixes": [ - "10.5.0.0/16" - ], - "subnets": { - "databricksPublic": { - "comments": "Databricks Public Delegated Subnet", - "name": "databrickspublic", - "addressPrefix": "10.5.5.0/25" - }, - "databricksPrivate": { - "comments": "Databricks Private Delegated Subnet", - "name": "databricksprivate", - "addressPrefix": "10.5.6.0/25" - }, - "privateEndpoints": { - "comments": "Private Endpoints Subnet", - "name": "privateendpoints", - "addressPrefix": "10.5.7.0/25" - }, - "web": { - "comments": "Azure Web App Delegated Subnet", - "name": "webapp", - "addressPrefix": "10.5.8.0/25" - }, - "optional": [ - { - "comments": "Optional Subnet 1", - "name": "virtualMachines", - "addressPrefix": "10.5.9.0/25", - "nsg": { - "enabled": true - }, - "udr": { - "enabled": true - } - }, - { - "comments": "Optional Subnet 2 with delegation for NetApp Volumes", - "name": "NetappVolumes", - "addressPrefix": "10.5.10.0/25", - "nsg": { - "enabled": false - }, - "udr": { - "enabled": false - }, - "delegations": { - "serviceName": "Microsoft.NetApp/volumes" - } - } - ] - } - } - } - } -} \ No newline at end of file diff --git a/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/f08c3057-1713-4a6f-b7e6-0df355b60c30_machinelearning_canadacentral.json b/config/subscriptions/CanadaPubSecALZ-main/DevTest/0a5970ed-b9d5-464a-acef-8a06f0d7a360_machinelearning_canadacentral.json similarity index 71% rename from config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/f08c3057-1713-4a6f-b7e6-0df355b60c30_machinelearning_canadacentral.json rename to config/subscriptions/CanadaPubSecALZ-main/DevTest/0a5970ed-b9d5-464a-acef-8a06f0d7a360_machinelearning_canadacentral.json index f1d4ffd8..ad33c1ca 100644 --- a/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/f08c3057-1713-4a6f-b7e6-0df355b60c30_machinelearning_canadacentral.json +++ b/config/subscriptions/CanadaPubSecALZ-main/DevTest/0a5970ed-b9d5-464a-acef-8a06f0d7a360_machinelearning_canadacentral.json @@ -1,45 +1,66 @@ { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "$schema": "https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-machinelearning.json#", "contentVersion": "1.0.0.0", "parameters": { "serviceHealthAlerts": { "value": { - "resourceGroupName": "service-health", - "incidentTypes": [ "Incident", "Security" ], - "regions": [ "Global", "Canada East", "Canada Central" ], - "receivers": { - "app": [ "alzcanadapubsec@microsoft.com" ], - "email": [ "alzcanadapubsec@microsoft.com" ], - "sms": [ { "countryCode": "1", "phoneNumber": "6045555555" } ], - "voice": [ { "countryCode": "1", "phoneNumber": "6045555555" } ] - }, - "actionGroupName": "Service health action group", - "actionGroupShortName": "health-alert", - "alertRuleName": "Incidents and Security", - "alertRuleDescription": "Service Health: Incidents and Security" + "alertRuleName": "Subscription Owners Alerts", + "receivers": { + "app": [ + "subscription-owners@example.com" + ], + "sms": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ], + "email": [ + "subscription-owners@example.com" + ], + "voice": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ] + }, + "regions": [ + "Global", + "Canada Central", + "Canada East" + ], + "resourceGroupName": "service-health-alerts-rg", + "actionGroupName": "Subscription Owners Alerts", + "actionGroupShortName": "sub-own-ag", + "incidentTypes": [ + "Incident", + "Security" + ], + "alertRuleDescription": "Subscription Owners Alerts for Incidents and Security" } }, "securityCenter": { "value": { - "email": "alzcanadapubsec@microsoft.com", - "phone": "6045555555" + "email": "security@example.com", + "phone": "6135555555" } }, "subscriptionRoleAssignments": { "value": [ { - "comments": "Built-in Role: Contributor", - "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c", + "comments": "Built-in Contributor Role", "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] + "49f9256a-d77a-43ba-8cc5-957eb40dedee" + ], + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" }, { "comments": "Custom Role: Landing Zone Application Owner", - "roleDefinitionId": "b4c87314-c1a1-5320-9c43-779585186bcc", "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] + "43d97b7e-27b8-4fa7-a339-c935f7752bb9" + ], + "roleDefinitionId": "b4c87314-c1a1-5320-9c43-779585186bcc" } ] }, @@ -126,13 +147,13 @@ }, "hubNetwork": { "value": { - "virtualNetworkId": "/subscriptions/ed7f4eed-9010-4227-b115-2a5e37728f27/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", + "virtualNetworkId": "/subscriptions/4fd845de-f6c8-4e6d-9a87-c21c4ebf7edd/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", "rfc1918IPRange": "10.18.0.0/22", "rfc6598IPRange": "100.60.0.0/16", "egressVirtualApplianceIp": "10.18.1.4", "privateDnsManagedByHub": true, - "privateDnsManagedByHubSubscriptionId": "ed7f4eed-9010-4227-b115-2a5e37728f27", - "privateDnsManagedByHubResourceGroupName": "pubsec-dns" + "privateDnsManagedByHubSubscriptionId": "4fd845de-f6c8-4e6d-9a87-c21c4ebf7edd", + "privateDnsManagedByHubResourceGroupName": "private-dns-rg" } }, "network": { @@ -182,4 +203,4 @@ } } } -} \ No newline at end of file +} diff --git a/config/subscriptions/CanadaPubSecALZ-main/DevTest/1f519216-5e39-4b51-a9b6-10cc2b79b6c7_healthcare_canadacentral.json b/config/subscriptions/CanadaPubSecALZ-main/DevTest/1f519216-5e39-4b51-a9b6-10cc2b79b6c7_healthcare_canadacentral.json new file mode 100644 index 00000000..33ad6983 --- /dev/null +++ b/config/subscriptions/CanadaPubSecALZ-main/DevTest/1f519216-5e39-4b51-a9b6-10cc2b79b6c7_healthcare_canadacentral.json @@ -0,0 +1,200 @@ +{ + "$schema": "https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-healthcare.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "serviceHealthAlerts": { + "value": { + "alertRuleName": "Subscription Owners Alerts", + "receivers": { + "app": [ + "subscription-owners@example.com" + ], + "sms": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ], + "email": [ + "subscription-owners@example.com" + ], + "voice": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ] + }, + "regions": [ + "Global", + "Canada Central", + "Canada East" + ], + "resourceGroupName": "service-health-alerts-rg", + "actionGroupName": "Subscription Owners Alerts", + "actionGroupShortName": "sub-own-ag", + "incidentTypes": [ + "Incident", + "Security" + ], + "alertRuleDescription": "Subscription Owners Alerts for Incidents and Security" + } + }, + "securityCenter": { + "value": { + "email": "security@example.com", + "phone": "6135555555" + } + }, + "subscriptionRoleAssignments": { + "value": [ + { + "comments": "Built-in Contributor Role", + "securityGroupObjectIds": [ + "49f9256a-d77a-43ba-8cc5-957eb40dedee" + ], + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "comments": "Custom Role: Landing Zone Application Owner", + "securityGroupObjectIds": [ + "43d97b7e-27b8-4fa7-a339-c935f7752bb9" + ], + "roleDefinitionId": "b4c87314-c1a1-5320-9c43-779585186bcc" + } + ] + }, + "subscriptionBudget": { + "value": { + "createBudget": false + } + }, + "subscriptionTags": { + "value": { + "ISSO": "isso-tag" + } + }, + "resourceTags": { + "value": { + "ClientOrganization": "client-organization-tag", + "CostCenter": "cost-center-tag", + "DataSensitivity": "data-sensitivity-tag", + "ProjectContact": "project-contact-tag", + "ProjectName": "project-name-tag", + "TechnicalContact": "technical-contact-tag" + } + }, + "resourceGroups": { + "value": { + "automation": "health-automation", + "compute": "health-compute", + "monitor": "health-monitor", + "networking": "health-network", + "networkWatcher": "NetworkWatcherRG", + "security": "health-security", + "storage": "health-storage" + } + }, + "useCMK": { + "value": true + }, + "keyVault": { + "value": { + "secretExpiryInDays": 3650 + } + }, + "automation": { + "value": { + "name": "automation" + } + }, + "sqldb": { + "value": { + "enabled": true, + "sqlAuthenticationUsername": "azadmin", + "aadAuthenticationOnly": false + } + }, + "synapse": { + "value": { + "aadAuthenticationOnly": true, + "aadLoginName": "az.admins", + "aadLoginObjectID": "e0357d81-55d8-44e9-9d9c-ab09dc710785", + "aadLoginType": "Group" + } + }, + "hubNetwork": { + "value": { + "virtualNetworkId": "/subscriptions/4fd845de-f6c8-4e6d-9a87-c21c4ebf7edd/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", + "rfc1918IPRange": "10.18.0.0/22", + "rfc6598IPRange": "100.60.0.0/16", + "egressVirtualApplianceIp": "10.18.1.4", + "privateDnsManagedByHub": true, + "privateDnsManagedByHubSubscriptionId": "4fd845de-f6c8-4e6d-9a87-c21c4ebf7edd", + "privateDnsManagedByHubResourceGroupName": "private-dns-rg" + } + }, + "network": { + "value": { + "peerToHubVirtualNetwork": true, + "useRemoteGateway": false, + "name": "health-vnet", + "dnsServers": [ + "10.18.1.4" + ], + "addressPrefixes": [ + "10.5.0.0/16" + ], + "subnets": { + "databricksPublic": { + "comments": "Databricks Public Delegated Subnet", + "name": "databrickspublic", + "addressPrefix": "10.5.5.0/25" + }, + "databricksPrivate": { + "comments": "Databricks Private Delegated Subnet", + "name": "databricksprivate", + "addressPrefix": "10.5.6.0/25" + }, + "privateEndpoints": { + "comments": "Private Endpoints Subnet", + "name": "privateendpoints", + "addressPrefix": "10.5.7.0/25" + }, + "web": { + "comments": "Azure Web App Delegated Subnet", + "name": "webapp", + "addressPrefix": "10.5.8.0/25" + }, + "optional": [ + { + "comments": "Optional Subnet 1", + "name": "virtualMachines", + "addressPrefix": "10.5.9.0/25", + "nsg": { + "enabled": true + }, + "udr": { + "enabled": true + } + }, + { + "comments": "Optional Subnet 2 with delegation for NetApp Volumes", + "name": "NetappVolumes", + "addressPrefix": "10.5.10.0/25", + "nsg": { + "enabled": false + }, + "udr": { + "enabled": false + }, + "delegations": { + "serviceName": "Microsoft.NetApp/volumes" + } + } + ] + } + } + } + } +} diff --git a/config/subscriptions/CanadaPubSecALZ-main/DevTest/8422552f-3840-4934-a971-6ee349ffbb05_generic-subscription_canadacentral.json b/config/subscriptions/CanadaPubSecALZ-main/DevTest/8422552f-3840-4934-a971-6ee349ffbb05_generic-subscription_canadacentral.json new file mode 100644 index 00000000..f7cd93c6 --- /dev/null +++ b/config/subscriptions/CanadaPubSecALZ-main/DevTest/8422552f-3840-4934-a971-6ee349ffbb05_generic-subscription_canadacentral.json @@ -0,0 +1,192 @@ +{ + "$schema": "https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-generic-subscription.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "location": { + "value": "canadacentral" + }, + "serviceHealthAlerts": { + "value": { + "alertRuleName": "Subscription Owners Alerts", + "receivers": { + "app": [ + "subscription-owners@example.com" + ], + "sms": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ], + "email": [ + "subscription-owners@example.com" + ], + "voice": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ] + }, + "regions": [ + "Global", + "Canada Central", + "Canada East" + ], + "resourceGroupName": "service-health-alerts-rg", + "actionGroupName": "Subscription Owners Alerts", + "actionGroupShortName": "sub-own-ag", + "incidentTypes": [ + "Incident", + "Security" + ], + "alertRuleDescription": "Subscription Owners Alerts for Incidents and Security" + } + }, + "securityCenter": { + "value": { + "email": "security@example.com", + "phone": "6135555555" + } + }, + "subscriptionRoleAssignments": { + "value": [ + { + "comments": "Built-in Contributor Role", + "securityGroupObjectIds": [ + "49f9256a-d77a-43ba-8cc5-957eb40dedee" + ], + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "comments": "Custom Role: Landing Zone Application Owner", + "securityGroupObjectIds": [ + "43d97b7e-27b8-4fa7-a339-c935f7752bb9" + ], + "roleDefinitionId": "b4c87314-c1a1-5320-9c43-779585186bcc" + } + ] + }, + "subscriptionBudget": { + "value": { + "createBudget": false + } + }, + "subscriptionTags": { + "value": { + "ISSO": "isso-tag" + } + }, + "resourceTags": { + "value": { + "ClientOrganization": "client-organization-tag", + "CostCenter": "cost-center-tag", + "DataSensitivity": "data-sensitivity-tag", + "ProjectContact": "project-contact-tag", + "ProjectName": "project-name-tag", + "TechnicalContact": "technical-contact-tag" + } + }, + "resourceGroups": { + "value": { + "automation": "automation", + "networking": "networking", + "networkWatcher": "NetworkWatcherRG", + "backupRecoveryVault": "backup" + } + }, + "automation": { + "value": { + "name": "automation" + } + }, + "backupRecoveryVault": { + "value": { + "enabled": true, + "name": "backup-vault" + } + }, + "hubNetwork": { + "value": { + "virtualNetworkId": "/subscriptions/4fd845de-f6c8-4e6d-9a87-c21c4ebf7edd/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", + "rfc1918IPRange": "10.18.0.0/22", + "rfc6598IPRange": "100.60.0.0/16", + "egressVirtualApplianceIp": "10.18.1.4" + } + }, + "network": { + "value": { + "deployVnet": true, + "peerToHubVirtualNetwork": true, + "useRemoteGateway": false, + "name": "vnet", + "dnsServers": [ + "10.18.1.4" + ], + "addressPrefixes": [ + "10.2.0.0/16" + ], + "subnets": [ + { + "comments": "App Management Zone (OZ)", + "name": "appManagement", + "addressPrefix": "10.2.1.0/25", + "nsg": { + "enabled": true + }, + "udr": { + "enabled": true + } + }, + { + "comments": "Presentation Zone (PAZ)", + "name": "web", + "addressPrefix": "10.2.2.0/25", + "nsg": { + "enabled": true + }, + "udr": { + "enabled": true + } + }, + { + "comments": "Application Zone (RZ)", + "name": "app", + "addressPrefix": "10.2.3.0/25", + "nsg": { + "enabled": true + }, + "udr": { + "enabled": true + } + }, + { + "comments": "Data Zone (HRZ)", + "name": "data", + "addressPrefix": "10.2.4.0/25", + "nsg": { + "enabled": true + }, + "udr": { + "enabled": true + } + }, + { + "comments": "App Service", + "name": "appservice", + "addressPrefix": "10.2.5.0/25", + "nsg": { + "enabled": false + }, + "udr": { + "enabled": false + }, + "delegations": { + "serviceName": "Microsoft.Web/serverFarms" + } + } + ] + } + } + } +} diff --git a/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/ec6c5689-db04-4f1e-b76d-834a51dd0e27_machinelearning_canadacentral.json b/config/subscriptions/CanadaPubSecALZ-main/DevTest/9d9817f5-c218-4553-b686-58be8d9ea82c_machinelearning_canadacentral.json similarity index 71% rename from config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/ec6c5689-db04-4f1e-b76d-834a51dd0e27_machinelearning_canadacentral.json rename to config/subscriptions/CanadaPubSecALZ-main/DevTest/9d9817f5-c218-4553-b686-58be8d9ea82c_machinelearning_canadacentral.json index 7860236e..c1b9bdb7 100644 --- a/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/ec6c5689-db04-4f1e-b76d-834a51dd0e27_machinelearning_canadacentral.json +++ b/config/subscriptions/CanadaPubSecALZ-main/DevTest/9d9817f5-c218-4553-b686-58be8d9ea82c_machinelearning_canadacentral.json @@ -1,45 +1,66 @@ { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "$schema": "https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-machinelearning.json#", "contentVersion": "1.0.0.0", "parameters": { "serviceHealthAlerts": { "value": { - "resourceGroupName": "service-health", - "incidentTypes": [ "Incident", "Security" ], - "regions": [ "Global", "Canada East", "Canada Central" ], - "receivers": { - "app": [ "alzcanadapubsec@microsoft.com" ], - "email": [ "alzcanadapubsec@microsoft.com" ], - "sms": [ { "countryCode": "1", "phoneNumber": "6045555555" } ], - "voice": [ { "countryCode": "1", "phoneNumber": "6045555555" } ] - }, - "actionGroupName": "Service health action group", - "actionGroupShortName": "health-alert", - "alertRuleName": "Incidents and Security", - "alertRuleDescription": "Service Health: Incidents and Security" + "alertRuleName": "Subscription Owners Alerts", + "receivers": { + "app": [ + "subscription-owners@example.com" + ], + "sms": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ], + "email": [ + "subscription-owners@example.com" + ], + "voice": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ] + }, + "regions": [ + "Global", + "Canada Central", + "Canada East" + ], + "resourceGroupName": "service-health-alerts-rg", + "actionGroupName": "Subscription Owners Alerts", + "actionGroupShortName": "sub-own-ag", + "incidentTypes": [ + "Incident", + "Security" + ], + "alertRuleDescription": "Subscription Owners Alerts for Incidents and Security" } }, "securityCenter": { "value": { - "email": "alzcanadapubsec@microsoft.com", - "phone": "6045555555" + "email": "security@example.com", + "phone": "6135555555" } }, "subscriptionRoleAssignments": { "value": [ { - "comments": "Built-in Role: Contributor", - "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c", + "comments": "Built-in Contributor Role", "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] + "49f9256a-d77a-43ba-8cc5-957eb40dedee" + ], + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" }, { "comments": "Custom Role: Landing Zone Application Owner", - "roleDefinitionId": "b4c87314-c1a1-5320-9c43-779585186bcc", "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] + "43d97b7e-27b8-4fa7-a339-c935f7752bb9" + ], + "roleDefinitionId": "b4c87314-c1a1-5320-9c43-779585186bcc" } ] }, @@ -126,13 +147,13 @@ }, "hubNetwork": { "value": { - "virtualNetworkId": "/subscriptions/ed7f4eed-9010-4227-b115-2a5e37728f27/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", + "virtualNetworkId": "/subscriptions/4fd845de-f6c8-4e6d-9a87-c21c4ebf7edd/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", "rfc1918IPRange": "10.18.0.0/22", "rfc6598IPRange": "100.60.0.0/16", "egressVirtualApplianceIp": "10.18.1.4", "privateDnsManagedByHub": true, - "privateDnsManagedByHubSubscriptionId": "ed7f4eed-9010-4227-b115-2a5e37728f27", - "privateDnsManagedByHubResourceGroupName": "pubsec-dns" + "privateDnsManagedByHubSubscriptionId": "4fd845de-f6c8-4e6d-9a87-c21c4ebf7edd", + "privateDnsManagedByHubResourceGroupName": "private-dns-rg" } }, "network": { @@ -182,4 +203,4 @@ } } } -} \ No newline at end of file +} diff --git a/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/f459218a-e8bb-49c9-b768-ee6828a144aa_machinelearning_canadacentral.json b/config/subscriptions/CanadaPubSecALZ-main/DevTest/bbe30f1a-fda5-4873-b51b-c838cf3f1a61_machinelearning_canadacentral.json similarity index 72% rename from config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/f459218a-e8bb-49c9-b768-ee6828a144aa_machinelearning_canadacentral.json rename to config/subscriptions/CanadaPubSecALZ-main/DevTest/bbe30f1a-fda5-4873-b51b-c838cf3f1a61_machinelearning_canadacentral.json index dfc39874..e6c64226 100644 --- a/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/f459218a-e8bb-49c9-b768-ee6828a144aa_machinelearning_canadacentral.json +++ b/config/subscriptions/CanadaPubSecALZ-main/DevTest/bbe30f1a-fda5-4873-b51b-c838cf3f1a61_machinelearning_canadacentral.json @@ -1,45 +1,66 @@ { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "$schema": "https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-machinelearning.json#", "contentVersion": "1.0.0.0", "parameters": { "serviceHealthAlerts": { "value": { - "resourceGroupName": "service-health", - "incidentTypes": [ "Incident", "Security" ], - "regions": [ "Global", "Canada East", "Canada Central" ], - "receivers": { - "app": [ "alzcanadapubsec@microsoft.com" ], - "email": [ "alzcanadapubsec@microsoft.com" ], - "sms": [ { "countryCode": "1", "phoneNumber": "6045555555" } ], - "voice": [ { "countryCode": "1", "phoneNumber": "6045555555" } ] - }, - "actionGroupName": "Service health action group", - "actionGroupShortName": "health-alert", - "alertRuleName": "Incidents and Security", - "alertRuleDescription": "Service Health: Incidents and Security" + "alertRuleName": "Subscription Owners Alerts", + "receivers": { + "app": [ + "subscription-owners@example.com" + ], + "sms": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ], + "email": [ + "subscription-owners@example.com" + ], + "voice": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ] + }, + "regions": [ + "Global", + "Canada Central", + "Canada East" + ], + "resourceGroupName": "service-health-alerts-rg", + "actionGroupName": "Subscription Owners Alerts", + "actionGroupShortName": "sub-own-ag", + "incidentTypes": [ + "Incident", + "Security" + ], + "alertRuleDescription": "Subscription Owners Alerts for Incidents and Security" } }, "securityCenter": { "value": { - "email": "alzcanadapubsec@microsoft.com", - "phone": "6045555555" + "email": "security@example.com", + "phone": "6135555555" } }, "subscriptionRoleAssignments": { "value": [ { - "comments": "Built-in Role: Contributor", - "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c", + "comments": "Built-in Contributor Role", "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] + "49f9256a-d77a-43ba-8cc5-957eb40dedee" + ], + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" }, { "comments": "Custom Role: Landing Zone Application Owner", - "roleDefinitionId": "b4c87314-c1a1-5320-9c43-779585186bcc", "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] + "43d97b7e-27b8-4fa7-a339-c935f7752bb9" + ], + "roleDefinitionId": "b4c87314-c1a1-5320-9c43-779585186bcc" } ] }, @@ -127,13 +148,13 @@ }, "hubNetwork": { "value": { - "virtualNetworkId": "/subscriptions/ed7f4eed-9010-4227-b115-2a5e37728f27/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", + "virtualNetworkId": "/subscriptions/4fd845de-f6c8-4e6d-9a87-c21c4ebf7edd/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", "rfc1918IPRange": "10.18.0.0/22", "rfc6598IPRange": "100.60.0.0/16", "egressVirtualApplianceIp": "10.18.1.4", "privateDnsManagedByHub": true, - "privateDnsManagedByHubSubscriptionId": "ed7f4eed-9010-4227-b115-2a5e37728f27", - "privateDnsManagedByHubResourceGroupName": "pubsec-dns" + "privateDnsManagedByHubSubscriptionId": "4fd845de-f6c8-4e6d-9a87-c21c4ebf7edd", + "privateDnsManagedByHubResourceGroupName": "private-dns-rg" } }, "network": { @@ -183,4 +204,4 @@ } } } -} \ No newline at end of file +} diff --git a/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/8c6e48a4-4c73-4a1f-9f95-9447804f2c98_machinelearning_canadacentral.json b/config/subscriptions/CanadaPubSecALZ-main/DevTest/f881fccb-2598-4b9c-b87c-b392f5e16f12_machinelearning_canadacentral.json similarity index 72% rename from config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/8c6e48a4-4c73-4a1f-9f95-9447804f2c98_machinelearning_canadacentral.json rename to config/subscriptions/CanadaPubSecALZ-main/DevTest/f881fccb-2598-4b9c-b87c-b392f5e16f12_machinelearning_canadacentral.json index d86daddb..3be3347c 100644 --- a/config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/8c6e48a4-4c73-4a1f-9f95-9447804f2c98_machinelearning_canadacentral.json +++ b/config/subscriptions/CanadaPubSecALZ-main/DevTest/f881fccb-2598-4b9c-b87c-b392f5e16f12_machinelearning_canadacentral.json @@ -1,38 +1,66 @@ { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "$schema": "https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-machinelearning.json#", "contentVersion": "1.0.0.0", "parameters": { "serviceHealthAlerts": { "value": { - "resourceGroupName": "service-health", - "incidentTypes": [ "Incident", "Security" ], - "regions": [ "Global", "Canada East", "Canada Central" ], - "receivers": { - "app": [ "alzcanadapubsec@microsoft.com" ], - "email": [ "alzcanadapubsec@microsoft.com" ], - "sms": [ { "countryCode": "1", "phoneNumber": "6045555555" } ], - "voice": [ { "countryCode": "1", "phoneNumber": "6045555555" } ] - }, - "actionGroupName": "Service health action group", - "actionGroupShortName": "health-alert", - "alertRuleName": "Incidents and Security", - "alertRuleDescription": "Service Health: Incidents and Security" + "alertRuleName": "Subscription Owners Alerts", + "receivers": { + "app": [ + "subscription-owners@example.com" + ], + "sms": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ], + "email": [ + "subscription-owners@example.com" + ], + "voice": [ + { + "countryCode": "1", + "phoneNumber": "6135555555" + } + ] + }, + "regions": [ + "Global", + "Canada Central", + "Canada East" + ], + "resourceGroupName": "service-health-alerts-rg", + "actionGroupName": "Subscription Owners Alerts", + "actionGroupShortName": "sub-own-ag", + "incidentTypes": [ + "Incident", + "Security" + ], + "alertRuleDescription": "Subscription Owners Alerts for Incidents and Security" } }, "securityCenter": { "value": { - "email": "alzcanadapubsec@microsoft.com", - "phone": "6045555555" + "email": "security@example.com", + "phone": "6135555555" } }, "subscriptionRoleAssignments": { "value": [ { - "comments": "Built-in Role: Contributor", - "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c", + "comments": "Built-in Contributor Role", "securityGroupObjectIds": [ - "38f33f7e-a471-4630-8ce9-c6653495a2ee" - ] + "49f9256a-d77a-43ba-8cc5-957eb40dedee" + ], + "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "comments": "Custom Role: Landing Zone Application Owner", + "securityGroupObjectIds": [ + "43d97b7e-27b8-4fa7-a339-c935f7752bb9" + ], + "roleDefinitionId": "b4c87314-c1a1-5320-9c43-779585186bcc" } ] }, @@ -122,13 +150,13 @@ }, "hubNetwork": { "value": { - "virtualNetworkId": "/subscriptions/ed7f4eed-9010-4227-b115-2a5e37728f27/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", + "virtualNetworkId": "/subscriptions/4fd845de-f6c8-4e6d-9a87-c21c4ebf7edd/resourceGroups/pubsec-hub-networking/providers/Microsoft.Network/virtualNetworks/hub-vnet", "rfc1918IPRange": "10.18.0.0/22", "rfc6598IPRange": "100.60.0.0/16", "egressVirtualApplianceIp": "10.18.1.4", "privateDnsManagedByHub": true, - "privateDnsManagedByHubSubscriptionId": "ed7f4eed-9010-4227-b115-2a5e37728f27", - "privateDnsManagedByHubResourceGroupName": "pubsec-dns" + "privateDnsManagedByHubSubscriptionId": "4fd845de-f6c8-4e6d-9a87-c21c4ebf7edd", + "privateDnsManagedByHubResourceGroupName": "private-dns-rg" } }, "network": { @@ -204,4 +232,4 @@ } } } -} \ No newline at end of file +} diff --git a/config/variables/CanadaESLZ-main.yml b/config/variables/CanadaESLZ-main.yml deleted file mode 100644 index 2e020d24..00000000 --- a/config/variables/CanadaESLZ-main.yml +++ /dev/null @@ -1,86 +0,0 @@ -# ---------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. -# -# THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, -# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. -# ---------------------------------------------------------------------------------- - -# Environment YAML files can be used to supplement -# the variables specified in 'config/variables/common.yml'. You can: -# * Override existing common-vars.yml variable value settings, and -# * Create new variable values not present in common-vars.yml -# -# The naming convention for these YAML files is: -# {organization}-{branch}.yml -# -# where {organization} is the organization variable from the -# common.yml file -# and {branch} is the Azure Repos branch name used by the -# currently executing pipeline. - -variables: - deploymentRegion: canadacentral - - # Management Groups - var-managementgroup-hierarchy: > - { - "name": "Tenant Root Group", - "id": "343ddfdb-bef5-46d9-99cf-ed67d5948783", - "children": [ - { - "name": "Azure Landing Zones for Canadian Public Sector", - "id": "pubsec", - "children": [ - { - "name": "Platform", "id": "pubsecPlatform", - "children": [ - { "name": "Identity", "id": "pubsecPlatformIdentity", "children": [] }, - { "name": "Connectivity", "id": "pubsecPlatformConnectivity", "children": [] }, - { "name": "Management", "id": "pubsecPlatformManagement", "children": [] } - ] - }, - { - "name": "LandingZones", "id": "pubsecLandingZones", - "children": [ - { "name": "DevTest", "id": "pubsecLandingZonesDevTest", "children": [] }, - { "name": "QA", "id": "pubsecLandingZonesQA", "children": [] }, - { "name": "Prod", "id": "pubsecLandingZonesProd", "children": [] } - ] - }, - { - "name": "Sandbox", "id": "pubsecSandbox", - "children": [] - } - ] - } - ] - } - - # Logging - var-logging-region: canadacentral - var-logging-managementGroupId: pubsecPlatformManagement - var-logging-subscriptionId: bc0a4f9f-07fa-4284-b1bd-fbad38578d3a - var-logging-configurationFileName: logging.parameters.json - - ## This parameter is only used for HIPAA/HITRUST Policy Assignment - var-logging-diagnosticSettingsforNetworkSecurityGroupsStoragePrefix: pubsecnsg - - # Platform Identity - var-identity-region: canadacentral - var-identity-managementGroupId: pubsecPlatformIdentity - var-identity-subscriptionId: b357bf7b-3328-4d21-b94b-4bfa84af97b1 - var-identity-configurationFileName: identity.parameters.json - - # Hub Networking - var-hubnetwork-region: canadacentral - var-hubnetwork-managementGroupId: pubsecPlatformConnectivity - var-hubnetwork-subscriptionId: ed7f4eed-9010-4227-b115-2a5e37728f27 - - ## Hub Network configuration using Azure Firewall - required when Azure Firewall is used - var-hubnetwork-azfwPolicy-configurationFileName: hub-azfw-policy/azure-firewall-policy.parameters.json - var-hubnetwork-azfw-configurationFileName: hub-azfw/hub-network.parameters.json - - ## Hub Network configuration using Network Virtual Appliance (NVA) - required when Network Virtual Appliance (NVA) like Fortigate Firewalls are used - var-hubnetwork-nva-configurationFileName: hub-nva/hub-network.parameters.json \ No newline at end of file diff --git a/config/variables/CanadaPubSecALZ-main.yml b/config/variables/CanadaPubSecALZ-main.yml new file mode 100644 index 00000000..010a4503 --- /dev/null +++ b/config/variables/CanadaPubSecALZ-main.yml @@ -0,0 +1,78 @@ +variables: + var-hubnetwork-region: canadacentral + var-hubnetwork-managementGroupId: Connectivity + var-hubnetwork-azfw-configurationFileName: hub-azfw/hub-network.parameters.json + var-hubnetwork-nva-configurationFileName: hub-nva/hub-network.parameters.json + var-hubnetwork-subscriptionId: 4fd845de-f6c8-4e6d-9a87-c21c4ebf7edd + var-hubnetwork-azfwPolicy-configurationFileName: hub-azfw-policy/azure-firewall-policy.parameters.json + var-logging-managementGroupId: Management + var-logging-subscriptionId: 91e1aaa2-a0a0-4770-8d90-02daa39bf57a + deploymentRegion: canadacentral + var-identity-managementGroupId: Identity + var-logging-diagnosticSettingsforNetworkSecurityGroupsStoragePrefix: pubsecnsg + var-logging-configurationFileName: logging.parameters.json + var-managementgroup-hierarchy: |- + { + "id": "0d466ba2-7ea1-420f-9820-2583fc040733", + "name": "Tenant Root Group", + "children": [ + { + "id": "pubsec", + "name": "Canadian Public Sector Azure Landing Zones", + "children": [ + { + "id": "Platform", + "name": "Platform", + "children": [ + { + "id": "Management", + "name": "Management", + "children": [] + }, + { + "id": "Connectivity", + "name": "Connectivity", + "children": [] + }, + { + "id": "Identity", + "name": "Identity", + "children": [] + } + ] + }, + { + "id": "LandingZones", + "name": "LandingZones", + "children": [ + { + "id": "DevTest", + "name": "DevTest", + "children": [] + }, + { + "id": "QA", + "name": "QA", + "children": [] + }, + { + "id": "Prod", + "name": "Prod", + "children": [] + } + ] + }, + { + "id": "Sandbox", + "name": "Sandbox", + "children": [] + } + ] + } + ] + } + var-logging-region: canadacentral + var-identity-configurationFileName: identity.parameters.json + var-identity-region: canadacentral + var-identity-subscriptionId: 2987c7a3-1e43-4b28-b983-ac0925e37d03 + diff --git a/docs/archetypes/authoring-guide.md b/docs/archetypes/authoring-guide.md index 8a01d27a..612e2fbe 100644 --- a/docs/archetypes/authoring-guide.md +++ b/docs/archetypes/authoring-guide.md @@ -339,24 +339,24 @@ Azure Resource Manager (ARM) parameters files provide deployment information to These parameter files are located in [config/subscription](../../config/subscriptions) folder. This folder is configurable in `common.yml` and you can override in environment configuration files using the `subscriptionsPathFromRoot` setting. By default it is set to `config/subscriptions`. -Immediate subfolder defines the environment which is based on Azure DevOps Organization (i.e. `CanadaESLZ`) & Git branch name (i.e. `main`), for example the subfolder will be called `CanadaESLZ-main`. You can have many environments based on Git branch names such as `CanadaESLZ-feature-1`, `CanadaESLZ-dev`, etc. +Immediate subfolder defines the environment which is based on Azure DevOps Organization (i.e. `CanadaPubSecALZ`) & Git branch name (i.e. `main`), for example the subfolder will be called `CanadaPubSecALZ-main`. You can have many environments based on Git branch names such as `CanadaPubSecALZ-feature-1`, `CanadaPubSecALZ-dev`, etc. ARM parameter files are used by `subscriptions-ci` Azure DevOps Pipeline when configuring subscriptions with Azure resources. The pipeline will detect environment, management group, subscription, deployment location and deployment parameters using the folder hierarchy, file name and file content. For example when the file path is: -`config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/8c6e48a4-4c73-4a1f-9f95-9447804f2c98_machinelearning_canadacentral.json` +`config/subscriptions/CanadaPubSecALZ-main/pubsec/LandingZones/DevTest/8c6e48a4-4c73-4a1f-9f95-9447804f2c98_machinelearning_canadacentral.json` -- **Folder hierarchy:** config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/ +- **Folder hierarchy:** config/subscriptions/CanadaPubSecALZ-main/pubsec/LandingZones/DevTest/ - **File name:** 8c6e48a4-4c73-4a1f-9f95-9447804f2c98_machinelearning_canadacentral.json | Deployment Information | Approach | Example | |:---------------------- |:-------- |:------- | -| Environment | DevOps organization name & Git branch name | `CanadaESLZ-main` | -| Management Group | Calculated based on concatenating the folder hierarchy under `config/subscription/CanadaESLZ-main` | pubsecLandingZonesDevTest (without the `/`). [See below for details](#management-group-id-detection). +| Environment | DevOps organization name & Git branch name | `CanadaPubSecALZ-main` | +| Management Group | Calculated based on concatenating the folder hierarchy under `config/subscription/CanadaPubSecALZ-main` | pubsecLandingZonesDevTest (without the `/`). [See below for details](#management-group-id-detection). | Subscription | Part of the file name | `8c6e48a4-4c73-4a1f-9f95-9447804f2c98` | | Deployment location | Part of the file name | `canadacentral` | -| Deployment parameters | Content of the file | [See file content](../../config/subscriptions/CanadaESLZ-main/pubsec/LandingZones/DevTest/8c6e48a4-4c73-4a1f-9f95-9447804f2c98_machinelearning_canadacentral.json) | +| Deployment parameters | Content of the file | [See file content](../../config/subscriptions/CanadaPubSecALZ-main/pubsec/LandingZones/DevTest/8c6e48a4-4c73-4a1f-9f95-9447804f2c98_machinelearning_canadacentral.json) | The ARM parameter file name can be in one of two formats: @@ -402,7 +402,7 @@ The `subscriptions-ci` management group detection logic is built to accommodate - Folder structure in `config/subscription/` is created without including the prefixes. For example: ```none - config/subscription/CanadaESLZ-main + config/subscription/CanadaPubSecALZ-main - pubsec - LandingZones - DevTest @@ -415,7 +415,7 @@ The `subscriptions-ci` management group detection logic is built to accommodate - Folder structure in `config/subscription/` should be flat. For example: ```none - config/subscription/CanadaESLZ-main + config/subscription/CanadaPubSecALZ-main - pubsec - LandingZones - DevTest diff --git a/docs/onboarding/azure-devops-pipelines.md b/docs/onboarding/azure-devops-pipelines.md index 4f71e426..fc17f7e0 100644 --- a/docs/onboarding/azure-devops-pipelines.md +++ b/docs/onboarding/azure-devops-pipelines.md @@ -4,6 +4,8 @@ This document provides steps required to onboard to the Azure Landing Zones desi > There are scripts available to help simplify the onboarding process to Azure Landing Zones design using Azure DevOps Pipelines. The [Azure DevOps Scripts](./azure-devops-scripts.md) document contains more detailed information on the those scripts. +> There are scripts available to help simplify the configuration process of the Azure Landing Zones design. The [Configuration Scripts](./configuration-scripts.md) document contains more detailed information on the those scripts. + **All steps will need to be repeated per Azure AD tenant.** --- @@ -201,6 +203,8 @@ This deployment diagram describes the steps for deploying one, many or all modul * [Migrate Logging configuration from Azure DevOps variables to JSON parameters file](#migrate-logging-configuration-from-azure-devops-variables-to-json-parameters-file) * [Migrate Hub Networking configuration from Azure DevOps variables to JSON parameters file](#migrate-hub-networking-configuration-from-azure-devops-variables-to-json-parameters-file) +>Note: For steps #3 - #9 above, there are scripts available to automate generating the JSON and YAML configuration files for environments. Refer to the [Configuration Scripts](./configuration-scripts.md) documentation for more information. + --- ## Step 1 - Create Service Principal Account & Assign RBAC @@ -342,7 +346,7 @@ Instructions: >**Note**: The ID of the default parent management group 'Tenant Root Group' is the same as the Azure Active Directory (AAD) Tenant ID (GUID). -2. Create/edit `./config/variables/-.yml` in Git (i.e. CanadaESLZ-main.yml). This file name is automatically inferred by the pipeline based on the Azure DevOps organization name and the branch name. +2. Create/edit `./config/variables/-.yml` in Git (i.e. CanadaPubSecALZ-main.yml). This file name is automatically inferred by the pipeline based on the Azure DevOps organization name and the branch name. **Sample environment YAML (v0.9.0 or later)** @@ -403,7 +407,7 @@ Instructions: * Specify the `id` and `name` of your existing Azure AD tenant for the topmost management group definition. The `id` attribute for this element is mapped into the `var-parentManagementGroupId` pipeline variable for backward compatibility. * Specify only 1 child management group definition for the topmost management group definition. You can specify more than 1 child of the topmost management group definition, but it is the first child of the topmost level that will be considered the root of of your management group hierarchy, and is the scope that the `policy-ci` pipeline will use to deploy built-in and custom policies. The `id` attribute for this element is mapped into the `var-topLevelManagementGroupName` pipeline variable for backward compatibility. - * The `id` attribute for management group elements can only be an ASCII letter, digit, `-`, `_`, `(`, `)`, `.` and cannot end with a period. In the sample environment configuration file (`CanadaESLZ-main.yml`), we illustrate a convention that prepends the id of the parent management group to the id of each child management group. This is an example only and not a requirement. You are welcome to choose any management group id convention that best suits your needs. + * The `id` attribute for management group elements can only be an ASCII letter, digit, `-`, `_`, `(`, `)`, `.` and cannot end with a period. In the sample environment configuration file (`CanadaPubSecALZ-main.yml`), we illustrate a convention that prepends the id of the parent management group to the id of each child management group. This is an example only and not a requirement. You are welcome to choose any management group id convention that best suits your needs. * If you are using **CanadaPubSecALZ v0.9.0 or later** and **do not** include a `var-managementgroup-hierarchy` variable setting in your configuration, it will fallback to using the pipeline variables `var-parentManagementGroupId` and `var-topLevelManagementGroupName`. This is to ensure backward compatibility, enabling newer versions of the code to run with older environment configurations. * If you are using **CanadaPubSecALZ v0.9.0** or later and **do** include a `var-managementgroup-hierarchy` variable setting in your configuration, it will override any pipeline variables `var-parentManagementGroupId` and `var-topLevelManagementGroupName` also present. @@ -485,8 +489,8 @@ This role assignment is used to grant users access to the logging subscription b > **The deployment automation will update the existing resources instead of creating new.** 1. Create directory `./config/logging`. -2. Create subdirectory based on the syntax: `-` (e.g. `CanadaESLZ-main` to create path `./config/logging/CanadaESLZ-main/`). -3. Create JSON parameters file with name `logging.parameters.json` (any name can be used) in directory created on step 2 (i.e. `./config/logging/CanadaESLZ-main/logging.parameters.json`). +2. Create subdirectory based on the syntax: `-` (e.g. `CanadaPubSecALZ-main` to create path `./config/logging/CanadaPubSecALZ-main/`). +3. Create JSON parameters file with name `logging.parameters.json` (any name can be used) in directory created on step 2 (i.e. `./config/logging/CanadaPubSecALZ-main/logging.parameters.json`). 4. Define deployment parameters based on example below. * Set valid contact information for the Azure Service Health Alerts: email and phone number. @@ -782,11 +786,11 @@ In order to configure audit stream for Azure Monitor, identify the following inf > * [Hub Networking with Fortigate Firewall (NVA)](../../docs/archetypes/hubnetwork-nva-fortigate.md) 1. Create directory `./config/networking`. -1. Create subdirectory based on the syntax: `-` (i.e. `CanadaESLZ-main` to create path `./config/networking/CanadaESLZ-main/`). +1. Create subdirectory based on the syntax: `-` (i.e. `CanadaPubSecALZ-main` to create path `./config/networking/CanadaPubSecALZ-main/`). 1. When using Hub Networking with Azure Firewall - 1. Create subdirectory: `hub-azfw-policy` (i.e. `./config/networking/CanadaESLZ-main/hub-azfw-policy`) - 1. Create JSON parameters file with name `azure-firewall-policy.parameters.json` (any name can be used) in the directory (i.e. `./config/networking/CanadaESLZ-main/hub-azfw-policy/azure-firewall-policy.parameters.json`). + 1. Create subdirectory: `hub-azfw-policy` (i.e. `./config/networking/CanadaPubSecALZ-main/hub-azfw-policy`) + 1. Create JSON parameters file with name `azure-firewall-policy.parameters.json` (any name can be used) in the directory (i.e. `./config/networking/CanadaPubSecALZ-main/hub-azfw-policy/azure-firewall-policy.parameters.json`). 1. Define deployment parameters based on example below. * Set the values for the Azure tags that would be applied to the logging resources. @@ -820,8 +824,8 @@ In order to configure audit stream for Azure Monitor, identify the following inf } ``` - 1. Create subdirectory: `hub-azfw` (i.e. `./config/networking/CanadaESLZ-main/hub-azfw`) - 1. Create JSON parameters file with name `hub-network.parameters.json` (any name can be used) in the directory (i.e. `./config/networking/CanadaESLZ-main/hub-azfw/hub-network.json`). + 1. Create subdirectory: `hub-azfw` (i.e. `./config/networking/CanadaPubSecALZ-main/hub-azfw`) + 1. Create JSON parameters file with name `hub-network.parameters.json` (any name can be used) in the directory (i.e. `./config/networking/CanadaPubSecALZ-main/hub-azfw/hub-network.json`). 1. Define deployment parameters based on example below. * Set valid contact information for the Azure Service Health Alerts: email and phone number. @@ -1058,8 +1062,8 @@ In order to configure audit stream for Azure Monitor, identify the following inf 1. When using Hub Networking with Fortigate Firewall (NVA) - 1. Create subdirectory: `hub-nva` (i.e. `./config/networking/CanadaESLZ-main/hub-nva`) - 1. Create JSON parameters file with name `hub-network.parameters.json` (any name can be used) in the directory (i.e. `./config/networking/CanadaESLZ-main/hub-nva/hub-network.parameters.json`). + 1. Create subdirectory: `hub-nva` (i.e. `./config/networking/CanadaPubSecALZ-main/hub-nva`) + 1. Create JSON parameters file with name `hub-network.parameters.json` (any name can be used) in the directory (i.e. `./config/networking/CanadaPubSecALZ-main/hub-nva/hub-network.parameters.json`). 1. Define deployment parameters based on example below. * Set valid contact information for the Azure Service Health Alerts: email and phone number. @@ -1515,9 +1519,9 @@ In order to configure audit stream for Azure Monitor, identify the following inf 1. Create directory ./config/identity. - 1. Create subdirectory based on the syntax: `-` (i.e. `CanadaESLZ-main` to create path `./config/identity/CanadaESLZ-main/`). + 1. Create subdirectory based on the syntax: `-` (i.e. `CanadaPubSecALZ-main` to create path `./config/identity/CanadaPubSecALZ-main/`). - 1. Make a copy of an existing subscription configuration file under `config/identity/CanadaESLZ-main` as a starting point + 1. Make a copy of an existing subscription configuration file under `config/identity/CanadaPubSecALZ-main` as a starting point 1. Define deployment parameters based on example below. @@ -1676,7 +1680,7 @@ In order to configure audit stream for Azure Monitor, identify the following inf *Review the [README.md under `/config/subscriptions`](../../config/subscriptions/README.md) to create the folder structure required for subscriptions deployments.* - 1. Make a copy of an existing subscription configuration file under `config/subscriptions/CanadaESLZ-main` as a starting point + 1. Make a copy of an existing subscription configuration file under `config/subscriptions/CanadaPubSecALZ-main` as a starting point 2. Be sure to rename the file in one of the following formats: * `[GUID]_[TYPE].json` @@ -1867,7 +1871,7 @@ As of `v0.10.0`, logging configuration have been migrated to JSON parameters fil * Separates Azure DevOps pipeline variables from ARM deployment parameters. * Simplifies support for multiple Log Analytics Workspaces in an Azure tenant (i.e. LAWs deployed by region or workload) -We added a new parameter to `common.yml` to set the folder for logging configuration. This folder is used by Azure DevOps Pipelines to create a fully qualified file path for logging configuration. A fully qualified path will have the following structure: ``/`-`/`logging.parameters.json`. For example: `config/logging/CanadaESLZ-main/logging.parameters.json` +We added a new parameter to `common.yml` to set the folder for logging configuration. This folder is used by Azure DevOps Pipelines to create a fully qualified file path for logging configuration. A fully qualified path will have the following structure: ``/`-`/`logging.parameters.json`. For example: `config/logging/CanadaPubSecALZ-main/logging.parameters.json` ```yaml loggingPathFromRoot: 'config/logging' @@ -1876,8 +1880,8 @@ We added a new parameter to `common.yml` to set the folder for logging configura Migration process: 1. Create directory `./config/logging`. -2. Create subdirectory based on the syntax: `-` (i.e. `CanadaESLZ-main` to create path `./config/logging/CanadaESLZ-main/`). -3. Create JSON parameters file with name `logging.parameters.json` (any name can be used) in the directory (i.e. `./config/logging/CanadaESLZ-main/logging.parameters.json`). +2. Create subdirectory based on the syntax: `-` (i.e. `CanadaPubSecALZ-main` to create path `./config/logging/CanadaPubSecALZ-main/`). +3. Create JSON parameters file with name `logging.parameters.json` (any name can be used) in the directory (i.e. `./config/logging/CanadaPubSecALZ-main/logging.parameters.json`). 4. Define deployment parameters based on example below. **Template to use for logging.parameters.json** @@ -2021,7 +2025,7 @@ As of `v0.10.0`, hub networking configuration have been migrated to JSON paramet * Separates Azure DevOps pipeline variables from ARM deployment parameters. * Simplifies support for multiple Hub Networks in an Azure tenant (i.e. Hub Network deployed by region) -We added a new parameter to `common.yml` to set the folder for networking configuration. This folder is used by Azure DevOps Pipelines to create a fully qualified file path for networking configuration. A fully qualified path will have the following structure: ``/`-`/`hub-capability`/`hub-network.parameters.json`. For example: `config/networking/CanadaESLZ-main/hub-azfw/hub-network.parameters.json` +We added a new parameter to `common.yml` to set the folder for networking configuration. This folder is used by Azure DevOps Pipelines to create a fully qualified file path for networking configuration. A fully qualified path will have the following structure: ``/`-`/`hub-capability`/`hub-network.parameters.json`. For example: `config/networking/CanadaPubSecALZ-main/hub-azfw/hub-network.parameters.json` ```yaml networkPathFromRoot: 'config/networking' @@ -2030,11 +2034,11 @@ We added a new parameter to `common.yml` to set the folder for networking config Migration process: 1. Create directory `./config/networking`. -2. Create subdirectory based on the syntax: `-` (i.e. `CanadaESLZ-main` to create path `./config/networking/CanadaESLZ-main/`). +2. Create subdirectory based on the syntax: `-` (i.e. `CanadaPubSecALZ-main` to create path `./config/networking/CanadaPubSecALZ-main/`). 3. When using Hub Networking with Azure Firewall - 1. Create subdirectory: `hub-azfw-policy` (i.e. `./config/networking/CanadaESLZ-main/hub-azfw-policy`) - 1. Create JSON parameters file with name `azure-firewall-policy.parameters.json` (any name can be used) in the directory (i.e. `./config/networking/CanadaESLZ-main/hub-azfw-policy/azure-firewall-policy.parameters.json`). + 1. Create subdirectory: `hub-azfw-policy` (i.e. `./config/networking/CanadaPubSecALZ-main/hub-azfw-policy`) + 1. Create JSON parameters file with name `azure-firewall-policy.parameters.json` (any name can be used) in the directory (i.e. `./config/networking/CanadaPubSecALZ-main/hub-azfw-policy/azure-firewall-policy.parameters.json`). 1. Define deployment parameters based on example below. **Template to use for azure-firewall-policy.parameters.json** @@ -2057,8 +2061,8 @@ Migration process: } ``` - 1. Create subdirectory: `hub-azfw` (i.e. `./config/networking/CanadaESLZ-main/hub-azfw`) - 1. Create JSON parameters file with name `hub-network.parameters.json` (any name can be used) in the directory (i.e. `./config/networking/CanadaESLZ-main/hub-azfw/hub-network.json`). + 1. Create subdirectory: `hub-azfw` (i.e. `./config/networking/CanadaPubSecALZ-main/hub-azfw`) + 1. Create JSON parameters file with name `hub-network.parameters.json` (any name can be used) in the directory (i.e. `./config/networking/CanadaPubSecALZ-main/hub-azfw/hub-network.json`). 1. Define deployment parameters based on example below. **Template to use for hub-network.parameters.json** @@ -2243,8 +2247,8 @@ Migration process: 4. When using Hub Networking with Network Virtual Appliance - 1. Create subdirectory: `hub-nva` (i.e. `./config/networking/CanadaESLZ-main/hub-nva`) - 1. Create JSON parameters file with name `hub-network.parameters.json` (any name can be used) in the directory (i.e. `./config/networking/CanadaESLZ-main/hub-nva/hub-network.parameters.json`). + 1. Create subdirectory: `hub-nva` (i.e. `./config/networking/CanadaPubSecALZ-main/hub-nva`) + 1. Create JSON parameters file with name `hub-network.parameters.json` (any name can be used) in the directory (i.e. `./config/networking/CanadaPubSecALZ-main/hub-nva/hub-network.parameters.json`). 1. Define deployment parameters based on example below. **Template to use for hub-network.parameters.json** diff --git a/docs/onboarding/configuration-scripts.md b/docs/onboarding/configuration-scripts.md new file mode 100644 index 00000000..e92fbe92 --- /dev/null +++ b/docs/onboarding/configuration-scripts.md @@ -0,0 +1,663 @@ +# Configuration Scripts + +## Introduction + +This document discusses the scripts available to help simplify creating and using configuration files for a CanadaPubSecALZ deployment. + +## Table of Contents + +- [Prerequisites](#prerequisites) +- [Creating the Service Principal](#creating-the-service-principal) +- [Working with Configuration Files](#working-with-configuration-files) +- [Working with Deployments](#working-with-deployments) + +--- + +## Prerequisites + +The instructions in this document and scripts in the `/scripts/configuration` folder have a number of prerequisites. Please review the prerequisites below and complete any missing prerequisites before proceeding. + +### Azure PowerShell + +Install the latest version of the Azure PowerShell module. Review the [Azure PowerShell documentation](https://docs.microsoft.com/powershell/azure/install-az-ps) for installation instructions. + +### Azure CLI + +Install the latest version of the Azure CLI. Review the [Azure CLI documentation](https://docs.microsoft.com/cli/azure/install-azure-cli) for installation instructions. + +### Required Permissions + +Review the [Required Permissions](./azure-devops-scripts.md#required-permissions) section of the [Azure DevOps Scripts](./azure-devops-scripts.md) document for the required permissions to run the scripts in the `/scripts/configuration` folder. + +--- + +## Overview + +The scripts in the `/scripts/configuration` folder can be used to simplify part of the Azure Landing Zones onboarding process related to creating the configuration files (`.yml` and `.json`) for your environment. + +Using these scripts is optional.If you choose not to use them, you can still follow the manual steps in the [Azure DevOps Pipelines Onboarding Guide](./azure-devops-pipelines.md) document to create the configuration files. + +These scripts consolidate most, but not all, of the common settings available in the configuration files used to for a CanadaPubSecALZ deployment. Notable examples of configuration values you will need to update directly in the finished configuration files include network settings such as IP addresses and CIDR ranges. Therefore, you will still need to review and update the configuration files after they are created by these scripts. + +Benefits of using these scripts: + +- Simplifies the process of creating a new service principal and credential file. +- Simplifies the process of creating a new set of configuration files based on an existing set of configuration files. +- Automatically copies an existing set of configuration files to a new set of configuration files, putting the new files in their correct folder locations, retaining common configuration settings, and overriding select configuration settings using a single YAML file stored outside of the repository. +- Provide detailed logging for all credential, configuration, and deployment operations in datetime-stamped log files located in your home directory for easier troubleshooting. +- Provide a foundation for automating the creation and management of configuration files using Azure DevOps pipelines or GitHub Actions. + +The following scripts are available: + +Script | Category | Description +---- | -------- | ------------ +Connect-AlzCredential.ps1 | Credentials | Connects to Azure using one of the following methods: Credential file, Service Principal, or Interactive login. +Get-AlzConfiguration.ps1 | Configuration | Gets the ALZ configuration file. Used primarily by other scripts in this folder. +Get-AlzSubscriptions.ps1 | Configuration | Gets an array of ALZ subscription identifiers. Used primarily by other scripts in this folder. +Install-Prerequisites.ps1 | Prerequisites | Installs the PowerShell module prerequisites. +New-AlzConfiguration.ps1 | Configuration | Creates the ALZ configuration files in a specified Target Environment from existing configuration files in a specified Source Environment. +New-AlzCredential.ps1 | Credentials | Creates an ALZ credential file in your home directory. +New-AlzDeployment.ps1 | Deployment | Deploys the ALZ configuration files to the specified Target Environment. Uses the `../deployments/RunWorkflows.ps1` script to deploy the configuration files. +Remove-AlzConfiguration.ps1 | Configuration | Removes the ALZ configuration file. +Remove-AlzCredential.ps1 | Credentials | Removes all elements of an ALZ credential created using the `New-AlzCredential.ps1` script. This includes: the credential file, the service principal, and the app registration. +Test-AlzCredential.ps1 | Credentials | Tests the ALZ credential file. + +>Note: The scripts in this folder are designed to be run from the folder they are located in (`/scripts/configuration`). Running them from any other location may result in errors. + +The configuration scripts take some common parameters. These parameters are used to specify the location of the ALZ configuration files. The default values for these parameters are as follows: + +```powershell +[string]$UserRootPath = "$HOME" +[string]$UserLogsPath = "$UserRootPath/ALZ/logs" +[string]$UserCredsPath = "$UserRootPath/ALZ/credentials" +[string]$UserConfigPath = "$UserRootPath/ALZ/config" +``` + +These parameters can be overridden by specifying the parameters when running the scripts. By default, they are set to the current user's home directory, which is usually outside of the repository. This is done to prevent the ALZ configuration files from being committed to the repository since some of these configuration files contain sensitive information such as credentials that should not be shared. + +The `logs` folder contains log output from the scripts. The `credentials` folder contains the ALZ credential files. The `config` folder contains the ALZ configuration input files. + +The following sections of this document will outline the end-to-end process of creating the ALZ credentials, creating the ALZ configuration files, and then using the ALZ configuration files to deploy the Azure Landing Zones design using either PowerShell or the Azure DevOps pipelines. + +--- + +## Creating the Service Principal + +This section deals with creating the ALZ credential, which is used by the CanadaPubSecALZ configuration scripts to authenticate to Azure during deployment. + +You must be signed in to Azure via the Azure PowerShell SDK before running the scripts in this section. You can do this by either running `Connect-AzAccount` or by using the `Connect-AlzCredential.ps1` script with the `-TenantId` parameter. + +For example: + +```powershell +PS> $TenantId = '10000000-0000-0000-0000-000000000000' +PS> .\Connect-AlzCredential.ps1 -TenantId $TenantId +``` + +You will need certain permissions to run the scripts in this section. Review the [Required Permissions](./azure-devops-scripts.md#required-permissions) section of the [Azure DevOps Scripts](./azure-devops-scripts.md) document for the required permissions to run the scripts in this folder. + +### The ALZ Credential + +The ALZ credential file is a JSON file that contains the following information: `appId`, `displayName`, `password`, and `tenant`. The `appId` and `password` are used to authenticate to Azure, and the `tenant` is used to identify the tenant to which the service principal belongs. The `displayName` is used to identify the service principal. + +### Generate the Credential + +The first step is to generate the credential. This is done by running the `New-AlzCredential.ps1` script. This script will create a service principal in the tenant specified by the `TenantId` parameter. The service principal will be created with the `Owner` role in the tenant. The script will also create the ALZ credential file in the `$HOME/ALZ/credentials` folder. The name of the ALZ credential file will be the same as the name of the environment specified by the `Environment` parameter. + +```powershell +PS> $Environment = 'MyAzureDevOpsOrg-main' +PS> .\New-AlzCredential.ps1 -Environment $Environment +``` + +```text +Creating Service Principal for environment (MyAzureDevOpsOrg-main) in tenant (domain.onmicrosoft.com) + appId: 20000000-0000-0000-0000-000000000000 + displayName: MyAzureDevOpsOrg-main + password: ********** + tenant: 10000000-0000-0000-0000-000000000000 + +Saving Service Principal to file (C:\Users\username\ALZ\credentials/MyAzureDevOpsOrg-main.json) + +Elapsed time: 00:00:05.3650544 +``` + +### Check Credential File Creation + +The next step is to check that the credential file was created successfully. The credential file will be located in the `$HOME/ALZ/credentials` folder. The name of the credential file will be the same as the name of the environment specified by the `Environment` parameter. + +```powershell +PS> $Environment = 'MyAzureDevOpsOrg-main' +PS> Get-ChildItem -Path $HOME/ALZ/credentials +``` + +```text + Directory: C:\Users\username\ALZ\credentials + +Mode LastWriteTime Length Name +---- ------------- ------ ---- +-a--- 7/1/2023 10:40 AM 206 MyAzureDevOpsOrg-main.json +``` + +### Check Credential File Contents + +The next step is to check that the credential file contains the correct information. The credential file will be a JSON file that contains the following information: `appId`, `displayName`, `password`, and `tenant`. The `appId` and `password` are used to authenticate to Azure, and the `tenant` is used to identify the tenant to which the service principal belongs. The `displayName` is used to identify the service principal. + +```powershell +PS> $Environment = 'MyAzureDevOpsOrg-main' +PS> Get-Content -Raw -Path $HOME/ALZ/credentials/$Environment.json +``` + +```text +{ + "appId": "20000000-0000-0000-0000-000000000000", + "displayName": "MyAzureDevOpsOrg-main", + "password": "this-is-not-a-real-password", + "tenant": "10000000-0000-0000-0000-000000000000" +} +``` + +### Test Credentials + +The next step is to test the credentials. This is done by running the `Test-AlzCredential.ps1` script. This script will test the credentials by logging in to Azure using the service principal specified by the `Environment` parameter. The script will check the following: + +- the service principal has the `Owner` role in the tenant +- the service principal can be used to log in to Azure +- the Azure context is set to the correct environment + +```powershell +PS> $Environment = 'MyAzureDevOpsOrg-main' +PS> .\Test-AlzCredential.ps1 -Environment $Environment +``` + +```text +Service Principal (MyAzureDevOpsOrg-main) for environment (MyAzureDevOpsOrg-main) from tenant (10000000-0000-0000-0000-000000000000) is an Owner of the tenant. + +Current Azure context: + +Name Account SubscriptionName Environment TenantId +---- ------- ---------------- ----------- -------- +ALZ-Workload (00000000-… user@domain ALZ-Workload AzureCloud 10000000-0000-0000-0000-000… + +Logging in to Azure using service principal... + +Service Principal Azure context: + +Id : 00000000-0000-0000-0000-000000000000 +Type : ServicePrincipal +Tenants : {10000000-0000-0000-0000-000000000000} +AccessToken : +Credential : +TenantMap : {} +CertificateThumbprint : + +Original Azure context: + +Name Account SubscriptionName Environment TenantId +---- ------- ---------------- ----------- -------- +ALZ-Workload (00000000-… user@domain ALZ-Workload AzureCloud 10000000-0000-0000-0000-000… + +Elapsed time: 00:00:04.9138939 +``` + +--- + +## Working with Configuration Files + +### Configuration File Structure + +Configurations for the CanadaPubSecALZ implementation are stored under the `config` folder in the root of the repository. There are four subfolders under the `config` folder: + +- The `logging` subfolder contains the configuration files for the logging and monitoring components of the implementation. +- The `networking` subfolder contains the configuration files for the networking components of the implementation. +- The `identity` subfolder contains the configuration files for the identity components of the implementation. +- The `subscriptions` subfolder contains the configuration files for the subscriptions used by the implementation. +- The `variables` subfolder contains the configuration files for the variables used by the implementation. + +The `logging`, `networking`, `identity`, and `subscriptions` subfolders contain JSON configuration files for each environment. An environment is named using the following convention: `-`. For example, the `CanadaPubSecALZ` environment corresponding to the `CanadaPubSecALZ` organization name (for Azure DevOps) or repository name (for GitHub) and the `main` branch of the repository is named `CanadaPubSecALZ-main`. + +The `variables` subfolder contains a YAML configuration file for each environment. An environment is named using the following convention: `-`. For example, the `CanadaPubSecALZ` environment corresponding to the `CanadaPubSecALZ` organization name (for Azure DevOps) or repository name (for GitHub) and the `main` branch of the repository is named `CanadaPubSecALZ-main`. + +Take a moment to familiarize yourself with the contents of the `config` folder, its subfolders, and the JSON and YAML configuration files therein. The configuration files in the main repository are provided as a starting point for your implementation. You will need to make copies of and modify the configuration files to suit your implementation. + +In order to streamline the process of creating configuration files tailored for your environment(s), the `New-AlzConfiguration.ps1` script has been provided. This script will create the configuration files for your implementation. It uses configuration files for an existing environment in the main repository as a starting point, combining information from a YAML file you create in your `$HOME/ALZ/config` folder that contains a subset of the most commonly updated configuration values. The script will then create the configuration files for your environment(s) in the correct location. + +### Using a Configuration Template + +Create a YAML file in your `$HOME/ALZ/config` folder that contains the configuration values for your implementation. The file name should be the name of your environment, followed by the `.yml` extension. For example, if your environment is named `MyAzureDevOpsOrg-main`, the file name should be `MyAzureDevOpsOrg-main.yml`. + +Some notes about the values shown in the sample YAML template below: + +- Some sections in the sample YAML file are commented out. You can uncomment these sections and provide values for them if you need to override the default values. + +- You may comment out sections that you do not need to override the existing values from the source environment configuration. + +- Most of the values you are likely to change are represented in the YAML template, with the exception of the network CIDR blocks. These are defined in the `networking` configuration files, and are discussed in more detail in the [Azure DevOps Pipelines Onboarding Guide](./azure-devops-pipelines.md). + +- The `Source` attribute is the name of the environment you are copying the configuration from. This environment must already exist in your repository's `config` folder. + +- The `Target` attribute is empty in the sample YAML file. When no value is specified, the base name of the YAML template file will be used to form your new environment name. For example, if your YAML template file is named `MyAzureDevOpsOrg-main.yml`, the `Target` attribute will be set to `MyAzureDevOpsOrg-main` by default. + +- The following GUID is provided as a placeholder for the Azure AD tenant identifier. You must replace them with the actual GUID value of the Azure AD tenant in your environment: + - `10000000-0000-0000-0000-000000000000`: Tenant + +- The following GUIDs are provided as placeholders for Azure subscription identifiers. You must replace them with the actual GUID values of the subscriptions in your environment: + - `20000000-0000-0000-0000-000000000000`: Logging subscription + - `30000000-0000-0000-0000-000000000000`: Network Hub subscription + - `40000000-0000-0000-0000-000000000000`: Identity subscription + - `70000000-0000-0000-0000-000000000000`: Generic subscription + - `80000000-0000-0000-0000-000000000000`: Machine Learning subscription + - `90000000-0000-0000-0000-000000000000`: Healthcare subscription + + Note that the last 3 subscriptions correspond to each of the 3 archetypes provided in the reference implementation: Generic, Machine Learning, and Healthcare. These are optional and one or more of these can be removed if not needed. + +- The following GUIDs are provided as placeholders for Azure AD security group identifiers. You must replace them with the actual GUID values of the security groups in your environment: + - `00000000-1000-0000-0000-000000000000`: Azure AD group to assign Contributor role in Logging subscription + - `00000000-2000-0000-0000-000000000000`: Azure AD group to assign Contributor role in Network subscription + - `00000000-3000-0000-0000-000000000000`: Azure AD group to assign Contributor role in Identity subscription + - `00000000-4000-0000-0000-000000000000`: Azure AD group to assign Contributor role in Generic subscription + - `00000000-5000-0000-0000-000000000000`: Azure AD group to assign custom landing zone application owners role in Generic subscription + - `00000000-6000-0000-0000-000000000000`: Azure AD group to assign Contributor role in Machine Learning subscription + - `00000000-7000-0000-0000-000000000000`: Azure AD group to assign custom landing zone application owners role in Machine Learning subscription + - `00000000-8000-0000-0000-000000000000`: Azure AD group to assign Contributor role in Healthcare subscription + - `00000000-9000-0000-0000-000000000000`: Azure AD group to assign custom landing zone application owners role in Healthcare subscription + +- The following partial GUIDs (8-character prefix) are provided as placeholders for the Azure subscription identifiers for 3 of the archetype subscriptions in the `CanadaPubSecALZ-main` configuration: Generic, Machine Learning, and Healthcare. You can replace them with partial GUIDs of alternate subscription configurations from a different source environment, or you can leave them as-is to use the subscription configurations provided in the `CanadaPubSecALZ-main` configuration: + - `8422552f`: The Generic subscription from the `CanadaPubSecALZ-main` configuration + - `f881fccb`: One of the Machine Learning subscriptions from the `CanadaPubSecALZ-main` configuration + - `1f519216`: The Healthcare subscription from the `CanadaPubSecALZ-main` configuration + +- The `Subscriptions:` element in the YAML template is a list of subscriptions from an existing source environment that will be used to create corresponding subscriptions in your target (new) environment. You may add or remove (delete or comment out) items in this list as needed. + +Copy the following sample into your YAML file, and update the values to suit your implementation. The values you provide will be used to create the configuration files for your environment(s). + +```yaml +Environment: + Source: CanadaPubSecALZ-main + Target: + +DeployRegion: canadacentral + +ManagementGroupHierarchy: + name: Tenant Root Group + id: 10000000-0000-0000-0000-000000000000 + children: + - name: Azure Landing Zones for Canadian Public Sector + id: pubsec + children: + - name: Platform + id: Platform + children: + - name: Management + id: Management + children: [] + - name: Connectivity + id: Connectivity + children: [] + - name: Identity + id: Identity + children: [] + - name: LandingZones + id: LandingZones + children: + - name: DevTest + id: DevTest + children: [] + - name: QA + id: QA + children: [] + - name: Prod + id: Prod + children: [] + - name: Sandbox + id: Sandbox + children: [] + +Logging: + SubscriptionId: 20000000-0000-0000-0000-000000000000 + ManagementGroupId: Management + SecurityCenter: + email: 'security@example.com' + phone: '6135555555' + ServiceHealthAlerts: + resourceGroupName: service-health-alerts-rg + incidentTypes: ['Incident', 'Security'] + regions: ['Global', 'Canada Central', 'Canada East'] + receivers: + app: ['logging@example.com'] + email: ['logging@example.com'] + sms: [{ countryCode: '1', phoneNumber: '6135555555' }] + voice: [{ countryCode: '1', phoneNumber: '6135555555' }] + actionGroupName: 'Logging Alerts' + actionGroupShortName: 'logging-ag' + alertRuleName: 'Logging Alerts' + alertRuleDescription: 'Logging Alerts for Incidents and Security' + RoleAssignments: + - comments: 'Built-in Contributor Role' + roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + securityGroupObjectIds: ['00000000-1000-0000-0000-000000000000'] + # SubscriptionTags: + # ISSO: isso-tag + # ResourceTags: + # ClientOrganization: client-organization-tag + # CostCenter: cost-center-tag + # DataSensitivity: data-sensitivity-tag + # ProjectContact: project-contact-tag + # ProjectName: project-name-tag + # TechnicalContact: technical-contact-tag + DataCollectionRule: + Enabled: false + +HubNetwork: + SubscriptionId: 30000000-0000-0000-0000-000000000000 + ManagementGroupId: Connectivity + SecurityCenter: + email: 'security@example.com' + phone: '6135555555' + ServiceHealthAlerts: + resourceGroupName: service-health-alerts-rg + incidentTypes: ['Incident', 'Security'] + regions: ['Global', 'Canada Central', 'Canada East'] + receivers: + app: ['networking@example.com'] + email: ['networking@example.com'] + sms: [{ countryCode: '1', phoneNumber: '6135555555' }] + voice: [{ countryCode: '1', phoneNumber: '6135555555' }] + actionGroupName: 'Networking Alerts' + actionGroupShortName: 'network-ag' + alertRuleName: 'Networking Alerts' + alertRuleDescription: 'Networking Alerts for Incidents and Security' + RoleAssignments: + - comments: 'Built-in Contributor Role' + roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + securityGroupObjectIds: ['00000000-2000-0000-0000-000000000000'] + # SubscriptionTags: + # ISSO: isso-tag + # ResourceTags: + # ClientOrganization: client-organization-tag + # CostCenter: cost-center-tag + # DataSensitivity: data-sensitivity-tag + # ProjectContact: project-contact-tag + # ProjectName: project-name-tag + # TechnicalContact: technical-contact-tag + PrivateDNS: + enabled: true + resourceGroupName: private-dns-rg + DDoS: + # https://learn.microsoft.com/azure/ddos-protection/ddos-faq#are-services-unsafe-in-azure-without-the-service- + enabled: false + resourceGroupName: ddos-rg + planName: ddos-plan + +Identity: + SubscriptionId: 40000000-0000-0000-0000-000000000000 + ManagementGroupId: Identity + SecurityCenter: + email: 'security@example.com' + phone: '6135555555' + ServiceHealthAlerts: + resourceGroupName: service-health-alerts-rg + incidentTypes: ['Incident', 'Security'] + regions: ['Global', 'Canada Central', 'Canada East'] + receivers: + app: ['identity@example.com'] + email: ['identity@example.com'] + sms: [{ countryCode: '1', phoneNumber: '6135555555' }] + voice: [{ countryCode: '1', phoneNumber: '6135555555' }] + actionGroupName: 'Identity Alerts' + actionGroupShortName: 'identity-ag' + alertRuleName: 'Identity Alerts' + alertRuleDescription: 'Identity Alerts for Incidents and Security' + RoleAssignments: + - comments: 'Built-in Contributor Role' + roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + securityGroupObjectIds: ['00000000-3000-0000-0000-000000000000'] + # SubscriptionTags: + # ISSO: isso-tag + # ResourceTags: + # ClientOrganization: client-organization-tag + # CostCenter: cost-center-tag + # DataSensitivity: data-sensitivity-tag + # ProjectContact: project-contact-tag + # ProjectName: project-name-tag + # TechnicalContact: technical-contact-tag + +Subscriptions: +- '8422552f': # Generic subscription ID prefix from {$Environment.Source} + SubscriptionId: 70000000-0000-0000-0000-000000000000 + ManagementGroupId: DevTest + Location: canadacentral + SecurityCenter: + email: 'security@example.com' + phone: '6135555555' + ServiceHealthAlerts: + resourceGroupName: service-health-alerts-rg + incidentTypes: ['Incident', 'Security'] + regions: ['Global', 'Canada Central', 'Canada East'] + receivers: + app: ['subscription-owners@example.com'] + email: ['subscription-owners@example.com'] + sms: [{ countryCode: '1', phoneNumber: '6135555555' }] + voice: [{ countryCode: '1', phoneNumber: '6135555555' }] + actionGroupName: 'Subscription Owners Alerts' + actionGroupShortName: 'sub-own-ag' + alertRuleName: 'Subscription Owners Alerts' + alertRuleDescription: 'Subscription Owners Alerts for Incidents and Security' + RoleAssignments: + - comments: 'Built-in Contributor Role' + roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + securityGroupObjectIds: ['00000000-4000-0000-0000-000000000000'] + - comments: 'Custom Role: Landing Zone Application Owner' + roleDefinitionId: 'b4c87314-c1a1-5320-9c43-779585186bcc' + securityGroupObjectIds: ['00000000-5000-0000-0000-000000000000'] + # SubscriptionTags: + # ISSO: isso-tag + # ResourceTags: + # ClientOrganization: client-organization-tag + # CostCenter: cost-center-tag + # DataSensitivity: data-sensitivity-tag + # ProjectContact: project-contact-tag + # ProjectName: project-name-tag + # TechnicalContact: technical-contact-tag + +- 'f881fccb': # Machine Learning 1 subscription ID prefix from {$Environment.Source} + SubscriptionId: 80000000-0000-0000-0000-000000000000 + ManagementGroupId: DevTest + Location: canadacentral + SecurityCenter: + email: 'security@example.com' + phone: '6135555555' + ServiceHealthAlerts: + resourceGroupName: service-health-alerts-rg + incidentTypes: ['Incident', 'Security'] + regions: ['Global', 'Canada Central', 'Canada East'] + receivers: + app: ['subscription-owners@example.com'] + email: ['subscription-owners@example.com'] + sms: [{ countryCode: '1', phoneNumber: '6135555555' }] + voice: [{ countryCode: '1', phoneNumber: '6135555555' }] + actionGroupName: 'Subscription Owners Alerts' + actionGroupShortName: 'sub-own-ag' + alertRuleName: 'Subscription Owners Alerts' + alertRuleDescription: 'Subscription Owners Alerts for Incidents and Security' + RoleAssignments: + - comments: 'Built-in Contributor Role' + roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + securityGroupObjectIds: ['00000000-6000-0000-0000-000000000000'] + - comments: 'Custom Role: Landing Zone Application Owner' + roleDefinitionId: 'b4c87314-c1a1-5320-9c43-779585186bcc' + securityGroupObjectIds: ['00000000-7000-0000-0000-000000000000'] + # SubscriptionTags: + # ISSO: isso-tag + # ResourceTags: + # ClientOrganization: client-organization-tag + # CostCenter: cost-center-tag + # DataSensitivity: data-sensitivity-tag + # ProjectContact: project-contact-tag + # ProjectName: project-name-tag + # TechnicalContact: technical-contact-tag + +- '1f519216': # Healthcare subscription ID prefix from {$Environment.Source} + SubscriptionId: 90000000-0000-0000-0000-000000000000 + ManagementGroupId: DevTest + Location: canadacentral + SecurityCenter: + email: 'security@example.com' + phone: '6135555555' + ServiceHealthAlerts: + resourceGroupName: service-health-alerts-rg + incidentTypes: ['Incident', 'Security'] + regions: ['Global', 'Canada Central', 'Canada East'] + receivers: + app: ['subscription-owners@example.com'] + email: ['subscription-owners@example.com'] + sms: [{ countryCode: '1', phoneNumber: '6135555555' }] + voice: [{ countryCode: '1', phoneNumber: '6135555555' }] + actionGroupName: 'Subscription Owners Alerts' + actionGroupShortName: 'sub-own-ag' + alertRuleName: 'Subscription Owners Alerts' + alertRuleDescription: 'Subscription Owners Alerts for Incidents and Security' + RoleAssignments: + - comments: 'Built-in Contributor Role' + roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + securityGroupObjectIds: ['00000000-8000-0000-0000-000000000000'] + - comments: 'Custom Role: Landing Zone Application Owner' + roleDefinitionId: 'b4c87314-c1a1-5320-9c43-779585186bcc' + securityGroupObjectIds: ['00000000-9000-0000-0000-000000000000'] + # SubscriptionTags: + # ISSO: isso-tag + # ResourceTags: + # ClientOrganization: client-organization-tag + # CostCenter: cost-center-tag + # DataSensitivity: data-sensitivity-tag + # ProjectContact: project-contact-tag + # ProjectName: project-name-tag + # TechnicalContact: technical-contact-tag +``` + +### Creating Configuration Files from a Template + +The next step is to create the configuration files for the target environment. The following command will create a new set of configuration files in the `config` folder, based on the settings in the YAML template file created in the previous section. + +```powershell +PS> $Environment = 'MyAzureDevOpsOrg-main' +PS> .\New-AlzConfiguration.ps1 -Environment $Environment +``` + +Running the `New-AlzConfiguration.ps1` script as shown above will create the new set of configuration files and produce output similar to the following: + +```text +This script creates a new set of configuration files, using an existing CanadaPubSecALZ configuration. Select configuration elements are replaced with values specific to the target environment. + +Reading parameters from file (C:\Users\username\ALZ\config\MyAzureDevOpsOrg-main.yml) +Checking configuration path (P:\CanadaPubSecALZ\CanadaPubSecALZ\config) + Source environment: CanadaPubSecALZ-main + Target environment: MyAzureDevOpsOrg-main + +Generating Variables configurations + + Updating variables configuration + Writing variables configuration file: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/variables/MyAzureDevOpsOrg-main.yml + +Generating Logging configurations + + Reading source environment logging configuration file: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/logging/CanadaPubSecALZ-main/logging.parameters.json + Updating logging configuration + Writing logging configuration file: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/logging/MyAzureDevOpsOrg-main/logging.parameters.json + +Generating Network Azure Firewall configurations + + Reading source environment network Azure Firewall configuration file: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/networking/CanadaPubSecALZ-main/hub-azfw/hub-network.parameters.json + Updating network Azure Firewall configuration + Writing network Azure Firewall configuration file: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/networking/MyAzureDevOpsOrg-main/hub-azfw/hub-network.parameters.json + +Generating Network Azure Firewall Policy configurations + + Reading source environment network Azure Firewall Policy configuration file: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/networking/CanadaPubSecALZ-main/hub-azfw-policy/azure-firewall-policy.parameters.json + Updating network Azure Firewall Policy configuration + Writing network Azure Firewall Policy configuration file: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/networking/MyAzureDevOpsOrg-main/hub-azfw-policy/azure-firewall-policy.parameters.json + +Generating Network NVA configurations + + Reading source environment network NVA configuration file: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/networking/CanadaPubSecALZ-main/hub-nva/hub-network.parameters.json + Updating network NVA configuration + Writing network NVA configuration file: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/networking/MyAzureDevOpsOrg-main/hub-nva/hub-network.parameters.json + +Generating Identity configurations + + Reading source environment identity configuration file: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/identity/CanadaPubSecALZ-main/identity.parameters.json + Updating identity configuration + Writing identity configuration file: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/identity/MyAzureDevOpsOrg-main/identity.parameters.json + +Generating subscription configurations + + Looking for source environment subscription configuration file(s) matching specified pattern (8422552f) + Reading subscription configuration (8422552f-3840-4934-a971-6ee349ffbb05_generic-subscription_canadacentral.json) + Updating subscription configuration + Writing new subscription configuration (P:\CanadaPubSecALZ\CanadaPubSecALZ\config/subscriptions/MyAzureDevOpsOrg-main/DevTest/70000000-0000-0000-0000-000000000000_generic-subscription_canadacentral.json) + + Looking for source environment subscription configuration file(s) matching specified pattern (f881fccb) + Reading subscription configuration (f881fccb-2598-4b9c-b87c-b392f5e16f12_machinelearning_canadacentral.json) + Updating subscription configuration + Writing new subscription configuration (P:\CanadaPubSecALZ\CanadaPubSecALZ\config/subscriptions/MyAzureDevOpsOrg-main/DevTest/80000000-0000-0000-0000-000000000000_machinelearning_canadacentral.json) + + Looking for source environment subscription configuration file(s) matching specified pattern (1f519216) + Reading subscription configuration (1f519216-5e39-4b51-a9b6-10cc2b79b6c7_healthcare_canadacentral.json) + Updating subscription configuration + Writing new subscription configuration (P:\CanadaPubSecALZ\CanadaPubSecALZ\config/subscriptions/MyAzureDevOpsOrg-main/DevTest/90000000-0000-0000-0000-000000000000_healthcare_canadacentral.json) + +Elapsed time: 00:00:00.5295725 +``` + +### Deleting Configuration Files + +If you create a set of configuration files and decide you no longer need them, you can delete them using the `Remove-AlzConfiguration.ps1` script. The following command will delete the configuration files for the target environment. + +```powershell +PS> $Environment = 'MyAzureDevOpsOrg-main' +PS> .\Remove-AlzConfiguration.ps1 -Environment $Environment +``` + +Running the `Remove-AlzConfiguration.ps1` script as shown above will delete the configuration files for the target environment and produce output similar to the following: + +```text +This script removes an existing set of configuration files. + +Checking configuration path (P:\CanadaPubSecALZ\CanadaPubSecALZ\config) +Removing variables configuration file: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/variables/MyAzureDevOpsOrg-main.yml +Removing logging configuration directory: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/logging/MyAzureDevOpsOrg-main +Removing network configuration directory: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/networking/MyAzureDevOpsOrg-main +Removing identity configuration directory: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/identity/MyAzureDevOpsOrg-main +Removing subscription configuration directory: P:\CanadaPubSecALZ\CanadaPubSecALZ\config/subscriptions/MyAzureDevOpsOrg-main + +Elapsed time: 00:00:00.1053294 +``` + +If you have deleted the configuration files for an environment and want to recreate them, you can do so by running the `New-AlzConfiguration.ps1` script as described in the previous section, assuming you have not deleted the source environment configuration files and the YAML template file is still available. + +--- + +## Working with Deployments + +### Deploying a Configuration + +Once you have created a set of configuration files for an environment, you can deploy the configuration using the `New-AlzDeployment.ps1` script. + +>Note: The `New-AlzDeployment.ps1` script will deploy all stages of the specified environment configuration, including: management groups, logging, policies, networking, identity, and subscriptions. If you only want to deploy a subset of the configuration, you can use the `/scripts/deployments/RunWorkflows.ps1` script or invoke the individual Azure DevOps pipelines / GitHub workflows for each stage. + +The following invocation of the `New-AlzDeployment.ps1` script will deploy all stages of the configuration files for the target environment using the previously created credential file and the `AzFW` (Azure Firewall) network hub type: + +```powershell +PS> $Environment = 'MyAzureDevOpsOrg-main' +PS> .\New-AlzDeployment.ps1 -Environment $Environment -CredentialFile $Environment -NetworkType 'AzFW' +``` + +The following invocation of the `New-AlzDeployment.ps1` script will deploy all stages of the configuration files for the target environment using the previously created credential file and the `NVA` (Network Virtual Appliance) network hub type: + +```powershell +PS> $Environment = 'MyAzureDevOpsOrg-main' +PS> .\New-AlzDeployment.ps1 -Environment $Environment -CredentialFile $Environment -NetworkType 'NVA' +``` + +The `.\New-AlzDeployment.ps1` script also provides for alternate authentication methods to the credential file. You may also use a service principal or interactive authentication. + +When you deploy from local configuration files, remember that detailed log output is available in `$HOME/ALZ/logs` (by default) and the log files generated for deployment operations can be very useful for troubleshooting. diff --git a/docs/policy/readme.md b/docs/policy/readme.md index 3524a4e9..9fdeeba0 100644 --- a/docs/policy/readme.md +++ b/docs/policy/readme.md @@ -154,12 +154,12 @@ Parameters can be templated using the syntax `{{PARAMETER_NAME}}`. Following pa | Templated Parameter | Source Value | Example | | --- | --- | --- | -| {{var-topLevelManagementGroupName}} | Environment configuration file such as [config/variables/CanadaESLZ-main.yml](../../config/variables/CanadaESLZ-main.yml) | `pubsec` -| {{var-logging-logAnalyticsWorkspaceResourceId}} | Resource ID is inferred using Log Analytics settings in environment configuration file such as [config/variables/CanadaESLZ-main.yml](../../config/variables/CanadaESLZ-main.yml) | `/subscriptions/bc0a4f9f-07fa-4284-b1bd-fbad38578d3a/resourcegroups/pubsec-central-logging/providers/microsoft.operationalinsights/workspaces/log-analytics-workspace` -| {{var-logging-logAnalyticsWorkspaceId}} | Workspace ID is inferred using Log Analytics settings in environment configuration file such as [config/variables/CanadaESLZ-main.yml](../../config/variables/CanadaESLZ-main.yml) | `fcce3f30-158a-4561-a714-361623f42168` -| {{var-logging-logAnalyticsResourceGroupName}} | Environment configuration file such as [config/variables/CanadaESLZ-main.yml](../../config/variables/CanadaESLZ-main.yml) | `pubsec-central-logging` -| {{var-logging-logAnalyticsRetentionInDays}} | Environment configuration file such as [config/variables/CanadaESLZ-main.yml](../../config/variables/CanadaESLZ-main.yml) | `730` -| {{var-logging-diagnosticSettingsforNetworkSecurityGroupsStoragePrefix}} | Environment configuration file such as [config/variables/CanadaESLZ-main.yml](../../config/variables/CanadaESLZ-main.yml) | `pubsecnsg` +| {{var-topLevelManagementGroupName}} | Environment configuration file such as [config/variables/CanadaPubSecALZ-main.yml](../../config/variables/CanadaPubSecALZ-main.yml) | `pubsec` +| {{var-logging-logAnalyticsWorkspaceResourceId}} | Resource ID is inferred using Log Analytics settings in environment configuration file such as [config/variables/CanadaPubSecALZ-main.yml](../../config/variables/CanadaPubSecALZ-main.yml) | `/subscriptions/bc0a4f9f-07fa-4284-b1bd-fbad38578d3a/resourcegroups/pubsec-central-logging/providers/microsoft.operationalinsights/workspaces/log-analytics-workspace` +| {{var-logging-logAnalyticsWorkspaceId}} | Workspace ID is inferred using Log Analytics settings in environment configuration file such as [config/variables/CanadaPubSecALZ-main.yml](../../config/variables/CanadaPubSecALZ-main.yml) | `fcce3f30-158a-4561-a714-361623f42168` +| {{var-logging-logAnalyticsResourceGroupName}} | Environment configuration file such as [config/variables/CanadaPubSecALZ-main.yml](../../config/variables/CanadaPubSecALZ-main.yml) | `pubsec-central-logging` +| {{var-logging-logAnalyticsRetentionInDays}} | Environment configuration file such as [config/variables/CanadaPubSecALZ-main.yml](../../config/variables/CanadaPubSecALZ-main.yml) | `730` +| {{var-logging-diagnosticSettingsforNetworkSecurityGroupsStoragePrefix}} | Environment configuration file such as [config/variables/CanadaPubSecALZ-main.yml](../../config/variables/CanadaPubSecALZ-main.yml) | `pubsecnsg` | {{var-policyAssignmentManagementGroupId}} | The management group scope for policy assignment. | `pubsec` --- diff --git a/scripts/configuration/Connect-AlzCredential.ps1 b/scripts/configuration/Connect-AlzCredential.ps1 new file mode 100644 index 00000000..277b6958 --- /dev/null +++ b/scripts/configuration/Connect-AlzCredential.ps1 @@ -0,0 +1,70 @@ +<# +---------------------------------------------------------------------------------- +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. + +THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +---------------------------------------------------------------------------------- +#> + +<# + .SYNOPSIS + This script connects to Azure using a service principal stored in a credential file, a service principal stored in a SecureString, or interactively. + + .DESCRIPTION + This script connects to Azure using a service principal stored in a credential file, a service principal stored in a SecureString, or interactively. + + .PARAMETER CredentialFile + The path to the credential file to use for login. + + .PARAMETER SecureServicePrincipal + The service principal to use for login. + + .PARAMETER TenantId + The tenant ID to use for interactive login. + + .EXAMPLE + PS> .\Connect-AlzCredential.ps1 -CredentialFile '$HOME/CanadaALZ.json' + + .EXAMPLE + PS> .\Connect-AlzCredential.ps1 -SecureServicePrincipal $SecureSP + + .EXAMPLE + PS> .\Connect-AlzCredential.ps1 -TenantId '00000000-0000-0000-0000-000000000000' +#> + +[CmdletBinding()] +Param( + [Parameter(Mandatory = $true, ParameterSetName = "CredentialFile")] + [string]$CredentialFile, + + [Parameter(Mandatory = $true, ParameterSetName = "ServicePrincipal")] + [SecureString]$SecureServicePrincipal, + + [Parameter(Mandatory = $true, ParameterSetName = "Interactive")] + [string]$TenantId +) + +switch ($PSCmdlet.ParameterSetName) { + "CredentialFile" { + $ServicePrincipalCredentials = Get-Content -Raw -Path $CredentialFile + $SecureSP = ConvertTo-SecureString -String $ServicePrincipalCredentials -AsPlainText -Force + .\Connect-AlzCredential.ps1 -SecureServicePrincipal $SecureSP + } + "ServicePrincipal" { + Write-Output "Logging in to Azure using service principal..." + $ServicePrincipal = ($SecureServicePrincipal | ConvertFrom-SecureString -AsPlainText) | ConvertFrom-Json + $Password = ConvertTo-SecureString $ServicePrincipal.password -AsPlainText -Force + $Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ServicePrincipal.appId, $Password + Connect-AzAccount -ServicePrincipal -TenantId $ServicePrincipal.tenant -Credential $Credential + } + "Interactive" { + $context = Get-AzContext + if ($context -eq $null) { + Write-Output "Logging in to Azure using interactive login..." + Connect-AzAccount -Tenant $TenantId + } + } +} diff --git a/scripts/configuration/Get-AlzConfiguration.ps1 b/scripts/configuration/Get-AlzConfiguration.ps1 new file mode 100644 index 00000000..301c63ef --- /dev/null +++ b/scripts/configuration/Get-AlzConfiguration.ps1 @@ -0,0 +1,55 @@ +<# +---------------------------------------------------------------------------------- +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. + +THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +---------------------------------------------------------------------------------- +#> + +<# + .SYNOPSIS + This script gets the main YAML configuration for a CanadaPubSecALZ deployment. + + .DESCRIPTION + This script gets the main YAML configuration for a CanadaPubSecALZ deployment. + + .PARAMETER Environment + The name of the environment. + + .PARAMETER RepoRootPath + The path to the repository directory. + + .PARAMETER ConfigVariablesByRef + The reference to the configuration variables hashtable. + + .EXAMPLE + PS> $ConfigVariablesYaml = @{} + PS> .\Get-AlzConfiguration.ps1 -Environment 'CanadaALZ-main' -ConfigVariablesByRef ([ref]$ConfigVariablesYaml) +#> + +[CmdletBinding()] +Param( + [Parameter(Mandatory = $true)] + [string]$Environment, + + [string]$RepoRootPath = "../..", + + [ref]$ConfigVariablesByRef +) + +#Requires -Modules powershell-yaml + +$ErrorActionPreference = "Stop" + +$RepoConfigPath = (Resolve-Path -Path "$RepoRootPath/config/variables/$Environment.yml").Path + +Write-Output "Getting environment configuration ($RepoConfigPath)" + +if (Test-Path -PathType Leaf -Path $RepoConfigPath) { + $ConfigVariablesByRef.value = Get-Content -Path $RepoConfigPath -Raw | ConvertFrom-Yaml +} else { + throw "Environment file not found ($RepoConfigPath)" +} diff --git a/scripts/configuration/Get-AlzSubscriptions.ps1 b/scripts/configuration/Get-AlzSubscriptions.ps1 new file mode 100644 index 00000000..0e9f9c9d --- /dev/null +++ b/scripts/configuration/Get-AlzSubscriptions.ps1 @@ -0,0 +1,55 @@ +<# +---------------------------------------------------------------------------------- +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. + +THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +---------------------------------------------------------------------------------- +#> + +<# + .SYNOPSIS + This script gets an array of subscription ids for a CanadaPubSecALZ deployment. + + .DESCRIPTION + This script gets an array of subscription ids for a CanadaPubSecALZ deployment. + + .PARAMETER Environment + The name of the environment. + + .PARAMETER RepoRootPath + The path to the repository directory. + + .PARAMETER SubscriptionIdsByRef + The reference to the subscription IDs array. + + .EXAMPLE + PS> $SubscriptionIds = @() + PS> .\Get-AlzSubscriptions.ps1 -Environment 'CanadaALZ-main' -SubscriptionIdsByRef ([ref]$SubscriptionIds) +#> + +[CmdletBinding()] +Param( + [Parameter(Mandatory = $true)] + [string]$Environment, + + [string]$RepoRootPath = "../..", + + [ref]$SubscriptionIdsByRef +) + +$ErrorActionPreference = "Stop" + +Write-Output "Getting subscription configurations for environment ($Environment)" + +$SubscriptionIdsByRef.value = @() + +$Pattern = "^[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}(_.*)(_.*)?\.json" + +$Subscriptions = @(Get-ChildItem -Path "$RepoRootPath/config/subscriptions/$Environment" -File -Recurse | ? { $_.Name -match $Pattern }) + +foreach ($Subscription in $Subscriptions) { + $SubscriptionIdsByRef.value += $Subscription.Name.Split('_')[0] +} diff --git a/scripts/configuration/Install-Prerequisites.ps1 b/scripts/configuration/Install-Prerequisites.ps1 new file mode 100644 index 00000000..aef06f5e --- /dev/null +++ b/scripts/configuration/Install-Prerequisites.ps1 @@ -0,0 +1,14 @@ +<# +---------------------------------------------------------------------------------- +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. + +THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +---------------------------------------------------------------------------------- +#> + +Install-Module Az -Repository PSGallery -Force +Install-Module powershell-yaml -Repository PSGallery -Force +Install-Module PSPasswordGenerator -Repository PSGallery -Force diff --git a/scripts/configuration/New-AlzConfiguration.ps1 b/scripts/configuration/New-AlzConfiguration.ps1 new file mode 100644 index 00000000..d6089dec --- /dev/null +++ b/scripts/configuration/New-AlzConfiguration.ps1 @@ -0,0 +1,475 @@ +<# +---------------------------------------------------------------------------------- +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. + +THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +---------------------------------------------------------------------------------- +#> + +<# + .SYNOPSIS + Creates a new configuration for the CanadaPubSecALZ deployment. + + .DESCRIPTION + This script creates a new set of configuration files, using an existing CanadaPubSecALZ configuration. Select configuration elements are replaced with values specific to the target environment. + + .PARAMETER Environment + The base name of the YAML environment configuration file. + + .PARAMETER SourceEnvironment + The name of the source environment. If not specified, the source environment attribute in the environment configuration file is used. If the environment configuration file does not specify a source environment, the environment configuration file base name is used. + + .PARAMETER TargetEnvironment + The name of the target environment. If not specified, the target environment attribute in the environment configuration file is used. If the environment configuration file does not specify a target environment, the environment configuration file base name is used. + + .PARAMETER RepoRootPath + The path to the repository directory. Defaults to ../.. + + .PARAMETER Force + If specified, the script will overwrite existing configuration files. + + .PARAMETER UserRootPath + The path to the user directory. Defaults to $HOME. + + .PARAMETER UserLogsPath + The path to the user logs directory. Defaults to $UserRootPath/ALZ/logs. + + .PARAMETER UserCredsPath + The path to the user credentials directory. Defaults to $UserRootPath/ALZ/credentials. + + .PARAMETER UserConfigPath + The path to the user configuration directory. Defaults to $UserRootPath/ALZ/config. + + .EXAMPLE + PS> .\New-AlzConfiguration.ps1 -Environment 'CanadaALZ-main' + + .EXAMPLE + PS> .\New-AlzConfiguration.ps1 -Environment 'CanadaALZ-main' -Force +#> + +[CmdletBinding()] +Param( + [Parameter(Mandatory = $true)] + [string]$Environment, + + [string]$SourceEnvironment = $null, + + [string]$TargetEnvironment = $null, + + [string]$RepoRootPath = "../..", + + [switch]$Force = $false, + + [string]$UserRootPath = "$HOME", + + [string]$UserLogsPath = "$UserRootPath/ALZ/logs", + + [string]$UserCredsPath = "$UserRootPath/ALZ/credentials", + + [string]$UserConfigPath = "$UserRootPath/ALZ/config" +) + +#Requires -Modules powershell-yaml + +$ErrorActionPreference = "Stop" + +function ValidateParameters { + param ( + [Parameter(Mandatory = $true)] + [object]$Parameters, + [Parameter(Mandatory = $true)] + [string]$ParameterFile + ) + Write-Output "Checking configuration path ($RepoConfigPath)" + if (-not (Test-Path -PathType Container -Path $RepoConfigPath)) { + throw "Configuration path does not exist." + } + + # How we determine the source environment name: + # 1. Use the '$SourceEnvironment' parameter if specified + # 2. Otherwise, use the 'Environment.Source' attribute in the parameter file if specified + # 3. Otherwise, use the parameter file (base) name + if (-not ([string]::IsNullOrEmpty($SourceEnvironment))) { + $Parameters.Environment.Source = $SourceEnvironment + } elseif (-not ([string]::IsNullOrEmpty($Parameters.Environment.Source))) { + $Parameters.Environment.Source = $Parameters.Environment.Source + } else { + $Parameters.Environment.Source = $ParameterFile | Split-Path -LeafBase + } + + # How we determine the target environment name: + # 1. Use the '$TargetEnvironment' parameter if specified + # 2. Otherwise, use the 'Environment.Target' attribute in the parameter file if specified + # 3. Otherwise, use the parameter file (base) name + if (-not ([string]::IsNullOrEmpty($TargetEnvironment))) { + $Parameters.Environment.Target = $TargetEnvironment + } elseif (-not ([string]::IsNullOrEmpty($Parameters.Environment.Target))) { + $Parameters.Environment.Target = $Parameters.Environment.Target + } else { + $Parameters.Environment.Target = $ParameterFile | Split-Path -LeafBase + } + + if ($Parameters.Environment.Source -eq $Parameters.Environment.Target) { + throw "Source ($Parameters.Environment.Source) and target ($Parameters.Environment.Target) environments cannot be the same." + } + + if (-not (Test-Path -PathType Leaf -Path "$RepoConfigPath/variables/$($Parameters.Environment.Source).yml")) { + throw "Source environment does not exist ($($Parameters.Environment.Source))" + } else { + Write-Output " Source environment: $($Parameters.Environment.Source)" + } + + if ((Test-Path -PathType Leaf -Path "$RepoConfigPath/variables/$($Parameters.Environment.Target).yml") -and (-not $Force)) { + throw "Target environment already exists ($($Parameters.Environment.Target)). Use the '-Force' parameter to overwrite it." + } else { + Write-Output " Target environment: $($Parameters.Environment.Target)" + } +} + +function VariablesConfiguration { + param ( + [Parameter(Mandatory = $true)] + [object]$Parameters, + [ref]$ConfigVariablesByRef + ) + Write-Output "" + Write-Output "Generating Variables configurations" + Write-Output "" + + $file = "$RepoConfigPath/variables/$($Parameters.Environment.Source).yml" + if (Test-Path -PathType Leaf -Path $file) { + $ConfigVariablesByRef.value = Get-Content -Path $file -Raw | ConvertFrom-Yaml + } else { + throw "Source environment file not found ($file)" + } + + Write-Output " Updating variables configuration" + + # Deployment variables + $ConfigVariablesByRef.value.variables['deploymentRegion'] = $Parameters.DeployRegion ?? $ConfigVariablesByRef.value.variables['deploymentRegion'] + + # Management Group Hierarchy variables + $ConfigVariablesByRef.value.variables['var-managementgroup-hierarchy'] = ($Parameters.ManagementGroupHierarchy | ConvertTo-Json -Depth 100) ?? $ConfigVariablesByRef.value.variables['var-managementgroup-hierarchy'] + + # Logging variables + $ConfigVariablesByRef.value.variables['var-logging-region'] = $Parameters.DeployRegion ?? $ConfigVariablesByRef.value.variables['var-logging-region'] + $ConfigVariablesByRef.value.variables['var-logging-managementGroupId'] = $Parameters.Logging.ManagementGroupId ?? $ConfigVariablesByRef.value.variables['var-logging-managementGroupId'] + $ConfigVariablesByRef.value.variables['var-logging-subscriptionId'] = $Parameters.Logging.SubscriptionId ?? $ConfigVariablesByRef.value.variables['var-logging-subscriptionId'] + $ConfigVariablesByRef.value.variables['var-logging-diagnosticSettingsforNetworkSecurityGroupsStoragePrefix'] = $Parameters.ManagementGroupHierarchy.children[0].id + 'nsg' + + # Identity variables + $ConfigVariablesByRef.value.variables['var-identity-region'] = $Parameters.DeployRegion ?? $ConfigVariablesByRef.value.variables['var-identity-region'] + $ConfigVariablesByRef.value.variables['var-identity-managementGroupId'] = $Parameters.Identity.ManagementGroupId ?? $ConfigVariablesByRef.value.variables['var-identity-managementGroupId'] + $ConfigVariablesByRef.value.variables['var-identity-subscriptionId'] = $Parameters.Identity.SubscriptionId ?? $ConfigVariablesByRef.value.variables['var-identity-subscriptionId'] + + # Hub Network variables + $ConfigVariablesByRef.value.variables['var-hubnetwork-region'] = $Parameters.DeployRegion ?? $ConfigVariablesByRef.value.variables['var-hubnetwork-region'] + $ConfigVariablesByRef.value.variables['var-hubnetwork-managementGroupId'] = $Parameters.HubNetwork.ManagementGroupId ?? $ConfigVariablesByRef.value.variables['var-hubnetwork-managementGroupId'] + $ConfigVariablesByRef.value.variables['var-hubnetwork-subscriptionId'] = $Parameters.HubNetwork.SubscriptionId ?? $ConfigVariablesByRef.value.variables['var-hubnetwork-subscriptionId'] + + # Write the variables configuration file + $ConfigVariablesFile = "$RepoConfigPath/variables/$($Parameters.Environment.Target).yml" + Write-Output " Writing variables configuration file: $ConfigVariablesFile" + New-Item -ItemType Directory -Path (Split-Path -Parent -Path $ConfigVariablesFile) -Force | Out-Null + $ConfigVariablesYaml | ConvertTo-Yaml | Set-Content -Path $ConfigVariablesFile | Out-Null +} + +function LoggingConfiguration { + param ( + [Parameter(Mandatory = $true)] + [object]$Parameters, + [Parameter(Mandatory = $true)] + [object]$ConfigVariablesYaml + ) + Write-Output "" + Write-Output "Generating Logging configurations" + Write-Output "" + + $file = "$RepoConfigPath/logging/$($Parameters.Environment.Source)/$($ConfigVariablesYaml.variables['var-logging-configurationFileName'])" + if (Test-Path -PathType Leaf -Path $file) { + Write-Output " Reading source environment logging configuration file: $file" + $ConfigLoggingJson = Get-Content -Path $file -Raw | ConvertFrom-Json + + Write-Output " Updating logging configuration" + $ConfigLoggingJson.{$schema} = 'https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-platform-logging.json#' + $ConfigLoggingJson.parameters.securityCenter.value = $Parameters.Logging.SecurityCenter ?? $ConfigLoggingJson.parameters.securityCenter.value + $ConfigLoggingJson.parameters.serviceHealthAlerts.value = $Parameters.Logging.ServiceHealthAlerts ?? $ConfigLoggingJson.parameters.serviceHealthAlerts.value + $ConfigLoggingJson.parameters.subscriptionRoleAssignments.value = $Parameters.Logging.RoleAssignments ?? $ConfigLoggingJson.parameters.subscriptionRoleAssignments.value + $ConfigLoggingJson.parameters.subscriptionTags.value = $Parameters.values.Logging.SubscriptionTags ?? $ConfigLoggingJson.parameters.subscriptionTags.value + $ConfigLoggingJson.parameters.resourceTags.value = $Parameters.values.Logging.ResourceTags ?? $ConfigLoggingJson.parameters.resourceTags.value + $ConfigLoggingJson.parameters.dataCollectionRule.value.enabled = $Parameters.Logging.DataCollectionRule.Enabled ?? $ConfigLoggingJson.parameters.dataCollectionRule.value.enabled + + $ConfigLoggingFile = "$RepoConfigPath/logging/$($Parameters.Environment.Target)/$($ConfigVariablesYaml.variables['var-logging-configurationFileName'])" + Write-Output " Writing logging configuration file: $ConfigLoggingFile" + New-Item -ItemType Directory -Path (Split-Path -Parent -Path $ConfigLoggingFile) -Force | Out-Null + $ConfigLoggingJson | ConvertTo-Json -Depth 100 | Set-Content -Path $ConfigLoggingFile | Out-Null + } else { + Write-Output " Source environment logging configuration file not found: $file" + } +} + +function NetworkAzfwConfiguration { + param ( + [Parameter(Mandatory = $true)] + [object]$Parameters, + [Parameter(Mandatory = $true)] + [object]$ConfigVariablesYaml, + [ref]$ConfigNetworkAzfwByRef + ) + Write-Output "" + Write-Output "Generating Network Azure Firewall configurations" + Write-Output "" + + $file = "$RepoConfigPath/networking/$($Parameters.Environment.Source)/$($ConfigVariablesYaml.variables['var-hubnetwork-azfw-configurationFileName'])" + if (Test-Path -PathType Leaf -Path $file) { + Write-Output " Reading source environment network Azure Firewall configuration file: $file" + $ConfigNetworkAzfwByRef.value = Get-Content -Path $file -Raw | ConvertFrom-Json + + Write-Output " Updating network Azure Firewall configuration" + $ConfigNetworkAzfwByRef.value.{$schema} = 'https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-platform-connectivity-hub-azfw.json#' + $ConfigNetworkAzfwByRef.value.parameters.securityCenter.value = $Parameters.HubNetwork.SecurityCenter ?? $ConfigNetworkAzfwByRef.value.parameters.securityCenter.value + $ConfigNetworkAzfwByRef.value.parameters.serviceHealthAlerts.value = $Parameters.HubNetwork.ServiceHealthAlerts ?? $ConfigNetworkAzfwByRef.value.parameters.serviceHealthAlerts.value + $ConfigNetworkAzfwByRef.value.parameters.subscriptionRoleAssignments.value = $Parameters.HubNetwork.RoleAssignments ?? $ConfigNetworkAzfwByRef.value.parameters.subscriptionRoleAssignments.value + $ConfigNetworkAzfwByRef.value.parameters.subscriptionTags.value = $Parameters.values.HubNetwork.SubscriptionTags ?? $ConfigNetworkAzfwByRef.value.parameters.subscriptionTags.value + $ConfigNetworkAzfwByRef.value.parameters.resourceTags.value = $Parameters.values.HubNetwork.ResourceTags ?? $ConfigNetworkAzfwByRef.value.parameters.resourceTags.value + $ConfigNetworkAzfwByRef.value.parameters.privateDnsZones.value = $Parameters.HubNetwork.PrivateDNS ?? $ConfigNetworkAzfwByRef.value.parameters.privateDnsZones.value + $ConfigNetworkAzfwByRef.value.parameters.ddosStandard.value = $Parameters.HubNetwork.DDoS ?? $ConfigNetworkAzfwByRef.value.parameters.ddosStandard.value + + $ConfigNetworkAzfwFile = "$RepoConfigPath/networking/$($Parameters.Environment.Target)/$($ConfigVariablesYaml.variables['var-hubnetwork-azfw-configurationFileName'])" + Write-Output " Writing network Azure Firewall configuration file: $ConfigNetworkAzfwFile" + New-Item -ItemType Directory -Path (Split-Path -Parent -Path $ConfigNetworkAzfwFile) -Force | Out-Null + $ConfigNetworkAzfwByRef.value | ConvertTo-Json -Depth 100 | Set-Content -Path $ConfigNetworkAzfwFile | Out-Null + } else { + Write-Output " Source environment network Azure Firewall configuration file not found: $file" + } +} + +function NetworkAzfwPolicyConfiguration { + param ( + [Parameter(Mandatory = $true)] + [object]$Parameters, + [Parameter(Mandatory = $true)] + [object]$ConfigVariablesYaml + ) + Write-Output "" + Write-Output "Generating Network Azure Firewall Policy configurations" + Write-Output "" + + $file = "$RepoConfigPath/networking/$($Parameters.Environment.Source)/$($ConfigVariablesYaml.variables['var-hubnetwork-azfwPolicy-configurationFileName'])" + if (Test-Path -PathType Leaf -Path $file) { + Write-Output " Reading source environment network Azure Firewall Policy configuration file: $file" + $ConfigNetworkAzfwPolicyJson = Get-Content -Path $file -Raw | ConvertFrom-Json + + Write-Output " Updating network Azure Firewall Policy configuration" + $ConfigNetworkAzfwPolicyJson.{$schema} = 'https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-platform-connectivity-hub-azfw-policy.json#' + $ConfigNetworkAzfwPolicyJson.parameters.resourceTags.value = $Parameters.values.HubNetwork.ResourceTags ?? $ConfigNetworkAzfwPolicyJson.parameters.resourceTags.value + + $ConfigNetworkAzfwPolicyFile = "$RepoConfigPath/networking/$($Parameters.Environment.Target)/$($ConfigVariablesYaml.variables['var-hubnetwork-azfwPolicy-configurationFileName'])" + Write-Output " Writing network Azure Firewall Policy configuration file: $ConfigNetworkAzfwPolicyFile" + New-Item -ItemType Directory -Path (Split-Path -Parent -Path $ConfigNetworkAzfwPolicyFile) -Force | Out-Null + $ConfigNetworkAzfwPolicyJson | ConvertTo-Json -Depth 100 | Set-Content -Path $ConfigNetworkAzfwPolicyFile | Out-Null + } else { + Write-Output " Source environment network Azure Firewall Policy configuration file not found: $file" + } +} + +function NetworkNvaConfiguration { + param ( + [Parameter(Mandatory = $true)] + [object]$Parameters, + [Parameter(Mandatory = $true)] + [object]$ConfigVariablesYaml + ) + Write-Output "" + Write-Output "Generating Network NVA configurations" + Write-Output "" + + $file = "$RepoConfigPath/networking/$($Parameters.Environment.Source)/$($ConfigVariablesYaml.variables['var-hubnetwork-nva-configurationFileName'])" + if (Test-Path -PathType Leaf -Path $file) { + Write-Output " Reading source environment network NVA configuration file: $file" + $ConfigNetworkNvaJson = Get-Content -Path $file -Raw | ConvertFrom-Json + + Write-Output " Updating network NVA configuration" + $ConfigNetworkNvaJson.{$schema} = 'https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-platform-connectivity-hub-nva.json#' + $ConfigNetworkNvaJson.parameters.securityCenter.value = $Parameters.HubNetwork.SecurityCenter ?? $ConfigNetworkNvaJson.parameters.securityCenter.value + $ConfigNetworkNvaJson.parameters.serviceHealthAlerts.value = $Parameters.HubNetwork.ServiceHealthAlerts ?? $ConfigNetworkNvaJson.parameters.serviceHealthAlerts.value + $ConfigNetworkNvaJson.parameters.subscriptionRoleAssignments.value = $Parameters.HubNetwork.RoleAssignments ?? $ConfigNetworkNvaJson.parameters.subscriptionRoleAssignments.value + $ConfigNetworkNvaJson.parameters.subscriptionTags.value = $Parameters.values.HubNetwork.SubscriptionTags ?? $ConfigNetworkNvaJson.parameters.subscriptionTags.value + $ConfigNetworkNvaJson.parameters.resourceTags.value = $Parameters.values.HubNetwork.ResourceTags ?? $ConfigNetworkNvaJson.parameters.resourceTags.value + $ConfigNetworkNvaJson.parameters.privateDnsZones.value = $Parameters.HubNetwork.PrivateDNS ?? $ConfigNetworkNvaJson.parameters.privateDnsZones.value + $ConfigNetworkNvaJson.parameters.ddosStandard.value = $Parameters.HubNetwork.DDoS ?? $ConfigNetworkNvaJson.parameters.ddosStandard.value + + $ConfigNetworkNvaFile = "$RepoConfigPath/networking/$($Parameters.Environment.Target)/$($ConfigVariablesYaml.variables['var-hubnetwork-nva-configurationFileName'])" + Write-Output " Writing network NVA configuration file: $ConfigNetworkNvaFile" + New-Item -ItemType Directory -Path (Split-Path -Parent -Path $ConfigNetworkNvaFile) -Force | Out-Null + $ConfigNetworkNvaJson | ConvertTo-Json -Depth 100 | Set-Content -Path $ConfigNetworkNvaFile | Out-Null + } else { + Write-Output " Source environment network NVA configuration file not found: $file" + } +} + +function IdentityConfiguration { + param ( + [Parameter(Mandatory = $true)] + [object]$Parameters, + [Parameter(Mandatory = $true)] + [object]$ConfigVariablesYaml + ) + Write-Output "" + Write-Output "Generating Identity configurations" + Write-Output "" + + $file = "$RepoConfigPath/identity/$($Parameters.Environment.Source)/$($ConfigVariablesYaml.variables['var-identity-configurationFileName'])" + if (Test-Path -PathType Leaf -Path $file) { + Write-Output " Reading source environment identity configuration file: $file" + $ConfigIdentityJson = Get-Content -Path $file -Raw | ConvertFrom-Json + + Write-Output " Updating identity configuration" + $ConfigIdentityJson.{$schema} = 'https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-platform-identity.json#' + $ConfigIdentityJson.parameters.securityCenter.value = $Parameters.Identity.SecurityCenter ?? $ConfigIdentityJson.parameters.securityCenter.value + $ConfigIdentityJson.parameters.serviceHealthAlerts.value = $Parameters.Identity.ServiceHealthAlerts ?? $ConfigIdentityJson.parameters.serviceHealthAlerts.value + $ConfigIdentityJson.parameters.subscriptionRoleAssignments.value = $Parameters.Identity.RoleAssignments ?? $ConfigIdentityJson.parameters.subscriptionRoleAssignments.value + $ConfigIdentityJson.parameters.subscriptionTags.value = $Parameters.values.Identity.SubscriptionTags ?? $ConfigIdentityJson.parameters.subscriptionTags.value + $ConfigIdentityJson.parameters.resourceTags.value = $Parameters.values.Identity.ResourceTags ?? $ConfigIdentityJson.parameters.resourceTags.value + $ConfigIdentityJson.parameters.hubNetwork.value.virtualNetworkId = "/subscriptions/$($ConfigVariablesYaml.variables['var-hubnetwork-subscriptionId'])/resourceGroups/$($ConfigNetworkAzfwJson.parameters.hub.value.resourceGroupName)/providers/Microsoft.Network/virtualNetworks/$($ConfigNetworkAzfwJson.parameters.hub.value.network.name)" + + $ConfigIdentityFile = "$RepoConfigPath/identity/$($Parameters.Environment.Target)/$($ConfigVariablesYaml.variables['var-identity-configurationFileName'])" + Write-Output " Writing identity configuration file: $ConfigIdentityFile" + New-Item -ItemType Directory -Path (Split-Path -Parent -Path $ConfigIdentityFile) -Force | Out-Null + $ConfigIdentityJson | ConvertTo-Json -Depth 100 | Set-Content -Path $ConfigIdentityFile | Out-Null + } else { + Write-Output " Source environment identity configuration file not found: $file" + } +} + +function SubscriptionConfiguration { + param ( + [Parameter(Mandatory = $true)] + [object]$Parameters, + [object]$ConfigNetworkAzfwJson + ) + Write-Output "" + Write-Output "Generating subscription configurations" + + foreach ($subscription in $Parameters.Subscriptions) { + $pattern = $subscription.keys[0] + + Write-Output "" + Write-Output " Looking for source environment subscription configuration file(s) matching specified pattern ($pattern)" + $templates = @(Get-ChildItem -Path "$RepoConfigPath/subscriptions/$($Parameters.Environment.Source)/*" -File -Recurse | ? { $_.Name -match $pattern }) + if ($templates.Count -gt 0) { + if ($templates.Count -gt 1) { + Write-Output " More than 1 source environment subscription configuration file(s) matching specified pattern found ($pattern); using the first one found" + } + $ConfigSubscriptionFile = $templates[0] + Write-Output " Reading subscription configuration ($($ConfigSubscriptionFile.Name))" + $ConfigSubscriptionJson = Get-Content -Path $ConfigSubscriptionFile.FullName -Raw | ConvertFrom-Json + } else { + Write-Output " Source environment subscription configuration file(s) matching specified pattern not found ($pattern)" + continue + } + + Write-Output " Updating subscription configuration" + $ConfigSubscriptionArchetype = $ConfigSubscriptionFile.Name.Split('_')[1] + $ConfigSubscriptionJson.{$schema} = "https://raw.githubusercontent.com/Azure/CanadaPubSecALZ/main/schemas/latest/landingzones/lz-$($ConfigSubscriptionArchetype).json#" + # Not all subscription configuration files have a location parameter + if ($ConfigSubscriptionJson.parameters.location -ne $null) { + $ConfigSubscriptionJson.parameters.location.value = $subscription.values.Location ?? $ConfigSubscriptionJson.parameters.location.value + } + # Not all subscription configuration files have a privateDnsManagedByHub parameter + if ($ConfigSubscriptionJson.parameters.hubNetwork.value.privateDnsManagedByHub -ne $null) { + $ConfigSubscriptionJson.parameters.hubNetwork.value.privateDnsManagedByHub = $Parameters.HubNetwork.PrivateDNS.Enabled ?? $ConfigSubscriptionJson.parameters.hubNetwork.value.privateDnsManagedByHub + } + # Not all subscription configuration files have a privateDnsManagedByHubSubscriptionId parameter + if ($ConfigSubscriptionJson.parameters.hubNetwork.value.privateDnsManagedByHubSubscriptionId -ne $null) { + $ConfigSubscriptionJson.parameters.hubNetwork.value.privateDnsManagedByHubSubscriptionId = $Parameters.HubNetwork.SubscriptionId ?? $ConfigSubscriptionJson.parameters.hubNetwork.value.privateDnsManagedByHubSubscriptionId + } + # Not all subscription configuration files have a privateDnsManagedByHub parameter + if ($ConfigSubscriptionJson.parameters.hubNetwork.value.privateDnsManagedByHubResourceGroupName -ne $null) { + $ConfigSubscriptionJson.parameters.hubNetwork.value.privateDnsManagedByHubResourceGroupName = $Parameters.HubNetwork.PrivateDNS.ResourceGroupName ?? $ConfigSubscriptionJson.parameters.hubNetwork.value.privateDnsManagedByHubResourceGroupName + } + # All subscription configuration files have the following parameters + $ConfigSubscriptionJson.parameters.securityCenter.value = $subscription.values.SecurityCenter ?? $ConfigSubscriptionJson.parameters.securityCenter.value + $ConfigSubscriptionJson.parameters.serviceHealthAlerts.value = $subscription.values.ServiceHealthAlerts ?? $ConfigSubscriptionJson.parameters.serviceHealthAlerts.value + $ConfigSubscriptionJson.parameters.subscriptionRoleAssignments.value = $subscription.values.RoleAssignments ?? $ConfigSubscriptionJson.parameters.subscriptionRoleAssignments.value + $ConfigSubscriptionJson.parameters.subscriptionTags.value = $subscription.values.SubscriptionTags ?? $ConfigSubscriptionJson.parameters.subscriptionTags.value + $ConfigSubscriptionJson.parameters.resourceTags.value = $subscription.values.ResourceTags ?? $ConfigSubscriptionJson.parameters.resourceTags.value + $ConfigSubscriptionJson.parameters.hubNetwork.value.virtualNetworkId = "/subscriptions/$($ConfigVariablesYaml.variables['var-hubnetwork-subscriptionId'])/resourceGroups/$($ConfigNetworkAzfwJson.parameters.hub.value.resourceGroupName)/providers/Microsoft.Network/virtualNetworks/$($ConfigNetworkAzfwJson.parameters.hub.value.network.name)" + + $NewConfigSubscriptionFile = "$RepoConfigPath/subscriptions/$($Parameters.Environment.Target)/$($subscription.values.ManagementGroupId)/$($subscription.values.SubscriptionId)_$($ConfigSubscriptionArchetype)_$($subscription.values.Location).json" + Write-Output " Writing new subscription configuration ($($NewConfigSubscriptionFile))" + New-Item -ItemType Directory -Path (Split-Path -Parent -Path $NewConfigSubscriptionFile) -Force | Out-Null + $ConfigSubscriptionJson | ConvertTo-Json -Depth 100 | Set-Content -Path $NewConfigSubscriptionFile | Out-Null + } + + Write-Output "" +} + +# Set script variables +$RepoConfigPath = (Resolve-Path -Path "$RepoRootPath/config").Path +$ParameterFile = (Resolve-Path -Path "$UserConfigPath/$Environment.yml").Path + +# Ensure paths exist and are normalized to the OS path format +New-Item -ItemType Directory -Path $UserCredsPath -Force | Out-Null +$UserCredsPath = (Resolve-Path -Path $UserCredsPath).Path +New-Item -ItemType Directory -Path $UserLogsPath -Force | Out-Null +$UserLogsPath = (Resolve-Path -Path $UserLogsPath).Path +New-Item -ItemType Directory -Path $UserConfigPath -Force | Out-Null +$UserConfigPath = (Resolve-Path -Path $UserConfigPath).Path + +# Local variables +$date = Get-Date -Format "yyMMdd-HHmmss-fff" +$script = $(Split-Path -Path $PSCommandPath -LeafBase) +$logFile = "$UserLogsPath/$date-$script-$Environment.log" +$stopWatch = [System.Diagnostics.Stopwatch]::New() + +try { + $stopWatch.Restart() + + Write-Output "" | Tee-Object -FilePath $logFile -Append + Write-Output "This script creates a new set of configuration files, using an existing CanadaPubSecALZ configuration. Select configuration elements are replaced with values specific to the target environment." | Tee-Object -FilePath $logFile -Append + Write-Output "" | Tee-Object -FilePath $logFile -Append + + Write-Output "Reading parameters from file ($ParameterFile)" + if (-not (Test-Path $ParameterFile)) { + throw "Parameter file '$ParameterFile' does not exist." + } + $Parameters = Get-Content $ParameterFile -Raw | ConvertFrom-Yaml + + ValidateParameters -Parameters $Parameters -ParameterFile $ParameterFile ` + | Tee-Object -FilePath $logFile -Append + + $ConfigVariablesYaml = @{} + VariablesConfiguration -Parameters $Parameters -ConfigVariablesByRef ([ref]$ConfigVariablesYaml) ` + | Tee-Object -FilePath $logFile -Append + + LoggingConfiguration -Parameters $Parameters -ConfigVariablesYaml $ConfigVariablesYaml ` + | Tee-Object -FilePath $logFile -Append + + $ConfigNetworkAzfwJson = @{} + NetworkAzfwConfiguration -Parameters $Parameters -ConfigVariablesYaml $ConfigVariablesYaml -ConfigNetworkAzfwByRef ([ref]$ConfigNetworkAzfwJson) ` + | Tee-Object -FilePath $logFile -Append + + NetworkAzfwPolicyConfiguration -Parameters $Parameters -ConfigVariablesYaml $ConfigVariablesYaml ` + | Tee-Object -FilePath $logFile -Append + + NetworkNvaConfiguration -Parameters $Parameters -ConfigVariablesYaml $ConfigVariablesYaml ` + | Tee-Object -FilePath $logFile -Append + + IdentityConfiguration -Parameters $Parameters -ConfigVariablesYaml $ConfigVariablesYaml ` + | Tee-Object -FilePath $logFile -Append + + SubscriptionConfiguration -Parameters $Parameters -ConfigNetworkAzfwJson $ConfigNetworkAzfwJson ` + | Tee-Object -FilePath $logFile -Append + +} catch { + Write-Output $_ | Tee-Object -FilePath $logFile -Append + Write-Output $_.Exception | Tee-Object -FilePath $logFile -Append + throw +} finally { + Write-Output "Elapsed time: $($stopWatch.Elapsed)" ` + | Tee-Object -FilePath $logFile -Append +} diff --git a/scripts/configuration/New-AlzCredential.ps1 b/scripts/configuration/New-AlzCredential.ps1 new file mode 100644 index 00000000..3d9cec56 --- /dev/null +++ b/scripts/configuration/New-AlzCredential.ps1 @@ -0,0 +1,132 @@ +<# +---------------------------------------------------------------------------------- +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. + +THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +---------------------------------------------------------------------------------- +#> + +<# + .SYNOPSIS + Creates a Service Principal for the specified environment. + + .DESCRIPTION + Creates a Service Principal for the specified environment. + + .PARAMETER Environment + The name of the environment. This is typically the repo/org name. + + .PARAMETER UserRootPath + The root path for the log, credential, and configuration files. Defaults to $HOME. + + .PARAMETER UserLogsPath + The path for the log files. Defaults to $UserRootPath/ALZ/logs. + + .PARAMETER UserCredsPath + The path for the credential files. Defaults to $UserRootPath/ALZ/credentials. + + .PARAMETER UserConfigPath + The path for the configuration files. Defaults to $UserRootPath/ALZ/config. + + .EXAMPLE + PS> .\New-AlzCredential.ps1 -Environment 'CanadaALZ' + + .EXAMPLE + PS> .\New-AlzCredential.ps1 -Environment 'CanadaALZ' -UserRootPath 'C:\Users\me\ALZ' + + .EXAMPLE + PS> .\New-AlzCredential.ps1 -Environment 'CanadaALZ' -UserLogsPath 'C:\Users\me\ALZ\logs' + + .EXAMPLE + PS> .\New-AlzCredential.ps1 -Environment 'CanadaALZ' -UserCredsPath 'C:\Users\me\ALZ\credentials' + + .EXAMPLE + PS> .\New-AlzCredential.ps1 -Environment 'CanadaALZ' -UserConfigPath 'C:\Users\me\ALZ\config' +#> + +[CmdletBinding()] +Param( + [Parameter(Mandatory = $true)] + [string]$Environment, + + [string]$UserRootPath = "$HOME", + + [string]$UserLogsPath = "$UserRootPath/ALZ/logs", + + [string]$UserCredsPath = "$UserRootPath/ALZ/credentials", + + [string]$UserConfigPath = "$UserRootPath/ALZ/config" +) + +#Requires -Modules Az + +$ErrorActionPreference = "Stop" + +function CreateServicePrincipal { + param( + [string]$Environment = $Environment, + [string]$UserCredsPath = $UserCredsPath + ) + <# Create JSON representation of the service principal using the Azure CLI + if ((az account show) -eq $null) { + throw "You must be logged in to Azure via the Azure CLI to create a service principal." + } + $json = (az ad sp create-for-rbac --name $Environment --role "Owner" --scopes "/") + #> + + # Create JSON representation of the service principal using Azure PowerShell + $context = Get-AzContext + if ($context -eq $null) { + throw "You must be logged in to Azure via Azure PowerShell to create a service principal." + } + + $tenant = Get-AzTenant -TenantId $context.Tenant.Id + Write-Output "Creating Service Principal for environment ($Environment) in tenant ($($tenant.DefaultDomain)))" + $sp = New-AzADServicePrincipal -DisplayName $Environment -Role Owner -Scope "/" + Write-Output " appId: $($sp.AppId)" + Write-Output " displayName: $($sp.DisplayName)" + Write-Output " password: **********" + Write-Output " tenant: $($context.Tenant.Id)" + $json = @{ + appId = $sp.AppId + displayName = $sp.DisplayName + password = $sp.PasswordCredentials.SecretText + tenant = $context.Tenant.Id + } | ConvertTo-Json + + # Save the service principal to a file + $credentialFile = "$UserCredsPath/$Environment.json" + Write-Output "Saving Service Principal to file ($credentialFile)" + Set-Content -Value $json -Path $credentialFile +} + +# Ensure paths exist and are normalized to the OS path format +New-Item -ItemType Directory -Path $UserCredsPath -Force | Out-Null +$UserCredsPath = (Resolve-Path -Path $UserCredsPath).Path +New-Item -ItemType Directory -Path $UserLogsPath -Force | Out-Null +$UserLogsPath = (Resolve-Path -Path $UserLogsPath).Path +New-Item -ItemType Directory -Path $UserConfigPath -Force | Out-Null +$UserConfigPath = (Resolve-Path -Path $UserConfigPath).Path + +# Local variables +$date = Get-Date -Format "yyMMdd-HHmmss-fff" +$script = $(Split-Path -Path $PSCommandPath -LeafBase) +$logFile = "$UserLogsPath/$date-$script-$Environment.log" +$stopWatch = [System.Diagnostics.Stopwatch]::New() + +# Create the service principal +try { + $stopWatch.Restart() + CreateServicePrincipal -Environment $Environment -CredsPath $UserCredsPath ` + | Tee-Object -FilePath $logFile -Append +} catch { + Write-Output $_ | Tee-Object -FilePath $logFile -Append + Write-Output $_.Exception | Tee-Object -FilePath $logFile -Append + throw +} finally { + Write-Output "Elapsed time: $($stopWatch.Elapsed)" ` + | Tee-Object -FilePath $logFile -Append +} diff --git a/scripts/configuration/New-AlzDeployment.ps1 b/scripts/configuration/New-AlzDeployment.ps1 new file mode 100644 index 00000000..6d48ea88 --- /dev/null +++ b/scripts/configuration/New-AlzDeployment.ps1 @@ -0,0 +1,217 @@ +<# +---------------------------------------------------------------------------------- +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. + +THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +---------------------------------------------------------------------------------- +#> + +<# + .SYNOPSIS + This script creates a CanadaPubSecALZ deployment, based on information present in the configuration files. + + .DESCRIPTION + This script creates a CanadaPubSecALZ deployment, based on information present in the configuration files. + + .PARAMETER Environment + The name of the environment to deploy. + + .PARAMETER NetworkType + The type of network to deploy. Valid values are "AzFW" and "NVA". Default is "AzFW". + + .PARAMETER CredentialFile + The path to the credential file to use for login. + + .PARAMETER SecureServicePrincipal + The service principal to use for login. + + .PARAMETER TenantId + The tenant ID to use for interactive login. + + .PARAMETER RepoRootPath + The path to the repository directory. + + .PARAMETER UserRootPath + The path to the user directory. + + .PARAMETER UserLogsPath + The path to the user logs directory. + + .PARAMETER UserCredsPath + The path to the user credentials directory. + + .PARAMETER UserConfigPath + The path to the user configuration directory. + + .EXAMPLE + PS> .\New-AlzDeployment.ps1 -Environment 'CanadaALZ-main' -CredentialFile 'CanadaALZ' -NetworkType 'AzFW' + + Deploy the CanadaALZ-main environment with Azure Firewall hub network using a credential file. + + .EXAMPLE + PS> .\New-AlzDeployment.ps1 -Environment 'CanadaALZ-main' -SecureServicePrincipal $SecureSP -NetworkType 'NVA' + + Deploy the CanadaALZ-main environment with NVA hub network using a service principal. +#> + +[CmdletBinding()] +Param( + [Parameter(Mandatory = $true)] + [string]$Environment, + + [Parameter(Mandatory = $true)] + [ValidateSet("AzFW", "NVA")] + [string]$NetworkType, + + [Parameter(Mandatory = $true, ParameterSetName = "CredentialFile")] + [string]$CredentialFile, + + [Parameter(Mandatory = $true, ParameterSetName = "ServicePrincipal")] + [SecureString]$SecureServicePrincipal, + + [Parameter(Mandatory = $true, ParameterSetName = "Interactive")] + [string]$TenantId, + + [string]$RepoRootPath = "../..", + + [string]$UserRootPath = "$HOME", + + [string]$UserLogsPath = "$UserRootPath/ALZ/logs", + + [string]$UserCredsPath = "$UserRootPath/ALZ/credentials", + + [string]$UserConfigPath = "$UserRootPath/ALZ/config" +) + +#Requires -Modules Az, powershell-yaml, PSPasswordGenerator + +$ErrorActionPreference = "Stop" + +#region Functions + +function CreateDeployment { + param( + [string]$Environment, + [string]$RepoRootPath, + [string]$NetworkType, + [string[]]$SubscriptionIds + ) + try { + Push-Location -Path "$RepoRootPath/scripts/deployments" + if ($NetworkType -ieq "AzFW") { + Write-Output "Deploying environment ($Environment) with Azure Firewall" + .\RunWorkflows.ps1 ` + -EnvironmentName $Environment ` + -DeployManagementGroups ` + -DeployRoles ` + -DeployLogging ` + -DeployCustomPolicyDefinitions ` + -DeployCustomPolicySetDefinitions ` + -DeployCustomPolicySetAssignments ` + -DeployBuiltinPolicySetAssignments ` + -DeployAzureFirewallPolicy ` + -DeployHubNetworkWithAzureFirewall ` + -DeployIdentity ` + -DeploySubscriptionIds $SubscriptionIds + } elseif ($NetworkType -ieq "NVA") { + Write-Output "Generating temporary NVA credentials" + $nvaUsername = ConvertTo-SecureString -String ($env:USER ?? $env:USERNAME) -AsPlainText + $nvaPassword = Get-RandomPassword -Length 16 -StartWithLetter + + Write-Output "Deploying environment ($Environment) with NVA firewall" + Write-Output "NVA credentials (save these in a secure location" + Write-Output " Username: $(ConvertFrom-SecureString -SecureString $nvaUsername -AsPlainText)" + Write-Output " Password: $(ConvertFrom-SecureString -SecureString $nvaPassword -AsPlainText)" + + .\RunWorkflows.ps1 ` + -EnvironmentName $Environment ` + -DeployManagementGroups ` + -DeployRoles ` + -DeployLogging ` + -DeployCustomPolicyDefinitions ` + -DeployCustomPolicySetDefinitions ` + -DeployCustomPolicySetAssignments ` + -DeployBuiltinPolicySetAssignments ` + -DeployHubNetworkWithNVA ` + -NvaUserName $nvaUsername ` + -NvaPassword $nvaPassword ` + -DeployIdentity ` + -DeploySubscriptionIds $SubscriptionIds + + } else { + throw "Invalid network type ($NetworkType)" + } + } catch { + throw + } finally { + Pop-Location + } +} + +#endregion Functions + +# Ensure paths exist and are normalized to the OS path format +New-Item -ItemType Directory -Path $UserCredsPath -Force | Out-Null +$UserCredsPath = (Resolve-Path -Path $UserCredsPath).Path +New-Item -ItemType Directory -Path $UserLogsPath -Force | Out-Null +$UserLogsPath = (Resolve-Path -Path $UserLogsPath).Path +New-Item -ItemType Directory -Path $UserConfigPath -Force | Out-Null +$UserConfigPath = (Resolve-Path -Path $UserConfigPath).Path + +# Local variables +$date = Get-Date -Format "yyMMdd-HHmmss-fff" +$script = $(Split-Path -Path $PSCommandPath -LeafBase) +$logFile = "$UserLogsPath/$date-$script-$Environment.log" +$stopWatch = [System.Diagnostics.Stopwatch]::New() + +try { + $stopWatch.Restart() + + Write-Output "" | Tee-Object -FilePath $logFile -Append + Write-Output "This script creates a new deployment, using an existing CanadaPubSecALZ configuration ($Environment)." | Tee-Object -FilePath $logFile -Append + Write-Output "" | Tee-Object -FilePath $logFile -Append + + $ConfigVariablesYaml = @{} + .\Get-AlzConfiguration.ps1 -Environment $Environment -RepoRootPath $RepoRootPath -ConfigVariablesByRef ([ref]$ConfigVariablesYaml) ` + | Tee-Object -FilePath $logFile -Append + + $mgh = ($ConfigVariablesYaml.variables['var-managementgroup-hierarchy'] | ConvertFrom-Json) + + switch ($PSCmdlet.ParameterSetName) { + "CredentialFile" { + .\Connect-AlzCredential.ps1 -CredentialFile "$UserCredsPath/$CredentialFile.json" ` + | Tee-Object -FilePath $logFile -Append + } + "ServicePrincipal" { + .\Connect-AlzCredential.ps1 -SecureServicePrincipal $SecureServicePrincipal ` + | Tee-Object -FilePath $logFile -Append + } + "Interactive" { + .\Connect-AlzCredential.ps1 -TenantId $mgh.id ` + | Tee-Object -FilePath $logFile -Append + } + } + + $context = Get-AzContext + if ($context.Tenant.Id -ne $mgh.id) { + throw "You are not logged in to the correct tenant. You are logged in to $($context.Tenant.Id), but you should be logged in to $($mgh.id)." + } + + $SubscriptionIds = @() + .\Get-AlzSubscriptions.ps1 -Environment $Environment -RepoRootPath $RepoRootPath -SubscriptionIdsByRef ([ref]$SubscriptionIds) ` + | Tee-Object -FilePath $logFile -Append + + CreateDeployment -Environment $Environment -RepoRootPath $RepoRootPath -NetworkType $NetworkType -SubscriptionIds $SubscriptionIds ` + | Tee-Object -FilePath $logFile -Append + +} catch { + Write-Output $_ | Tee-Object -FilePath $logFile -Append + Write-Output $_.Exception | Tee-Object -FilePath $logFile -Append + throw +} finally { + Write-Output "Elapsed time: $($stopWatch.Elapsed)" ` + | Tee-Object -FilePath $logFile -Append +} diff --git a/scripts/configuration/Remove-AlzConfiguration.ps1 b/scripts/configuration/Remove-AlzConfiguration.ps1 new file mode 100644 index 00000000..5fceb8d7 --- /dev/null +++ b/scripts/configuration/Remove-AlzConfiguration.ps1 @@ -0,0 +1,150 @@ +<# +---------------------------------------------------------------------------------- +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. + +THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +---------------------------------------------------------------------------------- +#> + +<# + .SYNOPSIS + Removes an existing environment configuration for a CanadaPubSecALZ deployment. + + .DESCRIPTION + This script removes an existing set of environment configuration files. + + .PARAMETER Environment + The name of the environment to remove. + + .PARAMETER RepoRootPath + The path to the repository directory. Defaults to ..\.. + + .PARAMETER UserRootPath + The path to the user directory. Defaults to $HOME. + + .PARAMETER UserLogsPath + The path to the user logs directory. Defaults to $UserRootPath/ALZ/logs. + + .PARAMETER UserCredsPath + The path to the user credentials directory. Defaults to $UserRootPath/ALZ/credentials. + + .PARAMETER UserConfigPath + The path to the user configuration directory. Defaults to $UserRootPath/ALZ/config. + + .EXAMPLE + PS> .\Remove-AlzConfiguration.ps1 -Environment 'DevOpsOrg-branch' +#> + +[CmdletBinding()] +Param( + [Parameter(Mandatory = $true)] + [string]$Environment, + + [string]$RepoRootPath = "../..", + + [string]$UserRootPath = "$HOME", + + [string]$UserLogsPath = "$UserRootPath/ALZ/logs", + + [string]$UserCredsPath = "$UserRootPath/ALZ/credentials", + + [string]$UserConfigPath = "$UserRootPath/ALZ/config" +) + +$ErrorActionPreference = "Stop" + +function RemovePaths { + param( + [string]$Environment, + [string]$RepoRootPath + ) + # Validate Parameters + $RepoConfigPath = (Resolve-Path -Path "$RepoRootPath/config").Path + Write-Output "Checking configuration path ($RepoConfigPath)" + if (-not (Test-Path -PathType Container -Path $RepoConfigPath)) { + throw "Configuration path does not exist." + } + + # Remove variables configuration file + $path = "$RepoConfigPath/variables/$Environment.yml" + if (Test-Path -PathType Leaf -Path $path) { + Write-Output "Removing variables configuration file: $path" + Remove-Item -Path $path + } else { + Write-Output "Variables configuration file not found ($path)" + } + + # Remove logging configuration file(s) + $path = "$RepoConfigPath/logging/$Environment" + if (Test-Path -PathType Container -Path $path) { + Write-Output "Removing logging configuration directory: $path" + Remove-Item -Path $path -Recurse + } else { + Write-Output "Logging configuration directory not found ($path)" + } + + # Remove identity configuration file(s) + $path = "$RepoConfigPath/identity/$Environment" + if (Test-Path -PathType Container -Path $path) { + Write-Output "Removing identity configuration directory: $path" + Remove-Item -Path $path -Recurse + } else { + Write-Output "Identity configuration directory not found ($path)" + } + + # Remove network configuration file(s) + $path = "$RepoConfigPath/networking/$Environment" + if (Test-Path -PathType Container -Path $path) { + Write-Output "Removing network configuration directory: $path" + Remove-Item -Path $path -Recurse + } else { + Write-Output "Network configuration directory not found ($path)" + } + + # Remove subscription configuration file(s) + $path = "$RepoConfigPath/subscriptions/$Environment" + if (Test-Path -PathType Container -Path $path) { + Write-Output "Removing subscription configuration directory: $path" + Remove-Item -Path $path -Recurse + } else { + Write-Output "Subscription configuration directory not found ($path)" + } + + Write-Output "" +} + +# Ensure paths exist and are normalized to the OS path format +New-Item -ItemType Directory -Path $UserCredsPath -Force | Out-Null +$UserCredsPath = (Resolve-Path -Path $UserCredsPath).Path +New-Item -ItemType Directory -Path $UserLogsPath -Force | Out-Null +$UserLogsPath = (Resolve-Path -Path $UserLogsPath).Path +New-Item -ItemType Directory -Path $UserConfigPath -Force | Out-Null +$UserConfigPath = (Resolve-Path -Path $UserConfigPath).Path + +# Local variables +$date = Get-Date -Format "yyMMdd-HHmmss-fff" +$script = $(Split-Path -Path $PSCommandPath -LeafBase) +$logFile = "$UserLogsPath/$date-$script-$Environment.log" +$stopWatch = [System.Diagnostics.Stopwatch]::New() + +try { + $stopWatch.Restart() + + Write-Output "" | Tee-Object -FilePath $logFile -Append + Write-Output "This script removes an existing set of configuration files." | Tee-Object -FilePath $logFile -Append + Write-Output "" | Tee-Object -FilePath $logFile -Append + + RemovePaths -Environment $Environment -RepoRootPath $RepoRootPath ` + | Tee-Object -FilePath $logFile -Append + +} catch { + Write-Output $_ | Tee-Object -FilePath $logFile -Append + Write-Output $_.Exception | Tee-Object -FilePath $logFile -Append + throw +} finally { + Write-Output "Elapsed time: $($stopWatch.Elapsed)" ` + | Tee-Object -FilePath $logFile -Append +} diff --git a/scripts/configuration/Remove-AlzCredential.ps1 b/scripts/configuration/Remove-AlzCredential.ps1 new file mode 100644 index 00000000..387bcf9c --- /dev/null +++ b/scripts/configuration/Remove-AlzCredential.ps1 @@ -0,0 +1,123 @@ +<# +---------------------------------------------------------------------------------- +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. + +THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +---------------------------------------------------------------------------------- +#> + +<# + .SYNOPSIS + Removes a Service Principal for the specified environment. + + .DESCRIPTION + Removes a Service Principal for the specified environment. + + .PARAMETER Environment + The name of the environment. This is typically the repo/org name. + + .PARAMETER RootPath + The root path for the log, credential, and configuration files. Defaults to $HOME. + + .PARAMETER LogsPath + The path for the log files. Defaults to $UserRootPath/ALZ/logs. + + .PARAMETER CredsPath + The path for the credential files. Defaults to $UserRootPath/ALZ/credentials. + + .PARAMETER ConfigPath + The path for the configuration files. Defaults to $UserRootPath/ALZ/config. + + .EXAMPLE + PS> .\Remove-AlzCredential.ps1 -Environment 'CanadaALZ' + + .EXAMPLE + PS> .\Remove-AlzCredential.ps1 -Environment 'CanadaALZ' -UserRootPath 'C:\Users\me\ALZ' + + .EXAMPLE + PS> .\Remove-AlzCredential.ps1 -Environment 'CanadaALZ' -UserLogsPath 'C:\Users\me\ALZ\logs' + + .EXAMPLE + PS> .\Remove-AlzCredential.ps1 -Environment 'CanadaALZ' -UserCredsPath 'C:\Users\me\ALZ\credentials' + + .EXAMPLE + PS> .\Remove-AlzCredential.ps1 -Environment 'CanadaALZ' -UserConfigPath 'C:\Users\me\ALZ\config' +#> + +[CmdletBinding()] +Param( + [Parameter(Mandatory = $true)] + [string]$Environment, + + [string]$UserRootPath = "$HOME", + + [string]$UserLogsPath = "$UserRootPath/ALZ/logs", + + [string]$UserCredsPath = "$UserRootPath/ALZ/credentials", + + [string]$UserConfigPath = "$UserRootPath/ALZ/config" +) + +#Requires -Modules Az + +$ErrorActionPreference = "Stop" + +function RemoveServicePrincipal { + param( + [string]$Environment = $Environment, + [string]$UserCredsPath = $UserCredsPath + ) + + $credentialFile = "$UserCredsPath/$Environment.json" + if (!(Test-Path -Path $credentialFile)) { + throw "Service principal file ($credentialFile) does not exist." + } + + $context = Get-AzContext + if ($context -eq $null) { + throw "You must be logged in to Azure via Azure PowerShell to remove a service principal." + } + + try { + $sp = (Get-Content -Raw -Path $credentialFile | ConvertFrom-Json -Depth 100) + Write-Output "Removing Service Principal ($($sp.displayName)) for environment ($Environment) from tenant ($($sp.tenant))))" + Remove-AzADServicePrincipal -ApplicationId $sp.appId + Remove-AzADApplication -DisplayName $sp.displayName + } catch { + throw "Failed to remove Service Principal ($($sp.displayName)) for environment ($Environment) from tenant ($($sp.tenant))): $($_.Exception.Message)" + } + + Write-Output "Removing Service Principal file ($credentialFile)" + Remove-Item -Path $credentialFile -Force +} + +# Ensure paths exist and are normalized to the OS path format +New-Item -ItemType Directory -Path $UserCredsPath -Force | Out-Null +$UserCredsPath = (Resolve-Path -Path $UserCredsPath).Path +New-Item -ItemType Directory -Path $UserLogsPath -Force | Out-Null +$UserLogsPath = (Resolve-Path -Path $UserLogsPath).Path +New-Item -ItemType Directory -Path $UserConfigPath -Force | Out-Null +$UserConfigPath = (Resolve-Path -Path $UserConfigPath).Path + +# Local variables +$date = Get-Date -Format "yyMMdd-HHmmss-fff" +$script = $(Split-Path -Path $PSCommandPath -LeafBase) +$logFile = "$UserLogsPath/$date-$script-$Environment.log" +$stopWatch = [System.Diagnostics.Stopwatch]::New() + +# Create the service principal +try { + $stopWatch.Restart() + RemoveServicePrincipal -Environment $Environment -CredsPath $UserCredsPath ` + | Tee-Object -FilePath $logFile -Append +} catch { + Write-Output $_ | Tee-Object -FilePath $logFile -Append + Write-Output $_.Exception | Tee-Object -FilePath $logFile -Append + throw +} finally { + Write-Output "Elapsed time: $($stopWatch.Elapsed)" ` + | Tee-Object -FilePath $logFile -Append +} diff --git a/scripts/configuration/Test-AlzCredential.ps1 b/scripts/configuration/Test-AlzCredential.ps1 new file mode 100644 index 00000000..cc380a4b --- /dev/null +++ b/scripts/configuration/Test-AlzCredential.ps1 @@ -0,0 +1,138 @@ +<# +---------------------------------------------------------------------------------- +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. + +THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +---------------------------------------------------------------------------------- +#> + +<# + .SYNOPSIS + Tests a Service Principal for the specified environment. + + .DESCRIPTION + Tests a Service Principal for the specified environment. + + .PARAMETER Environment + The name of the environment. This is typically the repo/org name. + + .PARAMETER UserRootPath + The root path for the log, credential, and configuration files. Defaults to $HOME. + + .PARAMETER UserLogsPath + The path for the log files. Defaults to $UserRootPath/ALZ/logs. + + .PARAMETER UserCredsPath + The path for the credential files. Defaults to $UserRootPath/ALZ/credentials. + + .PARAMETER UserConfigPath + The path for the configuration files. Defaults to $UserRootPath/ALZ/config. + + .EXAMPLE + PS> .\Test-AlzCredential.ps1 -Environment 'CanadaALZ' + + .EXAMPLE + PS> .\Test-AlzCredential.ps1 -Environment 'CanadaALZ' -UserRootPath 'C:\Users\me\ALZ' + + .EXAMPLE + PS> .\Test-AlzCredential.ps1 -Environment 'CanadaALZ' -UserLogsPath 'C:\Users\me\ALZ\logs' + + .EXAMPLE + PS> .\Test-AlzCredential.ps1 -Environment 'CanadaALZ' -UserCredsPath 'C:\Users\me\ALZ\credentials' + + .EXAMPLE + PS> .\Test-AlzCredential.ps1 -Environment 'CanadaALZ' -UserConfigPath 'C:\Users\me\ALZ\config' +#> + +[CmdletBinding()] +Param( + [Parameter(Mandatory = $true)] + [string]$Environment, + + [string]$UserRootPath = "$HOME", + + [string]$UserLogsPath = "$UserRootPath/ALZ/logs", + + [string]$UserCredsPath = "$UserRootPath/ALZ/credentials", + + [string]$UserConfigPath = "$UserRootPath/ALZ/config" +) + +#Requires -Modules Az + +$ErrorActionPreference = "Stop" + +function TestServicePrincipal { + param( + [string]$Environment = $Environment, + [string]$UserCredsPath = $UserCredsPath + ) + + $credentialFile = "$UserCredsPath/$Environment.json" + if (!(Test-Path -Path $credentialFile)) { + throw "Service principal file ($credentialFile) does not exist." + } + + $context = Get-AzContext + if ($context -eq $null) { + throw "You must be logged in to Azure via Azure PowerShell to test a service principal." + } + + $sp = (Get-Content -Raw -Path $credentialFile | ConvertFrom-Json -Depth 100) + + $role = Get-AzRoleAssignment -ServicePrincipalName $sp.appId + + Write-Output "" + if (($role | where { $_.RoleDefinitionName -eq 'Owner' -and $_.Scope -eq '/' }).Count -lt 1) { + throw "Service Principal ($($sp.displayName)) for environment ($Environment) from tenant ($($sp.tenant)) is not an Owner of the tenant." + } else { + Write-Output "Service Principal ($($sp.displayName)) for environment ($Environment) from tenant ($($sp.tenant)) is an Owner of the tenant." + } + + try { + Write-Output "" + Write-Output "Current Azure context:" + Get-AzContext + .\Connect-AlzCredential.ps1 -CredentialFile $credentialFile + Write-Output "" + Write-Output "Service Principal Azure context:" + Get-AzContext + Disconnect-AzAccount + Write-Output "" + Write-Output "Original Azure context:" + Get-AzContext + } catch { + throw + } +} + +# Ensure paths exist and are normalized to the OS path format +New-Item -ItemType Directory -Path $UserCredsPath -Force | Out-Null +$UserCredsPath = (Resolve-Path -Path $UserCredsPath).Path +New-Item -ItemType Directory -Path $UserLogsPath -Force | Out-Null +$UserLogsPath = (Resolve-Path -Path $UserLogsPath).Path +New-Item -ItemType Directory -Path $UserConfigPath -Force | Out-Null +$UserConfigPath = (Resolve-Path -Path $UserConfigPath).Path + +# Local variables +$date = Get-Date -Format "yyMMdd-HHmmss-fff" +$script = $(Split-Path -Path $PSCommandPath -LeafBase) +$logFile = "$UserLogsPath/$date-$script-$Environment.log" +$stopWatch = [System.Diagnostics.Stopwatch]::New() + +# Create the service principal +try { + $stopWatch.Restart() + TestServicePrincipal -Environment $Environment -CredsPath $UserCredsPath ` + | Tee-Object -FilePath $logFile -Append +} catch { + Write-Output $_ | Tee-Object -FilePath $logFile -Append + Write-Output $_.Exception | Tee-Object -FilePath $logFile -Append + throw +} finally { + Write-Output "Elapsed time: $($stopWatch.Elapsed)" ` + | Tee-Object -FilePath $logFile -Append +} diff --git a/scripts/deployments/Functions/EnvironmentContext.ps1 b/scripts/deployments/Functions/EnvironmentContext.ps1 index f3087232..c7feb483 100644 --- a/scripts/deployments/Functions/EnvironmentContext.ps1 +++ b/scripts/deployments/Functions/EnvironmentContext.ps1 @@ -22,7 +22,7 @@ function New-EnvironmentContext { [string] $Environment ) - $EnvironmentConfigurationYamlFilePath = "$WorkingDirectory/config/variables/$Environment.yml" + $EnvironmentConfigurationYamlFilePath = (Resolve-Path -Path "$WorkingDirectory/config/variables/$Environment.yml").Path # Load main environment variables file as YAML $EnvironmentConfiguration = Get-Content $EnvironmentConfigurationYamlFilePath | ConvertFrom-Yaml @@ -31,25 +31,25 @@ function New-EnvironmentContext { # Retrieve the management group hierarchy variable as JSON $ManagementGroupHierarchy = $Variables['var-managementgroup-hierarchy'] | ConvertFrom-Json - $PolicyDirectory = "$WorkingDirectory/policy" + $PolicyDirectory = (Resolve-Path -Path "$WorkingDirectory/policy").Path # Create a new context object return [PSCustomObject]@{ - WorkingDirectory = $WorkingDirectory + WorkingDirectory = (Resolve-Path -Path $WorkingDirectory).Path - RolesDirectory = "$WorkingDirectory/roles" + RolesDirectory = (Resolve-Path -Path "$WorkingDirectory/roles").Path - PolicyCustomDefinitionDirectory = "$PolicyDirectory/custom/definitions/policy" - PolicySetCustomDefinitionDirectory = "$PolicyDirectory/custom/definitions/policyset" - PolicySetCustomAssignmentsDirectory = "$PolicyDirectory/custom/assignments" - PolicySetBuiltInAssignmentsDirectory = "$PolicyDirectory/builtin/assignments" + PolicyCustomDefinitionDirectory = (Resolve-Path -Path "$PolicyDirectory/custom/definitions/policy").Path + PolicySetCustomDefinitionDirectory = (Resolve-Path -Path "$PolicyDirectory/custom/definitions/policyset").Path + PolicySetCustomAssignmentsDirectory = (Resolve-Path -Path "$PolicyDirectory/custom/assignments").Path + PolicySetBuiltInAssignmentsDirectory = (Resolve-Path -Path "$PolicyDirectory/builtin/assignments").Path - SchemaDirectory = "$WorkingDirectory/schemas/latest" + SchemaDirectory = (Resolve-Path -Path "$WorkingDirectory/schemas/latest").Path - LoggingDirectory = "$WorkingDirectory/config/logging/$Environment" - NetworkingDirectory = "$WorkingDirectory/config/networking/$Environment" - SubscriptionsDirectory = "$WorkingDirectory/config/subscriptions/$Environment" - IdentityDirectory = "$WorkingDirectory/config/identity/$Environment" + LoggingDirectory = (Resolve-Path -Path "$WorkingDirectory/config/logging/$Environment").Path + NetworkingDirectory = (Resolve-Path -Path "$WorkingDirectory/config/networking/$Environment").Path + SubscriptionsDirectory = (Resolve-Path -Path "$WorkingDirectory/config/subscriptions/$Environment").Path + IdentityDirectory = (Resolve-Path -Path "$WorkingDirectory/config/identity/$Environment").Path Variables = $Variables ManagementGroupHierarchy = $ManagementGroupHierarchy diff --git a/scripts/deployments/Functions/HubNetworkWithAzureFirewall.ps1 b/scripts/deployments/Functions/HubNetworkWithAzureFirewall.ps1 index 5cf60bbc..aa1bc444 100644 --- a/scripts/deployments/Functions/HubNetworkWithAzureFirewall.ps1 +++ b/scripts/deployments/Functions/HubNetworkWithAzureFirewall.ps1 @@ -84,7 +84,13 @@ function Set-HubNetwork-With-AzureFirewall { [String]$AzureFirewallPolicyResourceId, [Parameter(Mandatory = $true)] - [String]$LogAnalyticsWorkspaceResourceId + [String]$LogAnalyticsWorkspaceResourceId, + + [Parameter(HelpMessage = "Number of retries to deploy the Hub Network")] + [int]$RetryCount = 5, + + [Parameter(HelpMessage = "Delay, in seconds, between retries to deploy the Hub Network")] + [double]$RetryDelay = 60 ) Set-AzContext -Subscription $SubscriptionId @@ -134,14 +140,36 @@ function Set-HubNetwork-With-AzureFirewall { subscriptionId = $SubscriptionId } - Write-Output "Deploying $PopulatedParametersFilePath to $SubscriptionId in $Region" - New-AzSubscriptionDeployment ` - -Name "main-$Region" ` - -Location $Region ` - -TemplateFile "$($Context.WorkingDirectory)/landingzones/lz-platform-connectivity-hub-azfw/main.bicep" ` - -TemplateParameterFile $PopulatedParametersFilePath ` - -Verbose - + <# This 'New-AzSubscriptionDeployment` command to deploy the hub network has been observed to fail with a transient error condition. It is wrapped in a retry loop to solve for transient errors. #> + $deployAttempt = 1 + $deployed = $false + while (($deployAttempt -le $RetryCount) -and (-not $deployed)) { + if ($deployAttempt -gt 1) { + Write-Output "Waiting $RetryDelay seconds before retrying deployment" + Start-Sleep -Seconds $RetryDelay + } + try { + Write-Output "Deploying $PopulatedParametersFilePath to $SubscriptionId in $Region - Attempt $deployAttempt of $RetryCount" + New-AzSubscriptionDeployment ` + -Name "main-$Region" ` + -Location $Region ` + -TemplateFile "$($Context.WorkingDirectory)/landingzones/lz-platform-connectivity-hub-azfw/main.bicep" ` + -TemplateParameterFile $PopulatedParametersFilePath ` + -Verbose + $deployed = $true + } + catch { + if ($deployAttempt -eq $RetryCount) { + throw + } else { + Write-Output "Error deploying $PopulatedParametersFilePath to $SubscriptionId in $Region" + Write-Output $_.Exception.Message + Write-Output $_.Exception.StackTrace + } + } + $deployAttempt++ + } + #region Check if Private DNS Zones are managed in the Hub. If so, enable Private DNS Zones policy assignment if ($Configuration.parameters.privateDnsZones.value.enabled -eq $true) { $PolicyAssignmentFilePath = "$($Context.PolicySetCustomAssignmentsDirectory)/DNSPrivateEndpoints.bicep" diff --git a/scripts/deployments/Functions/HubNetworkWithNVA.ps1 b/scripts/deployments/Functions/HubNetworkWithNVA.ps1 index ebaff2cf..64d8b648 100644 --- a/scripts/deployments/Functions/HubNetworkWithNVA.ps1 +++ b/scripts/deployments/Functions/HubNetworkWithNVA.ps1 @@ -32,7 +32,13 @@ function Set-HubNetwork-With-NVA { [SecureString]$NvaUsername = $null, [Parameter(Mandatory = $false)] - [SecureString]$NvaPassword = $null + [SecureString]$NvaPassword = $null, + + [Parameter(HelpMessage = "Number of retries to deploy the Hub Network")] + [int]$RetryCount = 5, + + [Parameter(HelpMessage = "Delay, in seconds, between retries to deploy the Hub Network")] + [double]$RetryDelay = 60 ) Set-AzContext -Subscription $SubscriptionId @@ -100,13 +106,35 @@ function Set-HubNetwork-With-NVA { subscriptionId = $SubscriptionId } - Write-Output "Deploying $PopulatedParametersFilePath to $SubscriptionId in $Region" - New-AzSubscriptionDeployment ` - -Name "main-$Region" ` - -Location $Region ` - -TemplateFile "$($Context.WorkingDirectory)/landingzones/lz-platform-connectivity-hub-nva/main.bicep" ` - -TemplateParameterFile $PopulatedParametersFilePath ` - -Verbose + <# This 'New-AzSubscriptionDeployment` command to deploy the hub network has been observed to fail with a transient error condition. It is wrapped in a retry loop to solve for transient errors. #> + $deployAttempt = 1 + $deployed = $false + while (($deployAttempt -le $RetryCount) -and (-not $deployed)) { + if ($deployAttempt -gt 1) { + Write-Output "Waiting $RetryDelay seconds before retrying deployment" + Start-Sleep -Seconds $RetryDelay + } + try { + Write-Output "Deploying $PopulatedParametersFilePath to $SubscriptionId in $Region - Attempt $deployAttempt of $RetryCount" + New-AzSubscriptionDeployment ` + -Name "main-$Region" ` + -Location $Region ` + -TemplateFile "$($Context.WorkingDirectory)/landingzones/lz-platform-connectivity-hub-nva/main.bicep" ` + -TemplateParameterFile $PopulatedParametersFilePath ` + -Verbose + $deployed = $true + } + catch { + if ($deployAttempt -eq $RetryCount) { + throw + } else { + Write-Output "Error deploying $PopulatedParametersFilePath to $SubscriptionId in $Region" + Write-Output $_.Exception.Message + Write-Output $_.Exception.StackTrace + } + } + $deployAttempt++ + } #region Check if Private DNS Zones are managed in the Hub. If so, enable Private DNS Zones policy assignment if ($Configuration.parameters.privateDnsZones.value.enabled -eq $true) { diff --git a/scripts/deployments/Functions/Subscriptions.ps1 b/scripts/deployments/Functions/Subscriptions.ps1 index f257325c..ad33b3a9 100644 --- a/scripts/deployments/Functions/Subscriptions.ps1 +++ b/scripts/deployments/Functions/Subscriptions.ps1 @@ -26,19 +26,21 @@ function Set-Subscriptions { foreach ($subscriptionId in $SubscriptionIds) { # Find the ARM JSON parameters, ensure there's only 1 parameters file for each subscription - $SubscriptonConfigurations = Get-ChildItem -Path $Context.SubscriptionsDirectory -Filter "*$subscriptionId*.json" -Recurse + # $SubscriptionConfigurations = Get-ChildItem -Path $Context.SubscriptionsDirectory -Filter "*$subscriptionId*.json" -Recurse + $pattern = "^$subscriptionId.*(_.*)(_.*)?\.json" + $SubscriptionConfigurations = @(Get-ChildItem -Path $Context.SubscriptionsDirectory -Filter "*$subscriptionId*.json" -File -Recurse | ? { $_.Name -match $pattern }) - if ($SubscriptonConfigurations.Count -eq 0) { - Write-Output "No Subscription JSON paramters files found in $($Context.SubscriptionsDirectory) for $subscriptionId" + if ($SubscriptionConfigurations.Count -eq 0) { + Write-Output "No Subscription JSON parameters files found in $($Context.SubscriptionsDirectory) for $subscriptionId" continue - } elseif ($SubscriptonConfigurations.Count -gt 1) { + } elseif ($SubscriptionConfigurations.Count -gt 1) { Write-Output "Multiple Subscription JSON paramters files found in $($Context.SubscriptionsDirectory) for $subscriptionId. There must only be one." continue } - $DirectoryName = $SubscriptonConfigurations[0].DirectoryName - $FilePath = $SubscriptonConfigurations[0].FullName - $FileName = $SubscriptonConfigurations[0].Name + $DirectoryName = $SubscriptionConfigurations[0].DirectoryName + $FilePath = $SubscriptionConfigurations[0].FullName + $FileName = $SubscriptionConfigurations[0].Name # Parse the file name to get subscription id, archetype and region (optional). # If region is not available in the file name, the use the default region provided @@ -48,8 +50,8 @@ function Set-Subscriptions { $DeploymentRegion = $FileNameParts.Count -eq 3 ? $FileNameParts[2] : $Region # Compute the management group id from the folder structure - $FilePathWithoutBaseDirectory = $DirectoryName -Replace $($Context.SubscriptionsDirectory), "" - $ManagementGroupId = $FilePathWithoutBaseDirectory -Replace [IO.Path]::DirectorySeparatorChar, "" + $FilePathWithoutBaseDirectory = $DirectoryName -Replace [regex]::Escape($Context.SubscriptionsDirectory), "" + $ManagementGroupId = $FilePathWithoutBaseDirectory -Replace [regex]::Escape([IO.Path]::DirectorySeparatorChar.ToString()), "" Write-Output "Deploying Subscription: $SubscriptionId" Write-Output " - Management Group: $ManagementGroupId" @@ -58,10 +60,10 @@ function Set-Subscriptions { Set-AzContext -Subscription $SubscriptionId - $SchemaFilePath = "$($Context.SchemaDirectory)/landingzones/lz-$ArchetypeName.json" + $SchemaFile = (Resolve-Path -Path "$($Context.SchemaDirectory)/landingzones/lz-$ArchetypeName.json").Path Write-Output "Validation JSON parameter configuration using $SchemaFilePath" - Get-Content -Raw $FilePath | Test-Json -SchemaFile $SchemaFilePath + Get-Content -Raw $FilePath | Test-Json -SchemaFile $SchemaFile $Configuration = Get-Content $FilePath | ConvertFrom-Json -Depth 100 @@ -88,11 +90,12 @@ function Set-Subscriptions { $MoveDeploymentName=-join $MoveDeploymentName[0..63] Write-Output "Moving Subscription ($SubscriptionId) to Management Group ($ManagementGroupId)" + $TemplateFile = (Resolve-Path -Path "$($Context.WorkingDirectory)/landingzones/utils/mg-move/move-subscription.bicep").Path New-AzManagementGroupDeployment ` -Name $MoveDeploymentName ` -ManagementGroupId $ManagementGroupId ` -Location $Context.DeploymentRegion ` - -TemplateFile "$($Context.WorkingDirectory)/landingzones/utils/mg-move/move-subscription.bicep" ` + -TemplateFile $TemplateFile ` -TemplateParameterObject @{ managementGroupId = $ManagementGroupId subscriptionId = $SubscriptionId @@ -102,10 +105,11 @@ function Set-Subscriptions { Write-Output "Deploying $PopulatedParametersFilePath to $SubscriptionId in $Region" Set-AzContext -Subscription $SubscriptionId + $TemplateFile = (Resolve-Path -Path "$($Context.WorkingDirectory)/landingzones/lz-$ArchetypeName/main.bicep").Path New-AzSubscriptionDeployment ` -Name "main-$DeploymentRegion" ` -Location $DeploymentRegion ` - -TemplateFile "$($Context.WorkingDirectory)/landingzones/lz-$ArchetypeName/main.bicep" ` + -TemplateFile $TemplateFile ` -TemplateParameterFile $PopulatedParametersFilePath ` -Verbose diff --git a/scripts/deployments/RunWorkflows.ps1 b/scripts/deployments/RunWorkflows.ps1 index bacaa54a..0ece9e5b 100644 --- a/scripts/deployments/RunWorkflows.ps1 +++ b/scripts/deployments/RunWorkflows.ps1 @@ -94,42 +94,42 @@ OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. The firewall password to use for the Hub Network with NVA workflow. .EXAMPLE - PS> .\RunWorkflows.ps1 -EnvironmentName CanadaESLZ-main -LoginInteractiveTenantId '8188040d-6c67-4c5c-b112-36a304b66dad' -DeployManagementGroups + PS> .\RunWorkflows.ps1 -EnvironmentName CanadaPubSecALZ-main -LoginInteractiveTenantId '8188040d-6c67-4c5c-b112-36a304b66dad' -DeployManagementGroups Deploy management groups interactively. .EXAMPLE - PS> .\RunWorkflows.ps1 -EnvironmentName CanadaESLZ-main -LoginInteractiveTenantId '8188040d-6c67-4c5c-b112-36a304b66dad' -DeployManagementGroups -DeployRoles -DeployLogging -DeployCustomPolicyDefinitions -DeployCustomPolicySetDefinitions -DeployCustomPolicySetAssignments -DeployBuiltinPolicySetAssignments -DeployAzureFirewallPolicy -DeployHubNetworkWithAzureFirewall + PS> .\RunWorkflows.ps1 -EnvironmentName CanadaPubSecALZ-main -LoginInteractiveTenantId '8188040d-6c67-4c5c-b112-36a304b66dad' -DeployManagementGroups -DeployRoles -DeployLogging -DeployCustomPolicyDefinitions -DeployCustomPolicySetDefinitions -DeployCustomPolicySetAssignments -DeployBuiltinPolicySetAssignments -DeployAzureFirewallPolicy -DeployHubNetworkWithAzureFirewall Deploy all platform components interactively, with Azure Firewall. .EXAMPLE - PS> .\RunWorkflows.ps1 -EnvironmentName CanadaESLZ-main -LoginInteractiveTenantId '8188040d-6c67-4c5c-b112-36a304b66dad' -DeploySubscriptionIds 'a188040e-6c67-4c5c-b112-36a304b66dad,7188030d-6c67-4c5c-b112-36a304b66dac' + PS> .\RunWorkflows.ps1 -EnvironmentName CanadaPubSecALZ-main -LoginInteractiveTenantId '8188040d-6c67-4c5c-b112-36a304b66dad' -DeploySubscriptionIds 'a188040e-6c67-4c5c-b112-36a304b66dad,7188030d-6c67-4c5c-b112-36a304b66dac' Deploy 2 subscriptions interactively. .EXAMPLE - PS> .\RunWorkflows.ps1 -EnvironmentName CanadaESLZ-main -DeployCustomPolicyDefinitions -DeployCustomPolicySetDefinitions -DeployCustomPolicySetAssignments -DeployBuiltinPolicySetAssignments + PS> .\RunWorkflows.ps1 -EnvironmentName CanadaPubSecALZ-main -DeployCustomPolicyDefinitions -DeployCustomPolicySetDefinitions -DeployCustomPolicySetAssignments -DeployBuiltinPolicySetAssignments Deploy Built-In & Custom Policy Sets, including all default custom policy/policy set definitions. .EXAMPLE - PS> .\RunWorkflows.ps1 -EnvironmentName CanadaESLZ-main -DeployCustomPolicyDefinitions + PS> .\RunWorkflows.ps1 -EnvironmentName CanadaPubSecALZ-main -DeployCustomPolicyDefinitions Deploy Custom Policy Definitions only. .EXAMPLE - PS> .\RunWorkflows.ps1 -EnvironmentName CanadaESLZ-main -DeployCustomPolicySetAssignments -CustomPolicySetAssignmentManagementGroupId pubsec -CustomPolicySetAssignmentNames DefenderForCloud + PS> .\RunWorkflows.ps1 -EnvironmentName CanadaPubSecALZ-main -DeployCustomPolicySetAssignments -CustomPolicySetAssignmentManagementGroupId pubsec -CustomPolicySetAssignmentNames DefenderForCloud Deploy one Custom Policy Set Assignment at management group .EXAMPLE - PS> .\RunWorkflows.ps1 -EnvironmentName CanadaESLZ-main -DeployBuiltinPolicySetAssignments + PS> .\RunWorkflows.ps1 -EnvironmentName CanadaPubSecALZ-main -DeployBuiltinPolicySetAssignments Deploy Built In Policy Assignments .EXAMPLE - PS> .\RunWorkflows.ps1 -EnvironmentName CanadaESLZ-main -DeployBuiltinPolicySetAssignments -BuiltinPolicySetAssignmentManagementGroupId pubsec -BuiltinPolicySetAssignmentNames asb + PS> .\RunWorkflows.ps1 -EnvironmentName CanadaPubSecALZ-main -DeployBuiltinPolicySetAssignments -BuiltinPolicySetAssignmentManagementGroupId pubsec -BuiltinPolicySetAssignmentNames asb Deploy one Built In Policy Assignment at management group diff --git a/scripts/onboarding/create-pipelines.bat b/scripts/onboarding/create-pipelines.bat index f9b12fd6..3b9546d5 100644 --- a/scripts/onboarding/create-pipelines.bat +++ b/scripts/onboarding/create-pipelines.bat @@ -22,7 +22,7 @@ choice /C YN /M "Do you want to proceed?" if errorlevel 2 exit /b 0 REM Process all pipeline definitions -for %%N in (management-groups roles platform-logging policy platform-connectivity-hub-nva platform-connectivity-hub-azfw platform-connectivity-hub-azfw-policy subscriptions) do ( +for %%N in (management-groups roles platform-logging platform-identity policy platform-connectivity-hub-nva platform-connectivity-hub-azfw platform-connectivity-hub-azfw-policy subscriptions) do ( REM Check for pipeline existence set FOUND= diff --git a/scripts/onboarding/set-variables.CanadaPubSecALZ.bat b/scripts/onboarding/set-variables.CanadaPubSecALZ.bat new file mode 100644 index 00000000..d6138892 --- /dev/null +++ b/scripts/onboarding/set-variables.CanadaPubSecALZ.bat @@ -0,0 +1,54 @@ +@echo off +REM // ---------------------------------------------------------------------------------- +REM // Copyright (c) Microsoft Corporation. +REM // Licensed under the MIT license. +REM // +REM // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +REM // EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES +REM // OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +REM // ---------------------------------------------------------------------------------- + +REM Azure AD tenant GUID +set DEVOPS_TENANT_ID=df968f64-5f44-4e42-937b-47fb792ee373 + +REM Azure AD tenant root management group name +set DEVOPS_MGMT_GROUP_NAME=Tenant Root Group + +REM Azure service principal name for 'Owner' RBAC at tenant root scope +set DEVOPS_SP_NAME=spn-azure-platform-ops + +REM Azure security group name for 'Owner` RBAC subscription, network, and logging +set DEVOPS_SG_NAME=alz-owners + +REM Azure DevOps organization URL +set DEVOPS_ORG=https://dev.azure.com/CanadaPubSecALZ + +REM Azure DevOps project name (prefer no spaces) +set DEVOPS_PROJECT_NAME=CanadaPubSecALZ + +REM Repository name or URL +set DEVOPS_REPO_NAME_OR_URL=CanadaPubSecALZ + +REM Repository type: 'tfsgit' or 'github' +set DEVOPS_REPO_TYPE=tfsgit + +REM Repository branch name (default) +set DEVOPS_REPO_BRANCH=skeeler-quicksetup + +REM Azure DevOps pipeline name suffix (default) +set DEVOPS_PIPELINE_NAME_SUFFIX=-ci + +REM Azure DevOps service endpoint name (service connection in project settings) +set DEVOPS_SE_NAME=spn-azure-platform-ops + +REM Azure DevOps service endpoint template file (generated) +set DEVOPS_SE_TEMPLATE=service-endpoint.CanadaPubSecALZ.json + +REM Do not change this value (hard-coded in YAML pipeline definition) +set DEVOPS_VARIABLES_GROUP_NAME=firewall-secrets + +REM Are variables in the firewall-secrets group marked as secret? 'true' or 'false'. +set DEVOPS_VARIABLES_ARE_SECRET=true + +REM Folder path for generated output files +set DEVOPS_OUTPUT_DIR=.\output