How To Deploy an F5 VIP and add an existing virtual machines as members

Overview

There may be a time where you need to create an F5 VIP to add an existing virtual machine to it. This article will give you a working RestIPE to enable you to add a single IP Address as a member to a newly deployed VIP.


Considerations

  • Licensed the SovLabs F5 module
  • Have a working F5 deployment already


Procedure

The below steps are examples of how you can setup a blueprint.  The example below will give you a working example of passing in an IP Address to deploy your VIP with no Virtual Machine.

Summary of Steps

  • Create a custom RESTipe
  • Set up the vRA Blueprint
  • Deploy the vRA Blueprint

Creating a custom RESTipe

  1. In your catalog, search for RESTipe.  Once you locate Manage restipe Configuration - SovLabs Modules click on Request



  2. In the action drop down select Create.  Provide a Configuration label.  I tend to stick with a prefix of F5Config-<Name>.  In this example we will call it F5Config-demo


  3. In the RESTipe box, copy in the below code.
    NOTE:  Please read through the code as there are items that we have changed from the default to enable this to work without any dependencies on your Blueprint.  I have also configured a value called MyIPAddress which replaces memberIp = VirtualMachine.Network0.Address under the member variables heading. 

    F5 RESTipe
    ---
    #  RESTipe Version: 2020.8.1
    restipeName: SovLabs F5 vRA Integration
    
    #  debugLogging enables verbose logging about the METHOD, URL, and Parameters/body being sent to the rest calls
    debugLogging: true
    
    #  Use basic authentication to connect to F5
    useBasicAuth: true
    
    #  Set the Base URL for all of the Rest calls.
    baseurl: "{{restipe.host.protocol}}://{{restipe.host.hostname}}:{{restipe.host.port}}/mgmt/tm"
    
    ####################
    #  Input Variables
    ####################
    tenant: "{{ inputs.tenant }}"
    shortName: "{{ inputs.shortName }}"
    f5EndpointName: "{{ inputs.f5EndpointName }}"
    
    
    #  VIP variables
    createVIP: "{{ inputs.createVIP }}"
    virtualMask: 255.255.255.255
    #-- Virtual Naming
    assignVirtualName: "{{ inputs.assignVirtualName }}"
    virtualNameNamingStandard: "{{ inputs.virtualNameNamingStandard }}"
    virtualName: "{% if restipe.createVIP == 'true' %}{% if restipe.assignVirtualName == 'false' %}{{ inputs.virtualName }}{% else %}{{ outputs.virtualName }}{% endif %}{% else %}{{ inputs.virtualNameExisting }}{% endif %}"
    virtualDomain: "{{ inputs.virtualDomain }}"
    #-- Virtual DNS
    registerVipDns: "{{ inputs.registerVipDns }}"
    virtualAdditionalDomains: "{{ inputs.virtualAdditionalDomains | join: ',' }}"
    #-- Virtual IPAM
    assignVirtualIp: "{{ inputs.assignVirtualIp }}"
    virtualIpIpamProfile: "{{ inputs.virtualIpIpamProfile }}"
    virtualIp: "{% if restipe.assignVirtualIp == 'true' %}{{ outputs.virtualIp}}{% else %}{{ inputs.virtualIp }}{% endif %}"
    virtualPort: '{{ inputs.virtualPort | default: "80" | replace: ".0", "" }}'
    
    #--Virtual iRules
    virtualIRules: "{{ inputs.virtualiRules }}"
    setVirtualIRules: "{% if restipe.virtualIRules == '' %}false{% else %}true{% endif %}"
    #--Protocol
    ipProtocol: "{{ inputs.virtualProtocol }}"
    #--Profiles
    virtualProfiles: "{{ inputs.virtualProfiles | json_string }}"
    serverSSLProfiles: "{{ inputs.virtualServerSSLs }}"
    clientSSLProfiles: "{{ inputs.virtualClientSSLs }}"
    setServerSSLProfiles: "{% if restipe.serverSSLProfiles == '' %}false{% else %}true{% endif %}"
    setClientSSLProfiles: "{% if restipe.clientSSLProfiles == '' %}false{% else %}true{% endif %}"
    protocolProfileClient: "{{ inputs.virtualProtocolProfileClient }}"
    protocolProfileServer: "{{ inputs.virtualProtocolProfileServer }}"
    httpProfile: "{{ inputs.virtualHttpProfile }}"
    httpProxyConnectProfile: "{{ inputs.virtualHttpProxyConnectProfile }}"
    anyIpProfile: "{{ inputs.virtualAnyIpProfile }}"
    #--Source Address Translation / SNAT
    sourceAddressTranslation: "{{ inputs.virtualSourceAddressTranslation }}"
    snatPool: "{% if restipe.sourceAddressTranslation == 'snat' %}{{ inputs.virtualSNATPool }}{% endif %}"
    setSnatPool: "{% if restipe.sourceAddressTranslation == 'snat' %}true{% else %}false{% endif %}"
    #--Persistence Profiles
    defaultPersistenceProfile: "{{ inputs.virtualDefaultPersistenceProfile }}"
    fallbackPersistenceProfile: "{{ inputs.virtualFallbackPersistenceProfile }}"
    setDefaultPersistenceProfile: "{% if restipe.defaultPersistenceProfile == '' %}false{% else %}true{% endif %}"
    setFallbackPersistenceProfile: "{% if restipe.fallbackPersistenceProfile == '' %}false{% else %}true{% endif %}"
    
    #  Pool variables
    assignPoolNameAsVirtualName: "{{ inputs.assignPoolNameAsVirtualName}}"
    assignPoolName: "{% if restipe.assignPoolNameAsVirtualName == 'true' %}true{% else %}{{ inputs.assignPoolName }}{% endif %}"
    lbPoolNameNamingStandard: "{{ inputs.poolNameNamingStandard }}"
    lbPoolName: "{% if restipe.assignPoolName == 'true' %}{{ outputs.poolName }}{% else %}{{ inputs.poolName }}{% endif %}"
    loadBalancingMode: '{{ inputs.loadBalancingMode | default: "least-connections-member" }}'
    lbPartition: "{{ inputs.partition }}"
    #-- Health Monitors
    healthMonitors: "{{ inputs.poolHealthMonitors }}"
    assignHealthMonitors: "{{ inputs.assignHealthMonitors }}"
    hmAvailibilityRequirement: "{{ inputs.poolHealthMonitorAvailibilityRequirement }}"
    hmAtLeastMin: "{{ inputs.poolHealthMonitorAtLeastMin }}"
    
    #  Member variables
    memberIp: "{{ inputs.MyIPAddress }}" # This is my value on blueprint and must be inputs.<name>
    lbPort: '{{ SovLabs_F5LbPort | default: inputs.memberPort  | default: "80" | replace: ".0", "" }}'
    priorityGroup: '{{ SovLabs_F5PriorityGroup | default: inputs.memberPriorityGroup | default: "0" | replace: ".0", "" }}'
    ratio: '{{ SovLabs_F5Ratio | default: inputs.memberRadio | default: "1" | replace: ".0", "" }}'
    rateLimit: '{{ SovLabs_F5RateLimit | default: inputs.memberConnectionRateLimit | default: "0" | replace: ".0", "" }}'
    connectionLimit: '{{ SovLabs_F5ConnectionLimit | default: inputs.memberConnectionLimit | default: "0" | replace: ".0", "" }}'
    
    
    ####################
    #  Restipe Steps
    ####################
    
    steps:
      ############################################
      # provision step
      #  Determine which path to take based on createVIP boolean
      #  Run when the F5Config item is being created
      ############################################
      provision:
        description: Run a specific step based on boolean of createVIP
        method: condition
        nextStep: "{% if restipe.createVIP == 'true' %}checkIfGenerateVirtualName{% else %}checkVIPExists{% endif %}"
    
      checkVIPExists:
        description: Check to see if the Virtual Server exists
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{{restipe.virtualName}}"
        params:
          expandSubcollections: 'true'
        method: get
        nextStep: "{% if restipe.checkVIPExists.status == 404%}FAILURE{% else %}SUCCESS{% endif %}"
    
      # ------------------------------------------
      # checkIfGenerateVirtualName step
      #  Get generated custom name for Virtual Server
      # ------------------------------------------
      checkIfGenerateVirtualName:
        description: Check to see if a custom name needs to be generated for the Virtual Server
        method: condition
        nextStep: "{% if restipe.assignVirtualName == 'true' %}getVirtualName{% else %}checkIfVirtualExists{% endif %}"
    
      getVirtualName:
        description: Call 'Get custom name' vRO workflow to generate Virtual Server name
        workflowId: "c8e64937-1855-430a-9322-9346b96e43aa"
        method: workflow
        params:
            tenant: "{{ restipe.tenant }}"
            namingStandardName: "{{ restipe.virtualNameNamingStandard }}"
            type: "F5Config"
            itemShortName: "{{ restipe.shortName }}"
            outputKey: "virtualName"
            jsonString: "{{ SovLabs_AllProperties }}"
            domain: "{{ restipe.virtualDomain }}"
        nextStep: "checkIfGenerateVirtualIp"
    
      checkIfVirtualExists:
        description: Check to see if the Virtual Server exists
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{% if restipe.assignVirtualName == 'true' %}{{ restipe.getVirtualName.name }}{% else %}{{restipe.virtualName}}{% endif %}"
        params:
          expandSubcollections: 'true'
        method: get
        nextStep: "{% if restipe.checkIfVirtualExists.status == 404%}checkIfGenerateVirtualIp{% else  %}SUCCESS{% endif %}"
    
      # ------------------------------------------
      # checkIfGenerateVirtualIp step
      #  Get generated IP address for Virtual Server
      # ------------------------------------------
      checkIfGenerateVirtualIp:
        description: Check to see if an IP address needs to be generated for the Virtual Server
        method: condition
        nextStep: "{% if restipe.assignVirtualIp == 'true' %}getVirtualIp{% else %}checkIfRegisterVirtualDns{% endif %}"
    
      getVirtualIp:
        description: Call 'Get IP Address' vRO workflow to generate Virtual Server IP address
        workflowId: "54a650d9-861c-4f9d-8c36-f77d9a6ac2ff"
        method: workflow
        params:
            tenant: "{{ restipe.tenant }}"
            ipamProfileName: "{{ restipe.virtualIpIpamProfile }}"
            type: "F5Config"
            itemShortName: "{{ restipe.shortName }}"
            hostname: "{% if restipe.assignVirtualName == 'true' %}{{ restipe.getVirtualName.name }}{% else %}{{ restipe.virtualName }}.{{ restipe.virtualDomain }}{% endif %}"
            outputKey: "virtualIp"
            jsonString: "{{ SovLabs_AllProperties }}"
        nextStep: "checkIfRegisterVirtualDns"
    
      # ------------------------------------------
      # checkIfRegisterVirtualDns step
      #  Register DNS for Virtual Server hostname/IP address
      # ------------------------------------------
      checkIfRegisterVirtualDns:
        description: Check to see if the Virtual Server hostname/IP address needs to be registered in DNS
        method: condition
        nextStep: "{% if restipe.registerVipDns == 'true' %}registerVirtualDns{% else %}checkIfGeneratePoolName{% endif %}"
    
      registerVirtualDns:
        description: Call 'Register DNS' vRO workflow to register Virtual Server hostname/IP address
        workflowId: "e44c22d3-b64b-4dfb-bb0a-ed2f9a92974d"
        method: workflow
        params:
            tenant: "{{ restipe.tenant }}"
            type: "F5Config"
            itemShortName: "{{ restipe.shortName }}"
            hostname: "{% if restipe.assignVirtualName == 'true' %}{{ restipe.getVirtualName.name }}{% else %}{{ restipe.virtualName }}.{{ restipe.virtualDomain }}{% endif %}"
            ipAddress: "{% if restipe.assignVirtualIp == 'true' %}{{ restipe.getVirtualIp.ipAddress }}{% else %}{{ restipe.virtualIp }}{% endif %}"
            domain: "{{ restipe.virtualDomain }}"
            additionalDomains: "{{ restipe.virtualAdditionalDomains }}"
            jsonString: "{{ SovLabs_AllProperties }}"
        nextStep: "checkIfGeneratePoolName"
    
      # ------------------------------------------
      # checkIfGeneratePoolName step
      #  Get generated custom name for Pool
      # ------------------------------------------
      checkIfGeneratePoolName:
        description: Check to see if a custom name needs to be generated for the Pool
        method: condition
        nextStep: "{% if restipe.assignPoolName == 'true' %}getPoolName{% else %}checkPoolExists{% endif %}"
    
      getPoolName:
        description: Call 'Get custom name' vRO workflow to generate Pool name
        workflowId: "c8e64937-1855-430a-9322-9346b96e43aa"
        method: workflow
        params:
            tenant: "{{ restipe.tenant }}"
            namingStandardName: "{{ restipe.lbPoolNameNamingStandard }}"
            type: "F5Config"
            itemShortName: "{{ restipe.shortName }}"
            outputKey: "poolName"
            jsonString: "{{ SovLabs_AllProperties }}"
            domain: "{{ restipe.virtualDomain }}"
        nextStep: "checkPoolExists"
    
      checkPoolExists:
        description: Check to see if Pool already exists
        url: "{{restipe.baseurl}}/ltm/pool/~{{restipe.lbPartition}}~{% if restipe.assignPoolName == 'true' %}{{ restipe.getPoolName.name }}{% else %}{{restipe.lbPoolName}}{% endif %}"
        params:
          expandSubcollections: 'true'
        method: get
        #nextStep: "{% if restipe.checkPoolExists.status == 404%}createPool{% elsif restipe.checkPoolExists.status  == 401%}FAILURE{% else %}checkVirtualExists{% endif %}"
        nextStep: "{% assign mystatus = restipe.checkPoolExists.status|substring: 0,1 %}{% if restipe.checkPoolExists.status == 404%}createPool{% elsif mystatus  == 4 or mystatus == 5%}FAILURE{% else %}checkVirtualExists{% endif %}"
    
      createPool:
        description: Create the Pool
        url: "{{restipe.baseurl}}/ltm/pool"
        method: post
        body:
          kind: tm:ltm:pool:poolstate
          partition: "{{restipe.lbPartition}}"
          name: "{% if restipe.assignPoolName == 'true' %}{{ restipe.getPoolName.name }}{% else %}{{restipe.lbPoolName}}{% endif %}"
          description: "{% if restipe.assignPoolName == 'true' %}{{ restipe.getPoolName.name }}{% else %}{{restipe.lbPoolName}}{% endif %} created via vRA"
          loadBalancingMode: "{{ restipe.loadBalancingMode }}"
        nextStep: "{% if restipe.createPool.status == 200 %}checkIfAttachHealthMonitors{% else %}FAILURE{% endif %}"
    
      # ------------------------------------------
      # setMonitors step
      #  Attach Health Monitors to the Pool
      # ------------------------------------------
      checkIfAttachHealthMonitors:
        description: Check to see if Health Monitors needs to be attached to the Pool
        method: condition
        nextStep: "{% if restipe.assignHealthMonitors == 'true' %}setMonitors{% else %}checkVirtualExists{% endif %}"
    
      setMonitors:
        description: Set Health Monitors on Pool
        url: "{{restipe.baseurl}}/ltm/pool/~{{restipe.lbPartition}}~{% if restipe.assignPoolName == 'true' %}{{ restipe.getPoolName.name }}{% else %}{{restipe.lbPoolName}}{% endif %}"
        method: patch
        body:
          monitor: "{% if restipe.hmAvailibilityRequirement == 'At Least' %}min {{restipe.hmAtLeastMin}} of {{ restipe.healthMonitors | replace: ', ', ' ' | replace: '[', '' | replace: ']', '' }}{% else %}{{ restipe.healthMonitors | replace: ', ', ' and ' | replace: '[', '' | replace: ']', '' }}{% endif %}"
        nextStep: "{% if restipe.setMonitors.status == 200%}checkVirtualExists{% else %}FAILURE{% endif %}"
    
      checkVirtualExists:
        description: Check to see if the Virtual Server already exists
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{% if restipe.assignVirtualName == 'true' %}{{ restipe.getVirtualName.name }}{% else %}{{restipe.virtualName}}{% endif %}"
        params:
          expandSubcollections: 'true'
        method: get
        nextStep: "{% if restipe.checkVirtualExists.status == 404%}createVirtual{% else  %}SUCCESS{% endif %}"
    
      createVirtual:
        description: Create the Virtual Server
        url: "{{restipe.baseurl}}/ltm/virtual"
        method: post
        body:
          kind: tm:ltm:virtual:virtualstate
          partition: "{{restipe.lbPartition}}"
          name: "{% if restipe.assignVirtualName == 'true' %}{{ restipe.getVirtualName.name }}{% else %}{{restipe.virtualName}}{% endif %}"
          description: "{% if restipe.assignVirtualName == 'true' %}{{ restipe.getVirtualName.name }}{% else %}{{restipe.virtualName}}{% endif %} create via vRA"
          destination: "{% if restipe.assignVirtualIp == 'true' %}{{ restipe.getVirtualIp.ipAddress }}{% else %}{{restipe.virtualIp}}{% endif %}:{{restipe.virtualPort}}"
          mask: "{{restipe.virtualMask}}"
          ipProtocol: "{{ restipe.ipProtocol }}"
          pool: "{% if restipe.assignPoolName == 'true' %}{{ restipe.getPoolName.name }}{% else %}{{restipe.lbPoolName}}{% endif %}"
        nextStep: "{% if restipe.createVirtual.status == 200 %}getF5Version{% else %}FAILURE{% endif %}"
    
      # ------------------------------------------
      # getF5Version step
      #  Get F5 Version
      # ------------------------------------------
      getF5Version:
        description: Call 'Get F5 BIG-IP Version' vRO workflow to generate Pool name
        workflowId: "48e2a74a-6273-4629-bd39-cd651c6eaac3"
        method: workflow
        params:
            tenant: "{{ restipe.tenant }}"
            endpointName: "{{ restipe.f5EndpointName }}"
        nextStep: "setVipProfiles"
    
      # ------------------------------------------
      # setVipProfiles step
      #  Set the VIP Profiles
      # ------------------------------------------
      setVipProfiles:
        description: Set Protocol Profile Client on Virtual Server
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{% if restipe.assignVirtualName == 'true' %}{{ restipe.getVirtualName.name }}{% else %}{{restipe.virtualName}}{% endif %}"
        method: "{% if restipe.getF5Version.version < 12 %}put{% elsif restipe.getF5Version.version >= 12 %}patch{% endif %}"
        body: "{{ restipe.virtualProfiles }}"
        nextStep: "{% if restipe.setVipProfiles.status == 200%}checkIfAttachSNATPool{% else %}FAILURE{% endif %}"
    
      # ------------------------------------------
      # checkIfAttachSNATPool step
      #  Check to see if Source Address translation is SNAT for the Virtual Server
      # ------------------------------------------
      checkIfAttachSNATPool:
        description: Check to see if Source Address translation is SNAT for the Virtual Server
        method: condition
        nextStep: "{% if restipe.setSnatPool == 'true' %}applySnatPool{% else %}setSourceAddressTranslation{% endif %}"
    
      # ------------------------------------------
      # setSourceAddressTranslation step
      #  Set the Source Address Translation to automap or none
      # ------------------------------------------
      setSourceAddressTranslation:
        description:  Set the Source Address Translation to 'automap' or 'none' on the Virtual Server
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{% if restipe.assignVirtualName == 'true' %}{{ restipe.getVirtualName.name }}{% else %}{{restipe.virtualName}}{% endif %}"
        method: "{% if restipe.getF5Version.version < 12 %}put{% elsif restipe.getF5Version.version >= 12 %}patch{% endif %}"
        body:
          sourceAddressTranslation:
            type: "{{ restipe.sourceAddressTranslation }}"
        nextStep: "{% if restipe.setSourceAddressTranslation.status == 200%}checkIfAttachIRules{% else %}FAILURE{% endif %}"
    
      # ------------------------------------------
      # applySnatPool step
      #  Set the SNAT Pool
      # ------------------------------------------
      applySnatPool:
        description: Set the Source Address Translation to 'snat' and the SNAT Pool on Virtual Server
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{% if restipe.assignVirtualName == 'true' %}{{ restipe.getVirtualName.name }}{% else %}{{restipe.virtualName}}{% endif %}"
        method: "{% if restipe.getF5Version.version < 12 %}put{% elsif restipe.getF5Version.version >= 12 %}patch{% endif %}"
        body:
          sourceAddressTranslation:
            type: "{{ restipe.sourceAddressTranslation }}"
            pool: "{{ restipe.snatPool }}"
        nextStep: "{% if restipe.applySnatPool.status == 200%}checkIfAttachIRules{% else %}FAILURE{% endif %}"
    
      # ------------------------------------------
      # checkIfAttachIRules step
      #  Check to see if iRules need to attach to the Virtual Server
      # ------------------------------------------
      checkIfAttachIRules:
        description: Check to see if iRules needs to be attached to the Virtual Server
        method: condition
        nextStep: "{% if restipe.setVirtualIRules == 'true' %}attachVirtualIRules{% else %}checkIfSetDefaultPersistenceProfile{% endif %}"
    
      # ------------------------------------------
      # attachVirtualIRules step
      #  Attach iRules to the Virtual Server
      # ------------------------------------------
      attachVirtualIRules:
        description: Attach iRules to Virtual Server
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{% if restipe.assignVirtualName == 'true' and restipe.getVirtualName.name %}{{ restipe.getVirtualName.name }}{% else %}{{restipe.virtualName}}{% endif %}"
        body:
          rules: ["{{ restipe.virtualIRules | join: '","' | replace: ', ', '", "' | replace: '[', '' | replace: ']', ''}}"]
        method: "{% if restipe.getF5Version.version < 12 %}put{% elsif restipe.getF5Version.version >= 12 %}patch{% endif %}"
        nextStep: "{% if restipe.attachVirtualIRules.status == 200%}checkIfSetDefaultPersistenceProfile{% else %}FAILURE{% endif %}"
    
      checkIfSetDefaultPersistenceProfile:
        description: Check to see if Default Persistence Profile needs to be attached to the Virtual Server
        method: condition
        nextStep: "{% if restipe.setDefaultPersistenceProfile == 'true' %}applyDefaultPersistenceProfile{% else %}checkIfSetFallbackPersistenceProfile{% endif %}"
    
      applyDefaultPersistenceProfile:
        description: Set Default Persistence Profile on Virtual Server
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{% if restipe.assignVirtualName == 'true' %}{{ restipe.getVirtualName.name }}{% else %}{{restipe.virtualName}}{% endif %}"
        method: "{% if restipe.getF5Version.version < 12 %}put{% elsif restipe.getF5Version.version >= 12 %}patch{% endif %}"
        body:
          persist: "{{ restipe.defaultPersistenceProfile }}"
        nextStep: "{% if restipe.applyDefaultPersistenceProfile.status == 200%}checkIfSetFallbackPersistenceProfile{% else %}FAILURE{% endif %}"
    
      checkIfSetFallbackPersistenceProfile:
        description: Check to see if Fallback Persistence Profile needs to be attached to the Virtual Server
        method: condition
        nextStep: "{% if restipe.setFallbackPersistenceProfile == 'true' %}applyFallbackPersistenceProfile{% else %}checkIfSetProfiles{% endif %}"
    
      applyFallbackPersistenceProfile:
        description: Set Fallback Persistence Profile on Virtual Server
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{% if restipe.assignVirtualName == 'true' %}{{ restipe.getVirtualName.name }}{% else %}{{restipe.virtualName}}{% endif %}"
        method: "{% if restipe.getF5Version.version < 12 %}put{% elsif restipe.getF5Version.version >= 12 %}patch{% endif %}"
        body:
          fallbackPersistence: "{{ restipe.fallbackPersistenceProfile }}"
        nextStep: "{% if restipe.applyFallbackPersistenceProfile.status == 200%}checkIfSetProfiles{% else %}FAILURE{% endif %}"
    
      # ------------------------------------------
      # setProfiles step
      #  Attach Server and/or Client SSL profiles to the Virtual Server
      # ------------------------------------------
      checkIfSetProfiles:
        description: Check to see if Server/Client SSLs needs to be attached to the Virtual Server
        method: condition
        nextStep: "{% if restipe.setServerSSLProfiles == 'false' and restipe.setClientSSLProfiles == 'false' %}getPoolNameProvisionVM{% else %}checkIfSetServerProfiles{% endif %}" #Changes from SUCCESS to getPoolNameProvisionVM
    
      checkIfSetServerProfiles:
        description: Check to see if Server SSLs needs to be attached to the Virtual Server
        method: condition
        nextStep: "{% if restipe.setServerSSLProfiles == 'true' %}setServerSSLProfiles{% else %}checkIfSetClientProfiles{% endif %}"
    
      setServerSSLProfiles:
        description: Set Server SSL profiles on Virtual Server
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{% if restipe.assignVirtualName == 'true' %}{{ restipe.getVirtualName.name }}{% else %}{{restipe.virtualName}}{% endif %}"
        method: patch
        body:
          profiles: ["{{ restipe.serverSSLProfiles | join: ',' | replace: '[', '' | replace: ']', '' | replace: ' ', '' | replace: ',', '", "' }}"]
        nextStep: "{% if restipe.setServerProfiles.status == 200%}checkIfSetClientProfiles{% else %}FAILURE{% endif %}"
    
      checkIfSetClientProfiles:
        description: Check to see if Client SSLs needs to be attached to the Virtual Server
        method: condition
        nextStep: "{% if restipe.setClientSSLProfiles == 'true' %}setClientSSLProfiles{% else %}SUCCESS{% endif %}"
    
      setClientSSLProfiles:
        description: Set Server SSL profiles on Virtual Server
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{% if restipe.assignVirtualName == 'true' %}{{ restipe.getVirtualName.name }}{% else %}{{restipe.virtualName}}{% endif %}"
        method: patch
        body:
          profiles: ["{{ restipe.clientSSLProfiles | join: ',' | replace: '[', '' | replace: ']', '' | replace: ' ', '' | replace: ',', '", "' }}"]
        nextStep: "{% if restipe.setClientSSLProfiles.status == 200%}SUCCESS{% else %}FAILURE{% endif %}"
    
      ############################################
      # provisionVM step
      #  Run each time a VM is added to the VIP
      ############################################
    
      provisionVM:
        description: Determine first whether or not the VIP was created
        method: condition
        nextStep: "getPoolNameProvisionVM"
    
      getPoolNameProvisionVM:
        description: Get the Pool name from Virtual Server name
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{{restipe.getPoolName.name}}" #Changes last component from restipe.virtualname to restipe.getPoolName.name
        params:
          expandSubcollections: 'true'
        method: get
        nextStep: "{% if restipe.getPoolNameProvisionVM.status == 200%}checkIfMemberInPool{% else %}FAILURE{% endif %}"
    
      checkIfMemberInPool:
        description: Check to see if the member (VM IP:Port) is in the Pool already
        url: "{{restipe.baseurl}}/ltm/pool/{{ restipe.getPoolNameProvisionVM.body.pool | replace:'/', '~' }}/members/~{{restipe.lbPartition}}~{{restipe.memberIp}}:{{restipe.lbPort}}"
        method: get
        #nextStep: "{% if restipe.checkIfMemberInPool.status == 404%}addToPool{% elsif restipe.checkIfMemberInPool.status == 401%}FAILURE{% else %}SUCCESS{% endif %}"
        nextStep: "{% assign mystatus =  restipe.checkIfMemberInPool.status|substring: 0,1 %}{% if restipe.checkIfMemberInPool.status == 404%}addToPool{% elsif mystatus ==4 or mystatus == 5 %}FAILURE{% else %}SUCCESS{% endif %}"
    
      addToPool:
        description: Add the new member (VM IP:Port) to the Pool
        url: "{{restipe.baseurl}}/ltm/pool/{{ restipe.getPoolNameProvisionVM.body.pool | replace:'/', '~' }}/members"
        method: post
        body:
          name: "{{restipe.memberIp}}:{{restipe.lbPort}}"
          priorityGroup: "{{restipe.priorityGroup}}"
          ratio: "{{restipe.ratio}}"
          rateLimit: "{{restipe.rateLimit}}"
          connectionLimit: "{{restipe.connectionLimit}}"
          partition: "{{ restipe.lbPartition }}"
        nextStep: verifyPoolMemberAdded
    
      verifyPoolMemberAdded:
        description: Check to see if the member (VM IP:Port) got added into the Pool
        url: "{{restipe.baseurl}}/ltm/pool/{{ restipe.getPoolNameProvisionVM.body.pool | replace:'/', '~' }}/members/~{{restipe.lbPartition}}~{{restipe.memberIp}}:{{restipe.lbPort}}"
        method: get
        nextStep: "{% if restipe.verifyPoolMemberAdded.status == 200%}SUCCESS{% else %}FAILURE{% endif %}"
    
      ############################################
      # deprovisionVM step
      #  Run each time a VM is removed from the VIP
      ############################################
    
      deprovisionVM:
        description: Determine first whether or not the VIP was created
        method: condition
        nextStep: "getPoolNameDeprovisionVM"
    
      getPoolNameDeprovisionVM:
        description: Get the Pool name from Virtual Server name
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{{restipe.virtualName}}"
        params:
          expandSubcollections: 'true'
        method: get
        nextStep: "{% if restipe.getPoolNameDeprovisionVM.status == 200%}verifyPoolExists{% else %}FAILURE{% endif %}"
    
      verifyPoolExists:
        description: Check to see if the Pool exists
        url: "{{restipe.baseurl}}/ltm/pool/{{ restipe.getPoolNameDeprovisionVM.body.pool | replace:'/', '~' }}"
        params:
          expandSubcollections: 'true'
        method: get
        #nextStep: "{% if restipe.verifyPoolExists.status == 404%}SUCCESS{% elsif restipe.verifyPoolExists.status == 401%}FAILURE{% else %}checkMemberInPool{% endif %}"
        nextStep: "{% assign mystatus =  restipe.verifyPoolExists.status|substring: 0,1 %}{% if restipe.verifyPoolExists.status == 404 %}SUCCESS{% elsif mystatus == 4 or mystatus == 5 %}FAILURE{% else %}checkMemberInPool{% endif %}"
    
      checkMemberInPool:
        description: Check to see if member is in the Pool
        url: "{{restipe.baseurl}}/ltm/pool/{{ restipe.getPoolNameDeprovisionVM.body.pool | replace:'/', '~' }}/members/~{{restipe.lbPartition}}~{{restipe.memberIp}}:{{restipe.lbPort}}"
        method: get
        nextStep: "{% if restipe.checkMemberInPool.status == 200%}removeMemberFromPool{% else %}SUCCESS{% endif %}"
    
      removeMemberFromPool:
        description: Remove the member from Pool
        url: "{{restipe.baseurl}}/ltm/pool/{{ restipe.getPoolNameDeprovisionVM.body.pool | replace:'/', '~' }}/members/~{{restipe.lbPartition}}~{{restipe.memberIp}}:{{restipe.lbPort}}"
        method: delete
        nextStep: "{% if restipe.removeMemberFromPool.status == 200%}removeMember{% else %}FAILURE{% endif %}"
    
      removeMember:
        description: Remove member from Nodes
        url: "{{restipe.baseurl}}/ltm/node/~{{restipe.lbPartition}}~{{restipe.memberIp}}"
        method: delete
        nextStep: "{% if restipe.removeMember.status == 200%}SUCCESS{% else %}FAILURE{% endif %}"
    
      ############################################
      # deprovision step
      #  Run when the deployment (F5Config item) is being destroyed
      ############################################
      deprovision:
        description: Determine first whether or not the VIP was created
        method: condition
        nextStep: "getPoolNameDeprovision"
    
      getPoolNameDeprovision:
        description: Get the Pool name from Virtual Server name
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{% if restipe.assignVirtualName == 'true' %}{% if restipe.getVirtualName.name %}{{ restipe.getVirtualName.name }}{% else %}{{ restipe.virtualName }}{% endif %}{% else %}{{ restipe.virtualName }}{% endif %}"
        params:
          expandSubcollections: 'true'
        method: get
        nextStep: "{% if restipe.getPoolNameDeprovision.status == 200%}verifyPoolEmpty{% elsif restipe.getPoolNameDeprovision.status == 404 %}checkVirtualExistsRemove{% else %}FAILURE{% endif %}"
    
      verifyPoolEmpty:
        url: "{{restipe.baseurl}}/ltm/pool/{{ restipe.getPoolNameDeprovision.body.pool | replace:'/', '~' }}/members"
        method: get
        nextStep: "{% if restipe.verifyPoolEmpty.status == 200 and restipe.verifyPoolEmpty.body.items.size == 0 %}checkVirtualExistsRemove{% elsif restipe.verifyPoolEmpty.status == 404 %}checkVirtualExistsRemove{% else %}FAILURE{% endif %}"
    
      checkVirtualExistsRemove:
        description: Check to see if the Virtual Server exists and delete the Virtual Server
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{% if restipe.assignVirtualName == 'true' %}{% if restipe.getVirtualName.name %}{{ restipe.getVirtualName.name }}{% else %}{{ restipe.virtualName }}{% endif %}{% else %}{{ restipe.virtualName }}{% endif %}"
        params:
          expandSubcollections: 'true'
        method: get
        nextStep: "{% if restipe.checkVirtualExistsRemove.status == 200%}removeVirtual{% else %}checkPoolEmpty{% endif %}"
    
      removeVirtual:
        description: Delete the empty Virtual Server
        url: "{{restipe.baseurl}}/ltm/virtual/~{{restipe.lbPartition}}~{% if restipe.assignVirtualName == 'true' %}{% if restipe.getVirtualName.name %}{{ restipe.getVirtualName.name }}{% else %}{{ restipe.virtualName }}{% endif %}{% else %}{{ restipe.virtualName }}{% endif %}"
        method: delete
        nextStep: "{% if restipe.removeVirtual.status == 200%}checkPoolEmpty{% else %}FAILURE{% endif %}"
    
      checkPoolEmpty:
        description: Check the Pool size and delete the Pool if there are no members
        url: "{{restipe.baseurl}}/ltm/pool/{% if restipe.getPoolNameDeprovision.body.pool != '' and restipe.getPoolNameDeprovision.body.pool != nil %}{{ restipe.getPoolNameDeprovision.body.pool | replace:'/', '~' }}{% else %}{{ restipe.lbPoolName }}{% endif %}/members"
        method: get
        nextStep: "{% if restipe.checkPoolEmpty.status == 200 and restipe.checkPoolEmpty.body.items.size == 0 %}removePool{% else %}checkIfReleasePoolName{% endif %}"
    
      removePool:
        description: Delete the empty Pool
        url: "{{restipe.baseurl}}/ltm/pool/{% if restipe.getPoolNameDeprovision.body.pool != '' and restipe.getPoolNameDeprovision.body.pool != nil %}{{ restipe.getPoolNameDeprovision.body.pool | replace:'/', '~' }}{% else %}{{ restipe.lbPoolName }}{% endif %}"
        method: delete
        nextStep: "{% if restipe.removePool.status == 200 %}checkIfReleasePoolName{% else %}FAILURE{% endif %}"
    
      # ------------------------------------------
      # checkIfReleasePoolName sub-step
      #  Release generated custom name for Pool
      # ------------------------------------------
      checkIfReleasePoolName:
        description: Check to see if a custom name needs to be released for the Pool
        method: condition
        nextStep: "{% if restipe.createVIP == 'true' and restipe.assignPoolName == 'true' %}releasePoolName{% else %}checkIfUnregisterVirtualDns{% endif %}"
    
      releasePoolName:
        description: Call 'Release custom name' vRO workflow to release Pool name
        workflowId: "e9e64ae9-dba9-4d18-9802-d353bf42d5c1"
        method: workflow
        params:
            tenant: "{{ restipe.tenant }}"
            type: "F5Config"
            itemShortName: "{{ restipe.shortName }}"
            hostname: "{% if restipe.getPoolNameDeprovision.body.pool != '' and restipe.getPoolNameDeprovision.body.pool != nil %}{{ restipe.getPoolNameDeprovision.body.pool | split:'/' | last }}{% else %}{{ restipe.lbPoolName }}{% endif %}"
        nextStep: "checkIfUnregisterVirtualDns"
    
      # ------------------------------------------
      # checkIfUnregisterVirtualDns sub-step
      #  Release DNS records for Virtual Server
      # ------------------------------------------
    
      checkIfUnregisterVirtualDns:
        description: Check to see if the Virtual Server hostname/IP address needs to be unregistered from DNS
        method: condition
        nextStep: "{% if restipe.registerVipDns == 'true' %}unregisterVirtualDns{% else %}checkIfReleaseVirtualIp{% endif %}"
    
      unregisterVirtualDns:
        description: Call 'Unegister DNS' vRO workflow to register Virtual Server hostname/IP address
        workflowId: "a5bcd00f-fabc-4959-8f18-c25d5361022d"
        method: workflow
        params:
            tenant: "{{ restipe.tenant }}"
            type: "F5Config"
            itemShortName: "{{ restipe.shortName }}"
            hostname: "{% if restipe.assignVirtualName == 'true' %}{{ restipe.virtualName }}{% elsif restipe.getVirtualName.name %}{{ restipe.getVirtualName.name }}{% else %}{{ restipe.virtualName }}{% endif %}"
            ipAddress: "{{ restipe.virtualIp }}"
            domain: "{{ restipe.virtualDomain }}"
            additionalDomains: "{{ restipe.virtualAdditionalDomains }}"
            jsonString: "{{ SovLabs_AllProperties }}"
        nextStep: "checkIfReleaseVirtualIp"
    
      # ------------------------------------------
      # checkIfGenerateVirtualIp sub-step
      #  Release IP address for Virtual Server
      # ------------------------------------------
      checkIfReleaseVirtualIp:
        description: Check to see if an IP address needs to be released for the Virtual Server
        method: condition
        nextStep: "{% if restipe.assignVirtualIp == 'true' %}releaseVirtualIp{% else %}checkIfReleaseVirtualName{% endif %}"
    
      releaseVirtualIp:
        description: Call 'Release IP Address' vRO workflow to free Virtual Server IP address
        workflowId: "1aac6163-6881-40b7-b457-b45dc43a9527"
        method: workflow
        params:
            tenant: "{{ restipe.tenant }}"
            ipamProfileName: "{{ restipe.virtualIpIpamProfile }}"
            type: "F5Config"
            ipKey: "virtualIp"
            itemShortName: "{{ restipe.shortName }}"
            jsonString: "{{ SovLabs_AllProperties }}"
        nextStep: "checkIfReleaseVirtualName"
    
      # ------------------------------------------
      # checkIfReleaseVirtualName sub-step
      #  Release generated custom name for Virtual Server
      # ------------------------------------------
      checkIfReleaseVirtualName:
        description: Check to see if a custom name needs to be released for the Virtual Server
        method: condition
        nextStep: "{% if restipe.assignVirtualName == 'true' %}releaseVirtualName{% else %}SUCCESS{% endif %}"
    
      releaseVirtualName:
        description: Call 'Release custom name' vRO workflow to release Pool name
        workflowId: "e9e64ae9-dba9-4d18-9802-d353bf42d5c1"
        method: workflow
        params:
            tenant: "{{ restipe.tenant }}"
            type: "F5Config"
            itemShortName: "{{ restipe.shortName }}"
            hostname: "{% if restipe.assignVirtualName == 'true' %}{% if restipe.getVirtualName.name %}{{ restipe.getVirtualName.name }}{% else %}{{ restipe.virtualName }}{% endif %}{% else %}{{ restipe.virtualName }}{% endif %}"
        nextStep: "SUCCESS"



  4. Click on Submit to save the new RESTipe



  5. This will now create a new deployment for the RESTipe. Once this is completed, the RESTipe is ready to use.

