Update repo structure to focus on Bicep #45 (#47)

This commit is contained in:
Bernie White 2024-03-12 23:44:05 +10:00 коммит произвёл GitHub
Родитель d9edfdafe6
Коммит abc1d99ac8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
19 изменённых файлов: 707 добавлений и 838 удалений

10
.vscode/settings.json поставляемый
Просмотреть файл

@ -11,5 +11,13 @@
"[powershell]": {
"editor.formatOnSave": false,
"editor.tabSize": 4
}
},
"cSpell.words": [
"bicepparam",
"contoso",
"eastus"
],
"cSpell.enableFiletypes": [
"bicep"
]
}

Просмотреть файл

@ -13,10 +13,10 @@ To learn more about PSRule for Azure, see <https://aka.ms/ps-rule-azure>.
This repository includes:
- **Azure Templates** &mdash; Starter Azure Resource Manager (ARM) templates and parameter files.
- Use the files in the `template/` folder if you are using ARM templates to deploy resources.
- **Azure Bicep** &mdash; Starter Azure Bicep deployments and test files.
- Use the files in the `bicep/` folder if you are using Bicep deployments and modules to deploy resources.
- **Azure Bicep deployment** &mdash; Starter Azure Bicep deployments.
- Use the files in the `deployments/` folder if you are using Bicep to deploy resources.
- **Azure Bicep modules** &mdash; Starter Azure Bicep modules.
- Use the files in the `modules/` folder if you are using Bicep to create reusable modules with tests.
- **GitHub Actions** &mdash; Starter workflow for checking Azure Infrastructure as Code (IaC).
- Use the files in the `.github/workflows/` to check your Azure IaC with GitHub Actions.
- The `ms-analyze.yaml` file can be ignore or removed as this will not execute outside this repository.
@ -28,14 +28,21 @@ This repository includes:
- PSRule options are configures within `ps-rule.yaml`.
- Options include suppressing rules, configuring input/ output, and any rules modules.
> **ARM templates**
> PSRule for Azure supports ARM templates in addition to Bicep code.
> However going forward this repository will focus on Bicep deployments and modules.
> Existing ARM templates samples are no longer maintained and have been archived.
> To access these samples jump to the [archive/with-arm-templates][3] branch.
[3]: https://github.com/Azure/PSRule.Rules.Azure-quickstart/tree/archive/with-arm-templates
## What to expect?
This repository shows valid uses of PSRule for Azure, both pass and failure cases.
Inspect the following files for instructions to test PSRule for Azure rules by creating a failure.
- [bicep/deployments/contoso/landing-zones/subscription-1/rg-app-001/dev.bicepparam](bicep/deployments/contoso/landing-zones/subscription-1/rg-app-001/dev.bicepparam)
- [bicep/deployments/contoso/landing-zones/subscription-1/rg-app-002/deploy.bicep](bicep/deployments/contoso/landing-zones/subscription-1/rg-app-002/deploy.bicep)
- [template/deployments/contoso/landing-zones/subscription-1/rg-app-001/sttemplateapp001.parameters.json](template/deployments/contoso/landing-zones/subscription-1/rg-app-001/sttemplateapp001.parameters.json)
- [deployments/contoso/landing-zones/subscription-1/rg-app-001/dev.bicepparam](deployments/contoso/landing-zones/subscription-1/rg-app-001/dev.bicepparam)
- [deployments/contoso/landing-zones/subscription-1/rg-app-002/deploy.bicep](deployments/contoso/landing-zones/subscription-1/rg-app-002/deploy.bicep)
## Support
@ -61,7 +68,6 @@ or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any addi
## Maintainers
- [Bernie White](https://github.com/BernieWhite)
- [Sam Bell](https://github.com/ms-sambell)
## License

Просмотреть файл

@ -1,89 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
targetScope = 'resourceGroup'
metadata name = 'Storage Account'
metadata description = 'Create or update an Storage Account.'
@sys.description('The name of the Storage Account.')
param name string
@metadata({
strongType: 'location'
})
@sys.description('The Azure region to deploy to.')
param location string = resourceGroup().location
@allowed([
'Standard_GRS'
'Standard_LRS'
])
@sys.description('Create the Storage Account as LRS or GRS.')
param sku string = 'Standard_GRS'
@sys.description('Determines if any containers can be configured with the anonymous access types of blob or container.')
param allowBlobPublicAccess bool = true
@metadata({
example: {
service: '<service_name>'
env: 'prod'
}
})
@sys.description('Tags to apply to the resource.')
param tags object = resourceGroup().tags
@sys.description('Create or update an Storage Account.')
resource storageAccount 'Microsoft.Storage/storageAccounts@2019-06-01' = {
name: name
location: location
sku: {
name: sku
}
kind: 'StorageV2'
properties: {
networkAcls: {
bypass: 'AzureServices'
virtualNetworkRules: []
ipRules: []
defaultAction: 'Deny'
}
supportsHttpsTrafficOnly: true
accessTier: 'Hot'
allowBlobPublicAccess: allowBlobPublicAccess
minimumTlsVersion: 'TLS1_2'
}
tags: tags
}
@sys.description('Configure Blob Services for a Storage Account.')
resource blobServices 'Microsoft.Storage/storageAccounts/blobServices@2019-06-01' = {
parent: storageAccount
name: 'default'
properties: {
cors: {
corsRules: []
}
deleteRetentionPolicy: {
enabled: true
days: 7
}
containerDeleteRetentionPolicy: {
enabled: true
days: 7
}
}
}
@sys.description('Configure File Services for a Storage Account.')
resource fileServices 'Microsoft.Storage/storageAccounts/fileServices@2019-06-01' = {
parent: storageAccount
name: 'default'
properties: {
shareDeleteRetentionPolicy: {
enabled: true
days: 7
}
}
}

Просмотреть файл

@ -1,6 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// Note:
// This Azure Bicep parameter file demonstrates using parameters.
using 'main.bicep'
// The env tag must be test, dev, or prod.
// Try setting this to 'demo' to fail the custom organization Org.Azure.Tags rule.
// See .ps-rule/Org.Rule.yaml for details.
param environment = 'dev'
param name = 'kv-example-001'
// Key Vault should only accept explicitly allowed traffic through the firewall.

Просмотреть файл

@ -1,3 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// Note:
// This Azure Bicep code demonstrates using resources directly.
// Also see parameter file for configurable options.
targetScope = 'resourceGroup'
param name string
@ -20,9 +27,12 @@ resource vault 'Microsoft.KeyVault/vaults@2023-02-01' = {
name: 'standard'
}
tenantId: tenant().tenantId
// Try setting any of these to false to flag an issue.
enableSoftDelete: true
enablePurgeProtection: true
enableRbacAuthorization: true
networkAcls: {
defaultAction: defaultAction
}

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License.
// Note:
// This Azure Bicep code demonistrates a deployment of one or more modules.
// This Azure Bicep code demonstrates a deployment of one or more modules.
// This file has multiple template errors to show validation.
@description('Configures the location to deploy the Azure resources.')
@ -42,7 +42,8 @@ module keyvault '../../../../../modules/keyvault/v1/main.bicep' = {
workspaceId: '/subscriptions/<subscription_id>/resourceGroups/rg-test/providers/Microsoft.OperationalInsights/workspaces/latest001'
// An env tag must be test, dev, or prod.
// Try setting this to 'demo' to fail the Org.Azure.Tags rule.
// Try setting this to 'demo' to fail the custom organization Org.Azure.Tags rule.
// See .ps-rule/Org.Rule.yaml for details.
tags: {
env: 'dev'
}

Просмотреть файл

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// Note:
// This Azure Bicep code demonstrates using an AVM module.
module vault 'br/public:avm/res/key-vault/vault:0.3.5' = {
// The name of the deployment.
name: '${uniqueString(deployment().name)}-test-kvvwaf'
params: {
// The name of the key vault.
name: 'kvvwaf002'
// Try setting any of these to false to flag an issue.
enablePurgeProtection: true
enableRbacAuthorization: true
networkAcls: {
bypass: 'AzureServices'
// Try setting the firewall to 'Allow' traffic by default to flag an issue.
defaultAction: 'Deny'
}
diagnosticSettings: [
{
workspaceResourceId: '<workspaceResourceId>'
}
]
softDeleteRetentionInDays: 7
// An env tag must be test, dev, or prod.
// Try setting this to 'demo' to fail the custom organization Org.Azure.Tags rule.
// See .ps-rule/Org.Rule.yaml for details.
tags: {
env: 'dev'
}
}
}

Просмотреть файл

@ -2,9 +2,9 @@
// Licensed under the MIT License.
targetScope = 'resourceGroup'
metadata name = 'Key Vault'
metadata description = 'Create or update an Azure Key Vault.'
metadata version = '1.0.0'
@sys.description('The name of the Key Vault.')
param name string

Просмотреть файл

@ -0,0 +1,105 @@
// Create or update a Private Endpoint for the Storage Account.
// ----------
// PARAMETERS
// ----------
@description('The name of the Private Endpoint.')
param name string
@metadata({
strongType: 'location'
example: 'eastus'
})
@description('The Azure region to deploy to.')
param location string
@description('The unique resource identifer for the resource to expose through the Private Endpoint.')
param resourceId string
@allowed([
'blob'
'file'
'table'
'queue'
])
@description('The sub-resources to register the Private Endpoint for.')
param groupId string
@metadata({
strongType: 'Microsoft.Network/virtualNetworks/subnets'
})
@description('The unique resource identifer for the subnet to join the private endpoint to.')
param subnetId string
@metadata({
strongType: 'Microsoft.Network/privateDnsZones'
})
@description('The private DNS zone to register the private endpoint within.')
param privateDnsZoneId string = ''
@description('Tags to apply to the resource.')
param tags object
// ---------
// VARIABLES
// ---------
// ---------
// RESOURCES
// ---------
@description('Create or update a Private Endpoint for a resource.')
resource endpoint 'Microsoft.Network/privateEndpoints@2023-04-01' = {
location: location
name: name
properties: {
subnet: {
id: subnetId
}
privateLinkServiceConnections: [
{
name: name
properties: {
privateLinkServiceId: resourceId
groupIds: [
groupId
]
}
}
]
}
tags: tags
}
@description('Configures DNS for the Private Endpoint.')
resource endpointGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-04-01' = if (!empty(privateDnsZoneId)) {
parent: endpoint
name: 'default'
properties: {
privateDnsZoneConfigs: [
{
name: replace(last(split(privateDnsZoneId, '/')), '.', '-')
properties: {
privateDnsZoneId: privateDnsZoneId
}
}
]
}
}
// -------
// OUTPUTS
// -------
@description('A unique identifier for the Private Endpoint.')
output id string = endpoint.id
@description('The name of the associated Private DNS Zone.')
output privateDnsZone string = last(split(privateDnsZoneId, '/'))
@description('The name of the Resource Group where the Private Endpoint is deployed.')
output resourceGroupName string = resourceGroup().name
@description('The guid for the subscription where the Private Endpoint is deployed.')
output subscriptionId string = subscription().subscriptionId

Просмотреть файл

@ -0,0 +1,67 @@
// Configure role assignments for the Storage Account
// ----------
// PARAMETERS
// ----------
@sys.description('The display name of the role to assign or the GUID.')
param role string
@sys.description('The GUID of the identity object to assign.')
param principalId string
@sys.description('A description of the assignment.')
param description string = ''
@allowed([
'ServicePrincipal'
'Group'
'User'
'ForeignGroup'
'Device'
])
@sys.description('The principal type to assign.')
param principalType string = 'ServicePrincipal'
@sys.description('The name of the Storage Account.')
param resourceName string
// ---------
// VARIABLES
// ---------
var roles = {
Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')
Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')
'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')
'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')
'Storage Account Key Operator Service Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')
'Storage Blob Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')
'Storage Blob Data Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')
'Storage Blob Data Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')
'Storage File Data SMB Share Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')
'Storage File Data SMB Share Elevated Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')
'Storage File Data SMB Share Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')
}
var roleDefinitionId = contains(roles, role) ? roles[role] : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', role)
// ---------
// RESOURCES
// ---------
resource r 'Microsoft.Storage/storageAccounts@2023-01-01' existing = {
name: resourceName
}
@sys.description('Assign permissions to an Azure AD principal.')
resource rbac 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(r.id, principalId, roleDefinitionId)
scope: r
properties: {
principalId: principalId
roleDefinitionId: roleDefinitionId
principalType: principalType
description: description
}
}

