Rotate Azure Load Balancer Pool

Octopus.AzurePowerShell exported 2023-11-22 by REOScotte belongs to ‘Octopus’ category.

Updates all rules on an Azure load balancer to point to the next backend pool in a specified list. The current backend pool will be determined and the next pool in a provided list will become the target of all rules. If the current pool doesn’t exist in the list, the first pool in the list will be used. This means that a specific pool can be chosen by specifying only a single pool.

Parameters

When steps based on the template are included in a project’s deployment process, the parameters below can be set.

Account

RotateAzureLoadBalancerPool.Account =

null

Load Balancer

RotateAzureLoadBalancerPool.LoadBalancerName =

The name of the load balancer to use. Multiple load balancers can be selected if they have the same pool names. Wildcards are supported.

Enter one load balancer per line.

Available Pools

RotateAzureLoadBalancerPool.AvailablePools =

A list of available pools to use. If multiple pools are specified, rules will be updated to use the next pool in the list. If a single pool is specified, rules will use that pool. Wildcards are supported.

Enter one pool per line.

Rule Names

RotateAzureLoadBalancerPool.RuleNames = *

A list of rule names to update. Wildcards are supported.

Enter one rule per line.

What If

RotateAzureLoadBalancerPool.WhatIf = False

Will just report expected changes, but not update the load balancer.

Script body

Steps based on this template will execute the following PowerShell script.

$LoadBalancerNames = [string]$OctopusParameters['RotateAzureLoadBalancerPool.LoadBalancerName']
$AvailablePools = [string]$OctopusParameters['RotateAzureLoadBalancerPool.AvailablePools']
$RuleNames = [string]$OctopusParameters['RotateAzureLoadBalancerPool.RuleNames']
$WhatIf = [bool]::Parse($OctopusParameters['RotateAzureLoadBalancerPool.WhatIf'])

if ($null -eq $LoadBalancerNames) {
    throw 'No load balancers selected. Please select at least one load balancer.'
} else {
    # Trim white space and blank lines and get all load balancers that match the names.
    $loadBalancers = $LoadBalancerNames.Split("`n").Trim().Where({ $_ }) | ForEach-Object { Get-AzLoadBalancer -Name $_ }
}
if ($null -eq $AvailablePools) {
    throw 'No pools selected. Please select at least one pool.'
} else {
    $AvailablePools = $AvailablePools.Split("`n").Trim().Where({ $_ }) # Trim white space and blank lines.
}
if ($null -eq $RuleNames) {
    throw 'No rules selected. Please select at least one rule name or use an asterisk (*) to select all rules.'
} else {
    $RuleNames = $RuleNames.Split("`n").Trim().Where({ $_ }) # Trim white space and blank lines.
}

foreach ($loadBalancer in $loadBalancers) {
    $loadBalancerName = $loadBalancer.Name
    $resourceGroupName = $loadBalancer.ResourceGroupName
    $allPools = Get-AzLoadBalancerBackendAddressPool -LoadBalancerName $loadBalancerName -ResourceGroupName $resourceGroupName

    Write-Host "Updating Load Balancer '$loadBalancerName'."

    # Start by assuming no rules match
    $rules = @()

    # Add each distinct rule that matches one of the rule names
    foreach ($ruleName in $RuleNames) {
        $rules += $LoadBalancer.LoadBalancingRules | Where-Object {
            $_.Id.Split('/')[-1] -like $ruleName -and
            $_.Id -notin $rules.Id
        }
    }

    if ($rules.Count -eq 0) {
        Write-Warning "No matching rules were found on load balancer '$loadBalancerName'."
        continue
    }

    # Resolve any wildcards in AvailablePools to get a list of valid pool names.
    $validPoolNames = @()
    foreach ($name in $AvailablePools) {
        $validPoolNames += $allPools.Name | Where-Object { $_ -like $name }
    }

    # The same pool could match multiple wildcards so get a unique list
    if ($validPoolNames) {
        $validPoolNames = [array]($validPoolNames | Select-Object -Unique)
    } else {
        Write-Warning "No valid pools were found on load balancer '$loadBalancerName'."
        continue
    }

    # Update each rule with a new pool 
    foreach ($rule in $rules) {
        $currentPoolName = $rule.BackendAddressPool.Id.Split('/')[-1]

        # This will find the next pool in the list, cycling back to the beginning if at the end. If the current pool isn't in the list,
        # its index will be -1. The next index will be zero so the first pool will be selected. 
        $index = $validPoolNames.IndexOf($currentPoolName)
        $nextIndex = ($index + 1) % $validPoolNames.Count
        $newPoolName = $validPoolNames[$nextIndex]

        # Get the new pool to use
        if ($newPoolName -in $allPools.Name) {
            $newPool = Get-AzLoadBalancerBackendAddressPool -ResourceGroupName $resourceGroupName -LoadBalancerName $loadBalancerName -Name $newPoolName
        } else {
            throw "Backend Pool '$newPoolName' does not exist on load balancer '$loadBalancerName'."
        }

        if ($currentPoolName -eq $newPoolName) {
            Write-Highlight "Rule '$($rule.Name)' is already pointing to pool '$currentPoolName' on load balancer '$loadBalancerName'."
        } else {
            Write-Highlight "Rule '$($rule.Name)' is pointing to pool '$currentPoolName'. Updating to pool '$newPoolName' on load balancer '$loadBalancerName'."
            $rule.BackendAddressPool.Id = $newPool.Id

            foreach ($pool in $rule.BackendAddressPools) {
                $pool.Id = $newPool.Id
            }
        }
    }

    if ($WhatIf) {
        Write-Highlight "WhatIf is set to true so skipping changes on Azure for load balancer '$loadBalancerName'."
    } else {
        Write-Verbose "Writing changes to Azure for load balancer '$loadBalancerName'."
        Set-AzLoadBalancer -LoadBalancer $loadBalancer | Out-Null
    }
}

