Functions/Public/Groups.ps1
|
$script:HvDRSValidGroupTypes = @('Vm', 'Host', 'Csv') $script:HvDRSDefaultGroupsPath = Join-Path (Get-HvDRSDataRoot) 'HvDRS\groups.json' function Add-HvDRSGroup { <# .SYNOPSIS Defines a reusable named group of VMs, hosts, or CSVs that affinity rules can reference via -VMGroups / -HostGroups / -CSVGroups instead of listing members directly — the same "VM/Host DRS Group" concept vSphere uses. .DESCRIPTION Group membership is resolved dynamically by Get-AffinityRuleSet every time rules are loaded — editing a group's members with Set-HvDRSGroup takes effect on the next read of any rule that references it, with no need to edit or re-save the rules themselves. .PARAMETER ClusterName Failover Cluster this group applies to. Defaults to the local cluster if omitted. .PARAMETER Name Human-readable name for the group. Must be unique within a cluster. .PARAMETER Type Vm — a group of VM names, referenced via -VMGroups on an affinity rule. Host — a group of cluster node names, referenced via -HostGroups. Csv — a group of Cluster Shared Volume names, referenced via -CSVGroups. .PARAMETER Members Names of the VMs, hosts, or CSVs in this group. .PARAMETER Description Optional free-text description stored with the group. .PARAMETER GroupsPath Path to the JSON group store. Defaults to $env:ProgramData\HvDRS\groups.json. .EXAMPLE Add-HvDRSGroup -ClusterName 'PROD-CLUSTER' -Name 'SQL VMs' -Type Vm -Members 'SQL-PROD-01','SQL-PROD-02' #> [CmdletBinding(SupportsShouldProcess)] param( [string]$ClusterName = '', [Parameter(Mandatory)] [string]$Name, [Parameter(Mandatory)] [ValidateSet('Vm', 'Host', 'Csv')] [string]$Type, [Parameter(Mandatory)] [string[]]$Members, [string]$Description = '', [string]$GroupsPath = $script:HvDRSDefaultGroupsPath ) if (-not $ClusterName) { try { $ClusterName = (Get-Cluster -ErrorAction Stop).Name } catch { throw "No -ClusterName specified and no local cluster detected. $_" } } if (-not $PSCmdlet.ShouldProcess($Name, "Add HvDRS $Type group for cluster '$ClusterName'")) { return } $groups = [System.Collections.Generic.List[PSCustomObject]](Get-HvDRSGroupSet -Path $GroupsPath) if ($groups | Where-Object { $_.ClusterName -eq $ClusterName -and $_.Name -eq $Name }) { Write-Warning "A group named '$Name' already exists for cluster '$ClusterName'. Use Set-HvDRSGroup to modify it." return } $group = [PSCustomObject]@{ GroupId = [System.Guid]::NewGuid().ToString() Name = $Name ClusterName = $ClusterName Type = $Type Members = @($Members) Description = $Description CreatedAt = (Get-Date -Format 'o') } $groups.Add($group) Save-HvDRSGroupSet -Groups $groups.ToArray() -Path $GroupsPath Write-Host "Group '$Name' added for cluster '$ClusterName' (ID: $($group.GroupId))." return $group } function Get-HvDRSGroup { <# .SYNOPSIS Lists HVDRS groups, with optional filtering. .PARAMETER ClusterName When specified, returns only groups belonging to this cluster. .PARAMETER GroupId Return the group with this specific ID. .PARAMETER Name Filter by name. Supports wildcards (e.g. 'SQL*'). .PARAMETER Type Filter by group type (Vm, Host, Csv). .PARAMETER GroupsPath Path to the JSON group store. #> [CmdletBinding(DefaultParameterSetName = 'All')] param( [string] $ClusterName = '', [Parameter(ParameterSetName = 'ById', Mandatory)] [string] $GroupId, [Parameter(ParameterSetName = 'ByName')] [string] $Name, [Parameter(ParameterSetName = 'ByType')] [ValidateSet('Vm', 'Host', 'Csv')] [string] $Type, [string] $GroupsPath = $script:HvDRSDefaultGroupsPath ) $groups = Get-HvDRSGroupSet -Path $GroupsPath -ClusterName $ClusterName # See Get-HvDRSAffinityRule.ps1 for why each branch assigns $matched directly # rather than via a switch expression — a plain @(...) assignment inside an # if/elseif branch is the form that survives a 0-match result as an empty # array rather than collapsing it to $null. if ($PSCmdlet.ParameterSetName -eq 'ById') { $matched = @($groups | Where-Object { $_.GroupId -eq $GroupId }) } elseif ($PSCmdlet.ParameterSetName -eq 'ByName') { $matched = @($groups | Where-Object { $_.Name -like $Name }) } elseif ($PSCmdlet.ParameterSetName -eq 'ByType') { $matched = @($groups | Where-Object { $_.Type -eq $Type }) } else { $matched = @($groups) } if ($matched.Count -eq 0) { return ,@() } return $matched } function Remove-HvDRSGroup { <# .SYNOPSIS Removes an HVDRS group by ID or name. .DESCRIPTION Does not check whether any affinity rule references this group — removing a referenced group simply means that rule stops picking up its members on the next load (the rule's own literal VMs/Hosts/CSVs, if any, are unaffected). #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'ById')] param( [Parameter(ParameterSetName = 'ById', Mandatory)] [string] $GroupId, [Parameter(ParameterSetName = 'ByName', Mandatory)] [string] $Name, [string] $ClusterName = '', [string] $GroupsPath = $script:HvDRSDefaultGroupsPath ) $groups = [System.Collections.Generic.List[PSCustomObject]](Get-HvDRSGroupSet -Path $GroupsPath) $target = if ($PSCmdlet.ParameterSetName -eq 'ById') { $groups | Where-Object { $_.GroupId -eq $GroupId } } else { $groups | Where-Object { $_.Name -eq $Name -and (-not $ClusterName -or $_.ClusterName -eq $ClusterName) } } if (-not $target) { Write-Warning "No group found matching the specified criteria." return } if (@($target).Count -gt 1) { Write-Warning "Multiple groups match name '$Name'. Use -GroupId to remove a specific group." return } if (-not $PSCmdlet.ShouldProcess($target.Name, 'Remove HvDRS group')) { return } [void]$groups.Remove($target) Save-HvDRSGroupSet -Groups $groups.ToArray() -Path $GroupsPath Write-Host "Group '$($target.Name)' removed." } function Set-HvDRSGroup { <# .SYNOPSIS Updates the members or description of an existing HVDRS group. .PARAMETER GroupId ID of the group to update (use Get-HvDRSGroup to find it). .PARAMETER NewName Rename the group. .PARAMETER AddMembers Add member names to the group. .PARAMETER RemoveMembers Remove member names from the group. .PARAMETER Description Replace the group's description text. #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)] [string] $GroupId, [string] $NewName, [string[]] $AddMembers = @(), [string[]] $RemoveMembers = @(), [string] $Description, [string] $GroupsPath = $script:HvDRSDefaultGroupsPath ) $groups = [System.Collections.Generic.List[PSCustomObject]](Get-HvDRSGroupSet -Path $GroupsPath) $group = $groups | Where-Object { $_.GroupId -eq $GroupId } if (-not $group) { Write-Warning "No group found with ID '$GroupId'." return } if (-not $PSCmdlet.ShouldProcess($group.Name, 'Update HvDRS group')) { return } if ($PSBoundParameters.ContainsKey('NewName')) { $group.Name = $NewName } if ($PSBoundParameters.ContainsKey('Description')) { $group.Description = $Description } if ($AddMembers.Count -gt 0) { $group.Members = @(@($group.Members) + $AddMembers | Select-Object -Unique) } if ($RemoveMembers.Count -gt 0) { $group.Members = @(@($group.Members) | Where-Object { $RemoveMembers -notcontains $_ }) } Save-HvDRSGroupSet -Groups $groups.ToArray() -Path $GroupsPath Write-Host "Group '$($group.Name)' updated." return $group } |