https://www.terraform.io/docs/index.html 可以创建AWS/openstack/Azure等,一个开源的云平台管理工具,假如你用了Azure/openstack等混杂云环境,可以考虑用这个,官方文档非常的简洁。下边例子是创建VPC/subnet/若干个EC2,后端的subnet的ec2通过nat-box上网并且安装puppet/zabbix的yum repo。不多说了,talk is cheap, show me the code.
  
  注意provisioner有个问题:创建EIP和remote-exec, 由于remote-exec需要EIP但terraform会等remote-exec执行完才得到eip,这显然是不可能的,需要depends_on来指定先后顺序和用null_resource, 这这里也有说明。https://github.com/hashicorp/terraform/issues/557
# variables

  1. provider "aws" {
  2.   access_key = "${var.access_key}"
  3.   secret_key = "${var.secret_key}"
  4.   region = "${var.region}"
  5. }

  6. resource "aws_vpc" "demo" {
  7.   cidr_block = "10.0.0.0/16"

  8.   tags {
  9.     Name = "demo"
  10.   }
  11. }

  12. resource "aws_subnet" "public" {
  13.   vpc_id = "${aws_vpc.demo.id}"
  14.   cidr_block = "10.0.10.0/24"

  15.   tags {
  16.     Name = "demo-public"
  17.   }
  18. }

  19. resource "aws_subnet" "private" {
  20.   vpc_id = "${aws_vpc.demo.id}"
  21.   cidr_block = "10.0.80.0/24"

  22.   tags {
  23.     Name = "demo-private"
  24.   }
  25. }

  26. resource "aws_internet_gateway" "default" {
  27.   vpc_id = "${aws_vpc.demo.id}"

  28.   tags {
  29.     Name = "default-IGW"
  30.   }
  31. }

  32. resource "aws_route_table" "internet_access" {
  33.   vpc_id = "${aws_vpc.demo.id}"

  34.   route {
  35.     cidr_block = "0.0.0.0/0"
  36.     gateway_id = "${aws_internet_gateway.default.id}"
  37.   }

  38.   tags {
  39.     Name = "igw"
  40.   }
  41. }

  42. resource "aws_route_table_association" "public" {
  43.   subnet_id = "${aws_subnet.public.id}"
  44.   route_table_id = "${aws_route_table.internet_access.id}"
  45. }

  46. resource "aws_route_table" "private" {
  47.   vpc_id = "${aws_vpc.demo.id}"

  48.   route {
  49.     cidr_block = "0.0.0.0/0"
  50.     instance_id = "${aws_instance.nat.id}"
  51.   }
  52. }

  53. resource "aws_route_table_association" "private" {
  54.   subnet_id = "${aws_subnet.private.id}"
  55.   route_table_id = "${aws_route_table.private.id}"
  56. }

  57. resource "aws_elb" "web" {
  58.   name = "pcf-elb"
  59.   subnets = ["${aws_subnet.private.id}"]
  60.   security_groups = ["${aws_security_group.elb.id}"]

  61.   listener {
  62.     instance_port = 80
  63.     instance_protocol = "http"
  64.     lb_port = 80
  65.     lb_protocol = "http"
  66.   }

  67.   health_check {
  68.     healthy_threshold = 2
  69.     unhealthy_threshold = 2
  70.     timeout = 3
  71.     target = "HTTP:80/"
  72.     interval = 30
  73.   }

  74.   # The instance is registered automatically


  75.   # instances = ["${aws_instance.web.id}"]

  76.   cross_zone_load_balancing = true
  77.   idle_timeout = 400
  78.   connection_draining = true
  79.   connection_draining_timeout = 400
  80.   tags {
  81.     Name = "test-pcf"
  82.   }
  83. }

  84. /*
  85.    resource "aws_lb_cookie_stickiness_policy" "default" {
  86.    name = "lbpolicy"
  87.    load_balancer = "${aws_elb.web.id}"
  88.    lb_port = 80
  89.    cookie_expiration_period = 600
  90.    } */

  91. resource "aws_security_group" "elb" {
  92.   name = "example_elb"
  93.   description = "Nothing more"
  94.   vpc_id = "${aws_vpc.demo.id}"

  95.   # HTTP access from anywhere
  96.   ingress {
  97.     from_port = 80
  98.     to_port = 80
  99.     protocol = "tcp"
  100.     cidr_blocks = ["0.0.0.0/0"]
  101.   }

  102.   # outbound internet access
  103.   egress {
  104.     from_port = 0
  105.     to_port = 0
  106.     protocol = "-1"
  107.     cidr_blocks = ["0.0.0.0/0"]
  108.   }
  109. }

  110. resource "aws_eip" "demo" {
  111.   instance = "${aws_instance.zabbix-server.id}"
  112.   depends_on = ["aws_instance.zabbix-server"]
  113.   vpc = true
  114. }

  115. resource "aws_instance" "zabbix-server" {
  116.   ami = "${lookup(var.aws_opsman_ami,var.region)}"
  117.   instance_type = "m3.medium"
  118.   key_name = "${var.key_name}"
  119.   subnet_id = "${aws_subnet.public.id}"
  120.   vpc_security_group_ids = ["${aws_security_group.all_pass.id}"]

  121.   tags {
  122.     Name = "zabbix-server"
  123.   }

  124. }

  125. resource "null_resource" "server-provisioning" {
  126.   triggers {
  127.     instance = "${aws_instance.zabbix-server.id}"
  128.   }

  129.   connection {
  130.     host = "${aws_eip.demo.public_ip}"
  131.     user = "ec2-user"
  132.     timeout = "5m"
  133.     private_key = "${file("./hfu.pem")}"
  134.   }


  135.   provisioner "file" {
  136.     # puppet.conf
  137.     content = <<EOF
  138. [main]
  139. certname = ${var.puppetname}
  140. server = ${var.puppetname}
  141. strict_variables = true
  142. masterport = 8140

  143. [master]
  144. node_terminus = exec
  145. external_nodes = /etc/puppetlabs/enc.rb
  146. autosign = true
  147. vardir = /opt/puppetlabs/server/data/puppetserver
  148. logdir = /var/log/puppetlabs/puppetserver
  149. rundir = /var/run/puppetlabs/puppetserver
  150. pidfile = /var/run/puppetlabs/puppetserver/puppetserver.pid
  151. codedir = /etc/puppetlabs/code

  152. [agent]
  153. puppetmaster = ${var.puppetname}
  154. environment = production
  155. runinterval = 1h

  156. EOF

  157.     destination = "/tmp/puppetmaster"
  158.   }

  159.   provisioner "file" {

  160.     # external node classifier
  161.     content =<<EOF
  162. #!/usr/bin/env ruby

  163. require 'yaml'
  164. node = ARGV[0]

  165. # agent FQDN : ${app}.${env}.${Location || IDC}, e.g web01.prod.bjcnc

  166. if node =~ /(^[a-zA-Z]+)(\d+)\.(\S+)\.(\S+)$/

  167.     hostname = $1
  168.     env = $3
  169.     config = YAML.load_file('defaults.yaml')[env]
  170.     print YAML.dump(config.fetch(hostname))
  171.     exit 0

  172. else

  173.     config = YAML.load_file('defaults.yaml')
  174.     print YAML.dump(config.fetch('base'))
  175.     exit 0

  176. end

  177. EOF

  178.     destination = "/tmp/enc"

  179.   }

  180.   provisioner "remote-exec" {
  181.     inline = [
  182.       # reserve EC2 for future usage.
  183.       "sudo echo ${join(",", aws_instance.trial.*.private_ip)} >>/tmp/ec2_list" ,
  184.       "sudo bash -c 'echo puppet \"${aws_instance.zabbix-server.private_ip} puppet.xxx.com\" >> /etc/hosts'" ,
  185.       "sudo rpm -ivh http://repo.zabbix.com/zabbix/3.0/rhel/7/x86_64/zabbix-release-3.0-1.el7.noarch.rpm",
  186.       "sudo rpm -Uvh https://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm",
  187.       "sudo sed -i 's/enabled=0/enabled=1/g' /etc/yum.repos.d/redhat-rhui.repo",
  188.       "sudo yum -y install ruby mariadb mariadb-server vim-enhanced telnet zabbix-server-mysql zabbix-web-mysql puppetserver 1 >/dev/null ",

  189.       "sudo mv /tmp/puppetmaster /etc/puppetlabs/puppet/puppet.conf",
  190.       "sudo mv /tmp/enc /etc/puppetlabs/enc.rb",
  191.       "sudo chmod a+x /etc/puppetlabs/enc.rb",
  192.       # start puppetmaster
  193. # "sudo systemctl start puppetserver",
  194.     ]
  195.   }
  196. }

  197. resource "aws_instance" "trial" {
  198.   count = 1
  199.   ami = "${var.other_ami}"
  200.   instance_type = "t2.micro"
  201.   key_name = "${var.key_name}"
  202.   subnet_id = "${aws_subnet.private.id}"
  203.   vpc_security_group_ids = ["${aws_security_group.all_pass.id}"]

  204.   tags {
  205.     # generate sequential tag , Name = "${format("web-%03d", count.index)}"
  206.     Name = "${lookup(var.instance_tag, count.index)}"

  207.   }

  208.   provisioner "local-exec" {
  209.     /*
  210.     To reference attributes of your own resource, the syntax is self.ATTRIBUTE. For example ${self.private_ip_address} will interpolate that resource's private IP address. Note that this is only allowed/valid within provisioners.

  211.     */
  212.     command = "echo ${self.private_ip} > private_ips"
  213.   }

  214.   connection {
  215.     host = "${self.private_ip}"
  216.     bastion_host = "${aws_eip.demo.public_ip}"
  217.     user = "ec2-user"
  218.     timeout = "5m"
  219.     bastion_port = "22"
  220.     bastion_private_key = "${file("./hfu.pem")}"
  221.   }

  222.   provisioner "file" {
  223.     # puppet.conf of agent
  224.     content = <
  225. [agent]
  226. puppetmaster = ${var.puppetname}
  227. environment = production
  228. runinterval = 1h
  229. EOF

  230.     destination = "/tmp/init_env.sh"
  231.   }

  232.   provisioner "remote-exec" {
  233.     inline = [
  234.       "sudo bash -c 'echo puppet \"${aws_instance.zabbix-server.private_ip} puppet.xxx.com\" >> /etc/hosts


  1. variable "access_key" {
  2.   default = "xxxx"
  3. }

  4. variable "secret_key" {
  5.   default = "xxxxxxx"
  6. }

  7. variable "key_name" {
  8.   default = "hfu"
  9. }

  10. variable "region" {
  11.   default = "cn-north-1"
  12. }

  13. variable "aws_opsman_ami" {
  14.   type = "map"

  15.   default = {
  16.     cn-north-1 = "ami-52d1183f"
  17.     us-east-1 = "ami-xx"
  18.   }
  19. }

  20. variable "other_ami" {
  21.     default = "ami-52d1183f"
  22. }

  23. variable "aws_nat_ami" {
  24.   type = "map"

  25.   default = {
  26.     cn-north-1 = "ami-1848da21"
  27.     us-east-1 = "ami-xxx"
  28.   }
  29. }

  30. variable "puppetname" {

  31.     default = "puppet.xxx.com"
  32. }
  33. variable "instance_tag" {
  34.   default = {
  35.     "0" = "client"
  36.   }
  37. }

需要设置的变量都在variables.tf里,然后直接terrafrom apply, 详细的输出可以告诉你哪些资源被创建了。
安装完毕,puppetmaster,zabbix-server以及agent设置好了。

至于层次结构跟cloudformation差不多,只不过它不是json文件,这个要注意。其他什么input/output/provider/provisioner也大同小异不多说了。

09-04 15:51