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
- In your catalog, search for RESTipe. Once you locate Manage restipe Configuration - SovLabs Modules click on Request
- 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
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"
- Click on Submit to save the new RESTipe
- This will now create a new deployment for the RESTipe. Once this is completed, the RESTipe is ready to use.
Setting up your blueprint
- 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.
- 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)
- Other settings I have on my VIP are as pictured below.
General Tab: - My configuration under the Pool tab
- Once you have all your settings for your F5 ready and have selected the new RESTipe, click on the gear icon at the top
- Click on the properties tab and on custom properties
- 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. - Click on OK and click Finish on your blueprint. Go ahead and publish and make available the blueprint for use.
Deploy your Blueprint
- Locate your newly created blueprint in your catalog and select Request . In this example, my Blueprint is named F5 RESTipe Demo
- 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
- 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}$
0 Comments