-
Notifications
You must be signed in to change notification settings - Fork 195
Add Deploy-Hub helper script #2008
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,234 @@ | ||||||||||||||||||||||||||||||
| # Copyright (c) Microsoft Corporation. | ||||||||||||||||||||||||||||||
| # Licensed under the MIT License. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| <# | ||||||||||||||||||||||||||||||
| .SYNOPSIS | ||||||||||||||||||||||||||||||
| Deploys a FinOps hub instance for local testing. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .DESCRIPTION | ||||||||||||||||||||||||||||||
| Wrapper around Deploy-Toolkit that simplifies FinOps hub deployments by providing scenario-based flags instead of requiring you to remember all the Bicep parameter names. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| By default, deploys with Azure Data Explorer (dev SKU). Use -StorageOnly for storage-only or -Fabric for Fabric-based deployments. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| All resources use an "{initials}-{name}" naming convention where initials are pulled from git config user.name and name defaults to "adx". Pass a name as the first positional parameter to use a custom value (e.g., "216" for Feb 16). | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .EXAMPLE | ||||||||||||||||||||||||||||||
| Deploy-Hub | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Deploys a hub with ADX (e.g., RG "aa-adx", ADX "aa-adx"). | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .EXAMPLE | ||||||||||||||||||||||||||||||
| Deploy-Hub 216 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Deploys to a named environment (e.g., RG "aa-216", ADX "aa-216"). | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .EXAMPLE | ||||||||||||||||||||||||||||||
| Deploy-Hub -StorageOnly | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Deploys a storage-only hub (no ADX, no Fabric). | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .EXAMPLE | ||||||||||||||||||||||||||||||
| Deploy-Hub -Fabric "https://my-eventhouse.kusto.data.microsoft.com" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Deploys a hub connected to a Microsoft Fabric eventhouse. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .EXAMPLE | ||||||||||||||||||||||||||||||
| Deploy-Hub -Remove 210 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Deletes the resource group for the specified name (e.g., "aa-210"). | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .EXAMPLE | ||||||||||||||||||||||||||||||
| Deploy-Hub -Remove | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Lists all resource groups matching the "{initials}-*" naming convention. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .EXAMPLE | ||||||||||||||||||||||||||||||
| Deploy-Hub -Build | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Builds the template first, then deploys with ADX. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .EXAMPLE | ||||||||||||||||||||||||||||||
| Deploy-Hub -WhatIf | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Validates the deployment without making changes. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .PARAMETER Name | ||||||||||||||||||||||||||||||
| Optional. First positional parameter. Suffix for the "{initials}-{name}" naming convention used for resource group and ADX cluster. Default: "adx". | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .PARAMETER HubName | ||||||||||||||||||||||||||||||
| Optional. Name of the hub instance. Default: "hub". | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .PARAMETER ADX | ||||||||||||||||||||||||||||||
| Optional. Name of the Azure Data Explorer cluster. Overrides the "{initials}-{name}" convention. Only used when not using -StorageOnly or -Fabric. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .PARAMETER ResourceGroup | ||||||||||||||||||||||||||||||
| Optional. Name of the resource group. Overrides the "{initials}-{name}" convention. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .PARAMETER Fabric | ||||||||||||||||||||||||||||||
| Deploy with Microsoft Fabric. Provide the eventhouse query URI. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .PARAMETER StorageOnly | ||||||||||||||||||||||||||||||
| Deploy a storage-only hub (no Azure Data Explorer or Fabric). | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .PARAMETER Remove | ||||||||||||||||||||||||||||||
| Remove test environments. With a name, deletes the target resource group. Alone, lists all resource groups matching "{initials}-*". | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .PARAMETER Location | ||||||||||||||||||||||||||||||
| Optional. Azure location. Default: westus. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .PARAMETER Build | ||||||||||||||||||||||||||||||
| Optional. Build the template before deploying. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .PARAMETER WhatIf | ||||||||||||||||||||||||||||||
| Optional. Validate the deployment without making changes. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| .LINK | ||||||||||||||||||||||||||||||
| https://github.com/microsoft/finops-toolkit/blob/dev/src/scripts/README.md | ||||||||||||||||||||||||||||||
| #> | ||||||||||||||||||||||||||||||
| param( | ||||||||||||||||||||||||||||||
| [Parameter(Position = 0)] | ||||||||||||||||||||||||||||||
| [string]$Name, | ||||||||||||||||||||||||||||||
| [string]$HubName, | ||||||||||||||||||||||||||||||
| [string]$ADX, | ||||||||||||||||||||||||||||||
| [string]$ResourceGroup, | ||||||||||||||||||||||||||||||
| [string]$Fabric, | ||||||||||||||||||||||||||||||
| [switch]$StorageOnly, | ||||||||||||||||||||||||||||||
| [switch]$Remove, | ||||||||||||||||||||||||||||||
| [string]$Location, | ||||||||||||||||||||||||||||||
| [switch]$Build, | ||||||||||||||||||||||||||||||
| [switch]$WhatIf | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Get user initials from git config user.name (first letter of each word, lowercased) | ||||||||||||||||||||||||||||||
| function Get-Initials() | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| $name = (git config user.name 2>$null) | ||||||||||||||||||||||||||||||
| if ($name) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| $parts = $name.Trim() -split '\s+' | ||||||||||||||||||||||||||||||
| if ($parts.Count -ge 2) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| return (($parts | ForEach-Object { $_[0] }) -join '').ToLower() | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| return $name.Substring(0, [Math]::Min(2, $name.Length)).ToLower() | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Fall back to OS username | ||||||||||||||||||||||||||||||
| $u = ($env:USERNAME ?? $env:USER ?? "xx").ToLower() | ||||||||||||||||||||||||||||||
| $parts = $u -split '[\.\-_\s]' | ||||||||||||||||||||||||||||||
| if ($parts.Count -ge 2) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| return ($parts | ForEach-Object { $_[0] }) -join '' | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| return $u.Substring(0, [Math]::Min(2, $u.Length)) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| $initials = Get-Initials | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Default name to "adx" when not specified | ||||||||||||||||||||||||||||||
| if (-not $Name) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| $Name = "adx" | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Build the full name: {initials}-{name} | ||||||||||||||||||||||||||||||
| $fullName = "$initials-$Name" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| #------------------------------------------------------------------------------ | ||||||||||||||||||||||||||||||
| # Remove mode | ||||||||||||||||||||||||||||||
| #------------------------------------------------------------------------------ | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if ($Remove) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| if ($PSBoundParameters.ContainsKey('Name')) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| # Delete the specific resource group | ||||||||||||||||||||||||||||||
| $rgName = if ($ResourceGroup) { $ResourceGroup } else { $fullName } | ||||||||||||||||||||||||||||||
| $rg = Get-AzResourceGroup -Name $rgName -ErrorAction SilentlyContinue | ||||||||||||||||||||||||||||||
| if ($null -eq $rg) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| Write-Host "Resource group '$rgName' not found." | ||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Write-Host "Deleting resource group '$rgName'..." | ||||||||||||||||||||||||||||||
| Remove-AzResourceGroup -Name $rgName -Force | ||||||||||||||||||||||||||||||
| Write-Host "Deleted '$rgName'." | ||||||||||||||||||||||||||||||
|
Comment on lines
+154
to
+156
|
||||||||||||||||||||||||||||||
| Write-Host "Deleting resource group '$rgName'..." | |
| Remove-AzResourceGroup -Name $rgName -Force | |
| Write-Host "Deleted '$rgName'." | |
| if ($WhatIf) | |
| { | |
| # WhatIf mode: do not delete, just report the intended action | |
| Write-Host "WhatIf: would delete resource group '$rgName'." | |
| } | |
| else | |
| { | |
| Write-Host "Deleting resource group '$rgName'..." | |
| Remove-AzResourceGroup -Name $rgName -Force | |
| Write-Host "Deleted '$rgName'." | |
| } |
Copilot
AI
Feb 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$groups may be $null (no matches) or a single PSResourceGroup object (1 match), in which case $groups.Count isn't reliable. Use a null/empty check (e.g., if (-not $groups) or @($groups).Count) so the "no resource groups found" path behaves correctly.
| if ($groups.Count -eq 0) | |
| if (-not $groups) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR adds a new helper command. The repo release process states that new/updated functionality must be documented in
docs-mslearn/toolkit/changelog.md; please add an entry under the next release section describing the new Deploy-Hub helper (customer-friendly, no internal implementation details).