diff --git a/files/new_wrapper/create_challenge.py b/files/new_wrapper/create_challenge.py
new file mode 100644
index 0000000000000000000000000000000000000000..b205b09b077db70076929baa9b25e2be9c0fa859
--- /dev/null
+++ b/files/new_wrapper/create_challenge.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+""" Add Acme challenges to Infoblox """
+import os
+import time
+import configparser
+import requests
+
+
+def create_acme(iblox_domain, acme_token, iblox_user, iblox_pw):
+    """ upload txt record """
+    post_req = requests.post(
+        'https://infoblox.geant.org/wapi/v2.6.1/record:txt',
+        auth=(iblox_user, iblox_pw),
+        data={
+            'name': '_acme-challenge.{}'.format(iblox_domain),
+            'text': acme_token,
+            'ttl': '60',
+            "view": "External"
+        }
+    )
+    return post_req.status_code
+
+
+# Here we Go.
+if __name__ == "__main__":
+
+    CONFIG = configparser.RawConfigParser()
+    CONFIG.read_file(open('/root/.geant_acme.ini'))
+    IBLOX_PASS = CONFIG.get('geant_acme', 'iblox_pass')
+    IBLOX_USER = CONFIG.get('geant_acme', 'iblox_user')
+
+    ARGS = os.sys.argv
+    _ = ARGS.pop(0)
+
+    for host in ARGS:
+        if ARGS.index(host) % 2 == 0:
+            token = ARGS.index(host)+1
+            http_code = create_acme(host, ARGS[token], IBLOX_USER, IBLOX_PASS)
+            if http_code != 201:
+                print('could not create {} for {}'.format(ARGS[token], host))
+                os.sys.exit(1)
+
+    time.sleep(10)
diff --git a/files/new_wrapper/geant_acme.py b/files/new_wrapper/geant_acme.py
new file mode 100644
index 0000000000000000000000000000000000000000..e6031de18feefa6f8883ace0d941f17dbff8f944
--- /dev/null
+++ b/files/new_wrapper/geant_acme.py
@@ -0,0 +1,194 @@
+#!/usr/bin/env python3
+"""Geant Acme
+
+Usage:
+  geant_acme.py --domain <DOMAIN>... (--client <CLIENT>... | --wildcard) [--verbose]
+  geant_acme.py (-h | --help)
+
+Options:
+  -h --help                   Show this screen.
+  -c CLIENT --client=CLIENT   Client
+  -d DOMAIN --domain=DOMAIN   Domain
+  -w --wildcard               Use wildcard
+  -v --verbose                Print out messages
+"""
+import os
+import subprocess as sp
+import configparser
+from docopt import docopt
+import requests
+
+BASE_URL = 'https://infoblox.geant.org/wapi/v2.6.1'
+
+
+def os_exit(verbose=None):
+    """ exit """
+    if verbose:
+        print('+' + 72*'-' + '+')
+    os.sys.exit()
+
+
+def get_reference(iblox_domain, iblox_user, iblox_pw, verbose=None):
+    """ grab reference for txt object """
+    ref_obj = requests.get(
+        '{}/record:txt?name=_acme-challenge.{}&_return_as_object=1'.format(
+            BASE_URL, iblox_domain),
+        auth=(iblox_user, iblox_pw)
+    )
+
+    if ref_obj.status_code != 200:
+        return 'error'
+
+    try:
+        ref = ref_obj.json()['result'][0]['_ref']
+    except IndexError:
+        ref = None
+
+    if verbose:
+        if ref:
+            print('got reference object: {}'.format(ref))
+        else:
+            print('{} empty reference object: no challenge to delete'.format(
+                iblox_domain))
+        print('+' + 72*'-' + '+')
+
+    return ref
+
+
+def delete_challenge(object_reference, iblox_user, iblox_pw, verbose=None):
+    """ delete txt record """
+    del_req = requests.delete(
+        '{}/{}'.format(BASE_URL, object_reference),
+        auth=(iblox_user, iblox_pw)
+    )
+    if verbose:
+        print('delete challenge - http code (200 expected): {}'.format(del_req.status_code))
+        print('+' + 72*'-' + '+')
+
+    return del_req.status_code
+
+
+def create_challenge(iblox_domain, acme_token, iblox_user, iblox_pw, verbose=None):
+    """ upload txt record """
+    if verbose:
+        print('+' + 72*'-' + '+')
+        print('creating challenge _acme-challenge.{}'.format(iblox_domain))
+
+    post_req = requests.post(
+        '{}/record:txt'.format(BASE_URL),
+        auth=(iblox_user, iblox_pw),
+        data={
+            'name': '_acme-challenge.{}'.format(iblox_domain),
+            'text': acme_token,
+            'ttl': '30',
+            "view": "External"
+        }
+    )
+    if verbose:
+        print('create challenge - http code (201 expected): {} {}'.format(
+            post_req.status_code, post_req.reason))
+        print('+' + 72*'-' + '+')
+
+    return post_req.status_code
+
+
+def run_certbot(cbot_domain, wild_card=None, verbose=None):
+    """ get certificate from letsencrypt """
+    if wild_card:
+        domain_list = '*.{}'.format(cbot_domain)
+    else:
+        domain_list = ' -d '.join(list(reversed(cbot_domain)))
+
+    cbot_cmd = '/usr/local/bin/certbot certonly -c /etc/letsencrypt/cli.ini' \
+               + ' --manual-auth-hook=/root/bin/create_challenge.py' \
+               + ' --cert-name {} -d {}'.format(cbot_domain[0], domain_list)
+
+    if verbose:
+        print('+' + 72*'-' + '+')
+        print('running: {}'.format(cbot_cmd))
+
+    cbot_child = sp.Popen(cbot_cmd, stdout=sp.PIPE, stderr=sp.PIPE, shell=True)
+    cbot_out, cbot_err = cbot_child.communicate()
+    if cbot_child.returncode != 0:
+        msg = "error running certbot: {}".format(cbot_err.decode("utf-8"))
+    else:
+        decoded_msg = cbot_out.decode("utf-8")
+        msg = decoded_msg[:decoded_msg.rfind('\n')]
+
+    if verbose:
+        print('\n{}'.format(msg))
+
+    return msg
+
+
+# Here we Go.
+if __name__ == "__main__":
+
+    ARGS = docopt(__doc__)
+    DOMAIN = ARGS['--domain']
+    CLIENTS = ARGS['--client']
+    WILDCARD = ARGS['--wildcard']
+    VERBOSE = ARGS['--verbose']
+
+    CONFIG = configparser.RawConfigParser()
+    CONFIG.read_file(open('/root/.geant_acme.ini'))
+    IBLOX_PASS = CONFIG.get('geant_acme', 'iblox_pass')
+    IBLOX_USER = CONFIG.get('geant_acme', 'iblox_user')
+
+    if WILDCARD:
+        REAL_DOMAIN = 'domain *.{}'.format(DOMAIN[0])
+    else:
+        REAL_DOMAIN = 'host {}'.format(DOMAIN[0])
+
+    if VERBOSE:
+        print('+' + 72*'-' + '+')
+
+    # Maybe there is an Acme challenge left over. We try to delete it first.
+    for domain_item in DOMAIN:
+        REF_OBJ = get_reference(domain_item, IBLOX_USER, IBLOX_PASS, VERBOSE)
+        if REF_OBJ == 'error':
+            print('error retrieving reference object')
+            os_exit(VERBOSE)
+        if REF_OBJ:
+            DEL_STATUS = delete_challenge(
+                REF_OBJ, IBLOX_USER, IBLOX_PASS, VERBOSE)
+            if DEL_STATUS != 200:
+                print('{}: {} error deleting challenge on Infoblox'.format(
+                    domain_item, DEL_STATUS))
+                os_exit(VERBOSE)
+
+    # run certbot
+    run_certbot(DOMAIN, WILDCARD, VERBOSE)
+
+    if VERBOSE:
+        print('+' + 72*'-' + '+')
+
+    # remove the Acme challenges from Infoblox
+    for domain_item in DOMAIN:
+        REF_OBJ = get_reference(domain_item, IBLOX_USER, IBLOX_PASS, VERBOSE)
+        if REF_OBJ == 'error':
+            print('error retrieving reference object')
+            os_exit(VERBOSE)
+        if REF_OBJ:
+            DEL_STATUS = delete_challenge(
+                REF_OBJ, IBLOX_USER, IBLOX_PASS, VERBOSE)
+            if DEL_STATUS != 200:
+                print('{}: {} error deleting challenge on Infoblox'.format(
+                    domain_item, DEL_STATUS))
+                os_exit(VERBOSE)
+
+    # if we are here, everything went fine and we can upload the certificates
+    if WILDCARD:
+        UPLOADER = '/root/bin/upload_wildcards.py'
+        if VERBOSE:
+            UPLOADER += ' -v'
+        os.system(UPLOADER)
+    else:
+        for client in CLIENTS:
+            UPLOADER = '/root/bin/geant_acme_uploader.py -d {} -c {}'.format(
+                DOMAIN[0], client)
+            if VERBOSE:
+                UPLOADER += ' -v'
+            os.system(UPLOADER)
+
+    os_exit(VERBOSE)