def setup_demo(current_ip_address, ami_image_id, key_file_name):
    """
    Sets up prerequisites and creates instances used in the demo.
    When this function returns, the instances are running and ready to use.

    :param current_ip_address: The public IP address of the current computer.
    :param ami_image_id: The Amazon Machine Image (AMI) that is used to create the
                         instances for the demo.
    :param key_file_name: The name of a local file that contains the private key
                          that is used to connect to the instances using SSH.
    :return: The newly created instances, security groups, key pair, and
             Elastic IP object.
    """
    key_pair = ec2_setup.create_key_pair(make_unique_name('key'),
                                         key_file_name)
    print(
        f"Created a key pair {key_pair.key_name} and saved the private key to "
        f"{key_file_name}")

    ssh_sec_group = ec2_setup.setup_security_group(
        make_unique_name('ssh-group'),
        f'Demo group that allows SSH from {current_ip_address}.',
        current_ip_address)
    print(f"Created security group {ssh_sec_group.group_name} that allows SSH "
          f"access from {current_ip_address}.")

    no_ssh_sec_group = ec2_setup.setup_security_group(
        make_unique_name('no-ssh-group'),
        'Demo group that does not allow SSH.')
    print(
        f"Created security group {no_ssh_sec_group.group_name} that does not allow "
        f"SSH access.")

    ssh_instance = ec2_setup.create_instance(ami_image_id, 't2.micro',
                                             key_pair.key_name,
                                             [ssh_sec_group.group_name])
    print(f"Created instance {ssh_instance.instance_id} that can be accessed "
          f"through SSH.")

    no_ssh_instance = ec2_setup.create_instance(ami_image_id, 't2.micro',
                                                key_pair.key_name,
                                                [no_ssh_sec_group.group_name])
    print(
        f"Created instance {no_ssh_instance.instance_id} that cannot be accessed "
        f"through SSH.")

    elastic_ip = ec2_instance_management.allocate_elastic_ip()
    print(f"Allocated static Elastic IP address: {elastic_ip.public_ip}.")

    print(f"Waiting for instances to start...")
    ssh_instance.wait_until_running()
    print(f"Instance {ssh_instance.instance_id} is running.")

    no_ssh_instance.wait_until_running()
    print(f"Instance {no_ssh_instance.instance_id} is running.")

    return (ssh_instance, no_ssh_instance), (no_ssh_sec_group, ssh_sec_group),\
        key_pair, elastic_ip
def test_setup_security_group(
        make_stubber, make_unique_name, has_vpcs, include_ssh, error_func, error_code):
    ec2_stubber = make_stubber(ec2_setup.ec2.meta.client)
    vpc_filter = {'Name': 'isDefault', 'Values': ['true']}
    if has_vpcs:
        vpc_id = make_unique_name('vpc-')
        vpcs = {vpc_id: True}
    else:
        vpc_id = None
        vpcs = {}
    group_name = make_unique_name('group')
    group_description = "Just a test group."
    group_id = 'test-group-id'
    test_ip = '1.2.3.4' if include_ssh else None
    ip_permissions = [
        {'protocol': 'tcp', 'port': 80, 'ip_ranges': [{'CidrIp': '0.0.0.0/0'}]},
        {'protocol': 'tcp', 'port': 443, 'ip_ranges': [{'CidrIp': '0.0.0.0/0'}]}]
    if include_ssh:
        ip_permissions.append(
            {'protocol': 'tcp', 'port': 22, 'ip_ranges': [{'CidrIp': f'{test_ip}/32'}]})

    with ec2_stubber.conditional_stubs(error_func is not None, error_func):
        ec2_stubber.stub_describe_vpcs(
            vpcs, vpc_filters=[vpc_filter],
            error_code=error_code if error_func == 'describe_vpcs' else None)
        ec2_stubber.stub_create_security_group(
            group_name, group_description, vpc_id, group_id,
            error_code=error_code if error_func == 'create_security_group' else None)
        ec2_stubber.stub_authorize_security_group_ingress(
            group_id, ip_permissions,
            error_code=error_code if error_func == 'authorize_security_group_ingress'
            else None)

    if error_code is None:
        if has_vpcs:
            got_group = ec2_setup.setup_security_group(
                group_name, group_description, test_ip)
            assert got_group.id == group_id
        else:
            with pytest.raises(IndexError):
                ec2_setup.setup_security_group(
                    group_name, group_description, test_ip)
    else:
        with pytest.raises(ClientError) as exc_info:
            ec2_setup.setup_security_group(
                group_name, group_description, test_ip)
        assert exc_info.value.response['Error']['Code'] == error_code