Last updated on: 2023-10-18 04:01:19.

Scenario #3: Migrate a WAF Policy between two BIG-IPs

This lab is a variant of the previous one. It takes a manually managed F5 BIG-IP Advanced WAF Policy from an existing F5 BIG-IP and migrates it to a different BIG-IP through Terraform resources.

You can encounter this scenario in multiple use-cases:

  • Migrating from a BIG-IP to another (platform refresh).
  • Re-hosting (also known as, Lift & Shift) in a Cloud migration project.
  • Back-and-forth importing/exporting WAF Policies between the following environments: development, test/QA, and production.

The goal is to leverage the previous import scenario in order to carry and ingest the WAF Policy from one BIG-IP to another, while keeping its state through Terraform.

You can couple the WAF Policy and its children objects (parameters, URLs, attack signatures, exceptions, and other similar object) with a BIG-IP and/or share the objects across multiple policies, depending on the use case.

Pre-requisites

On the BIG-IP:

  • BIG-IP version 16.1 or newer
  • Advanced WAF Provisioned
  • Credentials with REST API access

On Terraform:

Migrate a Policy

  1. Create 4 files:

    • variables.tf
    • inputs.auto.tfvars
    • main.tf
    • outputs.tf
    variables.tf
    1
    2
    3
    4
    variable previous_bigip {}
    variable new_bigip {}
    variable username {}
    variable password {}
    

    inputs.auto.tfvars
    1
    2
    3
    4
    previous_bigip = "10.1.1.8:443"
    new_bigip      = "10.1.1.9:443"
    username       = "admin"
    password       = "whatIsYourBigIPPassword?"
    

    main.tf
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    terraform {
      required_providers {
        bigip = {
          source  = "F5Networks/bigip"
          version = "1.15"
        }
      }
    }
    provider "bigip" {
      alias    = "old"
      address  = var.previous_bigip
      username = var.username
      password = var.password
    }
    provider "bigip" {
      alias    = "new"
      address  = var.new_bigip
      username = var.username
      password = var.password
    }
    
    
    resource "bigip_waf_policy" "current" {
      provider                 = bigip.old
      partition            = "Common"
      name                 = "scenario3"
      template_name        = "POLICY_TEMPLATE_RAPID_DEPLOYMENT"
    }
    

    Note

    You can set the template value to anything. When you import the template, the value is overwritten.


    outputs.tf
    1
    2
    3
    4
    5
    6
    7
    output "policyId" {
     value    = bigip_waf_policy.current.policy_id
    }
    
    output "policyJSON" {
            value   = bigip_waf_policy.current.policy_export_json
    }
    

    This example defines two BIG-IPs: “old” and “new”. The “old” BIG-IP has the existing Advanced WAF Policies, the “new” is our target.

  2. Similar to Scenario #2: Manage an existing WAF policy with Terraform, you need the Advanced WAF Policy ID to make the initial import:

    • Check on the iControl REST API Endpoint: /mgmt/tm/asm/policies?$filter=name+eq+scenario3&$select=id
    • Get a script example in the lab/scripts/ folder
    • Run the following code sample in the Go PlayGround:
    package main
    
    import (
        "crypto/md5"
        b64 "encoding/base64"
        "fmt"
        "strings"
    )
    
    func Hasher(policyName string) string {
        hasher := md5.New()
        hasher.Write([]byte(policyName))
        encodedString := b64.StdEncoding.EncodeToString(hasher.Sum(nil))
    
        return strings.TrimRight(encodedString, "=")
    }
    
    func main() {
        var partition string = "Common"
        var policyName string = "scenario3"
    
        fullName := "/" + partition + "/" + policyName
        policyId := Hasher(fullName)
    
        r := strings.NewReplacer("/", "_", "-", "_", "+", "-")
        fmt.Println("Policy Id: ", r.Replace(policyId))
    }
    
  3. Run the following commands to:

    1. Initialize the Terraform Project.
    2. Import the current WAF policy from the “old” BIG-IP into your Terraform state.
    3. Create the Advanced WAF Policy resource for the “BIG-IP” pointing to the imported state.
    4. Configure the lifecycle of our WAF Policy.
    foo@bar:~$ terraform init
    Initializing the backend...
    
    Initializing provider plugins...
    [...]
    Terraform has been successfully initialized!
    
    foo@bar:~$ terraform import bigip_waf_policy.current YiEQ4l1Fw1U9UnB2-mTKWA
    bigip_waf_policy.this: Importing from ID "YiEQ4l1Fw1U9UnB2-mTKWA"...
    bigip_waf_policy.this: Import prepared!
      Prepared bigip_waf_policy for import
    bigip_waf_policy.this: Refreshing state... [id=YiEQ4l1Fw1U9UnB2-mTKWA]
    
    Import successful!
    
    The resources that were imported are shown above. These resources are now in
    your Terraform state and will henceforth be managed by Terraform.
    
  4. Update your terraform main.tf file with the outputs from the following two commands:

    foo@bar:~$ terraform show -json | jq '.values.root_module.resources[].values.policy_export_json | fromjson' > currentWAFPolicy.json
    
    foo@bar:~$ terraform show -no-color
    # bigip_waf_policy.this:
    resource "bigip_waf_policy" "this" {
        application_language = "utf-8"
        id                   = "YiEQ4l1Fw1U9UnB2-mTKWA"
        name                 = "/Common/scenario3"
        policy_export_json   = jsonencode(
            {
                [...]
            }
        )
        policy_id            = "YiEQ4l1Fw1U9UnB2-mTKWA"
        template_name        = "POLICY_TEMPLATE_COMPREHENSIVE"
        type                 = "security"
    }
    
  5. This is a migration use case, so you do not need the current WAF Policy from the existing BIG-IP. Using the collected data from the Terraform import, you can now update your main.tf file:

    resource "bigip_waf_policy" "migrated" {
        provider                  = bigip.new
        application_language = "utf-8"
        partition            = "Common"
        name                 = "scenario3"
        policy_id            = "YiEQ4l1Fw1U9UnB2-mTKWA"
        template_name        = "POLICY_TEMPLATE_COMPREHENSIVE"
        type                 = "security"
        policy_import_json   = file("${path.module}/currentWAFPolicy.json")
    }
    

    Note

    F5 replaced the policy_export_json argument with policy_import_json pointing to the imported WAF Policy JSON file.

  6. Plan and apply your new project.

    foo@bar:~$ terraform plan -out scenario3
    bigip_waf_policy.migrated: Refreshing state... [id=YiEQ4l1Fw1U9UnB2-mTKWA]
    
    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
      ~ update in-place
    [...]
    ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    
    Saved the plan to: scenario3
    
    To perform exactly these actions, run the following command to apply:
        terraform apply "scenario3"
    
    foo@bar:~$ terraform apply "scenario3"
    bigip_waf_policy.this: Modifying... [id=YiEQ4l1Fw1U9UnB2-mTKWA]
    bigip_waf_policy.this: Still modifying... [id=EdchwjSqo9cFtYP-iWUJmw, 10s elapsed]
    bigip_waf_policy.this: Modifications complete after 16s [id=EdchwjSqo9cFtYP-iWUJmw]
    
    Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
    
    Outputs:
    
    policyId = "EdchwjSqo9cFtYP-iWUJmw"
    policyJSON = "{[...]}"
    

