Skip to content

Commit

Permalink
Add Reader role for researchers on private and public storage (#123)
Browse files Browse the repository at this point in the history
Build support for AVM-style role assignments on resource creation
  • Loading branch information
SvenAelterman authored Sep 6, 2024
1 parent db711da commit da01fea
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 5 deletions.
17 changes: 17 additions & 0 deletions research-spoke/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,15 @@ var hubManagementVmSubscriptionId = split(hubManagementVmId, '/')[2]
var hubManagementVmResourceGroupName = split(hubManagementVmId, '/')[4]
var hubManagementVmName = split(hubManagementVmId, '/')[8]

import { roleAssignmentType } from '../shared-modules/types/roleAssignment.bicep'

// Create a role assignment representation for researchers to see the storage accounts
var storageAccountReaderRoleAssignmentForResearcherGroup = {
roleDefinitionId: rolesModule.outputs.roles.Reader
principalId: researcherEntraIdObjectId
description: 'Read access to the storage account is required to use Azure Storage Explorer.'
}

// Deploy the project's private storage account
module storageModule './spoke-modules/storage/main.bicep' = {
name: take(replace(deploymentNameStructure, '{rtype}', 'storage'), 64)
Expand Down Expand Up @@ -440,6 +449,10 @@ module storageModule './spoke-modules/storage/main.bicep' = {

createPolicyExemptions: createPolicyExemptions
policyAssignmentId: policyAssignmentId

storageAccountRoleAssignments: [
storageAccountReaderRoleAssignmentForResearcherGroup
]
}
}

Expand Down Expand Up @@ -602,6 +615,10 @@ module airlockModule './spoke-modules/airlock/main.bicep' = {
hubManagementVmSubscriptionId: hubManagementVmSubscriptionId
hubManagementVmUamiClientId: hubManagementVmUamiClientId
hubManagementVmUamiPrincipalId: hubManagementVmUamiPrincipalId

storageAccountRoleAssignments: [
storageAccountReaderRoleAssignmentForResearcherGroup
]
}
}

Expand Down
11 changes: 7 additions & 4 deletions research-spoke/spoke-modules/airlock/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,18 @@ param subWorkloadName string = 'airlock'
@allowed(['AADDS', 'AADKERB', 'None'])
param filesIdentityType string

@description('Role assignements to create on the storage account.')
param storageAccountRoleAssignments roleAssignmentType

import { roleAssignmentType } from '../../../shared-modules/types/roleAssignment.bicep'

param debugMode bool = false
param debugRemoteIp string = ''

// Types

import { activeDirectoryDomainInfo } from '../../../shared-modules/types/activeDirectoryDomainInfo.bicep'

// TODO: Pass in useCmk parameter

// LATER: Enable export without review (without Logic App, etc.)

// Get a reference to the already existing private storage account for this spoke
// Assumed in the same resource group
resource privateStorageAccount 'Microsoft.Storage/storageAccounts@2021-02-01' existing = {
Expand Down Expand Up @@ -320,6 +321,8 @@ module publicStorageAccountModule '../storage/storageAccount.bicep' = {
filesIdentityType: 'None'

allowSharedKeyAccess: false

storageAccountRoleAssignments: storageAccountRoleAssignments
}
}

Expand Down
7 changes: 7 additions & 0 deletions research-spoke/spoke-modules/storage/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ param storageAccountPrivateEndpointGroups array = [
'file'
]

@description('Role assignements to create on the storage account.')
param storageAccountRoleAssignments roleAssignmentType

import { roleAssignmentType } from '../../../shared-modules/types/roleAssignment.bicep'

@description('The type of identity to use for identity-based authentication to the file share. When using AD DS, set to None.')
@allowed(['AADDS', 'AADKERB', 'None'])
param filesIdentityType string
Expand Down Expand Up @@ -145,6 +150,8 @@ module storageAccountModule 'storageAccount.bicep' = {

createPolicyExemptions: createPolicyExemptions
policyAssignmentId: policyAssignmentId

storageAccountRoleAssignments: storageAccountRoleAssignments
}
}

Expand Down
20 changes: 19 additions & 1 deletion research-spoke/spoke-modules/storage/storageAccount.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ param applyDeleteLock bool = !debugMode

param allowedIpAddresses array = []

@description('Role assignements to create on the storage account.')
param storageAccountRoleAssignments roleAssignmentType

import { roleAssignmentType } from '../../../shared-modules/types/roleAssignment.bicep'

// If debug mode is enabled, deploy an IP rule for the debug IP address; otherwise, just use the specified list
// This will automatically deduplicate
var actualAllowedIpAddresses = debugMode ? concat(allowedIpAddresses, array(debugRemoteIp)) : allowedIpAddresses
Expand Down Expand Up @@ -235,12 +240,25 @@ resource policyExemption 'Microsoft.Authorization/policyExemptions@2022-07-01-pr
description: 'This storage account has the public endpoint disabled.'
displayName: 'Storage Account virtual network service endpoint exemption - ${storageAccount.name}'
exemptionCategory: 'Mitigated'
//expiresOn: 'string'
policyAssignmentId: policyAssignmentId
policyDefinitionReferenceIds: ['storageAccountsShouldUseAVirtualNetworkServiceEndpoint']
}
}

// Create any role assignments on the storage account level
resource accountRoleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [
for (roleAssignment, index) in (storageAccountRoleAssignments ?? []): {
name: guid(storageAccount.id, roleAssignment.principalId, roleAssignment.roleDefinitionId)
properties: {
roleDefinitionId: roleAssignment.roleDefinitionId
principalId: roleAssignment.principalId
description: roleAssignment.?description
principalType: roleAssignment.?principalType
}
scope: storageAccount
}
]

output id string = storageAccount.id
output name string = storageAccount.name
output primaryFileEndpoint string = storageAccount.properties.primaryEndpoints.file
Expand Down
30 changes: 30 additions & 0 deletions shared-modules/types/roleAssignment.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* This type is designed to be compatible with that of Azure Verified Modules for future adoption.
*/

@export()
type roleAssignmentType = {
// @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.')
// name: string?

@description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.')
roleDefinitionId: string

@description('Required. The principal ID of the principal (user/group/identity) to assign the role to.')
principalId: string

@description('Optional. The principal type of the assigned principal ID.')
principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')?

@description('Optional. The description of the role assignment.')
description: string?

// @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".')
// condition: string?

// @description('Optional. Version of the condition.')
// conditionVersion: '2.0'?

// @description('Optional. The Resource Id of the delegated managed identity resource.')
// delegatedManagedIdentityResourceId: string?
}[]?

0 comments on commit da01fea

Please sign in to comment.