Skip to content

Commit

Permalink
Allow passing in an existing Key Vault instance
Browse files Browse the repository at this point in the history
  • Loading branch information
flanakin committed Feb 3, 2024
1 parent 4950908 commit 9ed03f9
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 41 deletions.
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ Legend:
> 5. Remote hubs – Ingest cost data from other tenants.
> 6. Retention – Configure how long you want to keep Cost Management exports and normalized data in storage.
> 7. Analytics engine – Ingest cost data into an Azure Data Explorer cluster.
> 8. Allow specifying an existing Key Vault instance.
> - If using template deployment, set the `existingKeyVaultId` parameter to the fully-qualified resource ID.
> - If using the `Deploy-FinOpsHub` PowerShell command, set the `-ExistingKeyVaultId` parameter to the fully-qualified resource ID.
>
> ✏️ Changed:
>
Expand Down
15 changes: 8 additions & 7 deletions docs/finops-hub/template.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,14 @@ Please ensure the following prerequisites are met before deploying this template

## 📥 Parameters

| Parameter | Type | Description | Default value |
| ---------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| **hubName** | String | Optional. Name of the hub. Used to ensure unique resource names. | `"finops-hub"` |
| **location** | String | Optional. Azure location where all resources should be created. See https://aka.ms/azureregions. | (resource group location) |
| **storageSku** | String | Optional. Storage SKU to use. LRS = Lowest cost, ZRS = High availability. Note Standard SKUs are not available for Data Lake gen2 storage. Allowed: `Premium_LRS`, `Premium_ZRS`. | `Premium_LRS` |
| **tags** | Object | Optional. Tags to apply to all resources. We will also add the `cm-resource-parent` tag for improved cost roll-ups in Cost Management. |
| **exportScopes** | Array | Optional. List of scope IDs to create exports for. |
| Parameter | Type | Description | Default value |
| ---------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| **hubName** | String | Optional. Name of the hub. Used to ensure unique resource names. | `"finops-hub"` |
| **location** | String | Optional. Azure location where all resources should be created. See https://aka.ms/azureregions. | (resource group location) |
| **storageSku** | String | Optional. Storage SKU to use. LRS = Lowest cost, ZRS = High availability. Note Standard SKUs are not available for Data Lake gen2 storage. Allowed: `Premium_LRS`, `Premium_ZRS`. | `Premium_LRS` |
| **existingKeyVaultId** | String | Optional. Resource ID of the existing Key Vault resource to use. If not specified, a new Key Vault instance will be created. | |
| **tags** | Object | Optional. Tags to apply to all resources. We will also add the `cm-resource-parent` tag for improved cost roll-ups in Cost Management. | |
| **exportScopes** | Array | Optional. List of scope IDs to create exports for. | |

<br>

Expand Down
16 changes: 15 additions & 1 deletion docs/powershell/hubs/Deploy-FinOpsHub.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Deploy-FinOpsHub `
-Name <string> `
-ResourceGroup <string> `
-Location <string> `
[-KeyVaultId <string>] `
[-Version <string>] `
[-Preview] `
[-StorageSku <string>] `
Expand All @@ -57,6 +58,7 @@ Deploy-FinOpsHub `
| `‑ResourceGroup` | Required. Name of the resource group to deploy to. Will be created if it doesn't exist. |
| `‑Location` | Required. Azure location to execute the deployment from. |
| `‑Version` | Optional. Version of the FinOps hub template to use. Default = "latest". |
| `‑KeyVaultId` | Optional. Resource ID of the existing Key Vault instance to use. If not specified, one will be created. |
| `‑Preview` | Optional. Indicates that preview releases should also be included. Default = false. |
| `‑StorageSku` | Optional. Storage account SKU. Premium_LRS = Lowest cost, Premium_ZRS = High availability. Note Standard SKUs are not available for Data Lake gen2 storage. Default = "Premium_LRS". |
| `‑Tags` | Optional. Tags for all resources. |
Expand Down Expand Up @@ -88,12 +90,24 @@ Deploy-FinOpsHub `

Deploys a new FinOps hub instance named MyHub to a new resource group named MyNewResourceGroup using version {% include version.txt %} of the template.

### Use existing Key Vault instance

```powershell
Deploy-FinOpsHub `
-Name MyHub `
-ResourceGroupName MyExistingResourceGroup `
-Location westus `
-KeyVaultId "/subscriptions/###/resourceGroups/###/providers/Microsoft.KeyVault/vaults/foo"
```

Deploys a new FinOps hub instance named MyHub using an existing Key Vault instance.

<br>

---

## 🧰 Related tools

{% include tools.md hubs="1" %}
{% include tools.md hubs="1" pbi="1" %}

