Skip to content

Commit

Permalink
feat(new): Added Azure.ASE.AvailabilityZone (Azure#2985)
Browse files Browse the repository at this point in the history
* feat(new): Added Azure.ASE.AvailabilityZone

* fix: Changelog

* fix: Changelog

* Update change log

* Update src/PSRule.Rules.Azure/en/PSRule-rules.psd1

Co-authored-by: Bernie White <[email protected]>

* Update src/PSRule.Rules.Azure/rules/Azure.ASE.Rule.ps1

Co-authored-by: Bernie White <[email protected]>

* fix: Fixed test

---------

Co-authored-by: Bernie White <[email protected]>
  • Loading branch information
BenjaminEngeset and BernieWhite authored Jul 13, 2024
1 parent b8078d7 commit 28aa13c
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 9 deletions.
11 changes: 8 additions & 3 deletions docs/CHANGELOG-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,21 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers

## Unreleased

What's changed since v1.38.0:

- New rules:
- App Service:
- Verify that app service plans have availability zones configured by @BenjaminEngeset.
[#2964](https://github.com/Azure/PSRule.Rules.Azure/issues/2964)
- App Service Environment:
- Verify that app service environments have availability zones configured by @BenjaminEngeset.
[#2964](https://github.com/Azure/PSRule.Rules.Azure/issues/2964)
- Azure SQL Database:
- Verify that Azure SQL databases have a customer-controlled maintenance window configured by @BenjaminEngeset.
[#2956](https://github.com/Azure/PSRule.Rules.Azure/issues/2956)
- Azure SQL Managed Instance:
- Verify that Azure SQL Managed Instances have a customer-controlled maintenance window configured by @BenjaminEngeset.
[#2979](https://github.com/Azure/PSRule.Rules.Azure/issues/2979)
- App Service:
- Verify that app service plans have availability zones configured by @BenjaminEngeset.
[#2964](https://github.com/Azure/PSRule.Rules.Azure/issues/2964)

## v1.38.0

Expand Down
93 changes: 93 additions & 0 deletions docs/en/rules/Azure.ASE.AvailabilityZone.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
severity: Important
pillar: Reliability
category: RE:05 Regions and availability zones
resource: App Service Environment
online version: https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.ASE.AvailabilityZone/
---

# Deploy app service environments using availability zones

## SYNOPSIS

Deploy app service environments using availability zones in supported regions to ensure high availability and resilience.

## DESCRIPTION

App Service Environments support zone redundancy, which distributes your application running within the environment across Availablity Zones.
Each Availability Zone is a group of phyiscally separated data centers.
Deploying your application with zone redundancy:

- Scales your plan within the environment to a minimum of 3 instances in a highly available configuration.
Additional instances can be added manually or on-demand by using autoscale.
- Improves the resiliency against service disruptions or issues affecting a single zone.

Additionally:

- **Even Distribution**: If the instance count is larger than 3 and divisible by 3, instances are evenly distributed across the three zones.
- **Partial Distribution**: Instance counts beyond 3*N are spread across the remaining one or two zones to ensure balanced distribution.

**Important** Configuring zone redundancy with per-application scaling is possible but may increase costs and administrative overhead.
When `perSiteScaling` is enabled, each application can have its own scaling rules and run on dedicated instances.
To maintain zone redundancy, it is crucial that each application’s scaling rules ensure a minimum of 3 instances.
Without explicitly configuring this minimum, the application may not meet the zone redundancy requirement.

## RECOMMENDATION

Consider using enabling zone redundancy using availability zones to improve the resiliency of your solution.

## EXAMPLES

### Configure with Azure template

To configure a zone-redundant app service environment:

- Set the `properties.zoneRedundant` property to `true`.

For example:

```json
{
"type": "Microsoft.Web/hostingEnvironments",
"apiVersion": "2022-09-01",
"name": "[parameters('name')]",
"location": "[parameters('location')]",
"kind": "ASEV3",
"properties": {
"zoneRedundant": true
}
}
```

### Configure with Bicep

To configure a zone-redundant app service environment:

- Set the `properties.zoneRedundant` property to `true`.

For example:

```bicep
resource ase 'Microsoft.Web/hostingEnvironments@2022-09-01' = {
name: name
location: location
kind 'ASEV3'
properties: {
zoneRedundant: true
}
}
```

## NOTES

Zone-redundancy is only supported for the `ASEV3` version.

Zone-redundancy is not supported for environments deployed on a dedicated host group.

## LINKS

- [RE:05 Regions and availability zones](https://learn.microsoft.com/azure/well-architected/reliability/regions-availability-zones)
- [Reliability in Azure App Service](https://learn.microsoft.com/azure/reliability/reliability-app-service)
- [Availability zone support](https://learn.microsoft.com/azure/reliability/reliability-app-service#availability-zone-support)
- [About App Service Environment](https://learn.microsoft.com/azure/app-service/environment/overview)
- [Azure resource deployment](https://learn.microsoft.com/azure/templates/microsoft.web/hostingenvironments)
1 change: 1 addition & 0 deletions src/PSRule.Rules.Azure/en/PSRule-rules.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
InsecureParameterType = "The parameter '{0}' with type '{1}' is not secure."
AzureSQLMIMaintenanceWindow = "The managed instance ({0}) should have a customer-controlled maintenance window configured."
AzureSQLDatabaseMaintenanceWindow = "The {0} ({1}) should have a customer-controlled maintenance window configured."
ASEAvailabilityZoneVersion = "The app service environment ({0}) is not deployed with a version that supports zone-redundancy."
AppServiceAvailabilityZoneSKU = "The app service plan ({0}) is not deployed with a SKU that supports zone-redundancy."
AppServiceAvailabilityZone = "The app service plan ({0}) deployed to region ({1}) should use three availability zones from the following [{2}]."
}
29 changes: 29 additions & 0 deletions src/PSRule.Rules.Azure/rules/Azure.ASE.Rule.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,36 @@
# Validation rules for Azure App Service Environment
#

#region Rules

# Synopsis: Use ASEv3 as replacement for the classic app service environment versions ASEv1 and ASEv2.
Rule 'Azure.ASE.MigrateV3' -Ref 'AZR-000319' -Type 'Microsoft.Web/hostingEnvironments' -Tag @{ release = 'GA'; ruleSet = '2022_12'; 'Azure.WAF/pillar' = 'Operational Excellence'; } {
$Assert.HasFieldValue($TargetObject, 'kind', 'ASEV3').Reason($LocalizedData.ClassicASEDeprecated, $PSRule.TargetName, $TargetObject.kind)
}

# Synopsis: Deploy app service environments using availability zones in supported regions to ensure high availability and resilience.
Rule 'Azure.ASE.AvailabilityZone' -Ref 'AZR-000443' -Type 'Microsoft.Web/hostingEnvironments' -Tag @{ release = 'GA'; ruleSet = '2024_09 '; 'Azure.WAF/pillar' = 'Reliability'; } {
# Dedicated host group does not support zone-redundancy.
if ($TargetObject.properties.dedicatedHostCount) {
return $Assert.Pass()
}

# Check if the region supports availability zones.
$provider = [PSRule.Rules.Azure.Runtime.Helper]::GetResourceType('Microsoft.Web', 'hostingEnvironments')
$availabilityZones = GetAvailabilityZone -Location $TargetObject.Location -Zone $provider.ZoneMappings

# Don't flag if the region does not support availability zones.
if (-not $availabilityZones) {
return $Assert.Pass()
}

# Availability zones are only supported for the ASEv3 version (modern footprint).
$Assert.HasFieldValue($TargetObject, 'kind', 'ASEV3').Reason(
$LocalizedData.ASEAvailabilityZoneVersion,
$TargetObject.name
)

$Assert.HasFieldValue($TargetObject, 'properties.zoneRedundant', $true)
}

#endregion Rules
28 changes: 26 additions & 2 deletions tests/PSRule.Rules.Azure.Tests/Azure.ASE.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,32 @@ Describe 'Azure.ASE' -Tag 'ASE' {
# Pass
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 1;
$ruleResult.TargetName | Should -BeIn 'environment-C';
$ruleResult.Length | Should -Be 3;
$ruleResult.TargetName | Should -BeIn 'environment-C', 'environment-E', 'environment-F';
}

It 'Azure.ASE.AvailabilityZone' {
$filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.ASE.AvailabilityZone' };

# Fail
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
$ruleResult.Length | Should -Be 3;
$ruleResult.TargetName | Should -BeIn 'environment-A', 'environment-B', 'environment-D';

$ruleResult[0].Reason | Should -BeExactly @(
"The app service environment (environment-A) is not deployed with a version that supports zone-redundancy."
"Path properties.zoneRedundant: Does not exist."
):
$ruleResult[1].Reason | Should -BeExactly @(
"The app service environment (environment-B) is not deployed with a version that supports zone-redundancy."
"Path properties.zoneRedundant: Is set to 'False'."
):
$ruleResult[2].Reason | Should -BeExactly "The app service environment (environment-D) is not deployed with a version that supports zone-redundancy.";

# Pass
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
$ruleResult.Length | Should -Be 3;
$ruleResult.TargetName | Should -Be 'environment-C', 'environment-E', 'environment-F';
}
}
}
56 changes: 52 additions & 4 deletions tests/PSRule.Rules.Azure.Tests/Resources.ASE.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
"virtualNetwork": {
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/vnet-A",
"subnet": "subnet-A"
},
"zoneRedundant": true
}
},
"ResourceGroupName": "rg-test",
"Type": "microsoft.web/hostingenvironments",
Expand Down Expand Up @@ -52,7 +51,7 @@
"Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Web/hostingEnvironments/environment-B/serverfarms/plan-A",
"Identity": null,
"Kind": null,
"Location": "region",
"Location": "westeurope",
"ManagedBy": null,
"ResourceName": "plan-A",
"Name": "plan-A",
Expand Down Expand Up @@ -88,7 +87,7 @@
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Web/hostingEnvironments/environment-C",
"Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Web/hostingEnvironments/environment-C",
"Identity": null,
"Location": "westeurope",
"Location": "notregion",
"ManagedBy": null,
"ResourceName": "environment-C",
"Name": "environment-C",
Expand Down Expand Up @@ -167,5 +166,54 @@
"Sku": null,
"Tags": null,
"SubscriptionId": "00000000-0000-0000-0000-000000000000"
},
{
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Web/hostingEnvironments/environment-E",
"Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Web/hostingEnvironments/environment-E",
"Identity": null,
"Location": "westeurope",
"ManagedBy": null,
"ResourceName": "environment-E",
"Name": "environment-E",
"Kind": "ASEV3",
"Properties": {
"upgradePreference": "Early",
"virtualNetwork": {
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/vnet-A",
"subnet": "subnet-A"
},
"zoneRedundant": true
},
"ResourceGroupName": "rg-test",
"Type": "microsoft.web/hostingenvironments",
"ResourceType": "microsoft.web/hostingenvironments",
"Sku": null,
"Tags": null,
"SubscriptionId": "00000000-0000-0000-0000-000000000000"
},
{
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Web/hostingEnvironments/environment-F",
"Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Web/hostingEnvironments/environment-F",
"Identity": null,
"Location": "westeurope",
"ManagedBy": null,
"ResourceName": "environment-F",
"Name": "environment-E",
"Kind": "ASEV3",
"Properties": {
"upgradePreference": "Early",
"virtualNetwork": {
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/vnet-A",
"subnet": "subnet-A"
},
"dedicatedHostCount": 2,
"zoneRedundant": false
},
"ResourceGroupName": "rg-test",
"Type": "microsoft.web/hostingenvironments",
"ResourceType": "microsoft.web/hostingenvironments",
"Sku": null,
"Tags": null,
"SubscriptionId": "00000000-0000-0000-0000-000000000000"
}
]

0 comments on commit 28aa13c

Please sign in to comment.