Skip to content

Commit

Permalink
Merge pull request #621 from codatio/create-resource-builder
Browse files Browse the repository at this point in the history
Add Support for nested resource groups
  • Loading branch information
ninjarobot authored Jun 8, 2021
2 parents 3e8f53a + 5c5bfcd commit 5617600
Show file tree
Hide file tree
Showing 20 changed files with 818 additions and 248 deletions.
3 changes: 3 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
Release Notes
=============
## 1.6.0
* Added support for nesting resource groups

## 1.5.3
* CDN: Support for CDN rules
* Container Service (AKS): Support for using managed identity (msi) for the service principal.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
---
title: "ARM Deployment"
title: "Resource Group"
date: 2020-02-05T08:53:46+01:00
weight: 1
chapter: false
---

#### Overview
The ARM deployment builder is always the top-level element of your deployment. It contains the manifest of all Farmer resources that you create.
The Resource Group builder is always the top-level element of your deployment. It contains the manifest of all Farmer resources that you create.

#### Builder Keywords
| Keyword | Purpose |
Expand All @@ -15,19 +15,30 @@ The ARM deployment builder is always the top-level element of your deployment. I
| add_resource | Adds a resource to the template. |
| add_resources | Adds a collection of resources to the template. |
| output | Creates an output value that will be returned by the ARM template. Since Farmer does not require variables, and the only parameters supported are secure strings, these will typically be an ARM expressions that are generated at deployment-time, such as the publishing password of a web app or the fully-qualified domain name of a SQL instance etc. |
| add_tag | Add a tag to the resource group for top-level instances or to the deployment for nested resource groups |
| add_tags | Add multiple tags to the resource group for top-level instances or to the deployment for nested resource groups |
| name | the name of the resource group (only used for nested resource group deployments) |

#### Example
```fsharp
let deployment =
arm {
arm {
// All resources will share this location
location Location.NorthEurope
// Assume myStorageAccount and myWebApp have been defined...
add_resource myStorageAccount
add_resource myWebApp
add_resource (resourceGroup {
name "nestedResourceGroup"
add_resource myOtherStorage
})
output "webAppName" myWebApp.Name
output "webAppPassword" myWebApp.PublishingPassword
add_tag "deployed-by" "farmer"
}
```
25 changes: 25 additions & 0 deletions samples/scripts/nested-resourcegroups.fsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#r "../../src/Farmer/bin/Debug/net5.0/Farmer.dll"

open Farmer
open Farmer.Builders

let myStorage = storageAccount {
name "myfarmerstorage"
}
let myVm = vm {
name "farmer-test-vm"
username "codat"
}
let nested = resourceGroup {
name "farmer-test-inner"
add_resource myStorage
add_resource myVm
output "foo" "bax"
}
let template = arm {
location Location.UKSouth
add_resource nested
}

template |> Writer.quickWrite "template"
template |> Deploy.execute "farmer-test-rg" [("password-for-farmer-test-vm","Codat121!")]
187 changes: 187 additions & 0 deletions samples/scripts/template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"outputs": {
"farmer-test-inner.foo": {
"type": "string",
"value": "[reference('farmer-test-inner').outputs['foo'].value]"
}
},
"parameters": {
"password-for-farmer-test-vm": {
"type": "securestring"
}
},
"resources": [
{
"apiVersion": "2020-10-01",
"name": "farmer-test-inner",
"properties": {
"expressionEvaluationOptions": {
"scope": "Inner"
},
"mode": "Incremental",
"parameters": {
"password-for-farmer-test-vm": {
"value": "[parameters('password-for-farmer-test-vm')]"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"outputs": {
"foo": {
"type": "string",
"value": "bax"
}
},
"parameters": {
"password-for-farmer-test-vm": {
"type": "securestring"
}
},
"resources": [
{
"apiVersion": "2019-06-01",
"dependsOn": [],
"kind": "StorageV2",
"location": "westeurope",
"name": "myfarmerstorage",
"properties": {},
"sku": {
"name": "Standard_LRS"
},
"tags": {},
"type": "Microsoft.Storage/storageAccounts"
},
{
"apiVersion": "2018-10-01",
"dependsOn": [
"[resourceId('Microsoft.Network/networkInterfaces', 'farmer-test-vm-nic')]"
],
"location": "westeurope",
"name": "farmer-test-vm",
"properties": {
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": false
}
},
"hardwareProfile": {
"vmSize": "Basic_A0"
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', 'farmer-test-vm-nic')]"
}
]
},
"osProfile": {
"adminPassword": "[parameters('password-for-farmer-test-vm')]",
"adminUsername": "codat",
"computerName": "farmer-test-vm"
},
"storageProfile": {
"dataDisks": [
{
"createOption": "Empty",
"diskSizeGB": 1024,
"lun": 0,
"managedDisk": {
"storageAccountType": "Standard_LRS"
},
"name": "farmer-test-vm-datadisk-0"
}
],
"imageReference": {
"offer": "WindowsServer",
"publisher": "MicrosoftWindowsServer",
"sku": "2012-Datacenter",
"version": "latest"
},
"osDisk": {
"createOption": "FromImage",
"diskSizeGB": 128,
"managedDisk": {
"storageAccountType": "Standard_LRS"
},
"name": "farmer-test-vm-osdisk"
}
}
},
"tags": {},
"type": "Microsoft.Compute/virtualMachines"
},
{
"apiVersion": "2018-11-01",
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', 'farmer-test-vm-vnet')]",
"[resourceId('Microsoft.Network/publicIPAddresses', 'farmer-test-vm-ip')]"
],
"location": "westeurope",
"name": "farmer-test-vm-nic",
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', 'farmer-test-vm-ip')]"
},
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'farmer-test-vm-vnet', 'farmer-test-vm-subnet')]"
}
}
}
]
},
"tags": {},
"type": "Microsoft.Network/networkInterfaces"
},
{
"apiVersion": "2018-11-01",
"location": "westeurope",
"name": "farmer-test-vm-vnet",
"properties": {
"addressSpace": {
"addressPrefixes": [
"10.0.0.0/16"
]
},
"subnets": [
{
"name": "farmer-test-vm-subnet",
"properties": {
"addressPrefix": "10.0.0.0/24",
"delegations": []
}
}
]
},
"tags": {},
"type": "Microsoft.Network/virtualNetworks"
},
{
"apiVersion": "2018-11-01",
"location": "westeurope",
"name": "farmer-test-vm-ip",
"properties": {
"publicIPAllocationMethod": "Dynamic"
},
"sku": {
"name": "Basic"
},
"tags": {},
"type": "Microsoft.Network/publicIPAddresses"
}
]
}
},
"resourceGroup": "farmer-test-inner",
"tags": {},
"type": "Microsoft.Resources/deployments"
}
]
}
4 changes: 4 additions & 0 deletions src/Farmer/Aliases.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[<AutoOpen>]
module Farmer.Aliases

let arm = Farmer.Builders.ResourceGroup.resourceGroup
51 changes: 51 additions & 0 deletions src/Farmer/Arm/ResourceGroup.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
[<AutoOpen>]
module Farmer.Arm.ResourceGroup

open Farmer
open Farmer.Writer

let resourceGroupDeployment = ResourceType ("Microsoft.Resources/deployments","2020-10-01")

type DeploymentMode = Incremental|Complete

/// Represents all configuration information to generate an ARM template.
type ResourceGroupDeployment =
{ Name: ResourceName
Dependencies: ResourceId Set
Outputs : Map<string, string>
Location : Location
Resources : IArmResource list
Mode: DeploymentMode
Tags: Map<string,string> }
member this.ResourceId = resourceGroupDeployment.resourceId this.Name
member this.Parameters =
[ for resource in this.Resources do
match resource with
| :? IParameters as p -> yield! p.SecureParameters
| _ -> ()
] |> List.distinct
member this.Template =
{ Parameters = this.Parameters
Outputs = this.Outputs |> Map.toList
Resources = this.Resources }
interface IParameters with
member this.SecureParameters = this.Parameters
interface IArmResource with
member this.ResourceId = resourceGroupDeployment.resourceId this.Name
member this.JsonModel =
{| resourceGroupDeployment.Create(this.Name, this.Location, dependsOn = this.Dependencies, tags = this.Tags ) with
location = null // location is not supported for nested resource groups
resourceGroup = this.Name.Value
properties =
{| template = TemplateGeneration.processTemplate this.Template
parameters =
this.Parameters
|> Seq.map(fun (SecureParameter s) -> s, {| ``value`` = $"[parameters('%s{s}')]" |})
|> Map.ofSeq
mode =
match this.Mode with
| Incremental -> "Incremental"
| Complete -> "Complete"
expressionEvaluationOptions = {| scope = "Inner" |}
|}
|} :> _
Loading

0 comments on commit 5617600

Please sign in to comment.