Merge pull request #42 from AlexanderSehr/users/alsehr/managedPoolAVM
feat: Implemented published 'Managed DevOps Pool' AVM module
This commit is contained in:
Коммит
ed93813a32
|
@ -12,13 +12,13 @@ variables:
|
|||
## GENERAL ##
|
||||
#############
|
||||
#region shared
|
||||
vmImage_sbx: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents
|
||||
vmImage_dev: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents
|
||||
vmImage_prd: '' # 'ubuntu-latest' # Use this for microsoft-hosted agents
|
||||
vmImage_sbx: 'ubuntu-latest' # Use this for microsoft-hosted agents
|
||||
vmImage_dev: 'ubuntu-latest' # Use this for microsoft-hosted agents
|
||||
vmImage_prd: 'ubuntu-latest' # Use this for microsoft-hosted agents
|
||||
|
||||
poolName_sbx: 'core-vmss' # Use this for self-hosted agents
|
||||
poolName_dev: 'core-vmss' # Use this for self-hosted agents
|
||||
poolName_prd: 'core-vmss' # Use this for self-hosted agents
|
||||
poolName_sbx: '' # Use this for self-hosted agents
|
||||
poolName_dev: '' # Use this for self-hosted agents
|
||||
poolName_prd: '' # Use this for self-hosted agents
|
||||
|
||||
serviceConnection_sbx: '<PrivateConnection>'
|
||||
serviceConnection_dev: '<PrivateConnection>'
|
||||
|
|
|
@ -23,7 +23,7 @@ param waitForImageBuild bool = true
|
|||
/////////////////////////////
|
||||
|
||||
module imageDeployment '../templates/image.deploy.bicep' = {
|
||||
name: '${uniqueString(deployment().name)}-image-sbx'
|
||||
name: '${uniqueString(deployment().name, resourceLocation)}-image-sbx'
|
||||
params: {
|
||||
resourceLocation: resourceLocation
|
||||
deploymentsToPerform: deploymentsToPerform
|
||||
|
|
|
@ -107,7 +107,7 @@ param deploymentsToPerform string = 'Only assets & image'
|
|||
// =========== //
|
||||
|
||||
module imageConstruct 'br/public:avm/ptn/virtual-machine-images/azure-image-builder:0.1.1' = {
|
||||
name: '${deployment().name}-image-construct'
|
||||
name: '${uniqueString(deployment().name, resourceLocation)}-image-construct'
|
||||
params: {
|
||||
deploymentsToPerform: deploymentsToPerform
|
||||
resourceGroupName: resourceGroupName
|
||||
|
|
|
@ -7,15 +7,11 @@ targetScope = 'subscription'
|
|||
@description('Optional. Specifies the location for resources.')
|
||||
param resourceLocation string = 'NorthEurope'
|
||||
|
||||
///////////////////////////////
|
||||
// Deployment Properties //
|
||||
///////////////////////////////
|
||||
|
||||
/////////////////////////////
|
||||
// Template Deployment //
|
||||
/////////////////////////////
|
||||
module managedDevOpsPoolDeployment '../templates/pool.deploy.bicep' = {
|
||||
name: '${uniqueString(deployment().name)}-managedPool-sbx'
|
||||
name: '${uniqueString(deployment().name, resourceLocation)}-managedPool-sbx'
|
||||
params: {
|
||||
resourceLocation: resourceLocation
|
||||
computeGalleryName: '<computeGalleryName>'
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
@description('Required. ')
|
||||
param location string
|
||||
|
||||
@description('Required. The name of the Dev Center to use for the DevOps Infrastructure Pool. Must be lower case and may contain hyphens.')
|
||||
@minLength(3)
|
||||
@maxLength(26)
|
||||
param devCenterName string
|
||||
|
||||
@description('Required. The name of the Dev Center project to use for the DevOps Infrastructure Pool.')
|
||||
@minLength(3)
|
||||
@maxLength(63)
|
||||
param devCenterProjectName string
|
||||
|
||||
resource devCenter 'Microsoft.DevCenter/devcenters@2024-02-01' = {
|
||||
name: devCenterName
|
||||
location: location
|
||||
}
|
||||
|
||||
resource devCenterProject 'Microsoft.DevCenter/projects@2024-02-01' = {
|
||||
name: devCenterProjectName
|
||||
location: location
|
||||
properties: {
|
||||
devCenterId: devCenter.id
|
||||
}
|
||||
}
|
||||
|
||||
@description('The resource ID of the Dev Center project.')
|
||||
output devCenterProjectResourceId string = devCenterProject.id
|
|
@ -1,290 +0,0 @@
|
|||
@description('Required. ')
|
||||
param location string
|
||||
|
||||
@description('Required. Defines how many resources can there be created at any given time.')
|
||||
@minValue(1)
|
||||
@maxValue(10000)
|
||||
param maximumConcurrency int
|
||||
|
||||
@description('Required. The name of the subnet the agents should be deployed into.')
|
||||
param subnetName string
|
||||
|
||||
@description('Required. The resource Id of the Virtual Network the agents should be deployed into.')
|
||||
param virtualNetworkResourceId string
|
||||
|
||||
@description('Required. The name of the Azure DevOps agent pool to create.')
|
||||
param poolName string
|
||||
|
||||
@description('Required. The name of the Azure DevOps organization to register the agent pools in.')
|
||||
param organizationName string
|
||||
|
||||
@description('Optional. The Azure DevOps projects to register the agent pools in. In none is provided, the pool is only registered in the organization.')
|
||||
param projectNames string[]?
|
||||
|
||||
@description('Required. The name of the Dev Center to use for the DevOps Infrastructure Pool. Must be lower case and may contain hyphens.')
|
||||
@minLength(3)
|
||||
@maxLength(26)
|
||||
param devCenterName string
|
||||
|
||||
@description('Required. The name of the Dev Center project to use for the DevOps Infrastructure Pool.')
|
||||
@minLength(3)
|
||||
@maxLength(63)
|
||||
param devCenterProjectName string
|
||||
|
||||
@description('Optional. The Azure SKU name of the machines in the pool.')
|
||||
param poolSize string = 'Standard_B1ms'
|
||||
|
||||
@description('Optional. Defines how the machine will be handled once it executed a job.')
|
||||
param agentProfile agentProfileType = {
|
||||
kind: 'Stateless'
|
||||
}
|
||||
|
||||
@description('Required. The object ID (principal id) of the \'DevOpsInfrastructure\' Enterprise Application in your tenant.')
|
||||
param devOpsInfrastructureEnterpriseApplicationObjectId string
|
||||
|
||||
@description('Required. The name of the Azure Compute Gallery that hosts the image of the Managed DevOps Pool.')
|
||||
param computeGalleryName string
|
||||
|
||||
@description('Required. The name of Image Definition of the Azure Compute Gallery that hosts the image of the Managed DevOps Pool.')
|
||||
param computeGalleryImageDefinitionName string
|
||||
|
||||
@description('Optional. The version of the image to use in the Managed DevOps Pool.')
|
||||
param imageVersion string = 'latest' // Note, 'latest' is not supported by resource type
|
||||
|
||||
@description('Optional. The managed identity definition for the Managed DevOps Pool.')
|
||||
param poolManagedIdentities managedIdentitiesType?
|
||||
|
||||
var formattedUserAssignedIdentities = reduce(
|
||||
map((poolManagedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }),
|
||||
{},
|
||||
(cur, next) => union(cur, next)
|
||||
) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} }
|
||||
|
||||
var poolIdentity = !empty(poolManagedIdentities)
|
||||
? {
|
||||
type: !empty(poolManagedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None'
|
||||
userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null
|
||||
}
|
||||
: null
|
||||
|
||||
resource computeGallery 'Microsoft.Compute/galleries@2022-03-03' existing = {
|
||||
name: computeGalleryName
|
||||
|
||||
resource imageDefinition 'images@2022-03-03' existing = {
|
||||
name: computeGalleryImageDefinitionName
|
||||
|
||||
resource version 'versions@2022-03-03' existing = {
|
||||
name: imageVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource vnet 'Microsoft.Network/virtualNetworks@2024-01-01' existing = {
|
||||
name: last(split(virtualNetworkResourceId, '/'))
|
||||
|
||||
resource subnet 'subnets@2024-01-01' existing = {
|
||||
name: subnetName
|
||||
}
|
||||
}
|
||||
|
||||
resource imageVersionPermission 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
|
||||
name: guid(
|
||||
computeGallery::imageDefinition.id,
|
||||
devOpsInfrastructureEnterpriseApplicationObjectId,
|
||||
subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')
|
||||
)
|
||||
properties: {
|
||||
principalId: devOpsInfrastructureEnterpriseApplicationObjectId
|
||||
roleDefinitionId: subscriptionResourceId(
|
||||
'Microsoft.Authorization/roleDefinitions',
|
||||
'acdd72a7-3385-48ef-bd42-f606fba81ae7'
|
||||
) // Reader
|
||||
principalType: 'ServicePrincipal'
|
||||
}
|
||||
scope: computeGallery::imageDefinition // ::imageVersion Not using imageVersion as scope to enable to principal to find 'latest'. A role assignment on 'latest' is not possible
|
||||
}
|
||||
|
||||
resource vnetPermission 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
|
||||
name: guid(
|
||||
vnet.id,
|
||||
devOpsInfrastructureEnterpriseApplicationObjectId,
|
||||
subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')
|
||||
)
|
||||
properties: {
|
||||
principalId: devOpsInfrastructureEnterpriseApplicationObjectId
|
||||
roleDefinitionId: subscriptionResourceId(
|
||||
'Microsoft.Authorization/roleDefinitions',
|
||||
'4d97b98b-1d4f-4787-a291-c67834d212e7'
|
||||
) // Network Contributor
|
||||
principalType: 'ServicePrincipal'
|
||||
}
|
||||
scope: vnet
|
||||
}
|
||||
|
||||
resource devCenter 'Microsoft.DevCenter/devcenters@2024-02-01' = {
|
||||
name: devCenterName
|
||||
location: location
|
||||
}
|
||||
|
||||
resource devCenterProject 'Microsoft.DevCenter/projects@2024-02-01' = {
|
||||
name: devCenterProjectName
|
||||
location: location
|
||||
properties: {
|
||||
devCenterId: devCenter.id
|
||||
}
|
||||
}
|
||||
|
||||
// Requires: https://github.com/Azure/bicep-registry-modules/pull/3401
|
||||
// module pool 'br/public:avm/res/dev-ops-infrastructure/pool:0.1.0' = {
|
||||
// name:
|
||||
// params: {
|
||||
// name: poolName
|
||||
// agentProfile: agentProfile
|
||||
// concurrency: maximumConcurrency
|
||||
// devCenterProjectResourceId: devCenterProject.id
|
||||
// fabricProfileSkuName: devOpsInfrastructurePoolSize
|
||||
// images: [
|
||||
// {
|
||||
// resourceId: computeGallery::imageDefinition::imageVersion.id
|
||||
// }
|
||||
// ]
|
||||
// organizationProfile: {
|
||||
// kind: 'AzureDevOps'
|
||||
// organizations: [
|
||||
// {
|
||||
// url: 'https://dev.azure.com/${organizationName}'
|
||||
// projects: projectNames
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
resource name 'Microsoft.DevOpsInfrastructure/pools@2024-04-04-preview' = {
|
||||
name: poolName
|
||||
location: location
|
||||
identity: poolIdentity
|
||||
properties: {
|
||||
maximumConcurrency: maximumConcurrency
|
||||
agentProfile: agentProfile
|
||||
organizationProfile: {
|
||||
kind: 'AzureDevOps'
|
||||
organizations: [
|
||||
{
|
||||
url: 'https://dev.azure.com/${organizationName}'
|
||||
projects: projectNames
|
||||
}
|
||||
]
|
||||
}
|
||||
devCenterProjectResourceId: devCenterProject.id
|
||||
fabricProfile: {
|
||||
sku: {
|
||||
name: poolSize
|
||||
}
|
||||
kind: 'Vmss'
|
||||
images: [
|
||||
{
|
||||
resourceId: computeGallery::imageDefinition::version.id
|
||||
}
|
||||
]
|
||||
networkProfile: {
|
||||
subnetId: vnet::subnet.id
|
||||
}
|
||||
}
|
||||
}
|
||||
dependsOn: [
|
||||
imageVersionPermission
|
||||
vnetPermission
|
||||
]
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Definitions //
|
||||
/////////////////////
|
||||
|
||||
@export()
|
||||
@discriminator('kind')
|
||||
type agentProfileType = agentStatefulType | agentStatelessType
|
||||
|
||||
type agentStatefulType = {
|
||||
@description('Required. Stateful profile meaning that the machines will be returned to the pool after running a job.')
|
||||
kind: 'Stateful'
|
||||
|
||||
@description('Required. How long should stateful machines be kept around. The maximum is one week.')
|
||||
maxAgentLifetime: string
|
||||
|
||||
@description('Required. How long should the machine be kept around after it ran a workload when there are no stand-by agents. The maximum is one week.')
|
||||
gracePeriodTimeSpan: string
|
||||
|
||||
@description('Optional. Defines pool buffer/stand-by agents.')
|
||||
resourcePredictions: object?
|
||||
|
||||
@discriminator('kind')
|
||||
@description('Optional. Determines how the stand-by scheme should be provided.')
|
||||
resourcePredictionsProfile: (resourcePredictionsProfileAutomaticType | resourcePredictionsProfileManualType)?
|
||||
}
|
||||
|
||||
type agentStatelessType = {
|
||||
@description('Required. Stateless profile meaning that the machines will be cleaned up after running a job.')
|
||||
kind: 'Stateless'
|
||||
|
||||
@description('Optional. Defines pool buffer/stand-by agents.')
|
||||
resourcePredictions: {
|
||||
@description('Required. The time zone in which the daysData is provided. To see the list of available time zones, see: https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-time-zones?view=windows-11#time-zones or via PowerShell command `(Get-TimeZone -ListAvailable).StandardName`.')
|
||||
timeZone: string
|
||||
|
||||
@description('Optional. The number of agents needed at a specific time.')
|
||||
@metadata({
|
||||
example: '''
|
||||
[
|
||||
{} // Sunday
|
||||
{ // Monday
|
||||
'09:00:00': 1
|
||||
'17:00:00': 0
|
||||
}
|
||||
{ // Tuesday
|
||||
'09:00:00': 1
|
||||
'17:00:00': 0
|
||||
}
|
||||
{ // Wednesday
|
||||
'09:00:00': 1
|
||||
'17:00:00': 0
|
||||
}
|
||||
{ // Thursday
|
||||
'09:00:00': 1
|
||||
'17:00:00': 0
|
||||
}
|
||||
{ // Friday
|
||||
'09:00:00': 1
|
||||
'17:00:00': 0
|
||||
}
|
||||
{} // Saturday
|
||||
]
|
||||
'''
|
||||
})
|
||||
daysData: object[]?
|
||||
}?
|
||||
|
||||
@discriminator('kind')
|
||||
@description('Optional. Determines how the stand-by scheme should be provided.')
|
||||
resourcePredictionsProfile: (resourcePredictionsProfileAutomaticType | resourcePredictionsProfileManualType)?
|
||||
}
|
||||
|
||||
type resourcePredictionsProfileAutomaticType = {
|
||||
@description('Required. The stand-by agent scheme is determined based on historical demand.')
|
||||
kind: 'Automatic'
|
||||
|
||||
@description('Required. Determines the balance between cost and performance.')
|
||||
predictionPreference: 'Balanced' | 'MostCostEffective' | 'MoreCostEffective' | 'MorePerformance' | 'BestPerformance'
|
||||
}
|
||||
|
||||
type resourcePredictionsProfileManualType = {
|
||||
@description('Required. Customer provides the stand-by agent scheme.')
|
||||
kind: 'Manual'
|
||||
}
|
||||
|
||||
@export()
|
||||
type managedIdentitiesType = {
|
||||
@description('Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption.')
|
||||
userAssignedResourceIds: string[]?
|
||||
}
|
|
@ -28,6 +28,9 @@ param virtualNetworkSubnets array = [
|
|||
}
|
||||
]
|
||||
|
||||
@description('Required. The name of the Resource Group containing the Azure Compute Gallery that hosts the image of the Managed DevOps Pool.')
|
||||
param computeGalleryResourceGroupName string = resourceGroupName
|
||||
|
||||
@description('Required. The name of the Azure Compute Gallery that hosts the image of the Managed DevOps Pool.')
|
||||
param computeGalleryName string
|
||||
|
||||
|
@ -49,11 +52,14 @@ param poolMaximumConcurrency int = 1
|
|||
param poolSize string = 'Standard_B1ms'
|
||||
|
||||
@description('Optional. The managed identity definition for the Managed DevOps Pool.')
|
||||
import { managedIdentitiesType } from './nestedPool.bicep'
|
||||
param poolManagedIdentities managedIdentitiesType?
|
||||
import { managedIdentitiesType } from 'br/public:avm/res/dev-ops-infrastructure/pool:0.1.1'
|
||||
param poolManagedIdentities managedIdentitiesType
|
||||
|
||||
// import { managedIdentityOnlyUserAssignedType } from 'br/public:avm/utl/types/avm-common-types:0.1.0'
|
||||
// param poolManagedIdentities managedIdentityOnlyUserAssignedType?
|
||||
|
||||
@description('Optional. Defines how the machine will be handled once it executed a job.')
|
||||
import { agentProfileType } from './nestedPool.bicep'
|
||||
import { agentProfileType } from 'br/public:avm/res/dev-ops-infrastructure/pool:0.1.1'
|
||||
param poolAgentProfile agentProfileType = {
|
||||
kind: 'Stateless'
|
||||
}
|
||||
|
@ -93,7 +99,7 @@ resource rg 'Microsoft.Resources/resourceGroups@2024-03-01' = {
|
|||
|
||||
// Network Security Group
|
||||
module nsg 'br/public:avm/res/network/network-security-group:0.3.0' = {
|
||||
name: '${deployment().name}-nsg'
|
||||
name: '${uniqueString(deployment().name, resourceLocation)}-nsg'
|
||||
scope: rg
|
||||
params: {
|
||||
name: networkSecurityGroupName
|
||||
|
@ -103,7 +109,7 @@ module nsg 'br/public:avm/res/network/network-security-group:0.3.0' = {
|
|||
|
||||
// Virtual Network
|
||||
module vnet 'br/public:avm/res/network/virtual-network:0.4.0' = {
|
||||
name: '${deployment().name}-vnet'
|
||||
name: '${uniqueString(deployment().name, resourceLocation)}-vnet'
|
||||
scope: rg
|
||||
params: {
|
||||
name: virtualNetworkName
|
||||
|
@ -115,25 +121,82 @@ module vnet 'br/public:avm/res/network/virtual-network:0.4.0' = {
|
|||
}
|
||||
}
|
||||
|
||||
module pool 'nestedPool.bicep' = {
|
||||
module devCenter 'devCenter.bicep' = {
|
||||
scope: rg
|
||||
name: '${deployment().name}-pool'
|
||||
name: '${uniqueString(deployment().name, resourceLocation)}-devCenter'
|
||||
params: {
|
||||
location: resourceLocation
|
||||
devCenterName: devCenterName
|
||||
devCenterProjectName: devCenterProjectName
|
||||
maximumConcurrency: poolMaximumConcurrency
|
||||
poolName: poolName
|
||||
poolSize: poolSize
|
||||
poolManagedIdentities: poolManagedIdentities
|
||||
agentProfile: poolAgentProfile
|
||||
organizationName: organizationName
|
||||
projectNames: projectNames
|
||||
virtualNetworkResourceId: vnet.outputs.resourceId
|
||||
subnetName: vnet.outputs.subnetNames[0]
|
||||
computeGalleryImageDefinitionName: computeGalleryImageDefinitionName
|
||||
computeGalleryName: computeGalleryName
|
||||
imageVersion: imageVersion
|
||||
devOpsInfrastructureEnterpriseApplicationObjectId: devOpsInfrastructureEnterpriseApplicationObjectId
|
||||
}
|
||||
}
|
||||
|
||||
resource computeGallery 'Microsoft.Compute/galleries@2022-03-03' existing = {
|
||||
name: computeGalleryName
|
||||
scope: resourceGroup(computeGalleryResourceGroupName)
|
||||
|
||||
resource imageDefinition 'images@2022-03-03' existing = {
|
||||
name: computeGalleryImageDefinitionName
|
||||
|
||||
resource version 'versions@2022-03-03' existing = {
|
||||
name: imageVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module imagePermission 'br/public:avm/ptn/authorization/resource-role-assignment:0.1.1' = {
|
||||
scope: resourceGroup(computeGalleryResourceGroupName)
|
||||
name: '${uniqueString(deployment().name, resourceLocation)}-devOpsInfrastructureEAObjectId-permission-image'
|
||||
params: {
|
||||
principalId: devOpsInfrastructureEnterpriseApplicationObjectId
|
||||
resourceId: computeGallery::imageDefinition.id
|
||||
roleDefinitionId: subscriptionResourceId(
|
||||
'Microsoft.Authorization/roleDefinitions',
|
||||
'acdd72a7-3385-48ef-bd42-f606fba81ae7'
|
||||
) // Reader
|
||||
}
|
||||
}
|
||||
module vnetPermission 'br/public:avm/ptn/authorization/resource-role-assignment:0.1.1' = {
|
||||
scope: rg
|
||||
name: '${uniqueString(deployment().name, resourceLocation)}-devOpsInfrastructureEAObjectId-permission-vnet'
|
||||
params: {
|
||||
principalId: devOpsInfrastructureEnterpriseApplicationObjectId
|
||||
resourceId: vnet.outputs.resourceId
|
||||
roleDefinitionId: subscriptionResourceId(
|
||||
'Microsoft.Authorization/roleDefinitions',
|
||||
'4d97b98b-1d4f-4787-a291-c67834d212e7'
|
||||
) // Network Contributor
|
||||
}
|
||||
}
|
||||
|
||||
module pool 'br/public:avm/res/dev-ops-infrastructure/pool:0.1.1' = {
|
||||
name: '${uniqueString(deployment().name, resourceLocation)}-pool'
|
||||
scope: rg
|
||||
params: {
|
||||
name: poolName
|
||||
managedIdentities: poolManagedIdentities
|
||||
agentProfile: poolAgentProfile
|
||||
concurrency: poolMaximumConcurrency
|
||||
devCenterProjectResourceId: devCenter.outputs.devCenterProjectResourceId
|
||||
fabricProfileSkuName: poolSize
|
||||
images: [
|
||||
{
|
||||
resourceId: computeGallery::imageDefinition::version.id
|
||||
}
|
||||
]
|
||||
organizationProfile: {
|
||||
kind: 'AzureDevOps'
|
||||
organizations: [
|
||||
{
|
||||
url: 'https://dev.azure.com/${organizationName}'
|
||||
projects: projectNames
|
||||
}
|
||||
]
|
||||
}
|
||||
subnetResourceId: vnet.outputs.subnetResourceIds[0]
|
||||
}
|
||||
dependsOn: [
|
||||
imagePermission
|
||||
vnetPermission
|
||||
]
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче