Massdriver provisioner for managing resources with Bicep.
This provisioner expects the path
to contain a single bicep file named template.bicep
. While other files may exists in the directory, this template.bicep
file will be what is used for provisioning and managing the Azure resources.
The following tools are included in this provisioner:
- Checkov: Included to scan bicep templates for common policy and compliance violations.
The following configuration options are available:
Configuration Option | Type | Default | Description |
---|---|---|---|
azure_service_principal |
object | .connections.azure_service_principal |
jq path to a massdriver/azure-service-principal connection for authentication to Azure |
region |
string | "eastus" |
Azure region to deploy template resources into. Defaults to "eastus" . |
resource_group |
string | (package name) | Specifies the resource group name. Defaults to the Massdriver package name if not specified. |
delete_resource_group |
boolean | true |
Determines whether the resource group will be deleted during decommissioning. |
checkov.enable |
boolean | true |
Enables Checkov policy evaluation. If false , Checkov will not be run. |
checkov.quiet |
boolean | true |
Only display failed checks if true (adds the --quiet flag). |
checkov.halt_on_failure |
boolean | false |
Halt provisioning run and mark deployment as failed on a policy failure (removes the --soft-fail flag). |
Bicep accepts parameters in JSON format. However, Bicep expects the parameters to have a specific structure, where top level keys are unchanged, but the values are nested under a value
key. For example, the following JSON object:
{
"foo": "bar",
"baz": {
"nested": "field"
}
}
Needs to be changed to:
{
"foo": {
"value": "bar"
},
"baz": {
"value": {
"nested": "field"
}
}
}
This restructuring is performed automatically by the provisioner on params and connections before passing them to Bicep.
In order to view the structure of the params and connections fields you can run mass bundle build
with the Massdriver CLI, and it will append Bicep parameter definitions to the end of the template.bicep
file with full type expressions. If modifications to fields are required, use Bicep variables
to manipulate the values as needed.
After every provision, this provider will scan the template directory for files matching the pattern artifact_<name>.jq
. If a file matching this pattern is present, it will be used as a JQ template to render and publish a Massdriver artifact. The inputs to the JQ template will be a JSON object with the params, connections, envs, secrets and Bicep outputs as top level fields. The outputs
field is copied directly from the output of the Bicep command. These output fields have the same format mentioned in the above Inputs section, where the value of the output is nested underneath a value
block. This is something to be aware of when referencing the values in a Bicep output. You'll see this pattern reflected in the examples below.
{
"params": {
...
},
"connections": {
...
},
"envs": {
...
},
"secrets": {
...
},
"outputs": {
...
}
}
To demonstrate, let's say there is a Azure Storage Account bundle with a single param (region
), a single connection (azure_service_principal
), and a single artifact (storage_account
). The massdriver.yaml
would be similar to:
params:
required:
- region
properties:
region:
type: string
connections:
required:
- azure_service_principal
properties:
azure_service_principal:
$ref: massdriver/azure-service-principal
artifacts:
required:
- storage_account
properties:
storage_account:
$ref: massdriver/azure-storage-account-blob
Since the artifact is named storage_account
a file named artifact_storage_account.jq
would need to be in the template directory and the provisioner would use this file as a JQ template, passing the params, connections and outputs to it. There are two approaches to building the proper artifact structure:
- Fully render the artifact in the Bicep output
- Build the artifact structure using the JQ template
Here are examples of each approach.
If you choose to fully render the artifact in a Bicep output, it would be similar to:
param region string
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-04-01' = {
...
}
output artifact_storage_account object = {
data: {
infrastructure: {
ari: storageAccount.id
endpoint: storageAccount.properties.primaryEndpoints.blob
}
security: {}
}
specs: {
azure: {
region: region
}
}
}
In this case, the input to the artifact_storage_account.jq
template file would be:
{
"params": {
"region": "eastus"
},
"connections": {
"azure_service_principal": {
"data": {
"client_id": "00000000-1111-2222-3333-444444444444",
"client_secret": "s0mes3cr3tv@lue",
"subscription_id": "00000000-1111-2222-3333-444444444444",
"tenant_id": "00000000-1111-2222-3333-444444444444"
}
}
},
"envs": {},
"secrets": {},
"outputs": {
"artifact_storage_account": {
"value": {
"data": {
"infrastructure": {
"ari": "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/resource-group-name/providers/Microsoft.Storage/storageAccounts/storageaccountname",
"endpoint": "https://storageaccountname.blob.core.windows.net/"
},
"security": {}
},
"specs": {
"azure": {
"region": "eastus"
}
}
}
}
}
}
Thus, the artifact_storage_account.jq
file would simply be:
.outputs.artifact_storage_account.value
Alternatively, you can build the artifact structure using the JQ template. This approach is best if you are attempting to minimize changes to your Bicep template. With this approach, you would need to output the storage account ID and endpoint.
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-04-01' = {
...
}
output storageAccountId string = storageAccount.id
output storageAccountEndpoint string = storageAccount.properties.primaryEndpoints.blob
In this case, the input to the artifact_storage_account.jq
template file would be:
{
"params": {
"region": "eastus"
},
"connections": {
"azure_service_principal": {
"data": {
"client_id": "00000000-1111-2222-3333-444444444444",
"client_secret": "s0mes3cr3tv@lue",
"subscription_id": "00000000-1111-2222-3333-444444444444",
"tenant_id": "00000000-1111-2222-3333-444444444444"
}
}
},
"envs": {},
"secrets": {},
"outputs": {
"storageAccountEndpoint": {
"type": "String",
"value": "https://storageaccountname.blob.core.windows.net/"
},
"storageAccountId": {
"type": "String",
"value": "/subscriptions/00000000-1111-2222-3333-444444444444/resourceGroups/resource-group-name/providers/Microsoft.Storage/storageAccounts/storageaccountname"
}
}
}
Now the artifact structure must be built through the artifact_storage_account.jq
template:
{
"data": {
"infrastructure": {
"ari": .outputs.storageAccountId.value,
"endpoint": .outputs.storageAccountEndpoint.value
},
"security": {}
},
"specs": {
"azure": {
"region": .params.region
}
}
}