<br>
17 changes: 15 additions & 2 deletions src/powershell/Public/Deploy-FinOpsHub.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
.PARAMETER Location
Required. Azure location to execute the deployment from.
.PARAMETER KeyVaultId
Optional. Resource ID of the existing Key Vault instance to use. If not specified, one will be created.
.PARAMETER Version
Optional. Version of the FinOps hub template to use. Default = "latest".
Expand All @@ -35,12 +38,17 @@
Deploy-FinOpsHub -Name MyHub -ResourceGroupName MyExistingResourceGroup -Location westus
Deploys a new FinOps hub instance named MyHub to an existing resource group named MyExistingResourceGroup.
.EXAMPLE
Deploy-FinOpsHub -Name MyHub -Location westus -Version 0.1
Deploys a new FinOps hub instance named MyHub using version 0.1 of the template.
.EXAMPLE
Deploy-FinOpsHub -Name MyHub -ResourceGroupName MyExistingResourceGroup -Location westus -KeyVaultId "/subscriptions/###/resourceGroups/###/providers/Microsoft.KeyVault/vaults/foo"
Deploys a new FinOps hub instance named MyHub using an existing Key Vault instance.
.LINK
https://aka.ms/ftk/Deploy-FinOpsHub
#>
Expand All @@ -62,6 +70,10 @@ function Deploy-FinOpsHub
[string]
$Location,

[Parameter(Mandatory = $false)]
[string]
$KeyVaultId = '',

