Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Expand support for management group deployment #718

Merged
merged 9 commits into from
Dec 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions src/internal/classes/AzOpsScope.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,6 @@
return $null
}
[string] IsResource() {

if ($this.Scope -match $this.regex_managementgroupResource) {
return ($this.regex_managementgroupResource.Split($this.Scope) | Select-Object -last 1)
}
Expand All @@ -409,13 +408,18 @@
Should Return Management Group Name
#>
[string] GetManagementGroup() {

if ($this.GetManagementGroupName()) {
foreach ($mgmt in $script:AzOpsAzManagementGroup) {
if ($mgmt.Name -eq $this.GetManagementGroupName()) {
$private:mgmtHit = $true
return $mgmt.Name
}
}
if (-not $private:mgmtHit) {
$mgId = $this.Scope -split $this.regex_managementgroupExtract -split '/' | Where-Object { $_ } | Select-Object -First 1
Write-PSFMessage -Level Debug -String 'AzOpsScope.GetManagementGroup.NotFound' -StringValues $mgId -FunctionName AzOpsScope -ModuleName AzOps
return $mgId
}
}
if ($this.Subscription) {
foreach ($mgmt in $script:AzOpsAzManagementGroup) {
Expand Down Expand Up @@ -445,7 +449,8 @@
}
else {
Write-PSFMessage -Level Warning -Tag error -String 'AzOpsScope.GetAzOpsManagementGroupPath.NotFound' -StringValues $managementgroupName -FunctionName AzOpsScope -ModuleName AzOps
throw "Management Group not found: $managementgroupName"
$assumeNewResource = "azopsscope-assume-new-resource_$managementgroupName"
return $assumeNewResource.ToLower()
}
}

Expand All @@ -455,11 +460,10 @@
[string] GetManagementGroupName() {
if ($this.Scope -match $this.regex_managementgroupExtract) {
$mgId = $this.Scope -split $this.regex_managementgroupExtract -split '/' | Where-Object { $_ } | Select-Object -First 1

if ($mgId) {
$mgDisplayName = ($script:AzOpsAzManagementGroup | Where-Object Name -eq $mgId).Name
if ($mgDisplayName) {
#Write-PSFMessage -Level Debug -String 'AzOpsScope.GetManagementGroupName.Found.Azure' -StringValues $mgDisplayName -FunctionName AzOpsScope -ModuleName AzOps
Write-PSFMessage -Level Debug -String 'AzOpsScope.GetManagementGroupName.Found.Azure' -StringValues $mgDisplayName -FunctionName AzOpsScope -ModuleName AzOps
return $mgDisplayName
}
else {
Expand Down Expand Up @@ -560,4 +564,4 @@
throw "Unable to determine Resource Scope for: $($this.Scope)"
}
#endregion Data Accessors
}
}
32 changes: 31 additions & 1 deletion src/internal/functions/New-AzOpsDeployment.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
$deploymentCommand = 'New-AzSubscriptionDeployment'
}
# Management Groups
elseif ($scopeObject.managementGroup) {
elseif ($scopeObject.managementGroup -and (-not ($scopeObject.StatePath).StartsWith('azopsscope-assume-new-resource_'))) {
Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.ManagementGroup.Processing' -StringValues $defaultDeploymentRegion, $scopeObject -Target $scopeObject
$parameters.ManagementGroupId = $scopeObject.managementgroup
$whatIfCommand = 'Get-AzManagementGroupDeploymentWhatIfResult'
Expand All @@ -121,6 +121,36 @@
$whatIfCommand = 'Get-AzTenantDeploymentWhatIfResult'
$deploymentCommand = 'New-AzTenantDeployment'
}
# If Management Group resource was not found, validate and prepare for first time deployment of resource
elseif (($scopeObject.StatePath).StartsWith('azopsscope-assume-new-resource_')) {
$resourceScopeFileContent = Get-Content -Path $addition | ConvertFrom-Json -Depth 100
$resource = ($resourceScopeFileContent.resources | Where-Object {$_.type -eq 'Microsoft.Management/managementGroups'} | Select-Object -First 1)
$pathDir = (Get-Item -Path $addition).Directory | Resolve-Path -Relative
if ((Get-PSFConfigValue -FullName 'AzOps.Core.AutoGeneratedTemplateFolderPath') -ne '.') {
$pathDir = Split-Path -Path $pathDir -Parent
}
$parentDirScopeObject = New-AzOpsScope -Path (Split-Path -Path $pathDir -Parent) | Where-Object {(-not ($_.StatePath).StartsWith('azopsscope-assume-new-resource_'))}
$parentIdScope = New-AzOpsScope -Scope (($resource).properties.details.parent.id) | Where-Object {(-not ($_.StatePath).StartsWith('azopsscope-assume-new-resource_'))}
# Validate parent existence with content parent scope, statepath and name match, determines file location match deployment scope
if ($parentDirScopeObject -and $parentIdScope -and $parentDirScopeObject.Scope -eq $parentIdScope.Scope -and $parentDirScopeObject.StatePath -eq $parentIdScope.StatePath -and $parentDirScopeObject.Name -eq $parentIdScope.Name) {
# Validate directory name match resource information
if ((Get-Item -Path $pathDir).Name -eq "$($resource.properties.displayName) ($($resource.name))") {
Write-PSFMessage -Level Verbose -String 'New-AzOpsDeployment.Root.Processing' -StringValues $defaultDeploymentRegion, $scopeObject -Target $scopeObject
$whatIfCommand = 'Get-AzTenantDeploymentWhatIfResult'
$deploymentCommand = 'New-AzTenantDeployment'
}
# Invalid directory name
else {
Write-PSFMessage -Level Error -String 'New-AzOpsDeployment.Directory.NotFound' -Target $scopeObject -Tag Error -StringValues (Get-Item -Path $pathDir).Name, "$($resource.properties.displayName) ($($resource.name))"
throw
}
}
# Parent missing
else {
Write-PSFMessage -Level Error -String 'New-AzOpsDeployment.Parent.NotFound' -Target $scopeObject -Tag Error -StringValues $addition
throw
}
}
else {
Write-PSFMessage -Level Warning -String 'New-AzOpsDeployment.Scope.Unidentified' -Target $scopeObject -StringValues $scopeObject
$scopeFound = $false
Expand Down
7 changes: 5 additions & 2 deletions src/localized/en-us/Strings.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
'Assert-AzOpsBicepDependency.Success' = 'Bicep found in current path' #
'Assert-AzOpsBicepDependency.NotFound' = 'Unable to locate bicep binary. Will not be able to deploy bicep templates.' #

'AzOpsScope.GetAzOpsManagementGroupPath.NotFound' = 'Management Group not found: {0}' # $managementgroupName
'AzOpsScope.GetAzOpsManagementGroupPath.NotFound' = 'Management Group path not found: {0}' # $managementgroupName
'AzOpsScope.GetAzOpsResourcePath.NotFound' = 'Unable to determine Resource Scope for: {0}' # $this.Scope
'AzOpsScope.GetAzOpsResourcePath.Retrieving' = 'Getting Resource path for: {0}' # $this.Scope
'AzOpsScope.GetManagementGroupName.Found.Azure' = 'Management Group found in Azure: {0}' # $mgDisplayName
'AzOpsScope.GetManagementGroupName.NotFound' = 'Management Group not found in Azure. Using directory name instead: {0}' # $mgId
'AzOpsScope.GetManagementGroup.NotFound' = 'Management Group does not match any existing in Azure. Assume new resource, using directory name: {0}' # $mgId
'AzOpsScope.GetManagementGroupName.NotFound' = 'Management Group not found in Azure. Trying with directory name instead: {0}' # $mgId
'AzOpsScope.GetSubscription.Found' = 'SubscriptionId found in Azure: {0}' # $sub.Id
'AzOpsScope.GetSubscription.NotFound' = 'SubscriptionId not found in Azure. Using directory name instead: {0}' # $subId
'AzOpsScope.GetSubscriptionDisplayName.Found' = 'Subscription DisplayName found in Azure: {0}' # $sub.displayName
Expand Down Expand Up @@ -223,6 +224,8 @@
'New-AzOpsDeployment.WhatIfResults' = 'WhatIf Results: {0}' # $TemplateFilePath
'New-AzOpsDeployment.WhatIfFile' = 'Creating WhatIf Results file'
'New-AzOpsDeployment.SkipDueToWhatIf' = 'Skipping deployment due to WhatIf' #
'New-AzOpsDeployment.Parent.NotFound' = 'Failed to find parent scope for template {0}' # $addition
'New-AzOpsDeployment.Directory.NotFound' = 'Directory name {0} does not match expected {1}' # (Get-Item -Path $pathDir).Name, "$($resource.properties.displayName) ($($resource.name))"

'New-AzOpsStateDeployment.EnrollmentAccount.First' = 'No enrollment account defined, using the first account found: {0}' # @($enrollmentAccounts)[0].PrincipalName
'New-AzOpsStateDeployment.EnrollmentAccount.Selected' = 'Using the defined enrollment account {0}' # $cfgEnrollmentAccount
Expand Down
25 changes: 24 additions & 1 deletion src/tests/integration/Repository.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@ Describe "Repository" {
$script:bicepDeploymentName = "AzOps-{0}-{1}" -f $($script:bicepTemplatePath[0].Name.Replace(".bicep", '')), $deploymentLocationId
$script:bicepResourceGroupName = ((Get-Content -Path ($Script:bicepTemplatePath.FullName[1])) | ConvertFrom-Json).parameters.resourceGroupName.value

$script:pushmgmttest1idManagementGroupTemplatePath = Get-Item "$($global:testroot)/templates/pushmgmttest1displayname (pushmgmttest1id)" | Copy-Item -Destination $script:testManagementGroupDirectory -Recurse -PassThru -Force
$script:pushmgmttest1idManagementGroupDeploymentName = "AzOps-{0}-{1}" -f "$($script:pushmgmttest1idManagementGroupTemplatePath[1].Name.Replace(".json", ''))", $deploymentLocationId
$script:pushmgmttest1idName = ((Get-Content -Path ($script:pushmgmttest1idManagementGroupTemplatePath.FullName[1])) | ConvertFrom-Json).resources.name[0]

$script:pushmgmttest2idManagementGroupTemplatePath = Get-Item "$($global:testroot)/templates/pushmgmttest2displayname (pushmgmttest2id)" | Copy-Item -Destination $script:platformManagementGroupDirectory -Recurse -PassThru -Force
#endregion Paths

#Test push based on pulled resources
Expand All @@ -319,7 +324,8 @@ Describe "Repository" {
"A`t$script:routeTableFile",
"A`t$script:ruleCollectionGroupsFile",
"A`t$script:locksFile",
"A`t$($script:bicepTemplatePath.FullName[0])"
"A`t$($script:bicepTemplatePath.FullName[0])",
"A`t$($script:pushmgmttest1idManagementGroupTemplatePath.FullName[1])"
)
Invoke-AzOpsPush -ChangeSet $changeSet

Expand Down Expand Up @@ -930,6 +936,23 @@ Describe "Repository" {
}
#endregion

#region Deploy Management Group using folder structure and file
It "ManagementGroup deployment using folder structure and file should be successful" {
$script:pushmgmttest1Deployment = Get-AzTenantDeployment -Name $script:pushmgmttest1idManagementGroupDeploymentName
$pushmgmttest1Deployment.ProvisioningState | Should -Be "Succeeded"
}
It "ManagementGroup deployed using folder structure and file should exist" {
$script:pushmgmttest1Mg = Get-AzManagementGroup -GroupName $script:pushmgmttest1idName
$pushmgmttest1Mg.Name | Should -Be $script:pushmgmttest1idName
}
It "ManagementGroup deployed using folder structure and file at folder scope not matching content parent should fail" {
$changeSet = @(
"A`t$($script:pushmgmttest2idManagementGroupTemplatePath.FullName[1])"
)
{Invoke-AzOpsPush -ChangeSet $changeSet -WhatIf:$true} | Should -Throw
}
#endregion

#region Scope - Policy DeletionDependency
It "Deletion of policyDefinitionsFile with assignment dependency should fail" {
$changeSet = @(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "AzOps"
}
},
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.Management/managementGroups",
"name": "pushmgmttest1id",
"apiVersion": "2021-04-01",
"scope": "/",
"properties": {
"displayName": "pushmgmttest1displayname",
"details": {
"parent": {
"id": "/providers/Microsoft.Management/managementGroups/52fd72ab-b56e-5a52-83a1-1f87365f7998"
}
}
}
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2020-10-01",
"name": "AzOps-microsoft.management_managementgroups-nested",
"location": "[deployment().location]",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [],
"outputs": {}
}
},
"dependsOn": [
"Microsoft.Management/managementGroups/pushmgmttest1id"
]
}
],
"outputs": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "AzOps"
}
},
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.Management/managementGroups",
"name": "pushmgmttest2id",
"apiVersion": "2021-04-01",
"scope": "/",
"properties": {
"displayName": "pushmgmttest2displayname",
"details": {
"parent": {
"id": "/providers/Microsoft.Management/managementGroups/52fd72ab-b56e-5a52-83a1-1f87365f7998"
}
}
}
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2020-10-01",
"name": "AzOps-microsoft.management_managementgroups-nested",
"location": "[deployment().location]",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [],
"outputs": {}
}
},
"dependsOn": [
"Microsoft.Management/managementGroups/pushmgmttest2id"
]
}
],
"outputs": {}
}