Public/Variables/Set-RMMVariable.ps1
|
<#
Copyright (c) 2025-2026 Robert Faddes SPDX-License-Identifier: MPL-2.0 #> function Set-RMMVariable { <# .SYNOPSIS Updates an existing variable in the Datto RMM account or site. .DESCRIPTION The Set-RMMVariable function updates the name and/or value of an existing variable at either the account (global) level or at a specific site level. The function always fetches the latest state of the variable before updating to ensure changes are made against the current platform value. If a DRMMVariable object is piped in, the function checks for staleness and prompts the user if the object differs from the current value. NOTE: The Masked property can only be set during variable creation and cannot be changed after the variable has been created. Use New-RMMVariable with -Masked to create a masked variable. .PARAMETER Variable A DRMMVariable object to update. Accepts pipeline input from Get-RMMVariable. .PARAMETER VariableId The unique identifier of the variable to update. .PARAMETER SiteUid The unique identifier (GUID) of the site containing the variable. Required when updating site-level variables by VariableId. .PARAMETER Name The name of the variable to update (used for lookup when not using VariableId). .PARAMETER NewName The new name for the variable. If not specified, the existing name is preserved. Use this parameter to rename the variable. .PARAMETER Value The new value for the variable. If not specified, the current value is retained. Accepts both string and SecureString. When a SecureString is provided: - The value is securely converted for the API call - Plaintext is cleared from memory immediately after use - Note: The Masked property cannot be changed after creation .PARAMETER Force Bypasses the confirmation prompt. .EXAMPLE Get-RMMVariable -Name "CompanyName" | Set-RMMVariable -Value "Contoso Corporation" Updates the value of an account-level variable via pipeline. .EXAMPLE $Secret = Read-Host -AsSecureString -Prompt "Enter new password" PS > Get-RMMVariable -Name "AdminPassword" | Set-RMMVariable -Value $Secret Updates a masked variable value using SecureString for enhanced security. .EXAMPLE Set-RMMVariable -VariableId 12345 -NewName "CompanyName" -Value "New Company Ltd" Updates both name and value of an account-level variable by ID. .EXAMPLE Get-RMMSite -SiteName "Main Office" | Get-RMMVariable -Name "SiteCode" | Set-RMMVariable -Value "MO002" Updates a site-level variable via pipeline. .EXAMPLE Set-RMMVariable -SiteUid "a1b2c3d4-e5f6-7890-abcd-ef1234567890" -Name "BackupPath" -Value "\\newserver\backup" Updates a site-level variable by specifying site UID and variable name. .EXAMPLE Set-RMMVariable -VariableId 12345 -NewName "NewVarName" Renames an account-level variable by ID, keeping the current value. .INPUTS DRMMVariable. You can pipe variable objects from Get-RMMVariable. You can also pipe objects with VariableId and SiteUid properties. .OUTPUTS DRMMVariable. Returns the updated variable object. .NOTES This function requires an active connection to the Datto RMM API. Use Connect-DattoRMM to authenticate before calling this function. The Masked property cannot be changed after a variable is created. If you need to change a variable to be masked (or unmasked), you must delete and recreate it. If a DRMMVariable object is piped in, the function checks for staleness and prompts the user if the object is out of date compared to the current platform value. .LINK https://github.com/TheShadowTek/DattoRMM.Core/blob/main/docs/commands/Variables/Set-RMMVariable.md .LINK Connect-DattoRMM .LINK Get-RMMVariable .LINK New-RMMVariable .LINK Remove-RMMVariable .LINK Get-RMMSite #> [CmdletBinding(DefaultParameterSetName = 'ByVariableObject', SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter( ParameterSetName = 'ByVariableObject', Mandatory = $true, ValueFromPipeline = $true )] [DRMMVariable] $Variable, [Parameter( ParameterSetName = 'ByVariableId', Mandatory = $true )] [long] $VariableId, [Parameter( ParameterSetName = 'ByVariableId' )] [Parameter( ParameterSetName = 'ByVariableName' )] [guid] $SiteUid, [Parameter( ParameterSetName = 'ByVariableName', Mandatory = $true )] [string] $Name, [Parameter( Mandatory = $false )] [string] $NewName, [Parameter( Mandatory = $false )] [object] $Value, [Parameter( Mandatory = $false )] [switch] $Force ) process { # Get current variable for current value and staleness check $CurrentVariable = $null switch ($PSCmdlet.ParameterSetName) { 'ByVariableObject' { if ($Variable.Scope -eq 'Site') { $CurrentVariable = Get-RMMVariable -SiteUid $Variable.SiteUid -Id $Variable.Id } else { $CurrentVariable = Get-RMMVariable -Id $Variable.Id } if ($null -eq $CurrentVariable) { throw "Variable $($Variable.Name) with ID $($Variable.Id) not found in scope $($Variable.Scope)." } else { $Stale = $false if ($Variable.Name -ne $CurrentVariable.Name -or $Variable.Value -ne $CurrentVariable.Value) { $Stale = $true } if ($Stale) { $StaleMessage = "The variable object provided is stale compared to the current platform value." $StaleMessage += "`nCurrent Name: $($CurrentVariable.Name), Provided Name: $($Variable.Name)" $StaleMessage += "`nCurrent Value: $($CurrentVariable.Value), Provided Value: $($Variable.Value)" if (-not $PSCmdlet.ShouldContinue($StaleMessage, "Proceed with update?") -and -not $Force) { return } } } } 'ByVariableId' { if ($PSBoundParameters.ContainsKey('SiteUid')) { $CurrentVariable = Get-RMMVariable -SiteUid $SiteUid -Id $VariableId } else { $CurrentVariable = Get-RMMVariable -Id $VariableId } } 'ByVariableName' { if ($PSBoundParameters.ContainsKey('SiteUid')) { $CurrentVariable = Get-RMMVariable -SiteUid $SiteUid -Name $Name } else { $CurrentVariable = Get-RMMVariable -Name $Name } } } # Validate variable exists if ($null -eq $CurrentVariable) { throw "Variable not found for update." } # Handle SecureString value conversion $PlainValue = $null if ($PSBoundParameters.ContainsKey('Value')) { if ($Value -is [SecureString]) { $PlainValue = ConvertFrom-SecureStringToPlaintext -SecureString $Value Write-Verbose "SecureString detected - converting securely for API call" } else { $PlainValue = $Value } } # Set new values based on current if not specified $Body = @{ name = $null value = $null } switch ($PSBoundParameters.Keys) { 'NewName' {$Body.name = $NewName} default {$Body.name = $CurrentVariable.Name} } switch ($PSBoundParameters.Keys) { 'Value' {$Body.value = $PlainValue} default {$Body.value = $CurrentVariable.Value} } if ($CurrentVariable.Scope -eq 'Site') { $Path = "site/$($CurrentVariable.SiteUid)/variable/$($CurrentVariable.Id)" } else { $Path = "account/variable/$($CurrentVariable.Id)" } $APIMethod = @{ Path = $Path Method = 'Post' Body = $Body } try { Invoke-ApiMethod @APIMethod -WarningAction Stop | Out-Null } catch { Write-Warning "Failed to update variable: $($CurrentVariable.Name)" return } # Fetch the updated variable since API doesn't return it $RefreshVariable = @{ Id = $CurrentVariable.Id } if ($CurrentVariable.Scope -eq 'Site') { $RefreshVariable.SiteUid = $CurrentVariable.SiteUid } Get-RMMVariable @RefreshVariable } end { # Clear plaintext value from memory $PlainValue = $null } } # SIG # Begin signature block # MIIF+wYJKoZIhvcNAQcCoIIF7DCCBegCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAtytE+A4pqe5Ia # Yt5yWErB8cScXqMEEm5T9E70cyBGI6CCA04wggNKMIICMqADAgECAhB464iXHfI6 # gksEkDDTyrNsMA0GCSqGSIb3DQEBCwUAMD0xFjAUBgNVBAoMDVJvYmVydCBGYWRk # ZXMxIzAhBgNVBAMMGkRhdHRvUk1NLkNvcmUgQ29kZSBTaWduaW5nMB4XDTI2MDMz # MTAwMTMzMFoXDTI4MDMzMTAwMjMzMFowPTEWMBQGA1UECgwNUm9iZXJ0IEZhZGRl # czEjMCEGA1UEAwwaRGF0dG9STU0uQ29yZSBDb2RlIFNpZ25pbmcwggEiMA0GCSqG # SIb3DQEBAQUAA4IBDwAwggEKAoIBAQChn1EpMYQgl1RgWzQj2+wp2mvdfb3UsaBS # nxEVGoQ0gj96tJ2MHAF7zsITdUjwaflKS1vE6wAlOg5EI1V79tJCMxzM0bFpOdR1 # L5F2HE/ovIAKNkHxFUF5qWU8vVeAsOViFQ4yhHpzLen0WLF6vhmc9eH23dLQy5fy # tELZQEc2WbQFa4HMAitP/P9kHAu6CUx5s4woLIOyyR06jkr3l9vk0sxcbCxx7+dF # RrsSLyPYPH+bUAB8+a0hs+6qCeteBuUfLvGzpMhpzKAsY82WZ3Rd9X38i32dYj+y # dYx+nx+UEMDLjDJrZgnVa8as4RojqVLcEns5yb/XTjLxDc58VatdAgMBAAGjRjBE # MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU # H+B0vf97dYXqdUX1YMcWhFsY6fcwDQYJKoZIhvcNAQELBQADggEBAJmD4EEGNmcD # 1JtFoRGxuLJaTHxDwBsjqcRQRE1VPZNGaiwIm8oSQdHVjQg0oIyK7SEb02cs6n6Y # NZbwf7B7WZJ4aKYbcoLug1k1x9SoqwBmfElECeJTKXf6dkRRNmrAodpGCixR4wMH # KXqwqP5F+5j7bdnQPiIVXuMesxc4tktz362ysph1bqKjDQSCBpwi0glEIH7bv5Ms # Ey9Gl3fe+vYC5W06d2LYVebEfm9+7766hsOgpdDVgdtnN+e6uwIJjG/6PTG6TMDP # y+pr5K6LyUVYJYcWWUTZRBqqwBHiLGekPbxrjEVfxUY32Pq4QfLzUH5hhUCAk4HN # XpF9pOzFLMUxggIDMIIB/wIBATBRMD0xFjAUBgNVBAoMDVJvYmVydCBGYWRkZXMx # IzAhBgNVBAMMGkRhdHRvUk1NLkNvcmUgQ29kZSBTaWduaW5nAhB464iXHfI6gksE # kDDTyrNsMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKEC # gAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwG # CisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIBdm6GUQv5D8dOZ9hZNJ53HTQA5a # gAJddp52SPhi71VQMA0GCSqGSIb3DQEBAQUABIIBAAA2ClXic1KaL3iacirW0RpW # tGh7ACqL3+2bzszFetcM1NPPiyGJabF/WEnRuodd687vIEBoUZG9DP/1x1KRuId5 # S2EoBKN7QM/0hbwimOCFhv/R3QATiDnzjGpvRrFtQWEotP9XLxccqcr+oQW6viFZ # 2++D6pRSNuRNXBkc33SkLpyz+79yb0XqiuDu/E9PvJ7HdYzyBpPWfX4wxtp9jEB4 # LarTB1FXV9LCC7AHk2L5Q83EwiKjROF4mCbOYrF9XNktPDeM/pF5F0eVLjr5IQ77 # tAqp27F0WbIzNJWhjgRqNN2IagrMquYDF4f01rSLu6WPj9D/ksWHnF2zCOIQQfM= # SIG # End signature block |