Просмотреть файл

@ -0,0 +1,439 @@
// Create or update a Storage Account
targetScope = 'resourceGroup'
metadata name = 'Storage Account'
metadata description = 'Deploys and configures a Storage Account, optionally with a Private Endpoint. When Private Endpoints are enabled, access from the Internet is blocked.'
metadata version = '1.0.0'
// ----------
// PARAMETERS
// ----------
@minLength(3)
@maxLength(24)
@sys.description('The name of the Storage Account.')
#disable-next-line BCP334
param name string = take(deployment().name, 24)
@metadata({
strongType: 'location'
example: 'eastus'
ignore: true
})
@sys.description('The Azure region to deploy to.')
param location string = resourceGroup().location
@allowed([
'StorageV2'
'FileStorage'
])
@sys.description('The type of storage to use.')
param storageKind string = 'StorageV2'
@allowed([
'Standard_LRS'
'Standard_GRS'
'Standard_ZRS'
'Premium_LRS'
'Premium_ZRS'
])
@sys.description('Create the Storage Account as LRS, GRS, or ZRS.')
param sku string = 'Standard_GRS'
@metadata({
example: [
{
name: 'CONTAINER_NAME'
publicAccess: 'None'
metadata: {}
}
]
})
@sys.description('An list of storage containers to create on the storage account. [See docs](https://npccloud.com/docs)')
param containers containerType[] = []
@metadata({
example: {
enabled: true
name: 'RULE_NAME'
type: 'Lifecycle'
definition: {
actions: {
baseBlob: {
delete: {
daysAfterModificationGreaterThan: 7
}
}
}
filters: {
blobTypes: [
'blockBlob'
]
prefixMatch: [
'logs/'
]
}
}
}
})
@sys.description('An array of lifecycle management policies for the Storage Account.')
param lifecycleRules object[] = []
@minValue(0)
@maxValue(365)
@metadata({
example: 7
})
@sys.description('The number of days to retain deleted blobs. When set to 0, soft delete is disabled.')
param blobSoftDeleteDays int = 7
@minValue(0)
@maxValue(365)
@metadata({
example: 7
})
@sys.description('The number of days to retain deleted containers. When set to 0, soft delete is disabled.')
param containerSoftDeleteDays int = 7
@metadata({
example: [
{
name: 'SHARE_NAME'
shareQuota: 5
metadata: {}
}
]
})
@sys.description('An array of file shares to create on the Storage Account.')
param shares object[] = []
@metadata({
ignore: true
})
@sys.description('Determines if large file shares are enabled. This can not be disabled once enabled.')
param useLargeFileShares bool = false
@minValue(0)
@maxValue(365)
@metadata({
example: 7
})
@sys.description('The number of days to retain deleted shares. When set to 0, soft delete is disabled.')
param shareSoftDeleteDays int = 7
@allowed([
'Deny'
'Allow'
])
@sys.description('Deny or allow network traffic unless explicitly allowed.')
param defaultFirewallAction string = 'Deny'
@metadata({
example: [
'x.x.x.x'
]
})
@sys.description('Firewall rules to permit specific IP addresses access to storage.')
param firewallIPRules string[] = []
@metadata({
example: [
'/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.Network/virtualNetworks/VNET_NAME/subnets/SUBNET_NAME'
]
})
@sys.description('A list of resource IDs to subnets that are permitted access to storage. For each entry, a service endpoint firewall rule is created for the subnet.')
param firewallVirtualNetworkRules string[] = []
@sys.description('Determines if any containers can be configured with the anonymous access types of blob or container. By default, anonymous access to blobs and containers is disabled (`false`).')
param allowBlobPublicAccess bool = false
@sys.description('Determines if access keys and SAS tokens can be used to access storage. By default, access keys and SAS tokens are disabled (`false`).')
param allowSharedKeyAccess bool = false
@sys.description('Determines if the Azure Portal defaults to OAuth.')
param defaultToOAuthAuthentication bool = true
@sys.maxLength(0)
@sys.maxLength(5)
@sys.description('Configures any CORS rules to apply to blob requests.')
param cors corsRuleType[] = []
@metadata({
example: [
{
principalId: 'OBJECT_ID'
description: 'DESCRIPTION'
principalType: 'Group'
role: 'Contributor'
}
]
})
@sys.description('A list of additional role assignments for the Storage Account.')
param assignments assignmentType[] = []
@metadata({
strongType: 'Microsoft.Network/virtualNetworks/subnets'
})
@sys.description('The subnet to connect a private endpoint.')
param subnetId string = ''
@sys.description('Additional tags to apply to the resource. Tags from the resource group will automatically be applied.')
param tags object = {}
// -----
// TYPES
// -----
type corsRuleType = {
@sys.description('A list of headers allowed to be part of the cross-origin request.')
allowedHeaders: string[]
@sys.description('A list of HTTP methods that are allowed to be executed by the origin.')
allowedMethods: ('CONNECT' | 'DELETE' | 'GET' | 'HEAD' | 'MERGE' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE')[]
@sys.description('A list of origin domains that will be allowed via CORS, or `*` to allow all domains.')
allowedOrigins: string[]
@sys.description('A list of response headers to expose to CORS clients.')
exposedHeaders: string[]
@sys.description('The number of seconds that the client/ browser should cache a preflight response.')
maxAgeInSeconds: int
}
type assignmentType = {
@sys.minLength(36)
@sys.maxLength(36)
@sys.description('The GUID of the principal to assign.')
principalId: string
@sys.description('A description for the assignment.')
description: string?
@sys.description('The type of principal.')
principalType: 'ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device'
@sys.description('''
The a name or GUID for the role to assign. Common role assignments include:
- `Owner`
- `Contributor`
- `Reader`
- `User Access Administrator`
- `Storage Account Contributor`
- `Storage Account Key Operator Service Role`
- `Storage Blob Data Contributor`
- `Storage Blob Data Owner`
- `Storage Blob Data Reader`
- `Storage File Data SMB Share Contributor`
- `Storage File Data SMB Share Elevated Contributor`
- `Storage File Data SMB Share Reader`
''')
role: string
}
type containerType = {
@sys.description('The name of the container.')
name: string
@sys.description('Determines if the container is exposed without authentication.')
publicAccess: 'Blob' | 'Container' | 'None' | null
@sys.description('Additional metadata to assign to the container.')
metadata: object?
}
// ---------
// VARIABLES
// ---------
// Calculate storage account name using existing complex naming rules
var storageAccountName = toLower(name)
// Always use large file shares if using FileStorage
var configureLargeFileShares = storageKind == 'FileStorage' ? true : useLargeFileShares
var largeFileSharesState = configureLargeFileShares ? 'Enabled' : 'Disabled'
// Configure private endpoints based on blob or file
var blobEndpoint = [
'blob'
]
var fileEndpoint = [
'file'
]
var isFileStorage = storageKind == 'FileStorage'
var usePrivateEndpoint = !empty(subnetId)
var endpoints = !usePrivateEndpoint ? [] : isFileStorage ? fileEndpoint : blobEndpoint
// Configure tags
var allTags = union(resourceGroup().tags, tags)
// ---------
// RESOURCES
// ---------
@sys.description('Create or update a Storage Account.')
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
name: sku
}
kind: storageKind
properties: {
networkAcls: {
defaultAction: usePrivateEndpoint ? 'Deny' : defaultFirewallAction
bypass: 'AzureServices'
ipRules: [for item in firewallIPRules: {
action: 'Allow'
value: item
}]
virtualNetworkRules: [for item in firewallVirtualNetworkRules: {
action: 'Allow'
#disable-next-line use-resource-id-functions
id: item
}]
resourceAccessRules: [
{
tenantId: tenant().tenantId
#disable-next-line use-resource-id-functions
resourceId: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Security/datascanners/StorageDataScanner'
}
]
}
supportsHttpsTrafficOnly: true
encryption: {
services: {
file: {
enabled: true
}
blob: {
enabled: true
}
}
keySource: 'Microsoft.Storage'
}
accessTier: 'Hot'
largeFileSharesState: largeFileSharesState
allowBlobPublicAccess: allowBlobPublicAccess
allowSharedKeyAccess: allowSharedKeyAccess
defaultToOAuthAuthentication: allowSharedKeyAccess ? true : defaultToOAuthAuthentication
minimumTlsVersion: 'TLS1_2'
publicNetworkAccess: usePrivateEndpoint ? 'Disabled' : 'Enabled'
}
tags: allTags
}
@sys.description('Configure blob services for the Storage Account.')
resource blobServices 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = {
parent: storageAccount
name: 'default'
properties: {
cors: {
corsRules: cors ?? []
}
deleteRetentionPolicy: {
enabled: blobSoftDeleteDays > 0
#disable-next-line BCP329
days: blobSoftDeleteDays > 0 ? blobSoftDeleteDays : null
}
containerDeleteRetentionPolicy: {
enabled: containerSoftDeleteDays > 0
#disable-next-line BCP329
days: containerSoftDeleteDays > 0 ? containerSoftDeleteDays : null
}
}
}
@sys.description('Configure file services for the Storage Account.')
resource fileServices 'Microsoft.Storage/storageAccounts/fileServices@2023-01-01' = {
parent: storageAccount
name: 'default'
properties: {
shareDeleteRetentionPolicy: {
enabled: shareSoftDeleteDays > 0
#disable-next-line BCP329
days: shareSoftDeleteDays > 0 ? shareSoftDeleteDays : null
}
}
}
@sys.description('Create or update blob containers for the Storage Account.')
resource storageContainers 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = [for item in containers: if (!empty(containers)) {
parent: blobServices
name: item.name
properties: {
metadata: contains(item, 'metadata') ? item.metadata : {}
publicAccess: contains(item, 'publicAccess') ? item.publicAccess : 'None'
}
}]
@sys.description('Create or update file shares for the Storage Account.')
resource storageShares 'Microsoft.Storage/storageAccounts/fileServices/shares@2023-01-01' = [for item in shares: if (!empty(shares)) {
parent: fileServices
name: item.name
properties: {
metadata: contains(item, 'metadata') ? item.metadata : {}
shareQuota: contains(item, 'shareQuota') ? item.shareQuota : 5120
}
}]
@sys.description('Configure policies for managing blob lifecycle for the Storage Account.')
resource managementPolicies 'Microsoft.Storage/storageAccounts/managementPolicies@2023-01-01' = if (!empty(lifecycleRules)) {
parent: storageAccount
name: 'default'
properties: {
policy: {
rules: lifecycleRules
}
}
}
@sys.description('Create or update a Private Endpoint for the Storage Account.')
module pe '.bicep/pe.bicep' = [for endpoint in endpoints: {
name: 'pend-${storageAccountName}-${endpoint[0]}-001'
params: {
name: 'pend-${storageAccountName}-${endpoint[0]}-001'
location: location
resourceId: storageAccount.id
groupId: endpoint
subnetId: subnetId
tags: tags
}
}]
@sys.description('Create or update role assignments for the Storage Account.')
module rbac '.bicep/rbac.bicep' = [for assignment in assignments: {
name: 'assignment-${uniqueString(storageAccount.id, assignment.principalId, assignment.role)}'
params: {
principalId: assignment.principalId
description: contains(assignment, 'description') ? assignment.description : ''
principalType: assignment.principalType
role: assignment.role
resourceName: storageAccount.name
}
}]
// -------
// OUTPUTS
// -------
@sys.description('A unique identifier for the Storage Account.')
output id string = storageAccount.id
@sys.description('The name of the Storage Account.')
output storageAccountName string = storageAccountName
@sys.description('The name of the Resource Group where the Storage Account is deployed.')
output resourceGroupName string = resourceGroup().name
@sys.description('The guid for the subscription where the Storage Account is deployed.')
output subscriptionId string = subscription().subscriptionId
@sys.description('The primary blob endpoint for the Storage Account.')
output blobEndpoint string = isFileStorage ? '' : storageAccount.properties.primaryEndpoints.blob

