Skip to content

Commit

Permalink
Add bicep registry docs and scripts (#87)
Browse files Browse the repository at this point in the history
  • Loading branch information
flanakin authored Mar 25, 2023
1 parent 62986c1 commit 845cb63
Show file tree
Hide file tree
Showing 8 changed files with 622 additions and 32 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"quickstarts",
"Roadmap",
"startswith",
"templating",
"triaging",
"Unhide"
],
Expand Down
21 changes: 11 additions & 10 deletions src/architecture.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# Architecture

| Name | Description |
| ------------------------------------------------ | ------------------------------- |
| [docs](../docs) | Public-facing toolkit docs. |
|  [deploy](../docs/deploy) | How to deploy the toolkit. |
|  [reports](../docs/reports) | About Power BI reports. |
|  [templates](../docs/templates) | About ARM deployment templates. |
|      [modules](../docs/modules) | About Bicep modules. |
| [src](../src) | Source code and internal docs. |
|  [templates](../src/templates) | ARM deployment templates. |
|  [modules](../src/modules) | Bicep modules. |
| Name | Description |
| -------------------------------------------------- | -------------------------------- |
| [docs](../docs) | Public-facing toolkit docs. |
| ├─ [deploy](../docs/deploy) | How to deploy the toolkit. |
| ├─ [reports](../docs/reports) | About Power BI reports. |
| └─ [templates](../docs/templates) | About ARM deployment templates. |
|       └─ [modules](../docs/modules) | About Bicep modules. |
| [src](../src) | Source code and internal docs. |
| ├─ [bicep-registry](../src/bicep-registry) | Bicep registry module templates. |
| ├─ [modules](../src/modules) | Bicep modules. |
| └─ [templates](../src/templates) | ARM deployment templates. |

Files and folders should use kebab casing (e.g., `this-is-my-folder`). The only exception is for RP namespaces in module paths.

Expand Down
226 changes: 226 additions & 0 deletions src/bicep-registry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
# Bicep registry modules

