Chef-provisioning and multiple regions

I’m just getting started with chef-provisioning-aws and wasn’t sure how to handle the following:

  1. Allocate an elastic IP in eu-central-1
  2. Append that IP to a security group in us-west-1
  3. Spin up a machine in eu-central-1 with that elastic IP associated to it

I think part of what I’m getting hung up on is how to share the value of that elastic IP between the two pieces of code that need it. Do I need to stick the IP address in a data bag and then reference that data bag?

The other part is how to associate the IP to the machine during initial provisioning, it seems like the example associates the elastic IP after initial provisioning. But in order for initial provisioning to succeed, I need it to already be added to the security group.

Something like:

with_driver "aws::eu-central-1" do
    some_ip = aws_eip_address "some-machine-ip"
end

with_driver "aws::us-west-1" do
    aws_security_group "some-group" do
    vpc "some-vpc"
    inbound_rules ({
        "how should I reference some_ip.aws_object.public_ip here?" => [443]
    })
end

with_driver "aws::eu-central-1" do
    machine "some-machine" do
        role "my_role"
        chef_environment "prod"
        # how do i specify the elastic IP to use here?
    end
end

For your second question, one approach would be to bring the machine up but not converge it (either :ready or :setup actions), associate the elastic IP address, then converge it.

For the first question, perhaps something like this (not tested):
aws_security_group “some-group” do
vpc "some-vpc"
inbound_rules lazy { “#{some_ip.aws_object.public_ip}/32” => [443] }
end

If you need to pass information between recipes, then you might need to use run_state or look up the resource as needed from the resource collection.

  1. Allocate an elastic IP in eu-central-1
    You really want to have a VPC with a private and public subnet. set everything in that public subnet to get an EIP automatically.
    public_subnet = aws_subnet "eu-west-1-public" do vpc 'vpc-eu-west' cidr_block "10.0.0.0/23" availability_zone "eu-west-1a" map_public_ip_on_launch true end
  2. Append that IP to a security group in us-west-1
    You can just search for it like a normal chef node
    webserver = search(:node, "name:eu-west-1d-webserver101").first webserver_ipaddress = webserver['ec2']['public_ipv4']
  3. Spin up a machine in eu-central-1 with that elastic IP associated to it
    repeat step 1

Thanks for the feedback! After trying a bunch of different options and bugging some helpful folks on gitter, here’s what I ended up with:

# keep track of eips for later
eip_by_machine = {}
with_driver "aws::eu-central-1" do
    # get machine in ready state, don't converge yet though
    machine "some-machine" do
        action :setup
    end

    eip_by_machine["some-machine"] = aws_eip_address "some-machine" do
        machine "some-machine"
    end
end

with_driver "aws::us-west-1" do
    # now add the eips to the security group
    rules = {}
    eip_by_machine.each do |machine_name, eip|
        rules["#{eip.aws_object.public_ip}/32"] = [443]
    end
    aws_security_group "my_sg" do
        vpc "my_vpc"
        inbound_rules(lazy { rules })
    end
end

# now we can proceed with converge
with_driver "aws::eu-central-1" do
    machine "some-machine" do
        action :converge_only
    end
end

Maybe I spoke too soon. After adding “some-other-machine” to this provisioning script, it’s bailing out during compile phase with:

INFO: HTTP Request Returned 404 Object Not Found: Cannot load data bag item some-other-machine for data bag aws_eip_address
...
ERROR: undefined method `public_ip' for nil:NilClass

Stack trace indicates it’s originating from the rules["#{eip.aws_object.public_ip}/32"] = [443] line.

Could you provide a full example of how you added the second machine?

–Jp Robinson

I think I’ve got it working now. Had to stick the eip_by_machine loop in a ruby block:

with_driver "aws::us-west-1" do
    # now add the eips to the security group
    rules = {}
    ruby_block do
        block do
            eip_by_machine.each do |machine_name, eip|
                rules["#{eip.aws_object.public_ip}/32"] = [443]
            end
        end
    end
    aws_security_group "my_sg" do
        vpc "my_vpc"
        inbound_rules(lazy { rules })
    end
end