LibreDevOpsHelpers.Azure/Set-AzCurrentClientIpToKeyvault.ps1

function Set-AzCurrentClientIpToKeyvault
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]$KeyvaultResourceId,

        [Parameter(Mandatory)]
        [bool]$AddClientIP,

        [Parameter(Mandatory = $false)]
        [string]$ClientIpAddressCheckerUrl = "https://checkip.amazonaws.com"
    )

    Begin {
        try
        {
            $context = Get-AzContext
            if (-not $context)
            {
                throw "[$( $MyInvocation.MyCommand.Name )] Error: User is not logged into Azure. Please login using Connect-AzAccount."
            }
            Write-Verbose "[$( $MyInvocation.MyCommand.Name )] Info: AzContext is set...continuing"

            $resourceIdParts = $KeyvaultResourceId -split '/'
            $subscriptionId = $resourceIdParts[2]
            $resourceGroupName = $resourceIdParts[4]
            $keyVaultName = $resourceIdParts[-1]

            Write-Verbose "[$( $MyInvocation.MyCommand.Name )] Info: Fetching Key Vault: $keyVaultName in resource group: $resourceGroupName"
            $keyVault = Get-AzKeyVault -VaultName $keyVaultName -ResourceGroupName $resourceGroupName -SubscriptionId $subscriptionId
        }
        catch
        {
            Write-Error "[$( $MyInvocation.MyCommand.Name )] Error: An error occurred during Azure session verification or Key Vault retrieval: $_"
            return
        }
    }

    Process {
        try
        {
            $currentNetworkAcls = $keyVault.NetworkAcls
            $currentIp = (Invoke-RestMethod -Uri $ClientIpAddressCheckerUrl).Trim()
            Write-Verbose "[$( $MyInvocation.MyCommand.Name )] Info: Current client IP is $currentIp"

            # No change needed here, just fetch the current IP rules
            $currentIps = $currentNetworkAcls.IpAddressRanges
            Write-Verbose "[$( $MyInvocation.MyCommand.Name )] Info: Rules that already exist on the key vault are $currentIps"

            # Check if the current IP already exists in the list without adding /32 to existing entries
            $ipAlreadyExists = $currentIps -contains $currentIp -or $currentIps -contains "${currentIp}/32"
            $newIpRules = @($currentIps)

            if ($AddClientIP -and -not $ipAlreadyExists)
            {
                Write-Verbose "[$( $MyInvocation.MyCommand.Name )] Info: Adding current client IP to Key Vault network rules."
                # Append /32 only if the IP does not already have a subnet mask
                $newIpRules += if ($currentIp -notmatch '.+/\d+$')
                {
                    "${currentIp}/32"
                }
                else
                {
                    $currentIp
                }
            }
            elseif (-not $AddClientIP -and $ipAlreadyExists)
            {
                Write-Verbose "[$( $MyInvocation.MyCommand.Name )] Info: Removing current client IP from Key Vault network rules."
                # Remove both the plain IP and the IP with /32, to cover both scenarios
                $newIpRules = $newIpRules | Where-Object { $_ -ne $currentIp -and $_ -ne "${currentIp}/32" }
            }
            else
            {
                Write-Verbose "[$( $MyInvocation.MyCommand.Name )] Info: No changes needed for the IP rules."
                return
            }

            # This check now ensures that we don't add /32 to IPs that already have a subnet
            $newIpRules = $newIpRules | Where-Object { $_ -and $_.Trim() -ne '' }

            Write-Verbose "[$( $MyInvocation.MyCommand.Name )] Info: New ip rules are $newIpRules"

            # Validate the final list of IP rules
            $validIpRules = $newIpRules | Where-Object { $_ -match '^\d+\.\d+\.\d+\.\d+(/\d+)?$' }
            Write-Verbose "[$( $MyInvocation.MyCommand.Name )] Info: Valid IP rules are $validIpRules"

            if (-not $validIpRules)
            {
                Write-Error "No valid IP rules to update. Please check the IP format."
                return
            }

            Update-AzKeyVaultNetworkRuleSet -VaultName $keyVaultName -ResourceGroupName $resourceGroupName `
            -IpAddressRange $validIpRules -Bypass $currentNetworkAcls.Bypass -DefaultAction $currentNetworkAcls.DefaultAction

            Write-Verbose "[$( $MyInvocation.MyCommand.Name )] Info: Key Vault network rules updated successfully."
        }
        catch
        {
            Write-Error "[$( $MyInvocation.MyCommand.Name )] Error: An error occurred: $_"
        }
    }
}