docs: function (options, mCb) {
  		//https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ELB.html#describeLoadBalancers-property
  		
  		//https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ELB.html#createLoadBalancerListeners-property
  		//https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ELB.html#deleteLoadBalancerListeners-property
  		
  		//https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ELB.html#attachLoadBalancerToSubnets-property
  		//https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ELB.html#detachLoadBalancerFromSubnets-property
  		
  		//https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ELB.html#applySecurityGroupsToLoadBalancer-property
  		
  		//https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ELB.html#configureHealthCheck-property
  		const aws = options.infra.api;
  		getElbMethod(options.params.elbType || 'classic', 'update', (elbResponse) => {
  			const elb = getConnector({
  				api: elbResponse.api,
  				region: options.params.region,
  				keyId: aws.keyId,
  				secretAccessKey: aws.secretAccessKey
  			});
  			elb[elbResponse.method]({LoadBalancerNames: [options.params.name]}, function (err, response) {
  				if (err) {
  					return mCb(err);
  				}
  				if (response && response.LoadBalancerDescriptions && response.LoadBalancerDescriptions.length > 0) {
  					let lb = response.LoadBalancerDescriptions[0];
  					
  					async.parallel({
  						listeners: (callback) => {
  							let addListeners = {
  								LoadBalancerName: lb.LoadBalancerName,
  								Listeners: [],
  							}, deleteListeners = {
  								LoadBalancerName: lb.LoadBalancerName,
  								LoadBalancerPorts: [],
  							};
  							let certificates = [];
  							async.parallel({
  								//get ports needed to be added
  								add: (call) => {
  									async.each(options.params.rules, (rule, ruleCB) => {
  										let found = false;
  										async.each(lb.ListenerDescriptions, (port, listenersCB) => {
  											if (rule.frontendPort === port.Listener.LoadBalancerPort &&
  												rule.backendPort === port.Listener.InstancePort
  												&& rule.frontendProtocol.toUpperCase() === port.Listener.Protocol.toUpperCase()
  												&& rule.backendProtocol.toUpperCase() === port.Listener.InstanceProtocol.toUpperCase()) {
  												found = true;
  												if (rule.certificate !== port.Listener.SSLCertificateId) {
  													certificates.push({
  														LoadBalancerName: options.params.name, /* required */
  														LoadBalancerPort: port.Listener.LoadBalancerPort, /* required */
  														SSLCertificateId: rule.certificate /* required */
  													});
  												}
  											}
  											listenersCB();
  										}, () => {
  											if (!found) {
  												//add
  												let listenerObj = {
  													"Protocol": rule.frontendProtocol.toUpperCase(),
  													"LoadBalancerPort": rule.frontendPort,
  													"InstanceProtocol": rule.backendProtocol.toUpperCase(),
  													"InstancePort": rule.backendPort
  												};
  												
  												if (rule.certificate) {
  													listenerObj.SSLCertificateId = rule.certificate;
  												}
  												addListeners.Listeners.push(listenerObj);
  											}
  											ruleCB();
  										});
  									}, call);
  								},
  								//get ports needed to be deleted
  								delete: (call) => {
  									async.each(lb.ListenerDescriptions, (port, listenersCB) => {
  										let found = false;
  										async.each(options.params.rules, (rule, ruleCB) => {
  											if (rule.frontendPort === port.Listener.LoadBalancerPort &&
  												rule.backendPort === port.Listener.InstancePort
  												&& rule.frontendProtocol.toUpperCase() === port.Listener.Protocol.toUpperCase()
  												&& rule.backendProtocol.toUpperCase() === port.Listener.InstanceProtocol.toUpperCase()) {
  												found = true;
  											}
  											ruleCB();
  										}, () => {
  											if (!found) {
  												//delete
  												deleteListeners.LoadBalancerPorts.push(port.Listener.LoadBalancerPort);
  											}
  											listenersCB();
  										});
  									}, call);
  								}
  							}, () => {
  								async.series({
  									updateCertificate: (call) => {
  										if (certificates.length > 0) {
  											async.each(certificates, (certificate, miniCb) => {
  												elb.setLoadBalancerListenerSSLCertificate(certificate, miniCb)
  											}, call);
  										}
  										else {
  											return call();
  										}
  									},
  									delete: (call) => {
  										if (deleteListeners.LoadBalancerPorts.length === 0) {
  											return call();
  										}
  										elb.deleteLoadBalancerListeners(deleteListeners, call)
  									},
  									add: (call) => {
  										if (addListeners.Listeners.length === 0) {
  											return call();
  										}
  										elb.createLoadBalancerListeners(addListeners, call)
  									}
  								}, callback);
  							});
  						},
  						subnets: (callback) => {
  							let subnets = [];
  							options.params.subnets.forEach((oneSubnet) => {
  								subnets.push(oneSubnet);
  							});
  							let subnetsToDelete = _.differenceBy(lb.Subnets, subnets);
  							let subnetsToAdd = _.differenceBy(subnets, lb.Subnets);
  							async.series({
  								delete: (call) => {
  									if (subnetsToDelete.length === 0) {
  										return call();
  									}
  									elb.detachLoadBalancerFromSubnets({
  										LoadBalancerName: lb.LoadBalancerName,
  										Subnets: subnetsToDelete
  									}, call)
  								},
  								add: (call) => {
  									if (subnetsToAdd.length === 0) {
  										return call();
  									}
  									elb.attachLoadBalancerToSubnets({
  										LoadBalancerName: lb.LoadBalancerName,
  										Subnets: subnetsToAdd
  									}, call)
  								}
  							}, callback);
  						},
  						securityGroups: (callback) => {
  							if (_.differenceBy(lb.SecurityGroups, options.params.securityGroups).length > 0 || _.differenceBy(options.params.securityGroups, lb.SecurityGroups).length > 0) {
  								elb.applySecurityGroupsToLoadBalancer(
  									{
  										LoadBalancerName: lb.LoadBalancerName,
  										SecurityGroups: options.params.securityGroups
  									}, callback);
  							}
  							else {
  								return callback();
  							}
  						},
  						healthCheck: (callback) => {
  							if (options.params.healthProbe) {
  								let healthProbePath = "";
  								if (options.params.healthProbe.healthProbeProtocol && options.params.healthProbe.healthProbePort) {
  									healthProbePath += options.params.healthProbe.healthProbeProtocol.toUpperCase();
  									healthProbePath += ":" + options.params.healthProbe.healthProbePort;
  									if (options.params.healthProbe.healthProbeProtocol.toUpperCase() === "HTTP" || options.params.healthProbe.healthProbeProtocol.toUpperCase() === "HTTPS") {
  										if (options.params.healthProbe.healthProbePath) {
  											healthProbePath += options.params.healthProbe.healthProbePath;
  										}
  										else {
  											return callback(new Error("A path must be specified for protocols HTTP/HTTPS, when creating a LoadBalancer Health Check."));
  										}
  									}
  								}
  								if (healthProbePath === "") {
  									return callback(new Error("A Health Check Path must be specified!"));
  								}
  								if (healthProbePath !== lb.HealthCheck.Target
  									|| options.params.healthProbe.healthProbeInterval !== lb.HealthCheck.Interval
  									|| options.params.healthProbe.healthProbeTimeout !== lb.HealthCheck.Timeout
  									|| options.params.healthProbe.maxFailureAttempts !== lb.HealthCheck.UnhealthyThreshold
  									|| options.params.healthProbe.maxSuccessAttempts !== lb.HealthCheck.HealthyThreshold) {
  									elb.configureHealthCheck({
  										LoadBalancerName: lb.LoadBalancerName,
  										HealthCheck: {
  											Target: healthProbePath,
  											Interval: options.params.healthProbe.healthProbeInterval,
  											Timeout: options.params.healthProbe.healthProbeTimeout,
  											UnhealthyThreshold: options.params.healthProbe.maxFailureAttempts,
  											HealthyThreshold: options.params.healthProbe.maxSuccessAttempts,
  										}
  									}, callback);
  								}
  								else {
  									return callback();
  								}
  							}
  							else {
  								return callback();
  							}
  						}
  					}, mCb);
  				}
  				else {
  					return mCb(new Error("Load balancer not found!"));
  				}
  			});
  		});
  	}