F5 Automation with PowerShell - Part 4

Getting Started

Before jumping straight into the code, we should layout the activities that we will need to explore as well as the goal of this script as a composite. The basic objective will be to take a deployed F5 Big-IP that has only a management IP address and after completion be a fully deployed Big-IP with an example Virtual IP (VIP) and Pool. The configuration will be purposes terse, as the goal is less about complex configuration and more how to perform these actions with PowerShell. These objectives can be broken down as follows:

  • Collect username and password from the command line
  • Get an Authentication Token from the Big-IP
  • Verify the Authentication Token
  • Change the Password Policy
  • Change Passwords
  • Get the System Version with the Token
  • Upload the DO RPM
  • Upload the AS3 RPM
  • Install the DO Package
  • Install the AS3 Package
  • Upload a DO Configuration and Verify
  • Upload an AS3 Configuration and Verify

Part 4 of this series will cover the installing the DO and AS3 RPMs.

You should keep the iControlREST API reference handy to refer to as you progress through this article for more details on the end points and options that are not discussed in the article. For convenience, here are some references to look at:

iControlREST Home 
This is the home for iControlREST and has links to other useful topics like Cookbooks and introductions to iControl.

iControl API Reference 
This is the API reference page for REST end points. As we discuss specific steps, we will refer to specific topics within this list, but you may want to refer to items outside the scope of this article.

Install the DO Package

Installing the DO package is a lengthy routine, so we will break this up as we discuss it.

 

# ================================================================================
# Install DO 
# ================================================================================
Write-Output "==================================================================="
Write-Output "Install DO package"
Write-Output "==================================================================="
$file = "f5-declarative-onboarding-1.27.0-6.noarch.rpm"                         # X diff from AS3 install
$install_url = "{0}{1}" -f $big_ip, "/mgmt/shared/iapp/package-management-tasks"
$info_url = "{0}{1}" -f $big_ip, "/mgmt/shared/declarative-onboarding/info"     # X diff from AS3 install
$retries = 0            # Number of times the system has been polled
$retry_limit =  5       # max number of times the system will be polled
$poll_interval = 10     # interval, in seconds, between polls

$headers = @{
    'X-F5-Auth-Token' = $token
    'Origin' =  'https://10.1.1.151' #  change to $big_ip?
    'Content-Type' = 'application/json;charset=UTF-8'
}
$body = @{
    operation = 'INSTALL'
    packageFilePath = '/var/config/rest/downloads/' +$file
} | ConvertTo-Json

 

 

The first part of the task writes out where we are in the script to the console and like the previous tasks for uploading a binary, sets the $file variable to the name of the file we uploaded. Its important that this is the same name as we are going to tell the system to install this binary and if its not exactly the same as the name we uploaded, recall the destination name could be different, then this call will fail to locally find the binary and fail.

To obtain the binary, you should look here: f5-declarative-onboarding - latest . Note, if your downloaded version differs from the examples specified here you will need to update the code appropriately.

Next, we build two URLs. The first URL, $install_url is the REST endpoint that we will call in order to tell the system to install the RPM. The RPM we uploaded, in this case the Declarative Onboarding RPM, is an iControl/iApps LX package. The documentation for this call is located here: /mgmt/shared/iapp/package-management-tasks . We will refer to this in a moment. The $info_url is used towards the end of this routine to check/validate the ultimate success of the installation. Another URL, $poll_url, will be constructed that will be used to check the status of the installation. The process we will use is to poll the system over time for the success of this call, so the next three variables are established to control that logic. $retries, $retry_limit, and $poll_interval all corresponds later to a do..while loop that will continue to call this endpoint checking for installation.

Next, we build our header object starting with the authorization token. We define the Origin as the F5 system we are calling. The documentation doesn’t specify a necessity for this header but testing on a 14.1.x system had problems with success without it. Next, the content type for the body of the HTTP request is set to JSON and a character set it also specified.

Lastly, the body of the request is built and then converted into JSON. The operation parameter specifies what action we want the system to perform and the packageFilePath specifies the full path to the file we want that operation performed on. More directly, we are telling the system to INSTALL the file we uploaded. Again, its critical that the path and the name of the file are consistent, or this call will fail.

 

 