This folder contains modules that are published to the official [Bicep Registry](https://github.com/Azure/bicep-registry-modules). Modules are maintained here using a simple templating language to facilitate generating modules for multiple scopes without duplicating code.

- [Scheduled action](./scheduled-action)

<br>

On this page:

- [Creating a new module](#creating-a-new-module)
- [Building bicep registry modules](#building-bicep-registry-modules)
- [Testing bicep registry modules](#testing-bicep-registry-modules)
- [Publishing bicep registry modules](#publishing-bicep-registry-modules)
- [Templating language](#templating-language)

---

## Creating a new module

1. Create a folder for the module using kebab casing (e.g., `my-resource`). Module names should be:
- Singular (e.g., `my-resource` instead of `my-resources`).
- Named after the resource type (e.g., `virtual-machine` for `virtualMachines`).
2. Run `brm generate` to create placeholder files for the module. [Learn more](https://github.com/Azure/bicep-registry-modules/blob/main/CONTRIBUTING.md#generating-module-files).
3. Update the following files:

- `metadata.json` – Set the name, summary, and owner (should be `cost-management-contrib`).
- `version.json` – Set version to `1.0`.
- `README.md` – Add a title and description only. Do not set parameters, outputs, or examples.

> _Note: Do not update main.json._
4. Implement the module in `main.bicep` using the templating language below.
5. Implement tests in `test/main.test.bicep` also using the templating language.

<br>

## Building bicep registry modules

There are 2 ways to build bicep registry modules. To build all toolkit modules and templates, run:

```console
cd $repo/src/scripts
./Build-Toolkit
```

To build only a single module, run:

```console
cd $repo/src/scripts
./Build-Bicep ..\bicep-registry\<module>
```

The `Build-Bicep` script supports the following parameters:

| Parameter | Description |
| --------- | ------------------------------------------------------------------------- |
| `-Module` | Required. Specifies the module to build. |
| `-Scope` | Optional. Indicates one scope to generate. Default: all scopes. |
| `-Debug` | Optional. Writes primary file output to console. Does not generate files. |

> ℹ️ _Note: Both build scripts must be run from the `src/scripts` folder._
<br>

## Testing bicep registry modules

Before deploying a module, you first need to sign in to Azure:

```console
Connect-AzContext
Set-AzContext -Subscription "Trey Research R&D Playground"
```

> ℹ️ _**Microsoft contributors:** We recommend using the Trey Research R&D Playground subscription () for subscription deployments and the FinOps Toolkit tenant (38a09d9b-84be-4c40-8aef-99ebeed474ff) for tenant deployments. Contact @flanakin to request access._
>
> To sign in to the FinOps Toolkit tenant, run:
>
> ```console
> Connect-AzContext -Tenant 38a09d9b-84be-4c40-8aef-99ebeed474ff
> ```
Use the `Deploy-Toolkit` script to deploy a module. In its simplest form, you need only specify the name (not the path) of the module you want to deploy to run the local dev version of the module (not the generated versions):
```console
cd $repo/src/scripts
./Deploy-Toolkit scheduled-action
```
In most cases, you'll want to deploy the generated modules per scope using the test file since it includes all parameters rather than the module itself, where you would need to explicitly provide parameters. To do this, specify `-Build` to build the per-scope modules, `-Test` to use the test file, and specify the generated, scope-specific module name:

```console
cd $repo/src/scripts
./Deploy-Toolkit subscription-scheduled-action -Build -Test
```

Use `-WhatIf` to validate the module without deploying anything first.

> ℹ️ _**Note:** Resource group deployments default to a unique resource group based on your username and computer name: `ftk-<username>-<computername>`. Please delete resources after modules are validated._
The `Deploy-Toolkit` script supports the following parameters:

| Parameter | Description |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `-Template` | Required. Name of the template or module to deploy. Default = finops-hub. |
| `-ResourceGroup` | Optional. Name of the resource group to deploy to. Will be created if it doesn't exist. Default = `ftk-<username>-<computername>`. |
| `-Location` | Optional. Azure location to execute the deployment from. Default = `westus`. |
| `-Parameters` | Optional. Parameters to pass thru to the deployment. Defaults per template/module are configured in the script. |
| `-Build` | Optional. Indicates whether the the `Build-Toolkit` command should be executed first. Default = `false`. |
| `-Test` | Optional. Indicates whether to run the template or module test instead of the template or module itself. Default = `false`. |
| `-Debug` | Optional. Writes script execution troubleshooting details to console. Does not execute deployment. |
| `-WhatIf` | Optional. Validates the deployment without executing it or changing resources. |

<br>

## Publishing bicep registry modules

All modules in this folder are published to the official [Bicep Registry](https://github.com/Azure/bicep-registry-modules). If your module does not require multi-scope support, you can publish it directly to the Bicep Registry. If your module does require multi-scope support, follow the steps above for creating, building, and testing your module. Then, follow the [Bicep Registry contribution guide](https://github.com/Azure/bicep-registry-modules/blob/main/CONTRIBUTING.md) using the locally-generated and validated modules.

The Bicep Registry contribution guide will have you run `brm generate` before submitting your PR. Do not commit the changes from this command back to the FinOps toolkit repo. All other manual changes to the module itself should be made within the FinOps toolkit repo.

If you find a new requirement arises for Bicep Registry onboarding, please update this document and associated scripts. Please [file an issue](https://github.com/microsoft/cloud-hubs/issues) if you identify any bugs or have ideas to improve the process.

<br>

## Templating language

Bicep offers a clean solution to define reusable modules, but has a few limitations that make it onerous for modules that support multiple scopes. We developed a very simple templating language to work around the following limitations:

1. Modules cannot target multiple scopes.
2. Scope functions behave differently for each targeted scope.
3. No mechanism for conditional logic based on scope.

Our templating language supports "scope directives" that enable you to:

1. Leverage existing Bicep developer tooling for the primary scope.
2. Target multiple scopes.
3. Conditionally include a single line.
4. Conditionally include multiple lines.

### Scope directives

Scope directives are single-line comments that reference one or more Bicep-supported scopes (i.e., `resourceGroup`, `subscription`, `managementGroup`, `tenant`). Scopes are prefixed with `@` and are separated by spaces. Do not include additional characters after the scope directive.

The following example references only the resource group scope:

```bicep
// @resourceGroup
```

The following example references all scopes (note order is not important):

```bicep
// @resourceGroup @subscription @managementGroup @tenant
```

Refer to the sections below for how to use scope directives.

### Targeting multiple scopes

Modules are designed to work with a single scope. To target multiple scopes, use scope directives to indicate which lines and/or blocks of code should be included in (or excluded from) each scope. The build script will generate modules targeting each scope identified by scope directives used within your module.

If you don't have any conditional logic, add a scope directive on the `targetScope` line that includes all supported scopes. For instance, the following example supports all scopes:

```bicep
targetScope = 'subscription' // @resourceGroup @subscription @managementGroup @tenant
```

### Conditional lines

Conditional lines are used to include (or exclude) a single line within a Bicep module. To use a conditional line, add a scope directive to the end of the desired line.

The following example includes 2 conditional lines to represent allowed values that are only supported for specific scopes. `value1` is allowed for resource groups and subscriptions, and `value3` is allowed for tenant deployments:

```bicep
@allowed([
'value1' // @resourceGroup @subscription
'value2'
//'value3' // @tenant
])
```

Note this example is designed to use a primary scope of either resource group or subscription (based on those values being uncommented). Since `value3` is only allowed for tenant deployments, it is commented out by default. This approach ensures the module can be run as-is with existing tooling without modification.

### Conditional blocks

Conditional blocks are used to include (or exclude) multiple lines within a Bicep module. To use a conditional block, add a scope directive immediately before the code block. Conditional blocks end when either another conditional block is specified or an empty line.

The following example includes a single conditional block that is only includes `property1` and `property2` for subscriptions:

```bicep
resource myResource 'Microsoft.MyProvider/resource' = {
name: 'myResource'
properties: {
// @subscription
//// Comments must have 4 slashes
property1: 'value1'
property2: 'value2'
// ...
}
}
```

The following example includes multiple conditional blocks with at `scopeType` property that changes based on scope:

```bicep
resource myResource 'Microsoft.MyProvider/resource' = {
name: 'myResource'
properties: {
// @subscription
scopeType: 'Subscription'
// @resourceGroup
// scopeType: 'ResourceGroup'
// @managementGroup
// // Comments can be indented like code
// scopeType: 'ManagementGroup'
// ...
}
}
```

Similar to the conditional line example, the code block targeting the primary scope (subscription) is uncommented while the other code blocks are commented out by default. This approach ensures the module can be run as-is with existing tooling without modification or errors (e.g., having the same property defined multiple times). We recommend using extra indentation for each conditional block to make it easier to read.

<br>
Loading

0 comments on commit 845cb63

Please sign in to comment.