Просмотреть файл

@ -19,7 +19,7 @@ execution:
# Require minimum versions of modules.
requires:
PSRule: '@pre >=2.9.0'
PSRule.Rules.Azure: '@pre >=1.31.1'
PSRule.Rules.Azure: '@pre >=1.34.2'
# Use PSRule for Azure.
include:
@ -36,12 +36,11 @@ input:
- '**'
# Include deployments.
- '!bicep/deployments/**/*.bicepparam'
- '!bicep/deployments/**/deploy.bicep'
- '!template/**/*.parameters.json'
- '!deployments/**/*.bicepparam'
- '!deployments/**/deploy.bicep'
# Include module tests.
- '!bicep/modules/**/*.tests.bicep'
- '!modules/**/*.tests.bicep'
configuration:
# Enable automatic expansion of Azure parameter files.
@ -58,7 +57,10 @@ configuration:
AZURE_BICEP_CHECK_TOOL: true
# Configure the minimum version of the Bicep CLI.
AZURE_BICEP_MINIMUM_VERSION: '0.19.5'
AZURE_BICEP_MINIMUM_VERSION: '0.25.53'
AZURE_DEPLOYMENT_NONSENSITIVE_PARAMETER_NAMES:
- keys
# Suppression ignores rules for a specific Azure resource by name.
suppression:

Просмотреть файл

@ -1,41 +0,0 @@
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"template": "template/templates/policy-exemption/v1/template.json"
},
"parameters": {
"exemptionNameSuffix": {
"value": "awesome-app-exemption-001"
},
"assignmentId": {
"value": "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Authorization/policyAssignments/SecurityCenterBuiltIn"
},
"resourceGroup": {
"value": "rg-awesome-app-prod-eus-001"
},
"exemptionCategory": {
"value": "Waiver"
},
"description": {
"value": "Allow cognitive services to be public and called directly from mobile workforce. This is a temporary exemption until VPN is implemented."
},
"displayName": {
"value": "001: Temporary public access for Awesome App cognitive services"
},
"requestedBy": {
"value": "Awesome App Team"
},
"approvedBy": {
"value": "Security vTeam"
},
"expiresOnDate": {
"value": "2099-12-28T00:00:00+10:00"
},
"policyDefinitionReferenceIds": {
"value": [
"cognitiveServicesAccountsShouldEnableDataEncryptionWithACustomerManagedKeyMonitoringEffect"
]
}
}
}

Просмотреть файл

@ -1,29 +0,0 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"template": "template/templates/storage/v1/template.json"
},
"parameters": {
"storageAccountName": {
"value": "sttemplateapp001"
},
"sku": {
// The storage account must use GRS storage.
// Try setting this to 'Standard_LRS' to fail the Azure.Storage.UseReplication rule.
"value": "Standard_GRS"
},
"blobSoftDeleteDays": {
// The storage account must use soft delete on blobs.
// Try setting this to 0 or removing this parameter entirely to fail the 'Azure.Storage.SoftDelete' rule.
"value": 7
},
"tags": {
"value": {
// An env tag must be test, dev, or prod.
// Try setting this to 'demo' to fail the Org.Azure.Tags rule.
"env": "prod"
}
}
}
}