Setting up your blueprint

  1. Create a blueprint with just your F5 Load Balancer on the canvas.  You should have an understanding of what settings you need, however, I will provide some of the settings I used that were successful throughout this document. 

  2. Click on the VIP tab and make sure you select RESTipe : F5Config-<Name> (Where <name> is the name of the RESTipe you created, my example is F5Config-demo)



  3. Other settings I have on my VIP are as pictured below.  
    General Tab:



  4. My configuration under the Pool tab


  5. Once you have all your settings for your F5 ready and have selected the new RESTipe, click on the gear icon at the top


  6. Click on the properties tab and on custom properties


  7. Here you can add the values for your VIP Custom naming and also the value for the IP Address you wish to add. Click on New (1), Type in the name of the value (2), place a check under Show in Request (3) and click on OK (4)
    NOTE: I have pre created a property definition called MyIPAddress that is a text box with RegEX applied so ONLY an IP Address can be entered.  This is optional but adds safe guard. I will place the RegEx at the bottom of this document.


  8. Click on OK and click Finish on your blueprint.  Go ahead and publish and make available the blueprint for use.


Deploy your Blueprint

  1. Locate your newly created blueprint in your catalog and select Request . In this example, my Blueprint is named F5 RESTipe Demo



  2. On your request screen, you should see a text box to be able to enter your IP Address you want to add as a member to the VIP you will deploy


  3. Enter a valid IP and hit Submit.  Once submitted, wait until the job completes and verify that the VIP, Pool and the member have been added.
    VIP:


    POOL:


    MEMBER:

Additional information

SovLabs F5 Documentation : http://docs.sovlabs.com/latest/vmware-vra7x-plugin/modules/network-and-security/f5/
SovLabs RESTipe Guide : http://docs.sovlabs.com/latest/vmware-vra7x-plugin/framework/sovlabs-restipe-guide/
SovLabs Template Engine Documentation : http://docs.sovlabs.com/latest/vmware-vra7x-plugin/framework/sovlabs-template-engine/basics/intro/

REGEX used for Property  Definition (Very basic, just allows 3numbers in 4 sets.  You can still put an invalid IP in here, but this allows for some barriers)

^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$
Have more questions? Submit a request

0 Comments

Please sign in to leave a comment.