From 60317dca217084b46f1a95bda54505847772d7b2 Mon Sep 17 00:00:00 2001 From: Kungumaraj Nachimuthu Date: Wed, 19 Jun 2019 16:28:17 -0700 Subject: [PATCH] Resource Group validation setup and teardown (#43) * Deployment validation resource group setup and teardonw logic added * Minor change overwritten when copying over is now reversed * Minor update * Cleaned up pipeline yml file * Cleaned up pipeline yml file * Updates made based on PR feedback --- .../archetypeAzureDevopsPipeline.yml | 38 +++++++ orchestration/Factory/ImportModules.ps1 | 5 + .../AzureResourceManagerDeploymentService.ps1 | 35 ++++-- .../ModuleConfigurationDeployment.ps1 | 16 ++- .../ValidationResourceGroupSetup.ps1 | 103 ++++++++++++++++++ .../ValidationResourceGroupSetup.Tests.ps1 | 45 ++++++++ 6 files changed, 227 insertions(+), 15 deletions(-) create mode 100644 orchestration/OrchestrationService/ValidationResourceGroupSetup.ps1 create mode 100644 orchestration/Tests/UnitTests/ValidationResourceGroupSetup.Tests.ps1 diff --git a/archetypes/shared-services/archetypeAzureDevopsPipeline.yml b/archetypes/shared-services/archetypeAzureDevopsPipeline.yml index f6b7ea3..a585f82 100644 --- a/archetypes/shared-services/archetypeAzureDevopsPipeline.yml +++ b/archetypes/shared-services/archetypeAzureDevopsPipeline.yml @@ -8,9 +8,22 @@ variables: stages: - stage: Validate jobs: + - job: SetupValidationResourceGroup + pool: + name: 'vdc-self-hosted' + steps: + - task: AzurePowerShell@4 + displayName: "Setup Validation Resource Group" + inputs: + azureSubscription: 'vdc2-hub' + ScriptType: 'FilePath' + ScriptPath: 'orchestration/OrchestrationService/ValidationResourceGroupSetup.ps1' + ScriptArguments: '-ResourceGroupName vdc-validation-rg' + azurePowerShellVersion: 'LatestVersion' - job: StorageAccounts pool: name: 'vdc-self-hosted' + dependsOn: SetupValidationResourceGroup steps: - task: PowerShell@2 displayName: "Pester Tests for Module - Storage Accounts" @@ -49,6 +62,7 @@ stages: - job: LogAnalytics pool: name: 'vdc-self-hosted' + dependsOn: SetupValidationResourceGroup steps: - task: PowerShell@2 displayName: "Pester Tests for Module - Log Analytics" @@ -79,6 +93,7 @@ stages: - job: AutomationAccounts pool: name: 'vdc-self-hosted' + dependsOn: SetupValidationResourceGroup steps: - task: PowerShell@2 displayName: "Pester Tests for Module - Automation Accounts" @@ -101,6 +116,7 @@ stages: - job: ApplicationSecurityGroups pool: name: 'vdc-self-hosted' + dependsOn: SetupValidationResourceGroup steps: - task: PowerShell@2 displayName: "Pester Tests for Module - Application Security Groups" @@ -131,6 +147,7 @@ stages: - job: NetworkSecurityGroups pool: name: 'vdc-self-hosted' + dependsOn: SetupValidationResourceGroup steps: - task: PowerShell@2 displayName: "Pester Tests for Module - Network Security Groups" @@ -161,6 +178,7 @@ stages: - job: RouteTables pool: name: 'vdc-self-hosted' + dependsOn: SetupValidationResourceGroup steps: - task: PowerShell@2 displayName: "Pester Tests for Module - Route Tables" @@ -183,6 +201,7 @@ stages: - job: vNet pool: name: 'vdc-self-hosted' + dependsOn: SetupValidationResourceGroup steps: - task: PowerShell@2 displayName: "Pester Tests for Module - vNet" @@ -205,6 +224,7 @@ stages: - job: VirtualNetworkGateway pool: name: 'vdc-self-hosted' + dependsOn: SetupValidationResourceGroup steps: - task: PowerShell@2 displayName: "Pester Tests for Module - Virtual Network Gateway" @@ -227,6 +247,7 @@ stages: - job: VirtualNetworkGatewayConnection pool: name: 'vdc-self-hosted' + dependsOn: SetupValidationResourceGroup steps: - task: PowerShell@2 displayName: "Pester Tests for Module - Virtual Network Gateway Connection" @@ -257,6 +278,7 @@ stages: - job: AzureFirewall pool: name: 'vdc-self-hosted' + dependsOn: SetupValidationResourceGroup steps: - task: PowerShell@2 displayName: "Pester Tests for Module - Azure Firewall" @@ -279,6 +301,7 @@ stages: - job: KeyVault pool: name: 'vdc-self-hosted' + dependsOn: SetupValidationResourceGroup steps: - task: PowerShell@2 displayName: "Pester Tests for Module - Key Vault" @@ -301,6 +324,7 @@ stages: - job: Jumpbox pool: name: 'vdc-self-hosted' + dependsOn: SetupValidationResourceGroup steps: - task: PowerShell@2 displayName: "Pester Tests for Module - Jumpbox" @@ -323,6 +347,7 @@ stages: - job: ADDS pool: name: 'vdc-self-hosted' + dependsOn: SetupValidationResourceGroup steps: - task: PowerShell@2 displayName: "Pester Tests for Module - ADDS" @@ -342,6 +367,19 @@ stages: ScriptPath: 'orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1' ScriptArguments: '-ArchetypeDefinitionPath "archetypes/shared-services/archetypeDefinition.json" -ModuleConfigurationName "ADDS" -Validate' azurePowerShellVersion: 'LatestVersion' + - job: TearDownValidationResourceGroup + pool: + name: 'vdc-self-hosted' + dependsOn: [ StorageAccounts, LogAnalytics, AutomationAccounts, ApplicationSecurityGroups, NetworkSecurityGroups, RouteTables, vNet, VirtualNetworkGateway, VirtualNetworkGatewayConnection, AzureFirewall, Jumpbox, ADDS ] + steps: + - task: AzurePowerShell@4 + displayName: "Teardown Validation Resource Group" + inputs: + azureSubscription: 'vdc2-hub' + ScriptType: 'FilePath' + ScriptPath: 'orchestration/OrchestrationService/ValidationResourceGroupSetup.ps1' + ScriptArguments: '-TearDown' + azurePowerShellVersion: 'LatestVersion' - stage: Deploy jobs: - job: Deployment diff --git a/orchestration/Factory/ImportModules.ps1 b/orchestration/Factory/ImportModules.ps1 index fab8ded..bfc01bc 100644 --- a/orchestration/Factory/ImportModules.ps1 +++ b/orchestration/Factory/ImportModules.ps1 @@ -83,3 +83,8 @@ $modulePath = Join-Path (Join-Path (Join-Path $rootPath -ChildPath '..') -ChildP $scriptBlock = ". $modulePath"; $script = [scriptblock]::Create($scriptBlock); . $script; + +$modulePath = Join-Path (Join-Path (Join-Path $rootPath -ChildPath '..') -ChildPath 'OrchestrationService') -ChildPath 'ValidationResourceGroupSetup.ps1' +$scriptBlock = ". $modulePath"; +$script = [scriptblock]::Create($scriptBlock); +. $script; \ No newline at end of file diff --git a/orchestration/IntegrationService/Implementations/AzureResourceManagerDeploymentService.ps1 b/orchestration/IntegrationService/Implementations/AzureResourceManagerDeploymentService.ps1 index 207dba1..5bb117b 100644 --- a/orchestration/IntegrationService/Implementations/AzureResourceManagerDeploymentService.ps1 +++ b/orchestration/IntegrationService/Implementations/AzureResourceManagerDeploymentService.ps1 @@ -53,17 +53,32 @@ Class AzureResourceManagerDeploymentService: IDeploymentService { [string] $deploymentTemplate, ` [string] $deploymentParameters, ` [string] $location) { + + # Try to fetch the validation resource group + $validationResourceGroup = ` + Get-AzResourceGroup ` + -Name $resourceGroupName ` + -ErrorAction SilentlyContinue; - # call arm validation - $validation = ` - $this.InvokeARMOperation( - $tenantId, - $subscriptionId, - $resourceGroupName, - $deploymentTemplate, - $deploymentParameters, - $location, - "validate"); + # Does the validation resource group exists? + if($null -ne $validationResourceGroup) { + # call arm validation + $validation = ` + $this.InvokeARMOperation( + $tenantId, + $subscriptionId, + $resourceGroupName, + $deploymentTemplate, + $deploymentParameters, + $location, + "validate"); + } + else { + # Fail early if the validation resource group does not + # exists + Throw "Validation resource group - $resourceGroupName is not setup. Create the validation resource ` + group before invoking the ARM validation."; + } # Did the validation succeed? if($validation.error.code -eq "InvalidTemplateDeployment") { diff --git a/orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1 b/orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1 index 63f00a2..ef455e0 100644 --- a/orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1 +++ b/orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1 @@ -173,11 +173,16 @@ Function New-Deployment { -SubscriptionName $archetypeInstanceJson.ArchetypeParameters.Subscription ` -ModuleConfiguration $moduleConfiguration; - Write-Debug "Setting subscription context"; + # Do not change the subscription context if the operation is validate. + # This is because the script will expect the validation resource + # group to be present in all the subscriptions we are deploying. + if(-not $Validate.IsPresent) { + Write-Debug "Setting subscription context"; - Set-SubscriptionContext ` - -SubscriptionId $subscriptionInformation.SubscriptionId ` - -TenantId $subscriptionInformation.TenantId; + Set-SubscriptionContext ` + -SubscriptionId $subscriptionInformation.SubscriptionId ` + -TenantId $subscriptionInformation.TenantId; + } # Let's attempt to get the Audit Id from cache $auditCacheKey = ` @@ -1305,11 +1310,12 @@ Function Deploy-AzureResourceManagerTemplate { try { if($Validate.IsPresent) { Write-Debug "Validating the template"; + return ` $deploymentService.ExecuteValidation( $TenantId, $SubscriptionId, - $ResourceGroupName, + $defaultValidationResourceGroupName, $DeploymentTemplate, $DeploymentParameters, $Location); diff --git a/orchestration/OrchestrationService/ValidationResourceGroupSetup.ps1 b/orchestration/OrchestrationService/ValidationResourceGroupSetup.ps1 new file mode 100644 index 0000000..3889155 --- /dev/null +++ b/orchestration/OrchestrationService/ValidationResourceGroupSetup.ps1 @@ -0,0 +1,103 @@ +[CmdletBinding()] +param ( + [Parameter(Mandatory=$false)] + [string] + $ResourceGroupName, + [Parameter(Mandatory=$false)] + [string] + $ResourceGroupLocation, + [Parameter(Mandatory=$false)] + [switch] + $TearDown +) + +$defaultValidationResourceGroupName = "vdc-validation-rg"; +$defaultValidationResourceGroupLocation = "West US"; + +Function Get-ValidationResourceGroup() { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)] + [string] $ResourceGroupName + ) + # Is a resource group name passed? + if($null -ne $resourceGroupName) { + # Get the resource group by name + return ` + Get-AzResourceGroup ` + -Name $resourceGroupName ` + -ErrorAction SilentlyContinue; + } + else { + return $null; + } +} + +Function SetupResourceGroup() { + [CmdletBinding()] + param( + [Parameter(Mandatory=$false)] + [string] $ResourceGroupName, + [Parameter(Mandatory=$false)] + [string] $ResourceGroupLocation + ) + # Try to get the validation resource group by + # name + $existingValidationResourceGroup = ` + Get-ValidationResourceGroup ` + -ResourceGroupName $ResourceGroupName; + + # Does the resource group exists? + if($null -eq $existingValidationResourceGroup) { + # Create the resource group + return ` + New-AzResourceGroup ` + -Name $ResourceGroupName ` + -Location $ResourceGroupLocation; + } + else { + return ` + $existingValidationResourceGroup; + } +} + +Function TearDownResourceGroup() { + [CmdletBinding()] + param( + [Parameter(Mandatory=$false)] + [string] $ResourceGroupName + ) + # Try to get the validation resource group by + # name + $existingValidationResourceGroup = ` + Get-ValidationResourceGroup ` + -ResourceGroupName $ResourceGroupName; + + # Does the resource group exists? + if($null -ne $existingValidationResourceGroup) { + # Delete the resource group + Remove-AzResourceGroup ` + -Name $ResourceGroupName ` + -Force ` + -Confirm:$false; + } +} + +if([string]::IsNullOrEmpty($ResourceGroupName)) { + $ResourceGroupName = $defaultValidationResourceGroupName; +} +if([string]::IsNullOrEmpty($ResourceGroupLocation)) { + $ResourceGroupLocation = $defaultValidationResourceGroupLocation; +} + +if($TearDown.IsPresent) { + # Call function to tear down the validation resource group + TearDownResourceGroup ` + -ResourceGroupName $ResourceGroupName; +} +else { + # Call function to setup the validation resource group + SetupResourceGroup ` + -ResourceGroupName $ResourceGroupName ` + -ResourceGroupLocation $ResourceGroupLocation; +} \ No newline at end of file diff --git a/orchestration/Tests/UnitTests/ValidationResourceGroupSetup.Tests.ps1 b/orchestration/Tests/UnitTests/ValidationResourceGroupSetup.Tests.ps1 new file mode 100644 index 0000000..272e517 --- /dev/null +++ b/orchestration/Tests/UnitTests/ValidationResourceGroupSetup.Tests.ps1 @@ -0,0 +1,45 @@ +######################################################################################################################## +## +## ValidationResourceGroup.Tests.ps1 +## +## The purpose of this script is to perform the unit testing for the ValidationResourceGroup Module using Pester. +## The script will import the ValidationResourceGroup Module and any dependency moduels to perform the tests. +## +######################################################################################################################## +$rootPath = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent; +$scriptPath = Join-Path -Path $rootPath -ChildPath '..' -AdditionalChildPath @('..', 'OrchestrationService', 'ValidationResourceGroupSetup.ps1'); +. $scriptPath; + +Describe "Validation Resource Group Unit Test Cases" { + + Context "Setup and Teardown Resource Group" { + BeforeEach { + $defaultResourceGroupName = "vdc-toolkit-validation-rg"; + $defaultResourceGroupLocation = "West US 2"; + } + + It "Should setup the validation resource group by passing name and location" { + + SetupResourceGroup ` + -ResourceGroupName $ResourceGroupName ` + -ResourceGroupLocation $ResourceGroupLocation; + + $resourceGroup = ` + Get-ValidationResourceGroup ` + -ResourceGroupName $ResourceGroupName; + + $resourceGroup | Should Not Be $null; + } + + It "Should teardown the validation resource group by passing name" { + TearDownResourceGroup ` + -ResourceGroupName $ResourceGroupName; + + $resourceGroup = ` + Get-ValidationResourceGroup ` + -ResourceGroupName $ResourceGroupName; + + $resourceGroup | Should Be $null; + } + } +} \ No newline at end of file