Просмотреть файл

@ -1,185 +0,0 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "2.0.0.0",
"metadata": {
"name": "Key Vault",
"description": "Create or update a Key Vault."
},
"parameters": {
"vaultName": {
"type": "string",
"metadata": {
"description": "The name of the Key Vault.",
"example": "<name>"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "The Azure region to deploy to.",
"strongType": "location",
"example": "eastus"
}
},
"accessPolicies": {
"type": "array",
"defaultValue": [],
"metadata": {
"description": "The access policies defined for this vault.",
"example": [
{
"objectId": "<object_id>",
"tenantId": "<tenant_id>",
"permissions": {
"secrets": [
"Get",
"List",
"Set"
]
}
}
]
}
},
"useDeployment": {
"type": "bool",
"defaultValue": true,
"metadata": {
"description": "Determines if Azure can deploy certificates from this Key Vault."
}
},
"useTemplate": {
"type": "bool",
"defaultValue": true,
"metadata": {
"description": "Determines if templates can reference secrets from this Key Vault."
}
},
"useDiskEncryption": {
"type": "bool",
"defaultValue": true,
"metadata": {
"description": "Determines if this Key Vault can be used for Azure Disk Encryption."
}
},
"useSoftDelete": {
"type": "bool",
"defaultValue": true,
"metadata": {
"description": "Determine if soft delete is enabled on this Key Vault."
}
},
"usePurgeProtection": {
"type": "bool",
"defaultValue": true,
"metadata": {
"description": "Determine if purge protection is enabled on this Key Vault."
}
},
"softDeleteDays": {
"type": "int",
"defaultValue": 90,
"minValue": 7,
"maxValue": 90,
"metadata": {
"description": "The number of days to retain soft deleted vaults and vault objects."
}
},
"useRBAC": {
"type": "bool",
"defaultValue": false,
"metadata": {
"description": "Determines if access to the objects granted using RBAC. When true, access policies are ignored."
}
},
"networkAcls": {
"type": "object",
"defaultValue": {
"defaultAction": "Allow",
"bypass": "AzureServices",
"ipRules": [],
"virtualNetworkRules": []
},
"metadata": {
"description": "The network firewall defined for this vault."
}
},
"workspaceId": {
"type": "string",
"defaultValue": "",
"metadata": {
"description": "The workspace to store audit logs.",
"strongType": "Microsoft.OperationalInsights/workspaces",
"example": "/subscriptions/<subscription_id>/resourceGroups/<resource_group>/providers/Microsoft.OperationalInsights/workspaces/<workspace_name>"
}
},
"tags": {
"type": "object",
"metadata": {
"description": "Tags to apply to the resource.",
"example": {
"service": "<service_name>",
"env": "prod"
}
}
}
},
"resources": [
{
"comments": "Create or update a Key Vault.",
"type": "Microsoft.KeyVault/vaults",
"name": "[parameters('vaultName')]",
"apiVersion": "2019-09-01",
"location": "[parameters('location')]",
"properties": {
"enabledForDeployment": "[parameters('useDeployment')]",
"enabledForTemplateDeployment": "[parameters('useTemplate')]",
"enabledForDiskEncryption": "[parameters('useDiskEncryption')]",
"accessPolicies": "[parameters('accessPolicies')]",
"tenantId": "[subscription().tenantId]",
"sku": {
"name": "Standard",
"family": "A"
},
"networkAcls": "[parameters('networkAcls')]",
"enableSoftDelete": "[parameters('useSoftDelete')]",
"enablePurgeProtection": "[parameters('usePurgeProtection')]",
"softDeleteRetentionInDays": "[parameters('softDeleteDays')]",
"enableRbacAuthorization": "[parameters('useRBAC')]"
},
"tags": "[parameters('tags')]",
"resources": [
{
"comments": "Forward Key Vault audit events to a Log Analytics workspace.",
"condition": "[not(empty(parameters('workspaceId')))]",
"type": "Microsoft.KeyVault/vaults/providers/diagnosticSettings",
"name": "[concat(parameters('vaultName'), '/Microsoft.Insights/service')]",
"apiVersion": "2016-09-01",
"location": "[parameters('location')]",
"dependsOn": [
"[concat('Microsoft.KeyVault/vaults/', parameters('vaultName'))]"
],
"properties": {
"workspaceId": "[parameters('workspaceId')]",
"logs": [
{
"category": "AuditEvent",
"enabled": true
}
]
}
}
]
}
],
"outputs": {
"resourceId": {
"type": "string",
"value": "[resourceId('Microsoft.KeyVault/vaults', parameters('vaultName'))]",
"metadata": {
"description": "A unique resource identifier for the Key Vault."
}
}
}
}

