Public/GitLab/Set-GitLabCIVariable.ps1

function Set-GitLabCIVariable
{
    <#
    .SYNOPSIS
        Creates or updates a GitLab CI/CD variable.
    .DESCRIPTION
        Always deletes then recreates the variable. This handles the masked+hidden case
        (hidden variables cannot be updated in place via the API) and keeps the logic
        simple for all other cases.

        Defaults: Masked=true, Hidden=true, Protected=false.

        Requires the glab CLI (https://gitlab.com/gitlab-org/cli) to be installed and
        authenticated.
    .EXAMPLE
        Set-GitLabCIVariable -Name 'github_token' -Project 'mygroup/myrepo'
        Prompts securely for the value, then creates the variable with default settings.
    .EXAMPLE
        Set-GitLabCIVariable -Name 'github_token' -Project 'mygroup/myrepo' -Protected $true
        Creates the variable as protected (only available on protected branches and tags).
    .EXAMPLE
        Set-GitLabCIVariable -Name 'DEBUG_FLAG' -Project 'mygroup/myrepo' -Masked $false -Hidden $false
        Creates a non-sensitive variable visible in job logs and the UI.
    #>

    [CmdletBinding()]
    param
    (
        # The name of the CI/CD variable
        [Parameter(
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $Name,

        # The value of the variable (prompted securely if not provided)
        [Parameter(
            Mandatory = $false,
            Position = 1
        )]
        [SecureString]
        $Value,

        # The GitLab project path (e.g. 'group/repo')
        [Parameter(
            Mandatory = $true,
            Position = 2
        )]
        [string]
        $Project,

        # Optional description for the variable
        [Parameter(
            Mandatory = $false
        )]
        [string]
        $Description,

        # Whether the variable value should be masked in job logs
        [Parameter(
            Mandatory = $false
        )]
        [bool]
        $Masked = $true,

        # Whether the variable should be hidden in the UI
        [Parameter(
            Mandatory = $false
        )]
        [bool]
        $Hidden = $true,

        # Whether the variable should only be available on protected branches and tags
        [Parameter(
            Mandatory = $false
        )]
        [bool]
        $Protected = $false
    )

    begin
    {
        if (-not (Get-Command glab -ErrorAction SilentlyContinue))
        {
            throw 'glab CLI not found. Install from https://gitlab.com/gitlab-org/cli/-/releases'
        }
    }

    process
    {
        if (-not $Value)
        {
            $Value = Read-Host "Enter value for '$Name'" -AsSecureString
        }

        $BSTR = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($Value)
        try
        {
            $PlainText = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($BSTR)

            Write-Verbose "Removing existing '$Name' if present..."
            try
            {
                Invoke-NativeCommand `
                    -FilePath 'glab' `
                    -ArgumentList @('variable', 'delete', $Name, '--yes', '-R', $Project) `
                    -SuppressOutput `
                    -ErrorAction 'Stop'
            }
            catch
            {
                if ($_.Exception.Message -notmatch '404|not.found|does not exist')
                {
                    Write-Warning "Unexpected error deleting '$Name': $($_.Exception.Message)"
                }
            }

            Write-Verbose "Creating '$Name'..."
            $GlabArgs = [System.Collections.Generic.List[string]]@(
                'variable', 'set', $Name, '--value', $PlainText, '-R', $Project
            )
            if ($Description) { $GlabArgs.AddRange([string[]]@('--description', $Description)) }
            if ($Masked)      { $GlabArgs.Add('--masked') }
            if ($Hidden)      { $GlabArgs.Add('--hidden') }
            if ($Protected)   { $GlabArgs.Add('--protected') }

            try
            {
                Invoke-NativeCommand `
                    -FilePath 'glab' `
                    -ArgumentList $GlabArgs `
                    -SuppressOutput `
                    -ErrorAction 'Stop'
            }
            catch
            {
                throw "Failed to create variable '$Name'.`n$($_.Exception.Message)"
            }

            Write-Verbose "'$Name' set (Masked=$Masked, Hidden=$Hidden, Protected=$Protected)"
        }
        finally
        {
            [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
        }
    }

    end {}
}