Manage Policy lifecycle

Manage your WAF Policy using the previous lab, then check your WAF Policy on your BIG-IP after each Terraform apply.

Define parameters

  1. Create a parameters.tf file:

    data "bigip_waf_entity_parameter" "P1" {
      name            = "Parameter1"
      type            = "explicit"
      data_type       = "alpha-numeric"
      perform_staging = true
      signature_overrides_disable = [200001494, 200001472]
    }
    
  2. Add references to these parameters in the bigip_waf_policy Terraform resource in the main.tf file:

    resource "bigip_waf_policy" "migrated" {
      [...]
      parameters           = [data.bigip_waf_entity_parameter.P1.json]
    }
    
    foo@bar:~$ terraform plan -out scenario3
    foo@bar:~$ terraform apply "scenario3"
    

Define URLs

  1. Create a urls.tf file:

    data "bigip_waf_entity_url" "U1" {
      name                          = "/URL1"
      description                 = "this is a test for URL1"
      type                        = "explicit"
      protocol                    = "http"
      perform_staging             = true
      signature_overrides_disable = [12345678, 87654321]
      method_overrides {
        allow  = false
        method = "BCOPY"
      }
      method_overrides {
        allow  = true
        method = "BDELETE"
      }
    }
    
    data "bigip_waf_entity_url" "U2" {
      name                        = "/URL2"
    }
    
  2. Add references to this URL in the bigip_waf_policy Terraform resource in the main.tf file:

    resource "bigip_waf_policy" "migrated" {
      [...]
      urls                 = [data.bigip_waf_entity_url.U1.json, data.bigip_waf_entity_url.U2.json]
    }
    
  3. Run it:

    foo@bar:~$ terraform plan -out scenario3
    foo@bar:~$ terraform apply "scenario3"
    

Define attack signatures

  1. Create a signatures.tf file:

    data "bigip_waf_signatures" "S1" {
      provider         = bigip.new
      signature_id     = 200104004
      description      = "Java Code Execution"
      enabled          = true
      perform_staging  = true
    }
    
    data "bigip_waf_signatures" "S2" {
      provider         = bigip.new
      signature_id     = 200104005
      enabled          = false
    }
    
  2. Add references to this URL in the bigip_waf_policy Terraform resource in the main.tf file:

    resource "bigip_waf_policy" "migrated" {
      [...]
      signatures       = [data.bigip_waf_signatures.S1.json, data.bigip_waf_signatures.S2.json]
    }
    
  3. Run the plan:

    foo@bar:~$ terraform plan -out scenario3
    foo@bar:~$ terraform apply "scenario3"
    

What’s Next?