Просмотреть файл

@ -1,127 +0,0 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"name": "Policy Exemption",
"description": "Create or update an Azure Policy exemption for a Resource Group."
},
"parameters": {
"exemptionNameSuffix": {
"type": "string",
"metadata": {
"description": "This value will be added as a suffix to the exemption name.",
"example": ""
}
},
"assignmentId": {
"type": "string",
"metadata": {
"description": "The resource identifier to the policy assignment that will be exempt."
}
},
"resourceGroup": {
"type": "string",
"defaultValue": "",
"metadata": {
"description": "The name of the Resource Group where the exemption will be scoped.",
"example": "<resource_group_name>"
}
},
"exemptionCategory": {
"type": "string",
"defaultValue": "Waiver",
"allowedValues": [
"Waiver",
"Mitigated"
],
"metadata": {
"description": "The type of exemption."
}
},
"description": {
"type": "string",
"metadata": {
"description": "A description for the policy exemption.",
"example": "<description>"
}
},
"displayName": {
"type": "string",
"metadata": {
"description": "The display name of the policy exemption.",
"example": "<display_name>"
}
},
"requestedBy": {
"type": "string",
"metadata": {
"description": "The team that own the resource that the exemption is being created for.",
"example": "<requested_team>"
}
},
"approvedBy": {
"type": "string",
"metadata": {
"description": "The team that approved the exemption.",
"example": "<approval_team>"
}
},
"expiresOnDate": {
"type": "string",
"metadata": {
"description": "The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption.",
"example": "2021-04-28T00:00:00+10:00"
}
},
"policyDefinitionReferenceIds": {
"type": "array",
"metadata": {
"description": "An array of definition references that this resource is exempt from."
}
}
},
"variables": {
"exemptionName": "[concat(subscription().subscriptionId, '-', parameters('exemptionNameSuffix'))]"
},
"resources": [
{
"comments": "Create or update an Azure Policy exemption for a Resource Group.",
"name": "[variables('exemptionName')]",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2019-10-01",
"subscriptionId": "[subscription().subscriptionId]",
"resourceGroup": "[parameters('resourceGroup')]",
"location": "[if(empty(parameters('resourceGroup')), deployment().location, '')]",
"properties": {
"expressionEvaluationOptions": {
"scope": "outer"
},
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"name": "[variables('exemptionName')]",
"type": "Microsoft.Authorization/policyExemptions",
"apiVersion": "2020-07-01-preview",
"properties": {
"policyAssignmentId": "[parameters('assignmentId')]",
"policyDefinitionReferenceIds": "[parameters('policyDefinitionReferenceIds')]",
"exemptionCategory": "[parameters('exemptionCategory')]",
"expiresOn": "[parameters('expiresOnDate')]",
"displayName": "[parameters('displayName')]",
"description": "[parameters('description')]",
"metadata": {
"requestedBy": "[parameters('requestedBy')]",
"approvedBy": "[parameters('approvedBy')]",
"createdBy": "DevOps deployment"
}
}
}
]
}
}
}
]
}

