F5 Automation with PowerShell - Part 2

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 2 of this series will cover the first half of these tasks.

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.

Collect username and password from the command line

This step is optional and commented out in the source tree. However, it does acknowledge that there is a better way to handle usernames and passwords than writing them in clear text as a variable in our script, which is what we will do later. There are other options that are superior like accessing a vault and so on. We will keep this simple for now and just use a cmdlet to collect them off the command line. You are free to institute a more robust system if you require it.

 

Write-Output "==================================================================="
Write-Output "Get Credentials"
Write-Output "==================================================================="

$credential = Get-Credential

# Access credentials?
Write-Host $credential.UserName
Write-Host $(ConvertFrom-SecureString -SecureString $credential.Password -AsPlainText)

 

This routine is simple, we just output some text to mark what this part of the action is (and document the code a bit) and then call Get-Credential. Get-Credential will then request a username and password. The password is not shown on the command line. Next, we write these out to the screen for the purpose of showing how to access them. Obviously, it’s a bit silly to go to the trouble to ‘secure’ these only to display them but the intent is to show how you would modify the next section and use these as the credentials that will be used to obtain an access token from the Big-IP.

Before starting the next section, we establish a variable that will be used extensively throughout the script:

 

$big_ip = "https://10.1.1.151"

 

You should set this to the Management IP address of whatever Big-IP system you are working with.

Get an Authentication Token from the Big-IP

This is an important step in the process and if you want to copy and paste very specific parts of this script into your own code base you will want to include this as well. Alternatively, you can choose to authenticate with each call to the REST endpoint, but it doesn’t make sense to do so. This routine will authenticate the user and obtain an authentication token that can then be used in subsequent REST calls to authorize further calls without the need to authenticate again. Keep in mind that you want to protect this token for obvious reasons.
One special note is on appropriate access level for REST. At this time, you MUST be an admin user to access the REST API, no other roles are permitted at this time: https://support.f5.com/csp/article/K04326056 

The API reference for this end point resolves to Big-IQ unfortunately, but it is worth referring to even though we are not using Big-IQ here:

API - /mgmt/shared/authn/login 

 

# ================================================================================
# Get auth token
# ================================================================================
Write-Output "==================================================================="
Write-Output "Get auth token"
Write-Output "==================================================================="
$url = "{0}{1}" -f $big_ip, '/mgmt/shared/authn/login'
$body = @{
    username = "admin"
    password = "admin"
    loginProviderName = "tmos"
} | ConvertTo-Json

$result = Invoke-RestMethod -SkipCertificateCheck -Method 'POST' -Uri $url -ContentType 'application/json' -Body $body
$token = $($result.token.token)
Write-Output ""

 

The first part of this routine simply writes out what this step is. Next, the REST end point is built from the big_ip server variable and then the specific path for this call. Next, we need to build a payload that will be sent in the body of this request. The request parameters are defined as follows:

Name Type Description
username string The system’s default admin user ID, which is the user getting a token.
password string The system’s default admin user password.
loginReference string Reference to the authentication provider provided as a link. If you’re using the local authentication provider, you can omit the property or set it to an empty value.

If you are incorporating the command line entry of credentials you want to change the lines that have static values here to the values that were printed to the screen in the previous code snippet using Write-Host. If you are going to integrate access to a secure vault or other scheme you will want to build this payload accordingly. Notice that after the object declaration we use ConvertTo-Json to translate the object into a JSON payload. We could just build this as JSON alternatively to avoid the extra step.

Next, is the call using Invoke-RestMethod to the endpoint:

 

$result = Invoke-RestMethod -SkipCertificateCheck -Method 'POST' -Uri $url -ContentType 'application/json' -Body $body

 

  • -SkipCertificateCheck avoids the self-signed certificate that is currently installed on the Big-IP. Without this, the request will see a certificate it doesn’t trust and error out.
  • -Method allows control over which calling method is being used. Here, we use a POST.
  • -Uri is self-explanatory
  • -ContentType allows control over the content type header that is sent in the request. You could alternatively put this in the headers, but since we don’t have other headers, it was easier to include in this manner. In this case, we are telling the server that the payload is in JSON format.
  • -Body allows us to include the body variable.

The return value from the call is captured in the variable $result. There is a lot of data that is returned in this request, refer to the API link above to review them. For our purposes, we want to extract the token from the request so that it can be used later in subsequent calls. Of note is the assumption that this call will not fail or will return successfully. A better approach would be to place this call in a try/catch clause and ensure that those errors or exceptional cases are captured. This will be explored as we move on to other tasks.

Verify the Authentication Token

For this task, we will verify the token that we just obtained in the previous task. There are some more elegant ways of approaching this process that will be discussed towards the end. For now, this will just make a rudimentary check and indicate success if appropriate. Unfortunately, there doesn’t seem to be a google search resolvable link that documents this specific API end point.

 

# ================================================================================
# Verify token
# ================================================================================
Write-Output "==================================================================="
Write-Output "Verify Token"
Write-Output "==================================================================="
$url = "{0}{1}{2}" -f $big_ip, "/mgmt/shared/authz/tokens/", $token
$headers = @{
    'X-F5-Auth-Token' = $token
}

try {
    $result = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri $url -Headers $headers -ContentType 'application/json'

} catch {
    # Can't capture the response code with Invoke-RestMethod, but can catch the exception and assume failure.
    Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ 
    Write-Host "Authentication token / attempt failed.  Exiting..."
    exit
}
Write-Output "`nAuth succeeded!`n"

 