[Parameter()]
[string]
$Version = 'latest',
Expand Down Expand Up @@ -112,6 +124,7 @@ function Deploy-FinOpsHub
TemplateParameterObject = @{
hubName = $Name
storageSku = $StorageSku
existingKeyVaultId = $KeyVaultId
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/templates/finops-hub/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ param location string = resourceGroup().location
@description('Optional. Storage SKU to use. LRS = Lowest cost, ZRS = High availability. Note Standard SKUs are not available for Data Lake gen2 storage. Allowed: Premium_LRS, Premium_ZRS. Default: Premium_LRS.')
param storageSku string = 'Premium_LRS'

@description('Optional. Resource ID of the existing Key Vault resource to use. If not specified, a new Key Vault instance will be created.')
param existingKeyVaultId string = ''

@description('Optional. Tags to apply to all resources. We will also add the cm-resource-parent tag for improved cost roll-ups in Cost Management.')
param tags object = {}

Expand All @@ -39,6 +42,7 @@ module hub 'modules/hub.bicep' = {
hubName: hubName
location: location
storageSku: storageSku
existingKeyVaultId: existingKeyVaultId
tags: tags
tagsByResource: tagsByResource
exportScopes: exportScopes
Expand Down
9 changes: 5 additions & 4 deletions src/templates/finops-hub/modules/dataFactory.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
// Parameters
//==============================================================================

@description('Optional. Name of the hub. Used to ensure unique resource names. Default: "finops-hub".')
@description('Required. Name of the hub. Used to ensure unique resource names.')
param dataFactoryName string

@description('Required. The name of the Azure Key Vault instance.')
param keyVaultName string
@description('Optional. The resource ID of the Azure Key Vault instance.')
param keyVaultId string

@description('Required. The name of the Azure storage account instance.')
param storageAccountName string
Expand Down Expand Up @@ -293,7 +293,8 @@ resource stopHubTriggers 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
//------------------------------------------------------------------------------

resource keyVault 'Microsoft.KeyVault/vaults@2022-11-01' existing = {
name: keyVaultName
name: last(split(keyVaultId, '/'))
scope: resourceGroup(split(keyVaultId, '/')[2], split(keyVaultId, '/')[4])
}

resource linkedService_keyVault 'Microsoft.DataFactory/factories/linkedservices@2018-06-01' = {
Expand Down
7 changes: 6 additions & 1 deletion src/templates/finops-hub/modules/hub.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ param location string = resourceGroup().location
@description('Optional. Storage SKU to use. LRS = Lowest cost, ZRS = High availability. Note Standard SKUs are not available for Data Lake gen2 storage. Allowed: Premium_LRS, Premium_ZRS. Default: Premium_LRS.')
param storageSku string = 'Premium_LRS'

@description('Optional. Resource ID of the existing Key Vault resource to use. If not specified, a new Key Vault instance will be created.')
param existingKeyVaultId string = ''

@description('Optional. Tags to apply to all resources. We will also add the cm-resource-parent tag for improved cost roll-ups in Cost Management.')
param tags object = {}

Expand Down Expand Up @@ -122,7 +125,7 @@ module dataFactoryResources 'dataFactory.bicep' = {
params: {
dataFactoryName: dataFactoryName
convertToParquet: convertToParquet
keyVaultName: keyVault.outputs.name
keyVaultId: keyVault.outputs.resourceId
storageAccountName: storage.outputs.name
exportContainerName: storage.outputs.exportContainer
ingestionContainerName: storage.outputs.ingestionContainer
Expand All @@ -138,8 +141,10 @@ module dataFactoryResources 'dataFactory.bicep' = {

module keyVault 'keyVault.bicep' = {
name: 'keyVault'
scope: empty(existingKeyVaultId) ? resourceGroup() : resourceGroup(split(existingKeyVaultId, '/')[2], split(existingKeyVaultId, '/')[4])
params: {
hubName: hubName
existingKeyVaultName: last(split(existingKeyVaultId, '/'))
uniqueSuffix: uniqueSuffix
location: location
tags: resourceTags
Expand Down
73 changes: 47 additions & 26 deletions src/templates/finops-hub/modules/keyVault.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ param hubName string
@description('Required. Suffix to add to the KeyVault instance name to ensure uniqueness.')
param uniqueSuffix string

@description('Optional. Resource ID of the existing Key Vault resource to use. If not specified, a new Key Vault instance will be created.')
param existingKeyVaultName string

@description('Optional. Location for all resources.')
param location string = resourceGroup().location

Expand Down Expand Up @@ -49,11 +52,42 @@ var formattedAccessPolicies = [for accessPolicy in accessPolicies: {
tenantId: contains(accessPolicy, 'tenantId') ? accessPolicy.tenantId : tenant().tenantId
}]

var storageSecretProperties = {
attributes: {
enabled: true
exp: 1702648632
nbf: 10000
}
value: storageRef.listKeys().keys[0].value
}

//==============================================================================
// Resources
//==============================================================================

resource keyVault 'Microsoft.KeyVault/vaults@2022-11-01' = {
resource storageRef 'Microsoft.Storage/storageAccounts@2022-09-01' existing = {
name: storageAccountName
}

// Get existing key vault, if existingKeyVaultName is set
resource existingKeyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = if (!empty(existingKeyVaultName)) {
name: empty(existingKeyVaultName) ? 'placeholder' : existingKeyVaultName

resource existingKeyVault_accessPolicies 'accessPolicies@2023-07-01' = if (!empty(accessPolicies)) {
name: 'add'
properties: {
accessPolicies: formattedAccessPolicies
}
}

resource existingKeyVault_secrets 'secrets@2023-07-01' = {
name: storageRef.name
properties: storageSecretProperties
}
}

// Create new key vault, if existingKeyVaultName is not set
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = if (empty(existingKeyVaultName)) {
name: keyVaultName
location: location
tags: union(tags, contains(tagsByResource, 'Microsoft.KeyVault/vaults') ? tagsByResource['Microsoft.KeyVault/vaults'] : {})
Expand All @@ -73,30 +107,17 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-11-01' = {
family: 'A'
}
}
}

resource keyVault_accessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-11-01' = if (!empty(accessPolicies)) {
name: 'add'
parent: keyVault
properties: {
accessPolicies: formattedAccessPolicies
}
}

resource storageRef 'Microsoft.Storage/storageAccounts@2022-09-01' existing = {
name: storageAccountName
}

resource keyVault_secrets 'Microsoft.KeyVault/vaults/secrets@2022-11-01' = {
name: storageRef.name
parent: keyVault
properties: {
attributes: {
enabled: true
exp: 1702648632
nbf: 10000
resource keyVault_accessPolicies 'accessPolicies@2023-07-01' = if (!empty(accessPolicies)) {
name: 'add'
properties: {
accessPolicies: formattedAccessPolicies
}
value: storageRef.listKeys().keys[0].value
}

resource keyVault_secrets 'secrets@2023-07-01' = {
name: storageRef.name
properties: storageSecretProperties
}
}

Expand All @@ -105,10 +126,10 @@ resource keyVault_secrets 'Microsoft.KeyVault/vaults/secrets@2022-11-01' = {
//==============================================================================

@description('The resource ID of the key vault.')
output resourceId string = keyVault.id
output resourceId string = empty(existingKeyVaultName) ? keyVault.id : existingKeyVault.id

@description('The name of the key vault.')
output name string = keyVault.name
output name string = empty(existingKeyVaultName) ? keyVault.name : existingKeyVault.name

@description('The URI of the key vault.')
output uri string = keyVault.properties.vaultUri
output uri string = empty(existingKeyVaultName) ? keyVault.properties.vaultUri : existingKeyVault.properties.vaultUri

0 comments on commit 9ed03f9

Please sign in to comment.