Просмотреть файл

@ -1,349 +0,0 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "2.1.1.0",
"metadata": {
"name": "Storage Account",
"description": "Create or update a Storage Account."
},
"parameters": {
"storageAccountName": {
"type": "string",
"metadata": {
"description": "The name of the Storage Account.",
"example": "<name>"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "The Azure region to deploy to.",
"strongType": "location",
"example": "EastUS"
}
},
"sku": {
"type": "string",
"defaultValue": "Standard_LRS",
"allowedValues": [
"Standard_LRS",
"Standard_GRS"
],
"metadata": {
"description": "Create the Storage Account as LRS or GRS."
}
},
"suffixLength": {
"type": "int",
"defaultValue": 0,
"minValue": 0,
"maxValue": 13,
"metadata": {
"description": "Determine how many additional characters are added to the storage account name as a suffix."
}
},
"containers": {
"type": "array",
"defaultValue": [],
"metadata": {
"description": "An array of storage containers to create on the storage account.",
"example": [
{
"name": "logs",
"publicAccess": "None",
"metadata": {}
}
]
}
},
"lifecycleRules": {
"type": "array",
"defaultValue": [],
"metadata": {
"description": "An array of lifecycle management policies for the storage account.",
"example": {
"enabled": true,
"name": "<rule_name>",
"type": "Lifecycle",
"definition": {
"actions": {
"baseBlob": {
"delete": {
"daysAfterModificationGreaterThan": 7
}
}
},
"filters": {
"blobTypes": [
"blockBlob"
],
"prefixMatch": [
"logs/"
]
}
}
}
}
},
"blobSoftDeleteDays": {
"type": "int",
"defaultValue": 0,
"minValue": 0,
"maxValue": 365,
"metadata": {
"description": "The number of days to retain deleted blobs. When set to 0, soft delete is disabled.",
"example": 7
}
},
"containerSoftDeleteDays": {
"type": "int",
"defaultValue": 7,
"minValue": 0,
"maxValue": 365,
"metadata": {
"description": "The number of days to retain deleted containers. When set to 0, soft delete is disabled.",
"example": 7
}
},
"shares": {
"type": "array",
"defaultValue": [],
"metadata": {
"description": "An array of file shares to create on the storage account.",
"example": [
{
"name": "<share_name>",
"shareQuota": 5,
"metadata": {}
}
]
}
},
"useLargeFileShares": {
"type": "bool",
"defaultValue": false,
"metadata": {
"description": "Determines if large file shares are enabled. This can not be disabled once enabled."
}
},
"shareSoftDeleteDays": {
"type": "int",
"defaultValue": 0,
"minValue": 0,
"maxValue": 365,
"metadata": {
"description": "The number of days to retain deleted shares. When set to 0, soft delete is disabled.",
"example": 7
}
},
"allowBlobPublicAccess": {
"type": "bool",
"defaultValue": false,
"metadata": {
"description": "Determines if any containers can be configured with the anonymous access types of blob or container."
}
},
"keyVaultPrincipalId": {
"type": "string",
"defaultValue": "",
"metadata": {
"description": "Set to the objectId of Azure Key Vault to delegated permission for use with Key Managed Storage Accounts."
}
},
"tags": {
"type": "object",
"metadata": {
"description": "Tags to apply to the resource.",
"example": {
"service": "<service_name>",
"env": "prod"
}
}
}
},
"variables": {
"storageAccountName": "[concat(parameters('storageAccountName'), if(greater(parameters('suffixLength'), 0), substring(uniqueString(resourceGroup().id), 0, parameters('suffixLength')), ''))]",
"blobSoftDeleteLookup": {
"true": {
"enabled": true,
"days": "[parameters('blobSoftDeleteDays')]"
},
"false": {
"enabled": false
}
},
"containerSoftDeleteLookup": {
"true": {
"enabled": true,
"days": "[parameters('containerSoftDeleteDays')]"
},
"false": null
},
"shareSoftDeleteLookup": {
"true": {
"enabled": true,
"days": "[parameters('shareSoftDeleteDays')]"
},
"false": {
"enabled": false
}
},
"largeFileSharesState": "[if(parameters('useLargeFileShares'), 'Enabled', 'Disabled')]",
"storageAccountKeyOperatorRoleId": "[resourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]"
},
"resources": [
{
"comments": "Storage Account",
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[variables('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('sku')]",
"tier": "Standard"
},
"kind": "StorageV2",
"properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Deny"
},
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"enabled": true
},
"blob": {
"enabled": true
}
},
"keySource": "Microsoft.Storage"
},
"accessTier": "Hot",
"largeFileSharesState": "[variables('largeFileSharesState')]",
"allowBlobPublicAccess": "[parameters('allowBlobPublicAccess')]",
"minimumTlsVersion": "TLS1_2"
},
"tags": "[parameters('tags')]",
"resources": [
{
"comments": "Configure blob storage services",
"type": "Microsoft.Storage/storageAccounts/blobServices",
"apiVersion": "2019-06-01",
"name": "[concat(variables('storageAccountName'), '/default')]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
],
"sku": {
"name": "[parameters('sku')]"
},
"properties": {
"cors": {
"corsRules": []
},
"deleteRetentionPolicy": "[variables('blobSoftDeleteLookup')[string(greater(parameters('blobSoftDeleteDays'), 0))]]",
"containerDeleteRetentionPolicy": "[variables('containerSoftDeleteLookup')[string(greater(parameters('containerSoftDeleteDays'), 0))]]"
}
},
{
"comments": "Configure file storage services",
"type": "Microsoft.Storage/storageAccounts/fileServices",
"apiVersion": "2019-06-01",
"name": "[concat(variables('storageAccountName'), '/default')]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
],
"sku": {
"name": "[parameters('sku')]"
},
"properties": {
"shareDeleteRetentionPolicy": "[variables('shareSoftDeleteLookup')[string(greater(parameters('shareSoftDeleteDays'), 0))]]"
}
}
]
},
{
"comments": "Create a blob container",
"condition": "[not(equals(length(parameters('containers')), 0))]",
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2019-06-01",
"name": "[if(equals(length(parameters('containers')), 0), concat(variables('storageAccountName'), '/default/empty'), concat(variables('storageAccountName'), '/default/', parameters('containers')[copyIndex('containerIndex')].name))]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('storageAccountName'), 'default')]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
],
"copy": {
"mode": "Parallel",
"count": "[if(equals(length(parameters('containers')), 0), 1, length(parameters('containers')))]",
"name": "containerIndex"
},
"properties": {
"metadata": "[parameters('containers')[copyIndex('containerIndex')].metadata]",
"publicAccess": "[parameters('containers')[copyIndex('containerIndex')].publicAccess]"
}
},
{
"comments": "Create blob lifecycle policy",
"condition": "[not(empty(parameters('lifecycleRules')))]",
"name": "[concat(variables('storageAccountName'), '/default')]",
"type": "Microsoft.Storage/storageAccounts/managementPolicies",
"apiVersion": "2019-06-01",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
],
"properties": {
"policy": {
"rules": "[parameters('lifecycleRules')]"
}
}
},
{
"comments": "Create a share",
"condition": "[not(equals(length(parameters('shares')), 0))]",
"type": "Microsoft.Storage/storageAccounts/fileServices/shares",
"apiVersion": "2019-06-01",
"name": "[if(equals(length(parameters('shares')), 0), concat(variables('storageAccountName'), '/default/empty'), concat(variables('storageAccountName'), '/default/', parameters('shares')[copyIndex('shareIndex')].name))]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts/fileServices', variables('storageAccountName'), 'default')]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
],
"copy": {
"mode": "Parallel",
"count": "[if(equals(length(parameters('shares')), 0), 1, length(parameters('shares')))]",
"name": "shareIndex"
},
"properties": {
"metadata": "[parameters('shares')[copyIndex('shareIndex')].metadata]",
"shareQuota": "[parameters('shares')[copyIndex('shareIndex')].shareQuota]"
}
},
{
"comments": "Delegate Key Vault permission to rotate keys",
"condition": "[not(empty(parameters('keyVaultPrincipalId')))]",
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[concat(variables('storageAccountName'), '/Microsoft.Authorization/', guid(parameters('keyVaultPrincipalId'), variables('storageAccountKeyOperatorRoleId')))]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
],
"properties": {
"roleDefinitionId": "[variables('storageAccountKeyOperatorRoleId')]",
"principalId": "[parameters('keyVaultPrincipalId')]",
"scope": "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
"principalType": "ServicePrincipal"
}
}
],
"outputs": {
"resourceId": {
"type": "string",
"value": "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
"metadata": {
"description": "A unique resource identifier for the Storage Account."
}
}
}
}