Provided under the Apache License version 2.0.

Report an issue

To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.

{
  "Id": "947623c6-940d-4a54-a18b-c755a1035dce",
  "Name": "Rotate Azure Load Balancer Pool",
  "Description": "Updates all rules on an Azure load balancer to point to the next backend pool in a specified list. The current backend pool will be determined and the next pool in a provided list will become the target of all rules. If the current pool doesn't exist in the list, the first pool in the list will be used. This means that a specific pool can be chosen by specifying only a single pool.",
  "Version": 2,
  "ExportedAt": "2023-11-22T03:48:35.019Z",
  "ActionType": "Octopus.AzurePowerShell",
  "Author": "REOScotte",
  "Packages": [],
  "Parameters": [
    {
      "Id": "cef3a407-e9e1-44e5-9dcf-9dcf586f4958",
      "Name": "RotateAzureLoadBalancerPool.Account",
      "Label": "Account",
      "HelpText": null,
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "AzureAccount"
      }
    },
    {
      "Id": "7a8c14dd-072d-432e-9071-296381dd9cc9",
      "Name": "RotateAzureLoadBalancerPool.LoadBalancerName",
      "Label": "Load Balancer",
      "HelpText": "The name of the load balancer to use. Multiple load balancers can be selected if they have the same pool names. Wildcards are supported.\n\nEnter one load balancer per line.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "1295c989-efbd-4605-a362-b72ea9762c4f",
      "Name": "RotateAzureLoadBalancerPool.AvailablePools",
      "Label": "Available Pools",
      "HelpText": "A list of available pools to use. If multiple pools are specified, rules will be updated to use the next pool in the list. If a single pool is specified, rules will use that pool. Wildcards are supported.\n\nEnter one pool per line.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "ed5fc080-37c8-44ce-a89a-76313c89f10d",
      "Name": "RotateAzureLoadBalancerPool.RuleNames",
      "Label": "Rule Names",
      "HelpText": "A list of rule names to update. Wildcards are supported.\n\nEnter one rule per line.",
      "DefaultValue": "*",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "27caa59c-5f1a-42f8-a545-5075d47f3182",
      "Name": "RotateAzureLoadBalancerPool.WhatIf",
      "Label": "What If",
      "HelpText": "Will just report expected changes, but not update the load balancer.",
      "DefaultValue": "False",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    }
  ],
  "Properties": {
    "Octopus.Action.Script.ScriptSource": "Inline",
    "Octopus.Action.Script.Syntax": "PowerShell",
    "OctopusUseBundledTooling": "False",
    "Octopus.Action.Script.ScriptBody": "$LoadBalancerNames = [string]$OctopusParameters['RotateAzureLoadBalancerPool.LoadBalancerName']\n$AvailablePools = [string]$OctopusParameters['RotateAzureLoadBalancerPool.AvailablePools']\n$RuleNames = [string]$OctopusParameters['RotateAzureLoadBalancerPool.RuleNames']\n$WhatIf = [bool]::Parse($OctopusParameters['RotateAzureLoadBalancerPool.WhatIf'])\n\nif ($null -eq $LoadBalancerNames) {\n    throw 'No load balancers selected. Please select at least one load balancer.'\n} else {\n    # Trim white space and blank lines and get all load balancers that match the names.\n    $loadBalancers = $LoadBalancerNames.Split(\"`n\").Trim().Where({ $_ }) | ForEach-Object { Get-AzLoadBalancer -Name $_ }\n}\nif ($null -eq $AvailablePools) {\n    throw 'No pools selected. Please select at least one pool.'\n} else {\n    $AvailablePools = $AvailablePools.Split(\"`n\").Trim().Where({ $_ }) # Trim white space and blank lines.\n}\nif ($null -eq $RuleNames) {\n    throw 'No rules selected. Please select at least one rule name or use an asterisk (*) to select all rules.'\n} else {\n    $RuleNames = $RuleNames.Split(\"`n\").Trim().Where({ $_ }) # Trim white space and blank lines.\n}\n\nforeach ($loadBalancer in $loadBalancers) {\n    $loadBalancerName = $loadBalancer.Name\n    $resourceGroupName = $loadBalancer.ResourceGroupName\n    $allPools = Get-AzLoadBalancerBackendAddressPool -LoadBalancerName $loadBalancerName -ResourceGroupName $resourceGroupName\n\n    Write-Host \"Updating Load Balancer '$loadBalancerName'.\"\n\n    # Start by assuming no rules match\n    $rules = @()\n\n    # Add each distinct rule that matches one of the rule names\n    foreach ($ruleName in $RuleNames) {\n        $rules += $LoadBalancer.LoadBalancingRules | Where-Object {\n            $_.Id.Split('/')[-1] -like $ruleName -and\n            $_.Id -notin $rules.Id\n        }\n    }\n\n    if ($rules.Count -eq 0) {\n        Write-Warning \"No matching rules were found on load balancer '$loadBalancerName'.\"\n        continue\n    }\n\n    # Resolve any wildcards in AvailablePools to get a list of valid pool names.\n    $validPoolNames = @()\n    foreach ($name in $AvailablePools) {\n        $validPoolNames += $allPools.Name | Where-Object { $_ -like $name }\n    }\n\n    # The same pool could match multiple wildcards so get a unique list\n    if ($validPoolNames) {\n        $validPoolNames = [array]($validPoolNames | Select-Object -Unique)\n    } else {\n        Write-Warning \"No valid pools were found on load balancer '$loadBalancerName'.\"\n        continue\n    }\n\n    # Update each rule with a new pool \n    foreach ($rule in $rules) {\n        $currentPoolName = $rule.BackendAddressPool.Id.Split('/')[-1]\n\n        # This will find the next pool in the list, cycling back to the beginning if at the end. If the current pool isn't in the list,\n        # its index will be -1. The next index will be zero so the first pool will be selected. \n        $index = $validPoolNames.IndexOf($currentPoolName)\n        $nextIndex = ($index + 1) % $validPoolNames.Count\n        $newPoolName = $validPoolNames[$nextIndex]\n\n        # Get the new pool to use\n        if ($newPoolName -in $allPools.Name) {\n            $newPool = Get-AzLoadBalancerBackendAddressPool -ResourceGroupName $resourceGroupName -LoadBalancerName $loadBalancerName -Name $newPoolName\n        } else {\n            throw \"Backend Pool '$newPoolName' does not exist on load balancer '$loadBalancerName'.\"\n        }\n\n        if ($currentPoolName -eq $newPoolName) {\n            Write-Highlight \"Rule '$($rule.Name)' is already pointing to pool '$currentPoolName' on load balancer '$loadBalancerName'.\"\n        } else {\n            Write-Highlight \"Rule '$($rule.Name)' is pointing to pool '$currentPoolName'. Updating to pool '$newPoolName' on load balancer '$loadBalancerName'.\"\n            $rule.BackendAddressPool.Id = $newPool.Id\n\n            foreach ($pool in $rule.BackendAddressPools) {\n                $pool.Id = $newPool.Id\n            }\n        }\n    }\n\n    if ($WhatIf) {\n        Write-Highlight \"WhatIf is set to true so skipping changes on Azure for load balancer '$loadBalancerName'.\"\n    } else {\n        Write-Verbose \"Writing changes to Azure for load balancer '$loadBalancerName'.\"\n        Set-AzLoadBalancer -LoadBalancer $loadBalancer | Out-Null\n    }\n}\n",
    "Octopus.Action.Azure.AccountId": "#{RotateAzureLoadBalancerPool.Account}"
  },
  "Category": "Octopus",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/rotate_azure_load_balancer_pool.json",
  "Website": "/step-templates/947623c6-940d-4a54-a18b-c755a1035dce",
  "Logo": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAC1QTFRFT6Tl////L5Pg8vj9Y67omsvwPJrisdfzfbzs5fL7y+T32Ov5isLucLXqvt31CJPHWwAABMJJREFUeNrs3deW4jAMAFDF3U75/89dlp0ZhiU4blJEjvQ8hYubLJsA00UCBCIQgQhEIAIRiEAEIhCBCEQgAhGIQAQiEIEIhD8kJm+t+QprfdKfB9HbYpx6CWfspj8HMi+gMgHL/AmQA8W3JTKH+ALFvzCeL0RbpyoCPE9IJeNOSQwh5Z3qd6yRGWQ2qi2cZQWxqj1WzQYSjeoJmJlAklOd4VlArOqPhQEkqBERToeMcfRJBkC0Uep8CfBpjz4JsHJ0zF3dkEWNje0kiB/sUC6eApndaIiCMyAa1PiwJ0AWhRGJHJJQHG2dC7h1rNbO1QOxSA7lNCkkKrQIpJCAB1GREILYIC1NAiwbpKFJgGWDNExcwGstfExcZBCHC6nOglshHtmhViLIig1RNBCN7qjtW8C0Z1UvJcC1Z9XmwMBzzvobmgAyEzgq91dtEEsBsQSQQAFZCSBAATEEEApHZbrVBIkkEIUPSVeB+KtALA0kXQUSrwKZBCIQBnk8Y4i5CsReBeKvkqLM+BCSDWJlrZFvGk9SRTHshkgjZCGAaArIxm3H3grhVzFlW2msfl1ca79UJ1bofYvsDHHlNdTZnlh5MghuPd5NdBDUNZHyCkfktIh03XzALGRPlBDPac7qgWjHZzWcmF5zmmkhidMQ6boKiDXcDTUEaylZqCGJ0Vjvu/fLJtHqhSANEvqb2OYqkOUqEHuVMbJcZdZCGiPhKhC4yjqiIjEE7XThMp8fAWII3mY3kUIQD+AMKQTzPiBhgQ63HlT/KSvgtoi0dq5mCPah1UIE0eh3sT0NhOByvKeAkFzi8PgQomumFhsyOxpIzZN4gLOj5plVwNpR0b2AuePWKBEHQu24pSsJA+LVCeHHQxZ1SiyDIdqok8IOhSSnTottHEQTdyt4ettAj4KkzA4dMikk2Dht2S5ptm1vswnPDxn0YyDZ5oDM3iToo2T5voWaYe+Q+vdjH80QyAzZhCgcDtLMI1Tmtz9w++XHgziHQHJJu/OZ3bs9Xn8gQ72NcP3dKqEfkp10F51xhoIi2I91R+LurXV/5q7pH+wx061CzO16oSQleMyr8fXvwMA0Pro8432DPD/ySx8XrHfSuDAM8n6UhnjQabaiXf5Bq/lREHvEeNtn1rJ08+C/uXkQZHeguxAPC3UvtcJYUogLzZX5hhZZvS6onG5lxXtzWGaygwb79vT/IXhdlNibwlKYOR6T8xjI7W8n+xV7T+GH4tMzWwR+lZhRkJYSsC0thpmCYqyngOz3rN2FLBZ2wZflBCggUHF0Vnp88JKienzIXLSEZCZqU7IKr/gQW9yx3pzV7Y9kvWZWTRRIqDmTtRUnU7b2lLcTYmoqHqnmiO1poER0SPkAeZMAZxaJx0Y3TCdAclsIqDz03ALcyxfTCZBsthoGXWmigGyVhWPLFJJfuuKQWycoEFdXbH4dJJoJxNR1eD/kshz6yn48cF8yW8sFoitflB1w6Q8n+/15Za7oA17/pYNmYgP5fmWm8L1NOHPWgK8kuFew1/JXtOA0yJCv7ah7X8ObUuT5kObU30+fDZm8+zqP+HTIpK0xQ796b5Kv2hSIQAQiEIEIRCACEYhABCIQgQhEIAIRiEAEIpBf8UeAAQAEjtYmlDTcCgAAAABJRU5ErkJggg==",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Wednesday, November 22, 2023