1717
1818targetScope = 'subscription'
1919
20+ // param devopsInfrastructureId string
2021param devopsSubnetAddressPrefix string
22+ param privateEndpointSubnetAddressPrefix string
2123// param enableSoftDelete bool
2224param hubType string // live / nonlive
2325param region string = 'uksouth'
2426param regionShortName string = 'uks'
2527param vnetAddressPrefixes array
28+ param enableSoftDelete bool
2629
27- // var keyVaultName = 'kv-lungcs-${envConfig}-inf'
2830
31+ // removed when generalised
32+ var appShortName = 'lungcs'
33+
34+ var devCenterSuffix = substring (uniqueString (subscription ().id ), 0 , 3 )
35+ var devCenterName = 'devc-hub-${hubType }-${regionShortName }-${devCenterSuffix }'
2936var devopsSubnetName = 'sn-hub-${hubType }-${regionShortName }-devops'
30- var devCenterName = 'devc-hub-${hubType }-${regionShortName }'
3137var devCenterProjectName = 'prj-hub-${hubType }-${regionShortName }'
3238var poolName = 'private-pool-hub-${hubType }-${regionShortName }'
3339var resourceGroupName = 'rg-hub-${hubType }-${regionShortName }-bootstrap'
3440var virtualNetworkName = 'vnet-hub-${hubType }-${regionShortName }'
41+ var managedIdentityRGName = 'rg-mi-${hubType }-${regionShortName }'
42+ var miHub = 'mi-hub-${hubType }-${regionShortName }'
43+ var privateDNSZoneRGName = 'rg-hub-${hubType }-${regionShortName }-private-dns-zones'
44+ var keyVaultName = 'kv-${appShortName }-${hubType }-inf'
45+ var privateEndpointSubnetName = 'sn-hub-${hubType }-${regionShortName }-private-endpoint'
46+ var storageAccountName = 'sa${appShortName }${regionShortName }state'
47+ var computeGalleryName = '${appShortName }_hub_compute_gallery'
48+
49+ var miADOtoAZname = 'mi-${appShortName }-${hubType }-adotoaz-${regionShortName }'
50+ var miGHtoADOname = 'mi-${appShortName }-${hubType }-ghtoado-${regionShortName }'
51+
52+
53+ // See: https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles
54+ var roleID = {
55+ CDNContributor : 'ec156ff8-a8d1-4d15-830c-5b80698ca432'
56+ kvSecretsUser : '4633458b-17de-408a-b874-0445c86b69e6'
57+ networkContributor : '4d97b98b-1d4f-4787-a291-c67834d212e7'
58+ rbacAdmin : 'f58310d9-a9f6-439a-9e8d-f62e7b41a168'
59+ reader : 'acdd72a7-3385-48ef-bd42-f606fba81ae7'
60+ contributor : 'b24988ac-6180-42a0-ab88-20f7382dd24c'
61+ storageBlobDataContributor : 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'
62+ }
3563
36- // var miADOtoAZname = 'mi-${appShortName}-${envConfig}-adotoaz-uks'
37- // var miGHtoADOname = 'mi-${appShortName}-${envConfig}-ghtoado-uks'
3864
3965resource bootstrapRG 'Microsoft.Resources/resourceGroups@2025-04-01' = {
4066 name : resourceGroupName
@@ -54,7 +80,8 @@ module virtualNetwork 'modules/virtualNetwork.bicep' = {
5480module managedDevopsPool 'modules/managedDevopsPool.bicep' = {
5581 scope : bootstrapRG
5682 params : {
57- adoOrg : 'nhse-pps-1'
83+ //adoOrg: 'nhse-pps-1'
84+ adoOrg : 'nhse-dtos'
5885 agentProfileMaxAgentLifetime : '00.04:00:00'
5986 devCenterName : devCenterName
6087 devCenterProjectName : devCenterProjectName
@@ -64,3 +91,182 @@ module managedDevopsPool 'modules/managedDevopsPool.bicep' = {
6491 virtualNetworkName : virtualNetwork .outputs .name
6592 }
6693}
94+
95+ @description ('Retrieve existing managed identity resource group' )
96+ resource managedIdentityRG 'Microsoft.Resources/resourceGroups@2024-11-01' = {
97+ name : managedIdentityRGName
98+ location : region
99+ }
100+
101+ @description ('Retrieve existing private DNS zone resource group' )
102+ resource privateDNSZoneRG 'Microsoft.Resources/resourceGroups@2024-11-01' = {
103+ name : privateDNSZoneRGName
104+ location : region
105+ }
106+
107+ @description ('Create the managed identity assumed by Azure devops to connect to Azure' )
108+ module managedIdentiyHub 'modules/managedIdentity.bicep' = {
109+ scope : managedIdentityRG
110+ params : {
111+ name : miHub
112+ region : region
113+ }
114+ }
115+
116+ @description ('Storage Deployment' )
117+ module terraformStateStorageAccount 'modules/storage.bicep' = {
118+ scope : bootstrapRG
119+ params : {
120+ storageLocation : region
121+ storageName : storageAccountName
122+ enableSoftDelete : true
123+ miPrincipalID : managedIdentiyHub .outputs .miPrincipalID
124+ miName : miHub
125+ }
126+ }
127+
128+ @description ('Create private endpoint and register DNS' )
129+ module storageAccountPrivateEndpoint 'modules/privateEndpoint.bicep' = {
130+ scope : bootstrapRG
131+ params : {
132+ hub : hubType
133+ region : region
134+ name : storageAccountName
135+ vnetName : virtualNetwork .outputs .name
136+ virtualNetworkName : virtualNetwork .outputs .name
137+ privateEndpointSubnetName : privateEndpointSubnetName
138+ privateEndpointSubnetAddressPrefix : privateEndpointSubnetAddressPrefix
139+ RGName : bootstrapRG .name
140+ resourceServiceType : 'storage'
141+ resourceID : terraformStateStorageAccount .outputs .storageAccountID
142+ privateDNSZoneID : storagePrivateDNSZone .outputs .privateDNSZoneID
143+ }
144+ }
145+
146+ @description ('Retrieve storage private DNS zone' )
147+ module storagePrivateDNSZone 'modules/dns.bicep' = {
148+ scope : privateDNSZoneRG
149+ params : {
150+ resourceServiceType : 'storage'
151+ vnetId : virtualNetwork .outputs .id
152+ location : region
153+ }
154+ }
155+
156+ @description ('Create the managed identity assumed by Azure devops to connect to Azure' )
157+ module managedIdentiyADOtoAZ 'modules/managedIdentity.bicep' = {
158+ scope : managedIdentityRG
159+ params : {
160+ name : miADOtoAZname
161+ region : region
162+ }
163+ }
164+
165+ @description ('Let the managed identity configure vnet peering and DNS records' )
166+ resource networkContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
167+ name : guid (subscription ().subscriptionId , hubType , 'networkContributor' )
168+ properties : {
169+ roleDefinitionId : subscriptionResourceId ('Microsoft.Authorization/roleDefinitions' , roleID .networkContributor )
170+ principalId : managedIdentiyADOtoAZ .outputs .miPrincipalID
171+ description : '${miADOtoAZname } Network Contributor access to subscription'
172+ }
173+ }
174+
175+ @description ('Let the managed identity assign RBAC roles (required for Azure Virtual Desktop)' )
176+ resource userAccessAdministratorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
177+ name : guid (subscription ().subscriptionId , hubType , 'UserAccessAdministrator' )
178+ properties : {
179+ roleDefinitionId : subscriptionResourceId (
180+ 'Microsoft.Authorization/roleDefinitions' ,
181+ roleID .rbacAdmin
182+ )
183+ principalId : managedIdentiyADOtoAZ .outputs .miPrincipalID
184+ description : '${miADOtoAZname } User Access Administrator access to subscription'
185+ }
186+ }
187+
188+ @description ('Let the managed identity configure Front door and its resources' )
189+ resource CDNContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
190+ name : guid (subscription ().subscriptionId , hubType , 'CDNContributor' )
191+ properties : {
192+ roleDefinitionId : subscriptionResourceId ('Microsoft.Authorization/roleDefinitions' , roleID .CDNContributor )
193+ principalId : managedIdentiyADOtoAZ .outputs .miPrincipalID
194+ description : '${miADOtoAZname } CDN Contributor access to subscription'
195+ }
196+ }
197+
198+ @description ('Let the managed identity deploy terraform on the subscription' )
199+ resource TerraformContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
200+ name : guid (subscription ().subscriptionId , hubType , 'TerraformContributor' )
201+ properties : {
202+ roleDefinitionId : subscriptionResourceId ('Microsoft.Authorization/roleDefinitions' , roleID .contributor )
203+ principalId : managedIdentiyADOtoAZ .outputs .miPrincipalID
204+ description : '${miADOtoAZname } Terraform Contributor access to subscription'
205+ }
206+ }
207+
208+ @description ('Let the managed identity strore blobs in storage account' )
209+ resource StorageAccountBlobContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
210+ name : guid (subscription ().subscriptionId , hubType , 'StorageAccountBlobContributorAssignment' )
211+ properties : {
212+ roleDefinitionId : subscriptionResourceId ('Microsoft.Authorization/roleDefinitions' , roleID .storageBlobDataContributor )
213+ principalId : managedIdentiyADOtoAZ .outputs .miPrincipalID
214+ description : '${miADOtoAZname } Storage Account Blob Contributor access to subscription'
215+ }
216+ }
217+
218+ @description ('Create the managed identity assumed by Github actions to trigger Azure devops pipelines' )
219+ module managedIdentiyGHtoADO 'modules/managedIdentity.bicep' = {
220+ scope : managedIdentityRG
221+ params : {
222+ name : miGHtoADOname
223+ fedCredProperties : {
224+ audiences : [ 'api://AzureADTokenExchange' ]
225+ issuer : 'https://token.actions.githubusercontent.com'
226+ subject : 'repo:NHSDigital/dtos-manage-breast-screening:environment:${hubType }'
227+ }
228+ region : region
229+ }
230+ }
231+
232+ @description ('Let the GHtoADO managed identity access a subscription' )
233+ resource readerAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
234+ name : guid (subscription ().subscriptionId , hubType , 'reader' )
235+ properties : {
236+ roleDefinitionId : subscriptionResourceId ('Microsoft.Authorization/roleDefinitions' , roleID .reader )
237+ principalId : managedIdentiyGHtoADO .outputs .miPrincipalID
238+ description : '${miGHtoADOname } Reader access to subscription'
239+ }
240+ }
241+
242+ @description ('Deploy the Key Vault for storing secrets' )
243+ module keyVaultModule 'modules/keyVault.bicep' = {
244+ name : 'keyVaultDeployment'
245+ scope : resourceGroup (resourceGroupName )
246+ params : {
247+ enableSoftDelete : enableSoftDelete
248+ keyVaultName : keyVaultName
249+ miName : miADOtoAZname
250+ miPrincipalId : managedIdentiyADOtoAZ .outputs .miPrincipalID
251+ region : region
252+ }
253+ }
254+
255+ @description ('Retrieve key vault private DNS zone' )
256+ module keyVaultPrivateDNSZone 'modules/dns.bicep' = {
257+ scope : privateDNSZoneRG
258+ params : {
259+ resourceServiceType : 'keyVault'
260+ vnetId : virtualNetwork .outputs .id
261+ location : region
262+ }
263+ }
264+
265+
266+ module computeGallery 'modules/computeGallery.bicep' = {
267+ scope : resourceGroup (resourceGroupName )
268+ params : {
269+ galleryName : computeGalleryName
270+ location : region
271+ }
272+ }
0 commit comments