diff --git a/inventory/host_vars/209.132.184.111 b/inventory/host_vars/209.132.184.111
new file mode 100644
index 0000000000..48de62649c
--- /dev/null
+++ b/inventory/host_vars/209.132.184.111
@@ -0,0 +1,11 @@
+---
+instance_type: m1.xlarge
+image: emi-3F1C4010
+volumes:[ ]
+keypair: admin
+security_group: webserver
+zone: fedoracloud
+hostbase: jenkins-master-
+public_ip: 209.132.184.111
+root_auth_users: [ pingou, skvidal ]
+description: jenkins master instance
diff --git a/inventory/inventory b/inventory/inventory
index b997a8f225..8ca28d31b2 100644
--- a/inventory/inventory
+++ b/inventory/inventory
@@ -292,3 +292,7 @@ virthost12.phx2.fedoraproject.org
virthost13.phx2.fedoraproject.org
virthost-comm01.qa.fedoraproject.org
+
+[persistent-cloud]
+209.132.184.111
+209.132.184.112
diff --git a/library/ec2_create b/library/ec2_create
new file mode 100755
index 0000000000..e15ec6450b
--- /dev/null
+++ b/library/ec2_create
@@ -0,0 +1,187 @@
+#!/usr/bin/python -tt
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+
+DOCUMENTATION = '''
+---
+module: ec2_create
+short_description: create an instance in ec2, return instanceid
+description:
+ - creates ec2 instances and optionally waits for it to be 'running'
+version_added: "0.9"
+options:
+ keypair:
+ description:
+ - keypair to use on the instance
+ required: true
+ default: null
+ aliases: []
+ group:
+ description:
+ - security group to use on the instance
+ required: false
+ default: 'default'
+ aliases: []
+ instance_type:
+ description:
+ - instance type to use for the instance
+ required: true
+ default: null
+ aliases: []
+ image:
+ description:
+ - emi (or ami) to use for the instance
+ required: true
+ default: null
+ aliases: []
+ kernel:
+ description:
+ - kernel eki to use for the instance
+ required: false
+ default: null
+ aliases: []
+ ramdisk:
+ description:
+ - ramdisk eri to use for the instance
+ required: false
+ default: null
+ aliases: []
+ wait:
+ description:
+ - wait for the instance to be in state 'running' before returning
+ required: False
+ default: False
+ aliases: []
+ ec2_url:
+ description:
+ - url to use to connect to ec2 or your cloud
+ required: False
+ default: null
+ aliases: []
+ ec2_secret_key:
+ description:
+ - ec2 secret key
+ required: False
+ default: null
+ aliases: []
+ ec2_access_key:
+ description:
+ - ec2 access key
+ required: False
+ default: null
+ aliases: []
+
+examples:
+ - code: "local_action: ec2_create keypair=admin instance_type=m1.large image=emi-40603AD1 wait=true group=webserver"
+ description: "Examples from Ansible Playbooks"
+ - code:
+requirements: [ "euca2ools" ]
+author: Seth Vidal
+
+'''
+
+import euca2ools.commands.euca.runinstances
+import time
+
+def _run(cmd):
+ # returns (rc, stdout, stderr) from shell command
+ process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, shell=True)
+ stdout, stderr = process.communicate()
+ return (process.returncode, stdout, stderr)
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec = dict(
+ keypair = dict(required=True),
+ group = dict(default='default'),
+ instance_type = dict(aliases=['type']),
+ image = dict(required=True),
+ kernel = dict(),
+ #count = dict(default='1'), # maybe someday
+ ramdisk = dict(),
+ wait = dict(choices=BOOLEANS, default=False),
+ ec2_url = dict(aliases=['EC2_URL']),
+ ec2_secret_key = dict(aliases=['EC2_SECRET_KEY']),
+ ec2_access_key = dict(aliases=['EC2_ACCESS_KEY']),
+ )
+ )
+
+ keypair = module.params.get('keypair')
+ group = module.params.get('group')
+ instance_type = module.params.get('instance_type')
+ image = module.params.get('image')
+ #count = module.params.get('count')
+ kernel = module.params.get('kernel')
+ ramdisk = module.params.get('ramdisk')
+ wait = module.params.get('wait')
+ ec2_url = module.params.get('ec2_url')
+ ec2_secret_key = module.params.get('ec2_secret_key')
+ ec2_access_key = module.params.get('ec2_access_key')
+
+ if ec2_url:
+ os.environ['EC2_URL'] = ec2_url
+ if ec2_secret_key:
+ os.environ['EC2_SECRET_KEY'] = ec2_secret_key
+ if ec2_access_key:
+ os.environ['EC2_ACCESS_KEY'] = ec2_access_key
+
+
+ # yes I recognize how hacky this is - but it is easier than rewriting
+ # all the try/except's myself.
+ sys.argv.append(image)
+ eri = euca2ools.commands.euca.runinstances.RunInstances()
+ conn = eri.make_connection()
+ res = eri.make_request_cli(conn, 'run_instances',
+ image_id=image,
+ min_count=1,
+ max_count=1,
+ key_name=keypair,
+ security_groups=group,
+ instance_type=instance_type,
+ kernel_id=kernel,
+ ramdisk_id=ramdisk)
+
+ instids = [ i.id for i in res.instances ]
+
+ res_list = res.connection.get_all_instances(instids)
+ this_res = res_list[0]
+ if wait:
+ # wait here until the instances are up
+ num_running = 0
+ while num_running != len(instids):
+ res_list = res.connection.get_all_instances(instids)
+ this_res = res_list[0]
+ num_running = len([ i for i in this_res.instances if i.state=='running' ])
+ time.sleep(2)
+
+ # there's only one - but maybe one day there could be more
+ instances = []
+ for inst in this_res.instances:
+ d = {
+ 'id': inst.id,
+ 'public_ip': inst.ip_address,
+ }
+ instances.append(d)
+
+ result = {"changed": True,
+ "instances": instances }
+ module.exit_json(**result)
+
+# this is magic, see lib/ansible/module_common.py
+#<>
+
+main()
diff --git a/playbooks/hosts/jenkins.cloud.fedoraproject.org.yml b/playbooks/hosts/jenkins.cloud.fedoraproject.org.yml
new file mode 100644
index 0000000000..7b14bbda6d
--- /dev/null
+++ b/playbooks/hosts/jenkins.cloud.fedoraproject.org.yml
@@ -0,0 +1,37 @@
+- name: check/create instance
+ hosts: 209.132.184.111
+ user: root
+ gather_facts: False
+
+ vars_files:
+ - /srv/web/infra/ansible/vars/global.yml
+ - ${private}/vars.yml
+
+ tasks:
+ - include: $tasks/persistent_cloud.yml
+
+- name: provision instance
+ hosts: 209.132.184.111
+ user: root
+ gather_facts: True
+
+ vars_files:
+ - /srv/web/infra/ansible/vars/global.yml
+ - ${private}/vars.yml
+ - ${vars}/${ansible_distribution}.yml
+
+ tasks:
+ - include: $tasks/cloud_setup_basic.yml
+ - name: install pkgs for jenkins
+ action: yum state=installed pkg=$item
+ with_items:
+ - vim
+ - java-1.7.0-openjdk
+ - subversion
+ - bzr
+ - git
+ tags:
+ - packages
+
+ handlers:
+ - include: $handlers/restart_services.yml
diff --git a/tasks/persistent_cloud.yml b/tasks/persistent_cloud.yml
new file mode 100644
index 0000000000..764b46c7ed
--- /dev/null
+++ b/tasks/persistent_cloud.yml
@@ -0,0 +1,19 @@
+---
+- name: check it out
+ local_action: shell nc -d -z -w 5 ${inventory_hostname} 22 >>/dev/null
+ register: host_is_up
+ ignore_errors: true
+
+- name: spin it up
+ local_action: ec2_create keypair=${keypair} image=${image} type=${instance_type} wait=true
+ register: inst_res
+ only_if: "'${host_is_up.rc}' != '0'"
+
+- name: assign it a special ip
+ local_action: shell euca-associate-address -i ${inst_res.instances[0].id} ${public_ip}
+ only_if: "'${host_is_up.rc}' != '0'"
+
+- name: wait for the reassignation
+ local_action: wait_for host=${public_ip} port=22 delay=20 timeout=300
+ only_if: "'${host_is_up.rc}' != '0'"
+