The task starts out with the same announcement to the console and then builds the URL endpoint. Notice in this case, the $token variable is tacked on at the end, so we are asking for the status of this specific token. Also notice, we need to pass our token along in a header as this is a protected endpoint. This is accomplished by adding the header X-F5-Auth-Token and setting its value to the token we received. Notice also that we did not convert this object into JSON and left it as an object. Invoke-RestMethod will expect this format when we pass the headers to it.

When we call Invoke-RestMethod we have an additional argument to pass the headers for the REST call. Additionally, instead of a POST this time we use the GET method in order to retrieve data from the system. The remainder of the call is the same. What is different in this case is this entire call is wrapped in a try/catch statement. There are a couple of reasons for this. First, Invoke-RestMethod has an annoying ‘feature’ in that if the call is a 404 then Invoke-RestMethod with throw an error. If you do not catch this, then your script will abort and exit. In truth other error codes also throw and its not clear why it was decided to handle these issues in this way. Regardless, we need to work around this problem as the appropriate return value for a token that does not exist is a 404. Thus, our complication.

We resolve this by capturing any errors that are thrown and then extracting the status code, printing it out, and then exiting. You could do more in this routine, for example making it the default function you would call for getting the current access token. If that token has expired and this call fails, you could automatically renew or request a new one, for example. In this case, we just print out the problem and exit. If the call doesn’t throw, then we are assuming that the token is valid. This extends from the reality that the call succeeded, because we needed to the token to access it and that the endpoint existed. This seems redundant and even silly but consider that you needed to validate many tokens for many processes or users and a master system with its own admin access was making this check.

If the call resolves and we get a non-error response we assume that our authentication token is good. There are some improvements that can be made to be more definite with the various responses and close some of the edge conditions, but this is a start.

Change the Password Policy

Starting with TMOS version 14.x, you are required to change the password of the system when you first log in. Along with that, it makes sense to update the password policy of the system. This is a simple call to set that policy. Much like obtaining the authentication token, we need to create a JSON body that will be sent to the password policy API endpoint. So much of this task is a review of things we have done up to this point.

Documentation for this API endpoint is located here: /mgmt/tm/auth/password-policy 

 

# ================================================================================
# Change Password policy
# ================================================================================
Write-Output "==================================================================="
Write-Output "Change Password Policy"
Write-Output "==================================================================="
$url = "{0}{1}" -f $big_ip, "/mgmt/tm/auth/password-policy"
$headers = @{
    'X-F5-Auth-Token' = $token
    'Content-Type' = 'application/json'
}
$body = @{
    policyEnforcement = "disabled"
    expirationWarning = 14
    minimumLength = 6
    requiredLowercase = 1
    requiredUppercase = 0
    requiredNumeric = 0
    requiredSpecial = 0
    passwordMemory = 0
    maxDuration = 30
    maxLoginFailures = 5
    minDuration = 0
} | ConvertTo-Json

$result = Invoke-RestMethod -SkipCertificateCheck -Method 'PATCH' -Uri $url -Headers $headers -Body $body
Write-Output $result

 

We start out again with some output that defines where we are and build the URL. The headers object is filled out and notice again that we do NOT convert that to JSON. Notice that in this case, Content-Type is passed as a header as opposed to the command line option. Lastly, we build out the body and convert that to JSON. The documentation for the API endpoint describes the options available and what they do.

Finally, a call is made to Invoke-RestMethod with our parameters and the result is written to the screen. Notice that the method is PATCH, which is the first time we have seen this used so far. This task is commented out in the example code and should be refactored to be more robust, but it was included to be thorough with the topic.

Change Passwords

Next up is changing the admin password. Unfortunately, you cannot change the root password from the REST API so that will need to be performed either by logging in and changing it manually or by a different means. Additional error checking and better interrogation of return values would improve this routine but for now this is fine.

Documentation for this API endpoint is located here: /mgmt/tm/auth/user/ 

 

# ================================================================================
# Change Passwords
# Note:  you CANNOT do root over the REST interface.
# ================================================================================
Write-Output "==================================================================="
Write-Output "Change Passwords"
Write-Output "==================================================================="
$url = "{0}{1}" -f $big_ip, "/mgmt/tm/auth/user/admin"
$headers = @{
    'X-F5-Auth-Token' = $token
    'Content-Type' = 'application/json'
}
$body = @{
    password = "admin"
} | ConvertTo-Json

$result = Invoke-RestMethod -SkipCertificateCheck -Method 'PATCH' -Uri $url -Headers $headers -Body $body
Write-Output $result

 

This task is straightforward and much of a review of what we have done so far. The standard announcement of where we are in the script followed by building the URL to the API endpoint. We again set up the headers with our authentication token and set the content-type to JSON. The body is terse and only contains the password that we want to change for the user we are modifying. Notice that this task only assumes to change the password for admin. The API endpoint is actually “/mgmt/tm/auth/user/<the user you want to modify>”, so this routine could be made more robust by making it a function and then allowing credentials to be passed into the routine that would build the API endpoint, for the username, and provide the value in the body for the password. You could also make use of the credentials that are provided on the command line provided you are using that part of the script. Keep in mind, that complexity should be verified once those values are first attained as opposed to getting here and receiving a potential error for them not complying with the password policy you set.

Invoke-RestMethod is called using the PATCH method and the arguments built. Again, additional error checking and better interrogation of the return values would improve this routine.

Get the System Version with the Token

This task is another simple example that retrieves the version of the system and exercises that the token works:

 

# ================================================================================
# Testing Get Version with token
# ================================================================================
Write-Output "==================================================================="
Write-Output "Testing Get Version with token"
Write-Output "==================================================================="
$url = "{0}{1}" -f $big_ip, "/mgmt/tm/sys/version"
$headers = @{
    'X-F5-Auth-Token' = $token
}

$result = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri $url -Headers $headers -ContentType 'application/json'
Write-Output $result

 

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