# Initiate the installation request and capture the result id (task uuid) which we can poll for installation progress
try {
    $result = Invoke-RestMethod -SkipCertificateCheck -Method 'POST' -Uri $install_url -Headers $headers -Body $body
    $poll_url = "{0}{1}{2}" -f $big_ip, "/mgmt/shared/iapp/package-management-tasks/", $($result.id)

} catch {
    $code = $_.Exception.Response.StatusCode.value__
    Write-Host "Error at install DO request: " $code 
}

 

 

This next section performs two important tasks. First, we call Invoke-RestMethod and POST the body to the system which requests the system to install the package we uploaded. The result is captured in the variable $result. If this call succeeds, and by succeed that also means we don’t receive errors from the call because Invoke-RestMethod throws on 404s and 501 and so on, then we need to build our $poll_url variable. If the call succeeds, we will get a json reply like the one below:

{
"packageFilePath": "/var/config/rest/iapps/RPMS/NotMyWorker-0.1.0-0001.noarch.rpm",
"operation": "INSTALL",
"id": "86fe0fce-9220-40f8-89c0-699e67b61bd6",
"status": "CREATED",
"userReference": {
"link": "https://localhost/mgmt/shared/authz/users/admin"
},
"identityReferences": [
{
"link": "https://localhost/mgmt/shared/authz/users/admin"
}
],
"ownerMachineId": "44e002f8-c5e9-49b5-aed4-67a4e79c1b6b",
"generation": 1,
"lastUpdateMicros": 1517431876574386,
"kind": "shared:iapp:package-management-tasks:iapppackagemanagementtaskstate",
"selfLink": "https://localhost/mgmt/shared/iapp/package-management-tasks/86fe0fce-9220-40f8-89c0-699e67b61bd6"
}

The id is the management task id and we can query this specific task to check on its status. The $poll_url is built by attaching this task id to the end of the url which we will use in the aforementioned do..while loop. This is the URL that we will call to determine the actual success or failure of this installation process. If the call fails, then its caught and we print out the error message. This is different from an installation failure in that the request to INSTALL failed as opposed to succeeding in asking the system to perform the installation and the system was unable to perform the request. More robust handling of this circumstance is merited but not explored here.

 

 

# Enter a polling loop checking for completion or failure
do {
    # Wait for poll_interval seconds
    Start-Sleep -s $poll_interval
    
    # Poll the system to see what the status of the installation is
    $tmp = try {
        Invoke-WebRequest -SkipCertificateCheck -SkipHeaderValidation -Method 'GET' -Uri $poll_url -Headers $headers -ErrorAction Stop
    } catch [System.Net.WebException] { 
        $_.Exception.Response
    }
    
    # Pull out the status code from the response and increment the retries
    $response = $tmp.StatusCode
    $retries++

    # Handle the various responses, and adjust the logic for exiting the loop
    switch ( $response )
    {
        200 {
            Write-Host $response "- ATC service available for use"
            break
        }
        202 {
            Write-Host $response "- ATC service in use"
            break
        }
        204 {
            Write-Host $response "- No content returned - assuming ATC service available for use"
            $response = 200
            break
        }
        422 {
            Write-Host $response "- ATC service in failed state - likely from a previous run."
            $response = 200
        }
        4* {
            Write-Host $response "- ATC service in failed state - this may be transient"
            break
        }
        5* {
            Write-Host $response "- ATC service in failed state - this may be transient"
            break}
    
        default {
            Write-Host $response "- Unexpected ATC service availability"
            break
        }
    }

} while ( ($response -ne 200 ) -and ( $retries -lt $retry_limit ) )

 

 

This next section is where we continue to poll the task id from the system to determine if the installation completed or failed. Logic was liberally borrowed from Fellow F5 Architect Mark Menger’s work posted on GitHub here, Terraform onboarding , which is used for onboarding a system with Terraform. While this resource is not written in PowerShell, its an excellent reference to refer to for other activities beyond the scope of this article.

This section is governed by a do..while look that will continue to loop so long as we have NOT received a 200 from the $poll_url request AND we have not exceeded the number of retries dictated by $retry_limit:

# Enter a polling loop checking for completion or failure
do {
# logic here…
} while ( ($response -ne 200 ) -and ( $retries -lt $retry_limit ) )

Entering the do..while loop we first sleep the system for $poll_interval number of seconds. This is important as a rapid influx of “are we done yet” requests are not useful and could potentially be detected as nefarious. So, we give the system time to work on the task before checking the status.

Next, we call Invoke-WebRequest to poll the system. We need to use Invoke-WebRequest this time as Invoke-RestMethod provides some massaging of the return data that gets in the way of our polling. Invoke-WebRequest will let us handle the data. Notice at the end we specify -ErrorAction Stop, which specifies how the cmdlet handles non-terminating errors. By default, this is continued which should provide a response for both success and error conditions. Since the poll request is captured within a try/catch clause, and as we have stated before 404s and 501s, and so on will throw, this allows us to capture the return code no matter what happens. We collect this value in $tmp and then extract the StausCode and put it into $response. $retries is iterated to keep track of the number of times we have polled the system.

Next, we enter a switch or case statement the allows us to determine what to do with the possible responses:

  • A 200 simply breaks out of the switch statement where the logic of the do..while loop will let it exit.
  • A 202 breaks out of the switch statement but looping will continue
  • A 204 assumes, based on experience, that the system did install and is ready for use. The response is changed to a 200 where it will drop through the do..while loop.
  • A 422 indicates a specific failure which is likely the result of trying to perform another installation prior to another attempt completing or succeeding. The response code is rewritten to a 200 and this will drop through the loop and the check to the $info_url should be the determining factor of success.
  • All other 400s and 500s are captured and continue to loop.
  • Anything else is captured and continues to loop.

Lastly, the while part of the do..while loop is assessed and the loop either continues or completes.

 

 

# Installation should have concluded/succeeded, make a request to appsvcs/info to complete, remove origin from headers
$headers = @{
    'X-F5-Auth-Token' = $token
    'Content-Type' = 'application/json'
}

# Give the system a few more seconds to settle..
Start-Sleep -s $poll_interval

# Send out request to the info to get version, etc.. which validates installation and service availability
$result = try {
    Invoke-RestMethod -SkipCertificateCheck -SkipHeaderValidation -Method 'GET' -Uri $info_url -Headers $headers
} catch [System.Net.WebException] { 
    $_.Exception.Response
}

# Announce success and print out the versioning info
Write-Host "DO Installation succeeded"
Write-Host $($result | ConvertTo-Json)

 

 

The last part of this task rewrites the headers as we don’t need the same ones we previously used to make the installation request. We sleep the system once more for $poll_interval seconds which based on some testing was found to ensure that the system has not only completed the installation task but that any services that restart or database that get updated complete. Next, we make a request to the $info_url which should succeed and give us some versioning information for the installed package. If this fails, its just captured although in practice this should really assume that the installation failed for some reason and handle the exceptional event. I chose not to dig into this here as a complicated error handling routine, other than exiting, would lengthen and confuse the examples and likely wouldn’t fit as well into any code base the reader was putting this into. Best practices, however, would encourage a more robust approach that would likely need to reach into your orchestration and CI/CD process to determine what the next course of action should be.

Lastly, we assume success and announce the victory along with writing out the response to the $info_url request.

There are some other actions that you could explore like deleting the packages that are discussed in this training: Lab 4.5 - Delete the Package . These are not written in PowerShell so some translation will be necessary but hopefully these examples will make that translation simple.

Install the AS3 Package

This task is nearly the same as the Install the DO Package and its unnecessary to go over in detail. There are some changes however like the name of the package, the $info_url and some messaging so its worth making note of those specific differences. However, in a production system it would be better if the two installation tasks were refactored into a single function or better a custom cmdlet and the differences parameterized. That exercise is left to the reader.

To obtain the binary, you should look here: f5-appsvcs-extension - latest . Note, if your downloaded version differs from the examples specified here you will need to update the code appropriately.

Updated Mar 14, 2022
Version 2.0
No CommentsBe the first to comment