pkg/deploy/generator/resources_gateway.go (416 lines of code) (raw):
package generator
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"encoding/base64"
"fmt"
"strings"
mgmtcompute "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-12-01/compute"
mgmtkeyvault "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2019-09-01/keyvault"
mgmtmsi "github.com/Azure/azure-sdk-for-go/services/msi/mgmt/2018-11-30/msi"
mgmtnetwork "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2020-08-01/network"
"github.com/Azure/go-autorest/autorest/to"
"github.com/Azure/ARO-RP/pkg/env"
"github.com/Azure/ARO-RP/pkg/util/arm"
"github.com/Azure/ARO-RP/pkg/util/azureclient"
"github.com/Azure/ARO-RP/pkg/util/rbac"
"github.com/Azure/ARO-RP/pkg/util/version"
)
func (g *generator) gatewayManagedIdentity() *arm.Resource {
return &arm.Resource{
Resource: &mgmtmsi.Identity{
Type: to.StringPtr("Microsoft.ManagedIdentity/userAssignedIdentities"),
Name: to.StringPtr("[concat('aro-gateway-', resourceGroup().location)]"),
Location: to.StringPtr("[resourceGroup().location]"),
},
APIVersion: azureclient.APIVersion("Microsoft.ManagedIdentity"),
}
}
func (g *generator) gatewaySecurityGroup() *arm.Resource {
return g.securityGroup("gateway-nsg", nil, g.conditionStanza("deployNSGs"))
}
func (g *generator) gatewayVnet() *arm.Resource {
return g.virtualNetwork("gateway-vnet", "10.0.8.0/24", &[]mgmtnetwork.Subnet{
{
SubnetPropertiesFormat: &mgmtnetwork.SubnetPropertiesFormat{
AddressPrefix: to.StringPtr("10.0.8.0/24"),
NetworkSecurityGroup: &mgmtnetwork.SecurityGroup{
ID: to.StringPtr("[resourceId('Microsoft.Network/networkSecurityGroups', 'gateway-nsg')]"),
},
ServiceEndpoints: &[]mgmtnetwork.ServiceEndpointPropertiesFormat{
{
Service: to.StringPtr("Microsoft.AzureCosmosDB"),
Locations: &[]string{"*"},
},
{
Service: to.StringPtr("Microsoft.ContainerRegistry"),
Locations: &[]string{"*"},
},
{
Service: to.StringPtr("Microsoft.EventHub"),
Locations: &[]string{"*"},
},
{
Service: to.StringPtr("Microsoft.Storage"),
Locations: &[]string{"*"},
},
{
Service: to.StringPtr("Microsoft.KeyVault"),
Locations: &[]string{"*"},
},
},
PrivateLinkServiceNetworkPolicies: to.StringPtr("Disabled"),
},
Name: to.StringPtr("gateway-subnet"),
},
}, nil, []string{"[resourceId('Microsoft.Network/networkSecurityGroups', 'gateway-nsg')]"})
}
func (g *generator) gatewayLB() *arm.Resource {
return &arm.Resource{
Resource: &mgmtnetwork.LoadBalancer{
Sku: &mgmtnetwork.LoadBalancerSku{
Name: mgmtnetwork.LoadBalancerSkuNameStandard,
},
LoadBalancerPropertiesFormat: &mgmtnetwork.LoadBalancerPropertiesFormat{
FrontendIPConfigurations: &[]mgmtnetwork.FrontendIPConfiguration{
{
FrontendIPConfigurationPropertiesFormat: &mgmtnetwork.FrontendIPConfigurationPropertiesFormat{
Subnet: &mgmtnetwork.Subnet{
ID: to.StringPtr("[resourceId('Microsoft.Network/virtualNetworks/subnets', 'gateway-vnet', 'gateway-subnet')]"),
},
},
Zones: &[]string{},
Name: to.StringPtr("gateway-frontend"),
},
},
BackendAddressPools: &[]mgmtnetwork.BackendAddressPool{
{
Name: to.StringPtr("gateway-backend"),
},
},
LoadBalancingRules: &[]mgmtnetwork.LoadBalancingRule{
{
LoadBalancingRulePropertiesFormat: &mgmtnetwork.LoadBalancingRulePropertiesFormat{
FrontendIPConfiguration: &mgmtnetwork.SubResource{
ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', 'gateway-lb-internal', 'gateway-frontend')]"),
},
BackendAddressPool: &mgmtnetwork.SubResource{
ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', 'gateway-lb-internal', 'gateway-backend')]"),
},
Probe: &mgmtnetwork.SubResource{
ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/probes', 'gateway-lb-internal', 'gateway-probe')]"),
},
Protocol: mgmtnetwork.TransportProtocolTCP,
LoadDistribution: mgmtnetwork.LoadDistributionDefault,
FrontendPort: to.Int32Ptr(443),
BackendPort: to.Int32Ptr(443),
},
Name: to.StringPtr("gateway-lbrule-https"),
},
{
LoadBalancingRulePropertiesFormat: &mgmtnetwork.LoadBalancingRulePropertiesFormat{
FrontendIPConfiguration: &mgmtnetwork.SubResource{
ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', 'gateway-lb-internal', 'gateway-frontend')]"),
},
BackendAddressPool: &mgmtnetwork.SubResource{
ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', 'gateway-lb-internal', 'gateway-backend')]"),
},
Probe: &mgmtnetwork.SubResource{
ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/probes', 'gateway-lb-internal', 'gateway-probe')]"),
},
Protocol: mgmtnetwork.TransportProtocolTCP,
LoadDistribution: mgmtnetwork.LoadDistributionDefault,
FrontendPort: to.Int32Ptr(80),
BackendPort: to.Int32Ptr(80),
},
Name: to.StringPtr("gateway-lbrule-http"),
},
},
Probes: &[]mgmtnetwork.Probe{
{
ProbePropertiesFormat: &mgmtnetwork.ProbePropertiesFormat{
Protocol: mgmtnetwork.ProbeProtocolHTTP,
Port: to.Int32Ptr(80),
NumberOfProbes: to.Int32Ptr(2),
RequestPath: to.StringPtr("/healthz/ready"),
},
Name: to.StringPtr("gateway-probe"),
},
},
},
Name: to.StringPtr("gateway-lb-internal"),
Type: to.StringPtr("Microsoft.Network/loadBalancers"),
Location: to.StringPtr("[resourceGroup().location]"),
},
APIVersion: azureclient.APIVersion("Microsoft.Network"),
}
}
func (g *generator) gatewayPLS() *arm.Resource {
return &arm.Resource{
Resource: &mgmtnetwork.PrivateLinkService{
PrivateLinkServiceProperties: &mgmtnetwork.PrivateLinkServiceProperties{
LoadBalancerFrontendIPConfigurations: &[]mgmtnetwork.FrontendIPConfiguration{
{
ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', 'gateway-lb-internal', 'gateway-frontend')]"),
},
},
IPConfigurations: &[]mgmtnetwork.PrivateLinkServiceIPConfiguration{
{
PrivateLinkServiceIPConfigurationProperties: &mgmtnetwork.PrivateLinkServiceIPConfigurationProperties{
Subnet: &mgmtnetwork.Subnet{
ID: to.StringPtr("[resourceId('Microsoft.Network/virtualNetworks/subnets', 'gateway-vnet', 'gateway-subnet')]"),
},
},
Name: to.StringPtr("gateway-pls-001-nic"),
},
},
EnableProxyProtocol: to.BoolPtr(true),
},
Name: to.StringPtr("gateway-pls-001"),
Type: to.StringPtr("Microsoft.Network/privateLinkServices"),
Location: to.StringPtr("[resourceGroup().location]"),
},
APIVersion: azureclient.APIVersion("Microsoft.Network"),
DependsOn: []string{
"Microsoft.Network/loadBalancers/gateway-lb-internal",
},
}
}
func (g *generator) gatewayVMSS() *arm.Resource {
// TODO: there is a lot of duplication with rpVMSS()
parts := []string{
fmt.Sprintf("base64ToString('%s')", base64.StdEncoding.EncodeToString([]byte("set -ex\n\n"))),
}
for _, variable := range []string{
"acrResourceId",
"azureCloudName",
"azureSecPackQualysUrl",
"azureSecPackVSATenantId",
"databaseAccountName",
"mdmFrontendUrl",
"mdsdEnvironment",
"fluentbitImage",
"gatewayMdsdConfigVersion",
"gatewayDomains",
"gatewayFeatures",
"keyvaultDNSSuffix",
"keyvaultPrefix",
"rpImage",
"rpMdmAccount",
"rpMdsdAccount",
"rpMdsdNamespace",
} {
parts = append(parts,
fmt.Sprintf("'%s=$(base64 -d <<<'''", strings.ToUpper(variable)),
fmt.Sprintf("base64(parameters('%s'))", variable),
"''')\n'",
)
}
parts = append(parts,
"'MDMIMAGE=''"+version.MdmImage("")+"''\n'",
)
parts = append(parts,
"'LOCATION=$(base64 -d <<<'''",
"base64(resourceGroup().location)",
"''')\n'",
)
parts = append(parts,
"'SUBSCRIPTIONID=$(base64 -d <<<'''",
"base64(subscription().subscriptionId)",
"''')\n'",
)
parts = append(parts,
"'RESOURCEGROUPNAME=$(base64 -d <<<'''",
"base64(resourceGroup().name)",
"''')\n'",
)
// VMSS extensions only support one custom script
// Because of this, the util-*.sh scripts are prefixed to the bootstrapping script
// main is called at the end of the bootstrapping script, so appending them will not work
bootstrapScript := scriptUtilCommon +
scriptUtilPackages +
scriptUtilServices +
scriptUtilSystem +
scriptGatewayVMSS
trailer := base64.StdEncoding.EncodeToString([]byte(bootstrapScript))
parts = append(parts, "'\n'", fmt.Sprintf("base64ToString('%s')", trailer))
customScript := fmt.Sprintf("[base64(concat(%s))]", strings.Join(parts, ","))
return &arm.Resource{
Resource: &mgmtcompute.VirtualMachineScaleSet{
Sku: &mgmtcompute.Sku{
Name: to.StringPtr("[parameters('gatewayVmSize')]"),
Tier: to.StringPtr("Standard"),
Capacity: to.Int64Ptr(1339),
},
Tags: map[string]*string{},
VirtualMachineScaleSetProperties: &mgmtcompute.VirtualMachineScaleSetProperties{
// Reference: https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-automatic-upgrade#arm-templates
UpgradePolicy: &mgmtcompute.UpgradePolicy{
Mode: mgmtcompute.UpgradeModeAutomatic,
RollingUpgradePolicy: &mgmtcompute.RollingUpgradePolicy{
// Percentage equates to 1.02 instances out of 3
MaxBatchInstancePercent: to.Int32Ptr(34),
MaxUnhealthyInstancePercent: to.Int32Ptr(34),
MaxUnhealthyUpgradedInstancePercent: to.Int32Ptr(34),
PauseTimeBetweenBatches: to.StringPtr("PT10M"),
},
AutomaticOSUpgradePolicy: &mgmtcompute.AutomaticOSUpgradePolicy{
EnableAutomaticOSUpgrade: to.BoolPtr(true),
},
},
VirtualMachineProfile: &mgmtcompute.VirtualMachineScaleSetVMProfile{
OsProfile: &mgmtcompute.VirtualMachineScaleSetOSProfile{
ComputerNamePrefix: to.StringPtr("[concat('gateway-', parameters('vmssName'), '-')]"),
AdminUsername: to.StringPtr("cloud-user"),
LinuxConfiguration: &mgmtcompute.LinuxConfiguration{
DisablePasswordAuthentication: to.BoolPtr(true),
SSH: &mgmtcompute.SSHConfiguration{
PublicKeys: &[]mgmtcompute.SSHPublicKey{
{
Path: to.StringPtr("/home/cloud-user/.ssh/authorized_keys"),
KeyData: to.StringPtr("[parameters('sshPublicKey')]"),
},
},
},
},
},
StorageProfile: &mgmtcompute.VirtualMachineScaleSetStorageProfile{
ImageReference: &mgmtcompute.ImageReference{
// cbl-mariner-2-gen2-fips is not supported by Automatic OS Updates
// therefore the non fips image is used, and fips is configured manually
// Reference: https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-automatic-upgrade
// https://eng.ms/docs/cloud-ai-platform/azure-core/azure-compute/compute-platform-arunki/azure-compute-artifacts/azure-compute-artifacts-docs/project-standard/1pgalleryusageinstructions#vmss-deployment-with-1p-image-galleryarm-template
// https://eng.ms/docs/cloud-ai-platform/azure-core/core-compute-and-host/compute-platform-arunki/azure-compute-artifacts/azure-compute-artifacts-docs/project-standard/1pgalleryimagereference#cbl-mariner-2-images
SharedGalleryImageID: to.StringPtr("/sharedGalleries/CblMariner.1P/images/cbl-mariner-2-gen2/versions/latest"),
},
OsDisk: &mgmtcompute.VirtualMachineScaleSetOSDisk{
CreateOption: mgmtcompute.DiskCreateOptionTypesFromImage,
ManagedDisk: &mgmtcompute.VirtualMachineScaleSetManagedDiskParameters{
StorageAccountType: mgmtcompute.StorageAccountTypesPremiumLRS,
},
DiskSizeGB: to.Int32Ptr(1024),
},
},
NetworkProfile: &mgmtcompute.VirtualMachineScaleSetNetworkProfile{
HealthProbe: &mgmtcompute.APIEntityReference{
ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/probes', 'gateway-lb-internal', 'gateway-probe')]"),
},
NetworkInterfaceConfigurations: &[]mgmtcompute.VirtualMachineScaleSetNetworkConfiguration{
{
Name: to.StringPtr("gateway-vmss-nic"),
VirtualMachineScaleSetNetworkConfigurationProperties: &mgmtcompute.VirtualMachineScaleSetNetworkConfigurationProperties{
Primary: to.BoolPtr(true),
// disabling accelerated networking due to egress issues
// see icm 271210960 (egress) and 274977072 (accelerated networking team)
EnableAcceleratedNetworking: to.BoolPtr(false),
IPConfigurations: &[]mgmtcompute.VirtualMachineScaleSetIPConfiguration{
{
Name: to.StringPtr("gateway-vmss-ipconfig"),
VirtualMachineScaleSetIPConfigurationProperties: &mgmtcompute.VirtualMachineScaleSetIPConfigurationProperties{
Subnet: &mgmtcompute.APIEntityReference{
ID: to.StringPtr("[resourceId('Microsoft.Network/virtualNetworks/subnets', 'gateway-vnet', 'gateway-subnet')]"),
},
Primary: to.BoolPtr(true),
PublicIPAddressConfiguration: &mgmtcompute.VirtualMachineScaleSetPublicIPAddressConfiguration{
Name: to.StringPtr("gateway-vmss-pip"),
},
LoadBalancerBackendAddressPools: &[]mgmtcompute.SubResource{
{
ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', 'gateway-lb-internal', 'gateway-backend')]"),
},
},
},
},
},
},
},
},
},
ExtensionProfile: &mgmtcompute.VirtualMachineScaleSetExtensionProfile{
Extensions: &[]mgmtcompute.VirtualMachineScaleSetExtension{
{
Name: to.StringPtr("gateway-vmss-cse"),
VirtualMachineScaleSetExtensionProperties: &mgmtcompute.VirtualMachineScaleSetExtensionProperties{
Publisher: to.StringPtr("Microsoft.Azure.Extensions"),
Type: to.StringPtr("CustomScript"),
TypeHandlerVersion: to.StringPtr("2.0"),
AutoUpgradeMinorVersion: to.BoolPtr(true),
Settings: map[string]interface{}{},
ProtectedSettings: map[string]interface{}{
"script": customScript,
},
},
},
{
// az-secmonitor package no longer needs to be manually installed
// References:
// https://eng.ms/docs/products/azure-linux/gettingstarted/aks/monitoring
// https://msazure.visualstudio.com/ASMDocs/_wiki/wikis/ASMDocs.wiki/179541/Linux-AzSecPack-AutoConfig-Onboarding-(manual-for-C-AI)?anchor=3.1.1-using-arm-template-resource-elements
Name: to.StringPtr("AzureMonitorLinuxAgent"),
VirtualMachineScaleSetExtensionProperties: &mgmtcompute.VirtualMachineScaleSetExtensionProperties{
Publisher: to.StringPtr("Microsoft.Azure.Monitor"),
EnableAutomaticUpgrade: to.BoolPtr(true),
AutoUpgradeMinorVersion: to.BoolPtr(true),
TypeHandlerVersion: to.StringPtr("1.0"),
Type: to.StringPtr("AzureMonitorLinuxAgent"),
Settings: map[string]interface{}{
"GCS_AUTO_CONFIG": true,
},
},
},
},
},
DiagnosticsProfile: &mgmtcompute.DiagnosticsProfile{
BootDiagnostics: &mgmtcompute.BootDiagnostics{
Enabled: to.BoolPtr(true),
},
},
SecurityProfile: &mgmtcompute.SecurityProfile{
// Required for 1P Image Gallery Use
// https://eng.ms/docs/cloud-ai-platform/azure-core/azure-compute/compute-platform-arunki/azure-compute-artifacts/azure-compute-artifacts-docs/project-standard/1pgalleryusageinstructions#enable-trusted-launch-for-vmss
SecurityType: mgmtcompute.SecurityTypesTrustedLaunch,
},
},
Overprovision: to.BoolPtr(false),
},
Identity: &mgmtcompute.VirtualMachineScaleSetIdentity{
Type: mgmtcompute.ResourceIdentityTypeUserAssigned,
UserAssignedIdentities: map[string]*mgmtcompute.VirtualMachineScaleSetIdentityUserAssignedIdentitiesValue{
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', concat('aro-gateway-', resourceGroup().location))]": {},
},
},
Name: to.StringPtr("[concat('gateway-vmss-', parameters('vmssName'))]"),
Type: to.StringPtr("Microsoft.Compute/virtualMachineScaleSets"),
Location: to.StringPtr("[resourceGroup().location]"),
},
APIVersion: azureclient.APIVersion("Microsoft.Compute"),
DependsOn: []string{
"[resourceId('Microsoft.Network/loadBalancers', 'gateway-lb-internal')]",
},
}
}
func (g *generator) gatewayKeyvaultAccessPolicies() []mgmtkeyvault.AccessPolicyEntry {
return []mgmtkeyvault.AccessPolicyEntry{
{
TenantID: &tenantUUIDHack,
ObjectID: to.StringPtr("[parameters('gatewayServicePrincipalId')]"),
Permissions: &mgmtkeyvault.Permissions{
Secrets: &[]mgmtkeyvault.SecretPermissions{
mgmtkeyvault.SecretPermissionsGet,
},
},
},
}
}
func (g *generator) gatewayKeyvault() *arm.Resource {
return &arm.Resource{
Resource: &mgmtkeyvault.Vault{
Properties: &mgmtkeyvault.VaultProperties{
EnableSoftDelete: to.BoolPtr(true),
TenantID: &tenantUUIDHack,
Sku: &mgmtkeyvault.Sku{
Name: mgmtkeyvault.Standard,
Family: to.StringPtr("A"),
},
AccessPolicies: &[]mgmtkeyvault.AccessPolicyEntry{
{
ObjectID: to.StringPtr(gatewayAccessPolicyHack),
},
},
},
Name: to.StringPtr("[concat(parameters('keyvaultPrefix'), '" + env.GatewayKeyvaultSuffix + "')]"),
Type: to.StringPtr("Microsoft.KeyVault/vaults"),
Location: to.StringPtr("[resourceGroup().location]"),
},
APIVersion: azureclient.APIVersion("Microsoft.KeyVault"),
}
}
func (g *generator) gatewayRBAC() []*arm.Resource {
return []*arm.Resource{
rbac.ResourceRoleAssignment(
rbac.RoleNetworkContributor,
"parameters('rpServicePrincipalId')",
"Microsoft.Network/privateLinkServices",
"'gateway-pls-001'",
),
}
}