pkg/deploy/generator/resources_rp.go (1,443 lines of code) (raw):

package generator // Copyright (c) Microsoft Corporation. // Licensed under the Apache License 2.0. import ( "encoding/base64" "fmt" "strings" sdkcosmos "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cosmos/armcosmos/v2" 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" mgmtauthorization "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-09-01-preview/authorization" mgmtcontainerregistry "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2020-11-01-preview/containerregistry" mgmtinsights "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2018-03-01/insights" mgmtstorage "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-09-01/storage" "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) rpManagedIdentity() *arm.Resource { return &arm.Resource{ Resource: &mgmtmsi.Identity{ Type: to.StringPtr("Microsoft.ManagedIdentity/userAssignedIdentities"), Name: to.StringPtr("[concat('aro-rp-', resourceGroup().location)]"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.ManagedIdentity"), } } func (g *generator) rpSecurityGroupForPortalSourceAddressPrefixes() *arm.Resource { return g.securityRules("rp-nsg/portal_in", &mgmtnetwork.SecurityRulePropertiesFormat{ Protocol: mgmtnetwork.SecurityRuleProtocolTCP, SourcePortRange: to.StringPtr("*"), DestinationPortRange: to.StringPtr("444"), SourceAddressPrefixes: &[]string{}, DestinationAddressPrefix: to.StringPtr("*"), Access: mgmtnetwork.SecurityRuleAccessAllow, Priority: to.Int32Ptr(142), Direction: mgmtnetwork.SecurityRuleDirectionInbound, }, "[not(empty(parameters('rpNsgPortalSourceAddressPrefixes')))]") } func (g *generator) rpSecurityGroup() *arm.Resource { rules := []mgmtnetwork.SecurityRule{ { SecurityRulePropertiesFormat: &mgmtnetwork.SecurityRulePropertiesFormat{ Protocol: mgmtnetwork.SecurityRuleProtocolTCP, SourcePortRange: to.StringPtr("*"), DestinationPortRange: to.StringPtr("443"), SourceAddressPrefix: to.StringPtr("AzureResourceManager"), DestinationAddressPrefix: to.StringPtr("*"), Access: mgmtnetwork.SecurityRuleAccessAllow, Priority: to.Int32Ptr(120), Direction: mgmtnetwork.SecurityRuleDirectionInbound, }, Name: to.StringPtr("rp_in_arm"), }, { SecurityRulePropertiesFormat: &mgmtnetwork.SecurityRulePropertiesFormat{ Protocol: mgmtnetwork.SecurityRuleProtocolTCP, SourcePortRange: to.StringPtr("*"), DestinationPortRange: to.StringPtr("443"), SourceAddressPrefix: to.StringPtr("GenevaActions"), DestinationAddressPrefix: to.StringPtr("*"), Access: mgmtnetwork.SecurityRuleAccessAllow, Priority: to.Int32Ptr(130), Direction: mgmtnetwork.SecurityRuleDirectionInbound, }, Name: to.StringPtr("rp_in_geneva"), }, } if !g.production { // override production ARM flag for more open configuration in development rules[0].SecurityRulePropertiesFormat.SourceAddressPrefix = to.StringPtr("*") rules = append(rules, mgmtnetwork.SecurityRule{ SecurityRulePropertiesFormat: &mgmtnetwork.SecurityRulePropertiesFormat{ Protocol: mgmtnetwork.SecurityRuleProtocolTCP, SourcePortRange: to.StringPtr("*"), DestinationPortRange: to.StringPtr("22"), SourceAddressPrefix: to.StringPtr("*"), DestinationAddressPrefix: to.StringPtr("*"), Access: mgmtnetwork.SecurityRuleAccessAllow, Priority: to.Int32Ptr(125), Direction: mgmtnetwork.SecurityRuleDirectionInbound, }, Name: to.StringPtr("ssh_in"), }) } else { rules = append(rules, mgmtnetwork.SecurityRule{ SecurityRulePropertiesFormat: &mgmtnetwork.SecurityRulePropertiesFormat{ Protocol: mgmtnetwork.SecurityRuleProtocolTCP, SourcePortRange: to.StringPtr("*"), DestinationPortRange: to.StringPtr("*"), SourceAddressPrefix: to.StringPtr("10.0.8.0/24"), DestinationAddressPrefix: to.StringPtr("*"), Access: mgmtnetwork.SecurityRuleAccessDeny, Priority: to.Int32Ptr(145), Direction: mgmtnetwork.SecurityRuleDirectionInbound, }, Name: to.StringPtr("deny_in_gateway"), }, ) } return g.securityGroup("rp-nsg", &rules, g.conditionStanza("deployNSGs")) } func (g *generator) rpPESecurityGroup() *arm.Resource { return g.securityGroup("rp-pe-nsg", nil, g.conditionStanza("deployNSGs")) } func (g *generator) rpVnet() *arm.Resource { addressPrefix := "10.1.0.0/24" if g.production { addressPrefix = "10.0.0.0/24" } subnet := mgmtnetwork.Subnet{ SubnetPropertiesFormat: &mgmtnetwork.SubnetPropertiesFormat{ AddressPrefix: to.StringPtr(addressPrefix), NetworkSecurityGroup: &mgmtnetwork.SecurityGroup{ ID: to.StringPtr("[resourceId('Microsoft.Network/networkSecurityGroups', 'rp-nsg')]"), }, ServiceEndpoints: &[]mgmtnetwork.ServiceEndpointPropertiesFormat{ { Service: to.StringPtr("Microsoft.Storage"), Locations: &[]string{"*"}, }, }, }, Name: to.StringPtr("rp-subnet"), } if g.production { *subnet.ServiceEndpoints = append(*subnet.ServiceEndpoints, []mgmtnetwork.ServiceEndpointPropertiesFormat{ { Service: to.StringPtr("Microsoft.KeyVault"), Locations: &[]string{"*"}, }, { Service: to.StringPtr("Microsoft.AzureCosmosDB"), Locations: &[]string{"*"}, }, }...) } return g.virtualNetwork("rp-vnet", addressPrefix, &[]mgmtnetwork.Subnet{subnet}, nil, []string{"[resourceId('Microsoft.Network/networkSecurityGroups', 'rp-nsg')]"}) } func (g *generator) rpPEVnet() *arm.Resource { return g.virtualNetwork("rp-pe-vnet-001", "10.0.4.0/22", &[]mgmtnetwork.Subnet{ { SubnetPropertiesFormat: &mgmtnetwork.SubnetPropertiesFormat{ AddressPrefix: to.StringPtr("10.0.4.0/22"), NetworkSecurityGroup: &mgmtnetwork.SecurityGroup{ ID: to.StringPtr("[resourceId('Microsoft.Network/networkSecurityGroups', 'rp-pe-nsg')]"), }, PrivateEndpointNetworkPolicies: to.StringPtr("Disabled"), ServiceEndpoints: &[]mgmtnetwork.ServiceEndpointPropertiesFormat{ { Service: to.StringPtr("Microsoft.Storage"), Locations: &[]string{"*"}, }, }, }, Name: to.StringPtr("rp-pe-subnet"), }, }, nil, []string{"[resourceId('Microsoft.Network/networkSecurityGroups', 'rp-pe-nsg')]"}) } func (g *generator) rpLB() *arm.Resource { return &arm.Resource{ Resource: &mgmtnetwork.LoadBalancer{ Sku: &mgmtnetwork.LoadBalancerSku{ Name: mgmtnetwork.LoadBalancerSkuNameStandard, }, LoadBalancerPropertiesFormat: &mgmtnetwork.LoadBalancerPropertiesFormat{ FrontendIPConfigurations: &[]mgmtnetwork.FrontendIPConfiguration{ { FrontendIPConfigurationPropertiesFormat: &mgmtnetwork.FrontendIPConfigurationPropertiesFormat{ PublicIPAddress: &mgmtnetwork.PublicIPAddress{ ID: to.StringPtr("[resourceId('Microsoft.Network/publicIPAddresses', 'rp-pip')]"), }, }, Name: to.StringPtr("rp-frontend"), }, { FrontendIPConfigurationPropertiesFormat: &mgmtnetwork.FrontendIPConfigurationPropertiesFormat{ PublicIPAddress: &mgmtnetwork.PublicIPAddress{ ID: to.StringPtr("[resourceId('Microsoft.Network/publicIPAddresses', 'portal-pip')]"), }, }, Name: to.StringPtr("portal-frontend"), }, }, BackendAddressPools: &[]mgmtnetwork.BackendAddressPool{ { Name: to.StringPtr("rp-backend"), }, }, LoadBalancingRules: &[]mgmtnetwork.LoadBalancingRule{ { LoadBalancingRulePropertiesFormat: &mgmtnetwork.LoadBalancingRulePropertiesFormat{ FrontendIPConfiguration: &mgmtnetwork.SubResource{ ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', 'rp-lb', 'rp-frontend')]"), }, BackendAddressPool: &mgmtnetwork.SubResource{ ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', 'rp-lb', 'rp-backend')]"), }, Probe: &mgmtnetwork.SubResource{ ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/probes', 'rp-lb', 'rp-probe')]"), }, Protocol: mgmtnetwork.TransportProtocolTCP, LoadDistribution: mgmtnetwork.LoadDistributionDefault, FrontendPort: to.Int32Ptr(443), BackendPort: to.Int32Ptr(443), }, Name: to.StringPtr("rp-lbrule"), }, { LoadBalancingRulePropertiesFormat: &mgmtnetwork.LoadBalancingRulePropertiesFormat{ FrontendIPConfiguration: &mgmtnetwork.SubResource{ ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', 'rp-lb', 'portal-frontend')]"), }, BackendAddressPool: &mgmtnetwork.SubResource{ ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', 'rp-lb', 'rp-backend')]"), }, Probe: &mgmtnetwork.SubResource{ ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/probes', 'rp-lb', 'portal-probe-https')]"), }, Protocol: mgmtnetwork.TransportProtocolTCP, LoadDistribution: mgmtnetwork.LoadDistributionDefault, FrontendPort: to.Int32Ptr(443), BackendPort: to.Int32Ptr(444), }, Name: to.StringPtr("portal-lbrule"), }, { LoadBalancingRulePropertiesFormat: &mgmtnetwork.LoadBalancingRulePropertiesFormat{ FrontendIPConfiguration: &mgmtnetwork.SubResource{ ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', 'rp-lb', 'portal-frontend')]"), }, BackendAddressPool: &mgmtnetwork.SubResource{ ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', 'rp-lb', 'rp-backend')]"), }, Probe: &mgmtnetwork.SubResource{ ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/probes', 'rp-lb', 'portal-probe-ssh')]"), }, Protocol: mgmtnetwork.TransportProtocolTCP, LoadDistribution: mgmtnetwork.LoadDistributionDefault, FrontendPort: to.Int32Ptr(22), BackendPort: to.Int32Ptr(2222), }, Name: to.StringPtr("portal-lbrule-ssh"), }, }, Probes: &[]mgmtnetwork.Probe{ { ProbePropertiesFormat: &mgmtnetwork.ProbePropertiesFormat{ Protocol: mgmtnetwork.ProbeProtocolHTTPS, Port: to.Int32Ptr(443), NumberOfProbes: to.Int32Ptr(2), RequestPath: to.StringPtr("/healthz/ready"), }, Name: to.StringPtr("rp-probe"), }, { ProbePropertiesFormat: &mgmtnetwork.ProbePropertiesFormat{ Protocol: mgmtnetwork.ProbeProtocolHTTPS, Port: to.Int32Ptr(444), NumberOfProbes: to.Int32Ptr(2), RequestPath: to.StringPtr("/healthz/ready"), }, Name: to.StringPtr("portal-probe-https"), }, { ProbePropertiesFormat: &mgmtnetwork.ProbePropertiesFormat{ Protocol: mgmtnetwork.ProbeProtocolTCP, Port: to.Int32Ptr(2222), NumberOfProbes: to.Int32Ptr(2), }, Name: to.StringPtr("portal-probe-ssh"), }, }, }, Name: to.StringPtr("rp-lb"), Type: to.StringPtr("Microsoft.Network/loadBalancers"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.Network"), DependsOn: []string{ "[resourceId('Microsoft.Network/publicIPAddresses', 'portal-pip')]", "[resourceId('Microsoft.Network/publicIPAddresses', 'rp-pip')]", }, } } // rpLBAlert generates an alert resource for the rp-lb healthprobe metric func (g *generator) rpLBAlert(threshold float64, severity int32, name string, evalFreq string, windowSize string, metric string) *arm.Resource { return &arm.Resource{ Resource: mgmtinsights.MetricAlertResource{ MetricAlertProperties: &mgmtinsights.MetricAlertProperties{ Actions: &[]mgmtinsights.MetricAlertAction{ { ActionGroupID: to.StringPtr("[resourceId(parameters('subscriptionResourceGroupName'), 'Microsoft.Insights/actionGroups', 'rp-health-ag')]"), }, }, Enabled: to.BoolPtr(true), EvaluationFrequency: to.StringPtr(evalFreq), Severity: to.Int32Ptr(severity), Scopes: &[]string{ "[resourceId('Microsoft.Network/loadBalancers', 'rp-lb')]", }, WindowSize: to.StringPtr(windowSize), TargetResourceType: to.StringPtr("Microsoft.Network/loadBalancers"), AutoMitigate: to.BoolPtr(true), Criteria: mgmtinsights.MetricAlertSingleResourceMultipleMetricCriteria{ AllOf: &[]mgmtinsights.MetricCriteria{ { CriterionType: mgmtinsights.CriterionTypeStaticThresholdCriterion, MetricName: to.StringPtr(metric), MetricNamespace: to.StringPtr("microsoft.network/loadBalancers"), Name: to.StringPtr("HealthProbeCheck"), Operator: mgmtinsights.OperatorLessThan, Threshold: to.Float64Ptr(threshold), TimeAggregation: mgmtinsights.Average, }, }, OdataType: mgmtinsights.OdataTypeMicrosoftAzureMonitorSingleResourceMultipleMetricCriteria, }, }, Name: to.StringPtr("[concat('" + name + "-', resourceGroup().location)]"), Type: to.StringPtr("Microsoft.Insights/metricAlerts"), Location: to.StringPtr("global"), }, APIVersion: azureclient.APIVersion("Microsoft.Insights"), DependsOn: []string{ "[resourceId('Microsoft.Network/loadBalancers', 'rp-lb')]", }, } } func (g *generator) rpVMSS() *arm.Resource { // TODO: there is a lot of duplication with gatewayVMSS() (and other places) parts := []string{ fmt.Sprintf("base64ToString('%s')", base64.StdEncoding.EncodeToString([]byte("set -ex\n\n"))), } for _, variable := range []string{ "acrResourceId", "adminApiClientCertCommonName", "armApiClientCertCommonName", "armClientId", "azureCloudName", "azureSecPackQualysUrl", "azureSecPackVSATenantId", "clusterMdmAccount", "clusterMdsdAccount", "clusterMdsdConfigVersion", "clusterMdsdNamespace", "clusterParentDomainName", "databaseAccountName", "fluentbitImage", "fpClientId", "fpTenantId", "fpServicePrincipalId", "gatewayDomains", "gatewayResourceGroupName", "gatewayServicePrincipalId", "keyvaultDNSSuffix", "keyvaultPrefix", "mdmFrontendUrl", "mdsdEnvironment", "msiRpEndpoint", "portalAccessGroupIds", "portalClientId", "portalElevatedGroupIds", "rpFeatures", "rpImage", "rpMdmAccount", "rpMdsdAccount", "rpMdsdConfigVersion", "rpMdsdNamespace", "rpParentDomainName", "oidcStorageAccountName", "otelAuditQueueSize", // TODO: Replace with Live Service Configuration in KeyVault "clustersInstallViaHive", "clustersAdoptByHive", "clusterDefaultInstallerPullspec", } { parts = append(parts, fmt.Sprintf("'%s=$(base64 -d <<<'''", strings.ToUpper(variable)), fmt.Sprintf("base64(parameters('%s'))", variable), "''')\n'", ) } // convert array variables to string using ARM string() function to be passed via customScript later for _, variable := range []string{ "miseValidAudiences", "miseValidAppIDs", } { parts = append(parts, fmt.Sprintf("'%s=$(base64 -d <<<'''", strings.ToUpper(variable)), fmt.Sprintf("base64(string(parameters('%s')))", variable), "''')\n'", ) } for _, variable := range []string{ "adminApiCaBundle", "armApiCaBundle", } { parts = append(parts, fmt.Sprintf("'%s='''", strings.ToUpper(variable)), fmt.Sprintf("parameters('%s')", variable), "'''\n'", ) } parts = append(parts, "'MDMIMAGE=''"+version.MdmImage("")+"''\n'", ) parts = append(parts, "'OTELIMAGE=''"+version.OTelImage("")+"''\n'", ) parts = append(parts, "'MISEIMAGE=''"+version.MiseImage("")+"''\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 + scriptRpVMSS 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('vmSize')]"), Tier: to.StringPtr("Standard"), Capacity: to.Int64Ptr(1338), }, 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('rp-', 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{ // https://eng.ms/docs/products/azure-linux/gettingstarted/azurevm/azurevm 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', 'rp-lb', 'rp-probe')]"), }, NetworkInterfaceConfigurations: &[]mgmtcompute.VirtualMachineScaleSetNetworkConfiguration{ { Name: to.StringPtr("rp-vmss-nic"), VirtualMachineScaleSetNetworkConfigurationProperties: &mgmtcompute.VirtualMachineScaleSetNetworkConfigurationProperties{ Primary: to.BoolPtr(true), IPConfigurations: &[]mgmtcompute.VirtualMachineScaleSetIPConfiguration{ { Name: to.StringPtr("rp-vmss-ipconfig"), VirtualMachineScaleSetIPConfigurationProperties: &mgmtcompute.VirtualMachineScaleSetIPConfigurationProperties{ Subnet: &mgmtcompute.APIEntityReference{ ID: to.StringPtr("[resourceId('Microsoft.Network/virtualNetworks/subnets', 'rp-vnet', 'rp-subnet')]"), }, Primary: to.BoolPtr(true), PublicIPAddressConfiguration: &mgmtcompute.VirtualMachineScaleSetPublicIPAddressConfiguration{ Name: to.StringPtr("rp-vmss-pip"), }, LoadBalancerBackendAddressPools: &[]mgmtcompute.SubResource{ { ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', 'rp-lb', 'rp-backend')]"), }, }, }, }, }, }, }, }, }, ExtensionProfile: &mgmtcompute.VirtualMachineScaleSetExtensionProfile{ Extensions: &[]mgmtcompute.VirtualMachineScaleSetExtension{ { Name: to.StringPtr("rp-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-rp-', resourceGroup().location))]": {}, }, }, Name: to.StringPtr("[concat('rp-vmss-', parameters('vmssName'))]"), Type: to.StringPtr("Microsoft.Compute/virtualMachineScaleSets"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.Compute"), DependsOn: []string{ "[resourceId('Microsoft.Authorization/roleAssignments', guid(resourceGroup().id, parameters('rpServicePrincipalId'), 'RP / Reader'))]", "[resourceId('Microsoft.Network/loadBalancers', 'rp-lb')]", }, } } func (g *generator) rpParentDNSZone() *arm.Resource { return g.dnsZone("[parameters('rpParentDomainName')]") } func (g *generator) rpClusterParentDNSZone() *arm.Resource { return g.dnsZone("[parameters('clusterParentDomainName')]") } func (g *generator) rpDNSZone() *arm.Resource { return g.dnsZone("[concat(resourceGroup().location, '.', parameters('clusterParentDomainName'))]") } func (g *generator) rpClusterKeyvaultAccessPolicies() []mgmtkeyvault.AccessPolicyEntry { return []mgmtkeyvault.AccessPolicyEntry{ { TenantID: &tenantUUIDHack, ObjectID: to.StringPtr("[parameters('fpServicePrincipalId')]"), Permissions: &mgmtkeyvault.Permissions{ Secrets: &[]mgmtkeyvault.SecretPermissions{ mgmtkeyvault.SecretPermissionsGet, }, Certificates: &[]mgmtkeyvault.CertificatePermissions{ mgmtkeyvault.Create, mgmtkeyvault.Delete, mgmtkeyvault.Get, mgmtkeyvault.Update, }, }, }, } } func (g *generator) rpPortalKeyvaultAccessPolicies() []mgmtkeyvault.AccessPolicyEntry { return []mgmtkeyvault.AccessPolicyEntry{ { TenantID: &tenantUUIDHack, ObjectID: to.StringPtr("[parameters('rpServicePrincipalId')]"), Permissions: &mgmtkeyvault.Permissions{ Secrets: &[]mgmtkeyvault.SecretPermissions{ mgmtkeyvault.SecretPermissionsGet, }, }, }, } } func (g *generator) rpServiceKeyvaultAccessPolicies() []mgmtkeyvault.AccessPolicyEntry { return []mgmtkeyvault.AccessPolicyEntry{ { TenantID: &tenantUUIDHack, ObjectID: to.StringPtr("[parameters('rpServicePrincipalId')]"), Permissions: &mgmtkeyvault.Permissions{ Secrets: &[]mgmtkeyvault.SecretPermissions{ mgmtkeyvault.SecretPermissionsGet, mgmtkeyvault.SecretPermissionsList, }, }, }, } } func (g *generator) rpClusterKeyvault() *arm.Resource { vault := &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(clusterAccessPolicyHack), }, }, }, Name: to.StringPtr("[concat(parameters('keyvaultPrefix'), '" + env.ClusterKeyvaultSuffix + "')]"), Type: to.StringPtr("Microsoft.KeyVault/vaults"), Location: to.StringPtr("[resourceGroup().location]"), } if !g.production { *vault.Properties.AccessPolicies = append(g.rpClusterKeyvaultAccessPolicies(), mgmtkeyvault.AccessPolicyEntry{ TenantID: &tenantUUIDHack, ObjectID: to.StringPtr("[parameters('adminObjectId')]"), Permissions: &mgmtkeyvault.Permissions{ Certificates: &[]mgmtkeyvault.CertificatePermissions{ mgmtkeyvault.Get, mgmtkeyvault.List, }, }, }, ) } return &arm.Resource{ Resource: vault, APIVersion: azureclient.APIVersion("Microsoft.KeyVault"), } } func (g *generator) rpPortalKeyvault() *arm.Resource { vault := &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(portalAccessPolicyHack), }, }, }, Name: to.StringPtr("[concat(parameters('keyvaultPrefix'), '" + env.PortalKeyvaultSuffix + "')]"), Type: to.StringPtr("Microsoft.KeyVault/vaults"), Location: to.StringPtr("[resourceGroup().location]"), } if !g.production { *vault.Properties.AccessPolicies = append(g.rpPortalKeyvaultAccessPolicies(), mgmtkeyvault.AccessPolicyEntry{ TenantID: &tenantUUIDHack, ObjectID: to.StringPtr("[parameters('adminObjectId')]"), Permissions: &mgmtkeyvault.Permissions{ Certificates: &[]mgmtkeyvault.CertificatePermissions{ mgmtkeyvault.Delete, mgmtkeyvault.Get, mgmtkeyvault.Import, mgmtkeyvault.List, }, Secrets: &[]mgmtkeyvault.SecretPermissions{ mgmtkeyvault.SecretPermissionsSet, mgmtkeyvault.SecretPermissionsList, }, }, }, ) } return &arm.Resource{ Resource: vault, APIVersion: azureclient.APIVersion("Microsoft.KeyVault"), } } func (g *generator) rpServiceKeyvaultDynamic() *arm.Resource { vaultAccessPoliciesResource := &arm.DeploymentTemplateResource{ Name: "[concat(parameters('keyvaultPrefix'), '" + env.ServiceKeyvaultSuffix + "/add')]", Type: "Microsoft.KeyVault/vaults/accessPolicies", APIVersion: azureclient.APIVersion("Microsoft.KeyVault/vaults/accessPolicies"), Properties: &mgmtkeyvault.VaultProperties{ AccessPolicies: &[]mgmtkeyvault.AccessPolicyEntry{ { TenantID: &tenantUUIDHack, ObjectID: to.StringPtr("[reference(resourceId('Microsoft.ContainerService/managedClusters', 'aro-aks-cluster-001'), '2020-12-01', 'Full').properties.identityProfile.kubeletidentity.objectId]"), Permissions: &mgmtkeyvault.Permissions{ Secrets: &[]mgmtkeyvault.SecretPermissions{ mgmtkeyvault.SecretPermissionsGet, mgmtkeyvault.SecretPermissionsList, }, Certificates: &[]mgmtkeyvault.CertificatePermissions{ mgmtkeyvault.Get, }, }, }, }, }, } rpServiceKeyvaultDynamicDeployment := &arm.Deployment{ Properties: &arm.DeploymentProperties{ Template: &arm.DeploymentTemplate{ Schema: "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", ContentVersion: "1.0.0.0", Parameters: map[string]*arm.TemplateParameter{ "keyvaultPrefix": { Type: "string", }, }, Resources: []*arm.DeploymentTemplateResource{vaultAccessPoliciesResource}, }, Parameters: map[string]*arm.DeploymentTemplateResourceParameter{ "keyvaultPrefix": { Value: "[parameters('keyvaultPrefix')]", }, }, Mode: "Incremental", ExpressionEvaluationOptions: map[string]*string{ "scope": to.StringPtr("inner"), }, }, } return &arm.Resource{ Name: "rpServiceKeyvaultDynamic", Type: "Microsoft.Resources/deployments", APIVersion: azureclient.APIVersion("Microsoft.Resources/deployments"), DependsOn: []string{"[concat(parameters('keyvaultPrefix'), '" + env.ServiceKeyvaultSuffix + "')]"}, Resource: rpServiceKeyvaultDynamicDeployment, } } func (g *generator) rpServiceKeyvault() *arm.Resource { vault := &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(serviceAccessPolicyHack), }, }, }, Name: to.StringPtr("[concat(parameters('keyvaultPrefix'), '" + env.ServiceKeyvaultSuffix + "')]"), Type: to.StringPtr("Microsoft.KeyVault/vaults"), Location: to.StringPtr("[resourceGroup().location]"), } if !g.production { *vault.Properties.AccessPolicies = append(g.rpServiceKeyvaultAccessPolicies(), mgmtkeyvault.AccessPolicyEntry{ TenantID: &tenantUUIDHack, ObjectID: to.StringPtr("[parameters('adminObjectId')]"), Permissions: &mgmtkeyvault.Permissions{ Certificates: &[]mgmtkeyvault.CertificatePermissions{ mgmtkeyvault.Delete, mgmtkeyvault.Get, mgmtkeyvault.Import, mgmtkeyvault.List, }, Secrets: &[]mgmtkeyvault.SecretPermissions{ mgmtkeyvault.SecretPermissionsGet, mgmtkeyvault.SecretPermissionsSet, mgmtkeyvault.SecretPermissionsList, }, }, }, ) } return &arm.Resource{ Resource: vault, APIVersion: azureclient.APIVersion("Microsoft.KeyVault"), } } func (g *generator) rpCosmosDB() []*arm.Resource { dbType := sdkcosmos.DatabaseAccountKindGlobalDocumentDB consistency := sdkcosmos.DefaultConsistencyLevelStrong backupPolicy := sdkcosmos.BackupPolicyTypePeriodic minTLSVersion := sdkcosmos.MinimalTLSVersionTls12 cosmosdb := &sdkcosmos.DatabaseAccountCreateUpdateParameters{ Kind: &dbType, Properties: &sdkcosmos.DatabaseAccountCreateUpdateProperties{ ConsistencyPolicy: &sdkcosmos.ConsistencyPolicy{ DefaultConsistencyLevel: &consistency, }, Locations: []*sdkcosmos.Location{ { LocationName: to.StringPtr("[resourceGroup().location]"), }, }, DatabaseAccountOfferType: to.StringPtr("Standard"), BackupPolicy: &sdkcosmos.PeriodicModeBackupPolicy{ Type: &backupPolicy, PeriodicModeProperties: &sdkcosmos.PeriodicModeProperties{ BackupIntervalInMinutes: to.Int32Ptr(240), //4 hours BackupRetentionIntervalInHours: to.Int32Ptr(720), //30 days }, }, MinimalTLSVersion: &minTLSVersion, DisableLocalAuth: to.BoolPtr(true), // Disable local authentication }, Name: to.StringPtr("[parameters('databaseAccountName')]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts"), Location: to.StringPtr("[resourceGroup().location]"), Tags: map[string]*string{ "defaultExperience": to.StringPtr("Core (SQL)"), }, } r := &arm.Resource{ Resource: cosmosdb, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), Type: "Microsoft.DocumentDB/databaseAccounts", } if g.production { cosmosdb.Properties.IPRules = []*sdkcosmos.IPAddressOrRange{} cosmosdb.Properties.IsVirtualNetworkFilterEnabled = to.BoolPtr(true) cosmosdb.Properties.VirtualNetworkRules = []*sdkcosmos.VirtualNetworkRule{} cosmosdb.Properties.DisableKeyBasedMetadataWriteAccess = to.BoolPtr(true) } rs := []*arm.Resource{ r, } if g.production { rs = append(rs, g.database("'ARO'", true)...) rs = append(rs, g.rpCosmosDBAlert(10, 90, 3, "rp-cosmosdb-alert", "PT5M", "PT1H")) rs = append(rs, g.CosmosDBDataContributorRoleAssignment("'ARO'", "rp")) rs = append(rs, g.CosmosDBDataContributorRoleAssignment("'ARO'", "gateway")) rs = append(rs, g.CosmosDBDataContributorRoleAssignment("'ARO'", "globalDevops")) } else { rs = append(rs, g.CosmosDBDataContributorRoleAssignment("''", "rp")) rs = append(rs, g.CosmosDBDataContributorRoleAssignment("'ARO'", "globalDevops")) } return rs } func (g *generator) CosmosDBDataContributorRoleAssignment(databaseName, component string) *arm.Resource { var scope string if g.production { scope = "[resourceId('Microsoft.DocumentDB/databaseAccounts/dbs', parameters('databaseAccountName'), " + databaseName + ")]" } else { scope = "[resourceId('Microsoft.DocumentDB/databaseAccounts/', parameters('databaseAccountName'))]" } roleAssignment := &arm.Resource{ Resource: mgmtauthorization.RoleAssignment{ Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('" + component + "ServicePrincipalId'), 'DocumentDB Data Contributor'))]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments"), RoleAssignmentPropertiesWithScope: &mgmtauthorization.RoleAssignmentPropertiesWithScope{ Scope: &scope, RoleDefinitionID: to.StringPtr("[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('databaseAccountName'), '" + rbac.RoleDocumentDBDataContributor + "')]"), PrincipalID: to.StringPtr("[parameters('" + component + "ServicePrincipalId')]"), PrincipalType: mgmtauthorization.ServicePrincipal, }, }, DependsOn: []string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]", }, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), } return roleAssignment } func (g *generator) database(databaseName string, addDependsOn bool) []*arm.Resource { database := &arm.Resource{ Resource: &sdkcosmos.SQLDatabaseCreateUpdateParameters{ Properties: &sdkcosmos.SQLDatabaseCreateUpdateProperties{ Resource: &sdkcosmos.SQLDatabaseResource{ ID: to.StringPtr("[" + databaseName + "]"), }, Options: &sdkcosmos.CreateUpdateOptions{ Throughput: to.Int32Ptr(cosmosDbStandardProvisionedThroughputHack), }, }, Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', " + databaseName + ")]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlDatabases"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), Type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", } hashPartitionKey := sdkcosmos.PartitionKindHash portal := &arm.Resource{ Resource: &sdkcosmos.SQLContainerCreateUpdateParameters{ Properties: &sdkcosmos.SQLContainerCreateUpdateProperties{ Resource: &sdkcosmos.SQLContainerResource{ ID: to.StringPtr("Portal"), PartitionKey: &sdkcosmos.ContainerPartitionKey{ Paths: []*string{ to.StringPtr("/id"), }, Kind: &hashPartitionKey, }, DefaultTTL: to.Int32Ptr(-1), }, Options: &sdkcosmos.CreateUpdateOptions{ Throughput: to.Int32Ptr(cosmosDbPortalProvisionedThroughputHack), }, }, Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', " + databaseName + ", '/Portal')]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), DependsOn: []string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), " + databaseName + ")]", }, Type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", } gateway := &arm.Resource{ Resource: &sdkcosmos.SQLContainerCreateUpdateParameters{ Properties: &sdkcosmos.SQLContainerCreateUpdateProperties{ Resource: &sdkcosmos.SQLContainerResource{ ID: to.StringPtr("Gateway"), PartitionKey: &sdkcosmos.ContainerPartitionKey{ Paths: []*string{ to.StringPtr("/id"), }, Kind: &hashPartitionKey, }, DefaultTTL: to.Int32Ptr(-1), }, Options: &sdkcosmos.CreateUpdateOptions{ Throughput: to.Int32Ptr(cosmosDbGatewayProvisionedThroughputHack), }, }, Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', " + databaseName + ", '/Gateway')]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), DependsOn: []string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), " + databaseName + ")]", }, Type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", } mimo := &arm.Resource{ Resource: &sdkcosmos.SQLContainerCreateUpdateParameters{ Properties: &sdkcosmos.SQLContainerCreateUpdateProperties{ Resource: &sdkcosmos.SQLContainerResource{ ID: to.StringPtr("MaintenanceManifests"), PartitionKey: &sdkcosmos.ContainerPartitionKey{ Paths: []*string{ to.StringPtr("/clusterResourceID"), }, Kind: &hashPartitionKey, }, DefaultTTL: to.Int32Ptr(-1), }, Options: &sdkcosmos.CreateUpdateOptions{ Throughput: to.Int32Ptr(cosmosDbGatewayProvisionedThroughputHack), }, }, Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', " + databaseName + ", '/MaintenanceManifests')]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), DependsOn: []string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), " + databaseName + ")]", }, Type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", } if !g.production { database.Resource.(*sdkcosmos.SQLDatabaseCreateUpdateParameters).Properties.Options = &sdkcosmos.CreateUpdateOptions{ AutoscaleSettings: &sdkcosmos.AutoscaleSettings{ MaxThroughput: to.Int32Ptr(1000), }, } portal.Resource.(*sdkcosmos.SQLContainerCreateUpdateParameters).Properties.Options = &sdkcosmos.CreateUpdateOptions{} gateway.Resource.(*sdkcosmos.SQLContainerCreateUpdateParameters).Properties.Options = &sdkcosmos.CreateUpdateOptions{} mimo.Resource.(*sdkcosmos.SQLContainerCreateUpdateParameters).Properties.Options = &sdkcosmos.CreateUpdateOptions{} } rs := []*arm.Resource{ database, { Resource: &sdkcosmos.SQLContainerCreateUpdateParameters{ Properties: &sdkcosmos.SQLContainerCreateUpdateProperties{ Resource: &sdkcosmos.SQLContainerResource{ ID: to.StringPtr("AsyncOperations"), PartitionKey: &sdkcosmos.ContainerPartitionKey{ Paths: []*string{ to.StringPtr("/id"), }, Kind: &hashPartitionKey, }, DefaultTTL: to.Int32Ptr(7 * 86400), // 7 days }, Options: &sdkcosmos.CreateUpdateOptions{}, }, Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', " + databaseName + ", '/AsyncOperations')]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), DependsOn: []string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), " + databaseName + ")]", }, Type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", }, { Resource: &sdkcosmos.SQLContainerCreateUpdateParameters{ Properties: &sdkcosmos.SQLContainerCreateUpdateProperties{ Resource: &sdkcosmos.SQLContainerResource{ ID: to.StringPtr("OpenShiftVersions"), PartitionKey: &sdkcosmos.ContainerPartitionKey{ Paths: []*string{ to.StringPtr("/id"), }, Kind: &hashPartitionKey, }, DefaultTTL: to.Int32Ptr(-1), }, Options: &sdkcosmos.CreateUpdateOptions{}, }, Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', " + databaseName + ", '/OpenShiftVersions')]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), DependsOn: []string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), " + databaseName + ")]", }, Type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", }, { Resource: &sdkcosmos.SQLContainerCreateUpdateParameters{ Properties: &sdkcosmos.SQLContainerCreateUpdateProperties{ Resource: &sdkcosmos.SQLContainerResource{ ID: to.StringPtr("PlatformWorkloadIdentityRoleSets"), PartitionKey: &sdkcosmos.ContainerPartitionKey{ Paths: []*string{ to.StringPtr("/id"), }, Kind: &hashPartitionKey, }, DefaultTTL: to.Int32Ptr(-1), }, Options: &sdkcosmos.CreateUpdateOptions{}, }, Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', " + databaseName + ", '/PlatformWorkloadIdentityRoleSets')]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), DependsOn: []string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), " + databaseName + ")]", }, Type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", }, { Resource: &sdkcosmos.SQLContainerCreateUpdateParameters{ Properties: &sdkcosmos.SQLContainerCreateUpdateProperties{ Resource: &sdkcosmos.SQLContainerResource{ ID: to.StringPtr("Billing"), PartitionKey: &sdkcosmos.ContainerPartitionKey{ Paths: []*string{ to.StringPtr("/id"), }, Kind: &hashPartitionKey, }, }, Options: &sdkcosmos.CreateUpdateOptions{}, }, Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', " + databaseName + ", '/Billing')]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), DependsOn: []string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), " + databaseName + ")]", }, Type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", }, gateway, { Resource: &sdkcosmos.SQLContainerCreateUpdateParameters{ Properties: &sdkcosmos.SQLContainerCreateUpdateProperties{ Resource: &sdkcosmos.SQLContainerResource{ ID: to.StringPtr("Monitors"), PartitionKey: &sdkcosmos.ContainerPartitionKey{ Paths: []*string{ to.StringPtr("/id"), }, Kind: &hashPartitionKey, }, DefaultTTL: to.Int32Ptr(-1), }, Options: &sdkcosmos.CreateUpdateOptions{}, }, Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', " + databaseName + ", '/Monitors')]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), DependsOn: []string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), " + databaseName + ")]", }, Type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", }, { Resource: &sdkcosmos.SQLContainerCreateUpdateParameters{ Properties: &sdkcosmos.SQLContainerCreateUpdateProperties{ Resource: &sdkcosmos.SQLContainerResource{ ID: to.StringPtr("OpenShiftClusters"), PartitionKey: &sdkcosmos.ContainerPartitionKey{ Paths: []*string{ to.StringPtr("/partitionKey"), }, Kind: &hashPartitionKey, }, UniqueKeyPolicy: &sdkcosmos.UniqueKeyPolicy{ UniqueKeys: []*sdkcosmos.UniqueKey{ { Paths: []*string{ to.StringPtr("/key"), }, }, { Paths: []*string{ to.StringPtr("/clusterResourceGroupIdKey"), }, }, { Paths: []*string{ to.StringPtr("/clientIdKey"), }, }, }, }, }, Options: &sdkcosmos.CreateUpdateOptions{}, }, Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', " + databaseName + ", '/OpenShiftClusters')]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), DependsOn: []string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), " + databaseName + ")]", }, Type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", }, portal, { Resource: &sdkcosmos.SQLContainerCreateUpdateParameters{ Properties: &sdkcosmos.SQLContainerCreateUpdateProperties{ Resource: &sdkcosmos.SQLContainerResource{ ID: to.StringPtr("Subscriptions"), PartitionKey: &sdkcosmos.ContainerPartitionKey{ Paths: []*string{ to.StringPtr("/id"), }, Kind: &hashPartitionKey, }, }, Options: &sdkcosmos.CreateUpdateOptions{}, }, Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', " + databaseName + ", '/Subscriptions')]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), DependsOn: []string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), " + databaseName + ")]", }, Type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", }, } // Adding Triggers rs = append(rs, // Subscription g.rpCosmosDBTriggers(databaseName, "Subscriptions", "renewLease", renewLeaseTriggerFunction, sdkcosmos.TriggerTypePre, sdkcosmos.TriggerOperationAll), g.rpCosmosDBTriggers(databaseName, "Subscriptions", "retryLater", retryLaterTriggerFunction, sdkcosmos.TriggerTypePre, sdkcosmos.TriggerOperationAll), // Billing g.rpCosmosDBTriggers(databaseName, "Billing", "setCreationBillingTimeStamp", setCreationBillingTimeStampTriggerFunction, sdkcosmos.TriggerTypePre, sdkcosmos.TriggerOperationCreate), g.rpCosmosDBTriggers(databaseName, "Billing", "setDeletionBillingTimeStamp", setDeletionBillingTimeStampTriggerFunction, sdkcosmos.TriggerTypePre, sdkcosmos.TriggerOperationReplace), // OpenShiftClusters g.rpCosmosDBTriggers(databaseName, "OpenShiftClusters", "renewLease", renewLeaseTriggerFunction, sdkcosmos.TriggerTypePre, sdkcosmos.TriggerOperationAll), // Monitors g.rpCosmosDBTriggers(databaseName, "Monitors", "renewLease", renewLeaseTriggerFunction, sdkcosmos.TriggerTypePre, sdkcosmos.TriggerOperationAll), ) // Don't deploy the MIMO databases in production yet if !g.production { rs = append(rs, mimo, // MIMO DB triggers g.rpCosmosDBTriggers(databaseName, "MaintenanceManifests", "renewLease", renewLeaseTriggerFunction, sdkcosmos.TriggerTypePre, sdkcosmos.TriggerOperationAll), ) } if addDependsOn { for i := range rs { rs[i].DependsOn = append(rs[i].DependsOn, "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]", ) } } return rs } func (g *generator) rpCosmosDBTriggers(databaseName, containerName, triggerID, triggerFunction string, triggerType sdkcosmos.TriggerType, triggerOperation sdkcosmos.TriggerOperation) *arm.Resource { return &arm.Resource{ Resource: &sdkcosmos.SQLTriggerCreateUpdateParameters{ Properties: &sdkcosmos.SQLTriggerCreateUpdateProperties{ Resource: &sdkcosmos.SQLTriggerResource{ ID: to.StringPtr(triggerID), Body: to.StringPtr(triggerFunction), TriggerOperation: &triggerOperation, TriggerType: &triggerType, }, }, Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', " + databaseName + ", '/" + containerName + "/" + triggerID + "')]"), Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers"), Location: to.StringPtr("[resourceGroup().location]"), }, APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), DependsOn: []string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), " + databaseName + ")]", "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), " + databaseName + ", '" + containerName + "')]", }, Type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers", } } func (g *generator) rpCosmosDBAlert(throttledRequestThreshold float64, ruConsumptionThreshold float64, severity int32, name string, evalFreq string, windowSize string) *arm.Resource { throttledRequestMetricCriteria := mgmtinsights.MetricCriteria{ CriterionType: mgmtinsights.CriterionTypeStaticThresholdCriterion, MetricName: to.StringPtr("TotalRequests"), MetricNamespace: to.StringPtr("Microsoft.DocumentDB/databaseAccounts"), Name: to.StringPtr("ThrottledRequestCheck"), Operator: mgmtinsights.OperatorGreaterThan, Threshold: to.Float64Ptr(throttledRequestThreshold), TimeAggregation: mgmtinsights.Count, Dimensions: &[]mgmtinsights.MetricDimension{ { Name: to.StringPtr("StatusCode"), Operator: to.StringPtr("Include"), Values: &[]string{"429"}, }, }, } ruConsumptionMetricCriteria := mgmtinsights.MetricCriteria{ CriterionType: mgmtinsights.CriterionTypeStaticThresholdCriterion, MetricName: to.StringPtr("NormalizedRUConsumption"), MetricNamespace: to.StringPtr("Microsoft.DocumentDB/databaseAccounts"), Name: to.StringPtr("RUConsumptionCheck"), Operator: mgmtinsights.OperatorGreaterThan, Threshold: to.Float64Ptr(ruConsumptionThreshold), TimeAggregation: mgmtinsights.Average, } return &arm.Resource{ Resource: mgmtinsights.MetricAlertResource{ MetricAlertProperties: &mgmtinsights.MetricAlertProperties{ Actions: &[]mgmtinsights.MetricAlertAction{ { ActionGroupID: to.StringPtr("[resourceId(parameters('subscriptionResourceGroupName'), 'Microsoft.Insights/actionGroups', 'rp-health-ag')]"), }, }, Enabled: to.BoolPtr(true), EvaluationFrequency: to.StringPtr(evalFreq), Severity: to.Int32Ptr(severity), Scopes: &[]string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]", }, WindowSize: to.StringPtr(windowSize), TargetResourceType: to.StringPtr("Microsoft.DocumentDB/databaseAccounts"), AutoMitigate: to.BoolPtr(true), Criteria: mgmtinsights.MetricAlertSingleResourceMultipleMetricCriteria{ AllOf: &[]mgmtinsights.MetricCriteria{throttledRequestMetricCriteria, ruConsumptionMetricCriteria}, OdataType: mgmtinsights.OdataTypeMicrosoftAzureMonitorSingleResourceMultipleMetricCriteria, }, }, Name: to.StringPtr("[concat('" + name + "-', resourceGroup().location)]"), Type: to.StringPtr("Microsoft.Insights/metricAlerts"), Location: to.StringPtr("global"), }, APIVersion: azureclient.APIVersion("Microsoft.Insights"), DependsOn: []string{ "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]", }, } } func (g *generator) rpRoleDefinitionTokenContributor() *arm.Resource { return &arm.Resource{ Resource: &mgmtauthorization.RoleDefinition{ Name: to.StringPtr("48983534-3d06-4dcb-a566-08a694eb1279"), Type: to.StringPtr("Microsoft.Authorization/roleDefinitions"), RoleDefinitionProperties: &mgmtauthorization.RoleDefinitionProperties{ RoleName: to.StringPtr("ARO v4 ContainerRegistry Token Contributor"), AssignableScopes: &[]string{"[subscription().id]"}, Permissions: &[]mgmtauthorization.Permission{ { Actions: &[]string{ "Microsoft.ContainerRegistry/registries/generateCredentials/action", "Microsoft.ContainerRegistry/registries/scopeMaps/read", "Microsoft.ContainerRegistry/registries/tokens/delete", "Microsoft.ContainerRegistry/registries/tokens/operationStatuses/read", "Microsoft.ContainerRegistry/registries/tokens/read", "Microsoft.ContainerRegistry/registries/tokens/write", }, }, }, }, }, APIVersion: azureclient.APIVersion("Microsoft.Authorization/roleDefinitions"), } } func (g *generator) rpRBAC() []*arm.Resource { return []*arm.Resource{ rbac.ResourceGroupRoleAssignmentWithName( rbac.RoleReader, "parameters('rpServicePrincipalId')", "guid(resourceGroup().id, parameters('rpServicePrincipalId'), 'RP / Reader')", ), rbac.ResourceGroupRoleAssignmentWithName( rbac.RoleNetworkContributor, "parameters('fpServicePrincipalId')", "guid(resourceGroup().id, 'FP / Network Contributor')", ), rbac.ResourceRoleAssignmentWithName( rbac.RoleDocumentDBAccountContributor, "parameters('rpServicePrincipalId')", "Microsoft.DocumentDB/databaseAccounts", "parameters('databaseAccountName')", "concat(parameters('databaseAccountName'), '/Microsoft.Authorization/', guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('rpServicePrincipalId'), 'RP / DocumentDB Account Contributor'))", ), rbac.ResourceRoleAssignmentWithName( rbac.RoleDNSZoneContributor, "parameters('fpServicePrincipalId')", "Microsoft.Network/dnsZones", "concat(resourceGroup().location, '.', parameters('clusterParentDomainName'))", "concat(resourceGroup().location, '.', parameters('clusterParentDomainName'), '/Microsoft.Authorization/', guid(resourceId('Microsoft.Network/dnsZones', concat(resourceGroup().location, '.', parameters('clusterParentDomainName'))), 'FP / DNS Zone Contributor'))", ), } } func (g *generator) rpACR() *arm.Resource { return &arm.Resource{ Resource: &mgmtcontainerregistry.Registry{ Sku: &mgmtcontainerregistry.Sku{ Name: mgmtcontainerregistry.Premium, }, RegistryProperties: &mgmtcontainerregistry.RegistryProperties{ // enable data hostname stability: https://azure.microsoft.com/en-gb/blog/azure-container-registry-mitigating-data-exfiltration-with-dedicated-data-endpoints/ DataEndpointEnabled: to.BoolPtr(true), }, Name: to.StringPtr("[substring(parameters('acrResourceId'), add(lastIndexOf(parameters('acrResourceId'), '/'), 1))]"), Type: to.StringPtr("Microsoft.ContainerRegistry/registries"), // TODO: INT ACR has wrong location - should be redeployed at globalResourceGroupLocation then remove acrLocationOverride configurable. Location: to.StringPtr("[if(equals(parameters('acrLocationOverride'), ''), resourceGroup().location, parameters('acrLocationOverride'))]"), }, APIVersion: azureclient.APIVersion("Microsoft.ContainerRegistry"), } } func (g *generator) rpACRReplica() *arm.Resource { return &arm.Resource{ Resource: &mgmtcontainerregistry.Replication{ Name: to.StringPtr("[concat(substring(parameters('acrResourceId'), add(lastIndexOf(parameters('acrResourceId'), '/'), 1)), '/', parameters('location'))]"), Type: to.StringPtr("Microsoft.ContainerRegistry/registries/replications"), Location: to.StringPtr("[parameters('location')]"), }, APIVersion: azureclient.APIVersion("Microsoft.ContainerRegistry"), } } func (g *generator) rpACRRBAC() []*arm.Resource { return []*arm.Resource{ rbac.ResourceRoleAssignmentWithName( rbac.RoleACRPull, "parameters('rpServicePrincipalId')", "Microsoft.ContainerRegistry/registries", "substring(parameters('acrResourceId'), add(lastIndexOf(parameters('acrResourceId'), '/'), 1))", "concat(substring(parameters('acrResourceId'), add(lastIndexOf(parameters('acrResourceId'), '/'), 1)), '/', '/Microsoft.Authorization/', guid(concat(parameters('acrResourceId'), parameters('rpServicePrincipalId'), 'RP / AcrPull')))", ), rbac.ResourceRoleAssignmentWithName( rbac.RoleACRPull, "parameters('gatewayServicePrincipalId')", "Microsoft.ContainerRegistry/registries", "substring(parameters('acrResourceId'), add(lastIndexOf(parameters('acrResourceId'), '/'), 1))", "concat(substring(parameters('acrResourceId'), add(lastIndexOf(parameters('acrResourceId'), '/'), 1)), '/', '/Microsoft.Authorization/', guid(concat(parameters('acrResourceId'), parameters('gatewayServicePrincipalId'), 'RP / AcrPull')))", ), rbac.ResourceRoleAssignmentWithName( "48983534-3d06-4dcb-a566-08a694eb1279", // ARO v4 ContainerRegistry Token Contributor "parameters('fpServicePrincipalId')", "Microsoft.ContainerRegistry/registries", "substring(parameters('acrResourceId'), add(lastIndexOf(parameters('acrResourceId'), '/'), 1))", "concat(substring(parameters('acrResourceId'), add(lastIndexOf(parameters('acrResourceId'), '/'), 1)), '/', '/Microsoft.Authorization/', guid(concat(parameters('acrResourceId'), 'FP / ARO v4 ContainerRegistry Token Contributor')))", ), } } func (g *generator) rpVersionStorageAccount() []*arm.Resource { storageAccountName := "parameters('rpVersionStorageAccountName')" return []*arm.Resource{ g.storageAccount( fmt.Sprintf("[%s]", storageAccountName), &mgmtstorage.AccountProperties{ AllowBlobPublicAccess: to.BoolPtr(false), MinimumTLSVersion: mgmtstorage.MinimumTLSVersionTLS12, AllowSharedKeyAccess: to.BoolPtr(false), }, map[string]*string{}, ), rbac.ResourceRoleAssignmentWithName( rbac.RoleStorageAccountContributor, "parameters('globalDevopsServicePrincipalId')", resourceTypeStorageAccount, storageAccountName, fmt.Sprintf("concat(%s, '/Microsoft.Authorization/', guid(resourceId('%s', %s)))", storageAccountName, resourceTypeStorageAccount, storageAccountName), ), g.storageAccountBlobContainer( fmt.Sprintf("concat(%s, '/default', '/$web')", storageAccountName), storageAccountName, &mgmtstorage.ContainerProperties{}, ), rbac.ResourceRoleAssignmentWithScope( rbac.RoleStorageBlobDataContributor, "parameters('globalDevopsServicePrincipalId')", fmt.Sprintf("%s/%s", resourceTypeStorageAccount, resourceTypeBlobContainer), fmt.Sprintf("concat(resourceId('Microsoft.Storage/storageAccounts', %s), '/blobServices/default/containers/$web')", storageAccountName), fmt.Sprintf("concat(%s, '/default/$web/Microsoft.Authorization/', guid(%s))", storageAccountName, storageAccountName), ), } }