├── .chef ├── delivery-validator.pem.pub └── keys.MD ├── .gitignore ├── Berksfile ├── Berksfile.lock ├── CHANGELOG.md ├── README.md ├── chef_automate.tpl ├── chef_load.conf.tpl ├── example.tfvars ├── example_secrets.tfvars ├── files ├── chef_load.service └── installer.sh ├── main.tf ├── mount_data_volume ├── security.tf ├── setup.sh ├── testing.tf └── variables.tf /.chef/delivery-validator.pem.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDjXesfQjFFI8s5HgiexeuEQ0JFpMNc01/dC9/llaimceWqKdLk/KDeYCb0DiQNixKCwo6ah+5gRx/s70aPyfxeqmRlSf1WCuPWpPL4oPM6NaLRG0hJYjubVIumrwzsF5rL1JHGLwRmw3zxZ8LaSufIS+rp2r65oGZnyAGXJx0okWatZVcDie00ZhR2qRzfReyE/R50sgDfmujQwmJdKcangj6S6z/mbRhxn535dobOzNi9mT94Gj42FV65D7/q5TLEG7c9A1qEsKNPyFORInX3qE40RcA48S5u9TMxFeu+3IUD+YLH+LgOUzqe7hCUPk6+1qRTf1AF1PAu1x1hO0ED echohack@echohack 2 | -------------------------------------------------------------------------------- /.chef/keys.MD: -------------------------------------------------------------------------------- 1 | # Validator keys 2 | 3 | Place pre-generated delivery validator key in this directory. 4 | 5 | `delivery-validator.pem` - should be a private key 6 | `delivery-validator.pub` - should be generated off the private key using the command: `openssl rsa -in delivery-validator.pem -pubout -out delivery-validator.pub` 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.tfvars 2 | *.tfstate* 3 | 4 | .DS_Store 5 | secrets.tfvars 6 | !example.tfvars 7 | !example_secrets.tfvars 8 | *.keys 9 | *.license 10 | *.creds 11 | vendored-cookbooks/** 12 | .chef/delivery-validator.pem 13 | .chef/delivery-validator.pub 14 | -------------------------------------------------------------------------------- /Berksfile: -------------------------------------------------------------------------------- 1 | source 'https://supermarket.chef.io' 2 | 3 | cookbook 'chef-server-ctl', git: 'https://github.com/stephenlauck/chef-server-ctl.git' 4 | cookbook 'chef_server', path: 'cookbooks/chef_server' 5 | cookbook 'chef_automate', path: 'cookbooks/chef_automate' 6 | -------------------------------------------------------------------------------- /Berksfile.lock: -------------------------------------------------------------------------------- 1 | DEPENDENCIES 2 | chef-server-ctl 3 | git: https://github.com/stephenlauck/chef-server-ctl.git 4 | revision: 55bfa054f9d5cda86f1a96b28b5ccaddbec7a69d 5 | chef_automate 6 | path: cookbooks/chef_automate 7 | chef_server 8 | path: cookbooks/chef_server 9 | 10 | GRAPH 11 | chef-ingredient (0.20.0) 12 | compat_resource (>= 12.10) 13 | chef-server (5.0.1) 14 | chef-ingredient (>= 0.18.0) 15 | chef-server-ctl (0.1.0) 16 | chef-server (>= 0.0.0) 17 | chef_automate (0.1.0) 18 | chef-ingredient (>= 0.0.0) 19 | chef_server (0.1.0) 20 | chef-ingredient (>= 0.0.0) 21 | chef-server-ctl (>= 0.0.0) 22 | compat_resource (12.14.3) 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Deltron CHANGELOG 2 | 3 | ## 1.0.0 (06-28-2017) 4 | 5 | ## Features & Enhancements 6 | 7 | This release focuses on making deltron even easier to use and includes some minor refactors. 8 | 9 | - Pin to Terraform 0.9.9. 10 | - Use the built in ~/.aws/credentials file by default. You can still set a profile using ${var.aws_profile} 11 | - Generate a 4 byte random id that we add to tags in case you want to spin up multiple automate clusters. 12 | - Move security rules into a separate file. 13 | - Use high performance centos images. 14 | - Automate calculate subnet from vpc. We pick the first one available. 15 | - Move to using a built in installer for chef server. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | _Deltron_ is a blueprint for creating your own [Chef Automate](https://www.chef.io/automate/) cluster in AWS, using Terraform. 2 | 3 | # Quickstart 4 | 5 | 1. `git clone` the repo. 6 | 1. Remove *.tfvars and *.tfstate from your .gitignore. 7 | 1. Execute `setup.sh` from the root of the directory 8 | 1. Create a terraform.tfvars file and include your variables there. See the included example.tfvars. 9 | 1. Create a secrets.tfvars file and include any keys and secrets there. See the included example_secrets.tfvars. 10 | 1. Run `terraform plan -var-file secrets.tfvars`. 11 | 1. Run `terraform apply -var-file secrets.tfvars`. 12 | 1. Create a new private repo and commit your `terraform.tfvars`, `terraform.tfstate`, and any changes to your own repository. 13 | 14 | # Variables in terraform.tfvars 15 | 16 | - aws_default_region - The region name where your aws instances will live. Choose from one of the following: 17 | 18 | us-west-1 19 | 20 | us-west-2 21 | 22 | us-east-1 23 | 24 | eu-west-1 25 | 26 | eu-central-1 27 | 28 | ap-southeast-1 29 | 30 | ap-southeast-2 31 | 32 | ap-northeast-1 33 | 34 | ap-northeast-2 35 | 36 | - aws_instance_type - The size and type of machines you will spin up for all Chef Automate instances. 37 | - automate_instance_id - A unique identifier added to the names and tags of the machines to make finding them easier. 38 | 39 | ## VPCs, Security Groups, and Route Tables 40 | This project assumes that your security team has already created VPCs, security_groups, and route tables where applications can live in your organization. You should question your security team to understand their operating model, architecture, and maintenance of VPCs, Security Groups, and Route Tables. If this is not the case and your organization permits dynamic allocation of these resources, then you should modify the `main.tf` file to use terraform resources to maintain these. 41 | 42 | - automate_vpc - The [VPC](https://aws.amazon.com/vpc/) under which all aws resources you spin up will be created. 43 | - automate_subnet - The [Subnet](https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Subnets.html) under which all aws resource will be created. 44 | - automate_route_table_id - The [Route Table](https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Route_Tables.html) under which all aws resources will be created. 45 | 46 | # Builder keys 47 | 48 | You no longer need to provide builder keys. 49 | 50 | # Setup.sh 51 | 52 | Because of how Terraform's file interpolation works, files are read pre-execution. To work around this, we 53 | generate a validator key for the Delivery user in this script. If we can find a way to do this in the TF plan 54 | in the future, we should do so. 55 | -------------------------------------------------------------------------------- /chef_automate.tpl: -------------------------------------------------------------------------------- 1 | delivery_fqdn "${chef_automate_public_dns}" 2 | delivery['chef_username'] = "chef_automate" 3 | delivery['chef_private_key'] = "/etc/delivery/delivery.pem" 4 | delivery['chef_server'] = "https://${chef_server_public_dns}/organizations/delivery" 5 | delivery['default_search'] = "((recipes:delivery_build OR tags:delivery-build-node OR recipes:delivery_build\\\\\\\\:\\\\\\\\:default) AND chef_environment:_default)" 6 | insights['enable'] = true 7 | -------------------------------------------------------------------------------- /chef_load.conf.tpl: -------------------------------------------------------------------------------- 1 | # The chef_server_url, client_name and client_key parameters must be set if you want 2 | # to make API requests to a Chef Server. 3 | # 4 | # Be sure to include the organization name 5 | # For example: chef_server_url = "https://chef.example.com/organizations/demo/" 6 | chef_server_url = "https://${chef_server_fqdn}/organizations/delivery/" 7 | # 8 | # The client defined by client_name needs to be an admin user of the Chef Server org. 9 | client_name = "delivery-validator" 10 | client_key = "./delivery-validator.pem" 11 | 12 | # The data_collector_url must be set if you want to make API requests to an Automate server. 13 | # For example: data_collector_url = "https://automate.example.org/data-collector/v0/" 14 | data_collector_url = "https://${automate_server_fqdn}/data-collector/v0/" 15 | 16 | # The Authorization token for the Automate server. 17 | # The following default value is sufficient unless you set your own token in your Automate server. 18 | data_collector_token = "93a49a4f2482c64126f7b6015e6b0f30284287ee4054ff8807fb63d9cbd1c506" 19 | 20 | # Ohai data will be loaded from this file and used for the nodes' automatic attributes. 21 | # See the chef-load README for instructions for creating an ohai JSON file. 22 | ohai_json_file = "/home/centos/chef-load/sample-data/example-ohai.json" 23 | 24 | # Data from a converge status report will be loaded from this file and used 25 | # for each node's converge status report that is sent to the Automate server. 26 | # See the chef-load README for instructions for creating a converge status JSON file. 27 | converge_status_json_file = "/home/centos/chef-load/sample-data/example-converge-status.json" 28 | 29 | # Data from a compliance status report will be loaded from this file and used 30 | # for each node's compliance status report that is sent to the Automate server. 31 | # See the chef-load README for instructions for creating a compliance status JSON file. 32 | compliance_status_json_file = "/home/centos/chef-load/sample-data/example-compliance-status.json" 33 | 34 | # The number of Chef Client runs to be made per minute 35 | runs_per_minute = 1 36 | 37 | # Number of minutes between a node's chef-client runs 38 | interval = 30 39 | 40 | # This prefix will go at the beginning of each node name. 41 | # This enables running multiple instances of chef-load without affecting each others' nodes 42 | # For example, a value of "chef-load" will result in nodes named "chef-load-1", "chef-load-2", ... 43 | node_name_prefix = "chef-load" 44 | 45 | # Chef environment used for each node 46 | chef_environment = "_default" 47 | 48 | # run_list is the run list used for each node. It should be a list of strings. 49 | # For example: run_list = [ "role[role_name]", "recipe_name", "recipe[different_recipe_name@1.0.0]" ] 50 | # The default value is an empty run_list. 51 | run_list = [ ] 52 | 53 | # sleep_duration is an optional setting that is available to provide a delay to simulate 54 | # the amount of time a Chef Client takes actually converging all of the run list's resources. 55 | # sleep_duration is measured in seconds 56 | sleep_duration = 0 57 | 58 | # download_cookbooks controls which chef-client run downloads cookbook files. 59 | # Options are: "never", "first" (first chef-client run only), "always" 60 | # 61 | # Downloading cookbooks can significantly increase the number of API requests that chef-load 62 | # makes depending on the run_list. 63 | # 64 | # Normal TCP protocol requires the connections to be in TIME-WAIT for about two minutes and it is 65 | # recommended that the system's TIME-WAIT parameter's do not get changed. 66 | # Ref: http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html 67 | # 68 | # If chef-load makes enough API requests then the number of connections can increase until 69 | # the system runs out of ephemeral ports resulting in connect(2) error EADDRNOTAVAIL. 70 | # Ref: http://manpages.ubuntu.com/manpages/trusty/en/man2/connect.2freebsd.html 71 | # Ref: http://manpages.ubuntu.com/manpages/trusty/en/man7/ip.7.html 72 | # 73 | # If you aren't concerned with simulating the download of cookbook files then the recommendation is 74 | # to use "never" or "first". 75 | # 76 | # If you want to use "always" and you run out of ephemeral ports then consider increasing the range of 77 | # ephemeral ports or reducing load by reducing the requests_per_minute setting. 78 | # Ref: http://www.cyberciti.biz/tips/linux-increase-outgoing-network-sockets-range.html 79 | # 80 | download_cookbooks = "never" 81 | 82 | # api_get_requests is an optional list of API GET requests that are made during the chef-client run. 83 | # This is used to simulate the API requests that the cookbooks would make. 84 | # For example, it can make Chef Search or data bag item requests. 85 | # The values can be either full URLs that include the chef_server_url portion or just the portion of 86 | # the URL that comes after the chef_server_url. 87 | # For example, to make a Chef Search API request that searches for all nodes you can use either of the 88 | # following values: 89 | # 90 | # "https://chef.example.com/organizations/orgname/search/node?q=*%253A*&sort=X_CHEF_id_CHEF_X%20asc&start=0" 91 | # or 92 | # "search/node?q=*%253A*&sort=X_CHEF_id_CHEF_X%20asc&start=0" 93 | # 94 | api_get_requests = [ ] 95 | 96 | # Send data to the Chef server's Reporting service 97 | enable_reporting = false 98 | -------------------------------------------------------------------------------- /example.tfvars: -------------------------------------------------------------------------------- 1 | # fake example data 2 | 3 | # rename this file to terraform.tfvars so terraform automatically gets variables 4 | # during the run. See: https://www.terraform.io/intro/getting-started/variables.html 5 | aws_default_region = "us-west-2" 6 | aws_instance_type = "m4.xlarge" 7 | automate_instance_id = "myface_d3e1d124" # unique identifier for this instance of Chef Automate 8 | 9 | tag_dept = "mydepartment" 10 | tag_contact = "user@example.com" 11 | 12 | automate_vpc = "vpc-34e92e01" 13 | automate_subnet = "subnet-3cd236e4" 14 | -------------------------------------------------------------------------------- /example_secrets.tfvars: -------------------------------------------------------------------------------- 1 | # fake example secret data 2 | 3 | # rename this file to secrets.tfvars 4 | # never commit secrets to source control 5 | # include secrets during the run by using terraform plan -var-file secrets.tfvars. 6 | # See: https://www.terraform.io/intro/getting-started/variables.html 7 | aws_access_key_id = "FNEIORLWOSDFLSFJDKDJ" 8 | aws_secret_access_key = "oerA/VNEIW73jfeiFJ03+einvyHEiIIIJFSLWWWa" 9 | aws_key_pair_name = "myface_aws" -------------------------------------------------------------------------------- /files/chef_load.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Chef load testing tool 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/home/centos/chef-load-1.0.0 -config /home/centos/chef_load.conf 7 | Type=forking 8 | PIDFile=/tmp/chef_load.pid 9 | Restart=always 10 | ExecReload=/bin/kill -HUP $MAINPID 11 | KillMode=process 12 | Restart=on-failure 13 | 14 | [Install] 15 | WantedBy=default.target 16 | -------------------------------------------------------------------------------- /files/installer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Please provide an IP/FQDN for your chef server: domain.com 5 | # 6 | # Hab package? 7 | # 8 | 9 | usage=" 10 | This is an installer for Chef. It will install Chef Server, Chef Automate, and a build node for Automate.\n 11 | It will install the Chef server on the system you run this script from.\n 12 | 13 | You must specify the following options:\n 14 | 15 | -c|--chef-server-fqdn REQUIRED: The FQDN you want the Chef Server configured to use.\n 16 | -a|--chef-automate-fqdn The FQDN of the Chef Automate server.\n 17 | -b|--build-node-fqdn The FQDN of the build node.\n 18 | -u|--user The ssh username we'll use to connect to other systems.\n 19 | -p|--password The ssh password we'll use to connect to other systems.\n 20 | -i|--install-dir The directory to use for the installer. 21 | -cs-source|--chef-services-source The source for the chef-services cookbook 22 | 23 | If only -c is specified the local system will be configured with a Chef Server install. \n 24 | " 25 | 26 | if [ $# -eq 0 ]; then 27 | echo -e $usage 28 | exit 1 29 | fi 30 | 31 | while [[ $# -gt 0 ]] 32 | do 33 | key="$1" 34 | case $key in 35 | -c|--chef-server-fqdn) 36 | CHEF_SERVER_FQDN="$2" 37 | shift # past argument 38 | ;; 39 | -a|--chef-automate-fqdn) 40 | CHEF_AUTOMATE_FQDN="$2" 41 | shift # past argument 42 | ;; 43 | -b|--build-node-fqdn) 44 | CHEF_BUILD_FQDN="$2" 45 | shift # past argument 46 | ;; 47 | -u|--user) 48 | CHEF_USER="$2" 49 | shift 50 | ;; 51 | -p|--password) 52 | CHEF_PW="$2" 53 | shift 54 | ;; 55 | -i|--install-dir) 56 | INSTALL_DIR="$2" 57 | shift 58 | ;; 59 | -cs-source|--chef-services-source) 60 | CHEF_SERVICES_SOURCE="$2" 61 | shift 62 | ;; 63 | -h|--help) 64 | echo -e $usage 65 | exit 0 66 | ;; 67 | *) 68 | echo "Unknown option $1" 69 | echo -e $usage 70 | exit 1 71 | ;; 72 | esac 73 | shift # past argument or value 74 | done 75 | 76 | # ---------- Chef Server ---------- 77 | # ->install Chef 78 | if [ -z "$INSTALL_DIR" ]; then 79 | INSTALL_DIR=/tmp 80 | fi 81 | 82 | mkdir -p $INSTALL_DIR/chef_installer/.chef/cache/ 83 | cd $INSTALL_DIR/chef_installer 84 | if [ ! -d "/opt/chefdk" ]; then 85 | curl -O https://packages.chef.io/files/stable/chefdk/2.6.1/el/7/chefdk-2.6.1-1.el7.x86_64.rpm 86 | sudo rpm -Uvh ./chefdk-2.6.1-1.el7.x86_64.rpm 87 | fi 88 | 89 | # write out Berksfile of install cookbooks 90 | cat << EOF > $INSTALL_DIR/chef_installer/Berksfile 91 | source 'https://supermarket.chef.io' 92 | 93 | cookbook 'chef-services', git: 'https://github.com/itmustbejj/chef-services.git', branch: 'deltron-changes' 94 | cookbook 'chef-ingredient', git: 'https://github.com/itmustbejj/chef-ingredient', branch: 'debug-branch' 95 | cookbook 'collect_metrics', git: 'https://github.com/yzl/collect_metrics.git' 96 | cookbook 'elasticsearch', git: 'https://github.com/itmustbejj/cookbook-elasticsearch', branch: '2.x.x' 97 | cookbook 'java' 98 | cookbook 'sysctl' 99 | cookbook 'backend_search_cluster', git: 'https://github.com/itmustbejj/backend_search_cluster' 100 | cookbook 'audit' 101 | cookbook 'chef-client' 102 | EOF 103 | 104 | export PATH=/opt/chefdk/gitbin:$PATH 105 | 106 | # download cookbooks for install 107 | berks install 108 | berks update 109 | berks vendor cookbooks/ 110 | 111 | # write config and build chef-server 112 | echo -e "{\"chef_server\": {\"fqdn\":\"$CHEF_SERVER_FQDN\",\"install_dir\":\"$INSTALL_DIR\"}}" > attributes.json 113 | chef-client -z -j attributes.json --config-option file_cache_path=$INSTALL_DIR -r 'recipe[chef-services::chef-server]' 114 | 115 | # upload cookbooks from chef-server to itself 116 | berks upload --no-ssl-verify 117 | 118 | # ---------- All others ----------- 119 | # -> automate,chef-builder1,chef-builder2,chef-builder3,supermarket,compliance.domain.com 120 | # --> bootstrap with correct runlist 121 | 122 | if [ ! -z $CHEF_AUTOMATE_FQDN ]; then 123 | knife bootstrap $CHEF_AUTOMATE_FQDN -N $CHEF_AUTOMATE_FQDN -x $CHEF_USER -P $CHEF_PW --sudo -r "role[patch],recipe[chef-services::delivery]" -j "{\"chef_server\":{\"fqdn\":\"$CHEF_SERVER_FQDN\"},\"chef_automate\":{\"fqdn\":\"$CHEF_AUTOMATE_FQDN\"}}" -E delivered -y --node-ssl-verify-mode none 124 | fi 125 | 126 | if [ ! -z $CHEF_BUILD_FQDN ]; then 127 | knife bootstrap $CHEF_BUILD_FQDN -N $CHEF_BUILD_FQDN -x $CHEF_USER -P $CHEF_PW --sudo -r "role[patch],recipe[chef-services::install_build_nodes]" -j "{\"chef_server\":{\"fqdn\":\"$CHEF_SERVER_FQDN\"},\"chef_automate\":{\"fqdn\":\"$CHEF_AUTOMATE_FQDN\"},\"tags\":\"delivery-build-node\"}" -E delivered -y --node-ssl-verify-mode none 128 | fi 129 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "0.9.9" 3 | } 4 | 5 | provider "aws" { 6 | region = "${var.aws_region}" 7 | profile = "${var.aws_profile}" // uses ~/.aws/credentials by default 8 | } 9 | 10 | resource "random_id" "automate_instance_id" { 11 | byte_length = 4 12 | } 13 | 14 | data "aws_subnet_ids" "automate" { 15 | vpc_id = "${var.automate_vpc}" 16 | } 17 | 18 | data "aws_ami" "centos" { 19 | most_recent = true 20 | 21 | filter { 22 | name = "name" 23 | values = ["chef-highperf-centos7-*"] 24 | } 25 | 26 | filter { 27 | name = "virtualization-type" 28 | values = ["hvm"] 29 | } 30 | 31 | owners = ["446539779517"] 32 | } 33 | 34 | # Chef Server 35 | resource "aws_instance" "chef_server" { 36 | connection { 37 | user = "${var.aws_ami_user}" 38 | private_key = "${file("${var.aws_key_pair_file}")}" 39 | } 40 | 41 | ami = "${data.aws_ami.centos.id}" 42 | instance_type = "${var.aws_instance_type}" 43 | key_name = "${var.aws_key_pair_name}" 44 | subnet_id = "${data.aws_subnet_ids.automate.ids[1]}" 45 | vpc_security_group_ids = ["${aws_security_group.chef_server.id}"] 46 | associate_public_ip_address = true 47 | ebs_optimized = true 48 | 49 | root_block_device { 50 | delete_on_termination = true 51 | volume_size = 20 52 | volume_type = "gp2" 53 | 54 | #iops = 1000 55 | } 56 | 57 | ebs_block_device { 58 | device_name = "/dev/sdb" 59 | volume_type = "io1" 60 | iops = 5000 # iops = volume_size * 50 61 | volume_size = 100 62 | delete_on_termination = true 63 | } 64 | 65 | tags { 66 | Name = "${format("${var.tag_automate}_${random_id.automate_instance_id.hex}_chef_server_%02d", count.index + 1)}" 67 | X-Dept = "${var.tag_dept}" 68 | X-Contact = "${var.tag_contact}" 69 | } 70 | 71 | # Set hostname in separate connection. 72 | # Transient hostname doesn't set correctly in time otherwise. 73 | provisioner "remote-exec" { 74 | inline = ["sudo hostnamectl set-hostname ${aws_instance.chef_server.public_dns}"] 75 | } 76 | 77 | # mount the EBS volume 78 | provisioner "file" { 79 | source = "mount_data_volume" 80 | destination = "/tmp/mount_data_volume" 81 | } 82 | 83 | provisioner "remote-exec" { 84 | inline = [ 85 | "sudo bash -ex /tmp/mount_data_volume", 86 | ] 87 | } 88 | 89 | provisioner "file" { 90 | source = ".chef/delivery-validator.pub" 91 | destination = "/tmp/pre-delivery-validator.pub" 92 | } 93 | 94 | provisioner "file" { 95 | source = "files/installer.sh" 96 | destination = "/tmp/installer.sh" 97 | } 98 | 99 | provisioner "remote-exec" { 100 | inline = [ 101 | "sudo SVWAIT=30 bash /tmp/installer.sh -c ${aws_instance.chef_server.public_dns}", 102 | "sudo chef-server-ctl add-client-key delivery delivery-validator --public-key-path /tmp/pre-delivery-validator.pub", 103 | ] 104 | } 105 | } 106 | 107 | # template to delay reading of validator key 108 | data "template_file" "delivery_validator" { 109 | vars { 110 | delivery_validator = "${file(".chef/delivery-validator.pem")}" 111 | } 112 | 113 | template = "$${delivery_validator}" 114 | depends_on = ["aws_instance.chef_server"] 115 | } 116 | 117 | resource "aws_instance" "chef_automate" { 118 | connection { 119 | user = "${var.aws_ami_user}" 120 | private_key = "${file("${var.aws_key_pair_file}")}" 121 | } 122 | 123 | ami = "${data.aws_ami.centos.id}" 124 | instance_type = "${var.aws_instance_type}" 125 | key_name = "${var.aws_key_pair_name}" 126 | subnet_id = "${data.aws_subnet_ids.automate.ids[1]}" 127 | vpc_security_group_ids = ["${aws_security_group.chef_automate.id}"] 128 | ebs_optimized = true 129 | 130 | root_block_device { 131 | delete_on_termination = true 132 | volume_size = 20 133 | volume_type = "gp2" 134 | 135 | #iops = 1000 136 | } 137 | 138 | ebs_block_device { 139 | device_name = "/dev/sdb" 140 | volume_type = "io1" 141 | iops = 5000 # iops = volume_size * 50 142 | volume_size = 100 143 | delete_on_termination = true 144 | } 145 | 146 | tags { 147 | Name = "${format("${var.tag_automate}_${random_id.automate_instance_id.hex}_chef_automate_%02d", count.index + 1)}" 148 | X-Dept = "${var.tag_dept}" 149 | X-Contact = "${var.tag_contact}" 150 | } 151 | 152 | # Set hostname in separate connection. 153 | # Transient hostname doesn't set correctly in time otherwise. 154 | provisioner "remote-exec" { 155 | inline = [ 156 | "sudo hostnamectl set-hostname ${aws_instance.chef_automate.public_dns}", 157 | "sudo mkdir /etc/chef/", 158 | ] 159 | } 160 | 161 | # mount the EBS volume 162 | provisioner "file" { 163 | source = "mount_data_volume" 164 | destination = "/tmp/mount_data_volume" 165 | } 166 | 167 | provisioner "remote-exec" { 168 | inline = [ 169 | "sudo bash -ex /tmp/mount_data_volume", 170 | ] 171 | } 172 | 173 | provisioner "file" { 174 | source = "chef_automate.license" 175 | destination = "~/chef_automate.license" 176 | } 177 | 178 | provisioner "chef" { 179 | attributes_json = <<-EOF 180 | { 181 | "tags": "automate_server", 182 | "chef_automate": { 183 | "fqdn": "${aws_instance.chef_automate.public_dns}" 184 | }, 185 | "chef_server": { 186 | "fqdn": "${aws_instance.chef_server.public_dns}" 187 | } 188 | } 189 | EOF 190 | 191 | environment = "_default" 192 | fetch_chef_certificates = true 193 | run_list = ["chef-services::delivery"] 194 | node_name = "${aws_instance.chef_automate.public_dns}" 195 | server_url = "https://${aws_instance.chef_server.public_dns}/organizations/delivery" 196 | user_name = "delivery-validator" 197 | user_key = "${data.template_file.delivery_validator.rendered}" 198 | client_options = ["trusted_certs_dir = '/etc/chef/trusted_certs'"] 199 | } 200 | 201 | provisioner "local-exec" { 202 | command = "scp -oStrictHostKeyChecking=no -i ${var.aws_key_pair_file} ${var.aws_ami_user}@${aws_instance.chef_automate.public_dns}:/tmp/test.creds ./" 203 | } 204 | } 205 | 206 | resource "aws_instance" "build_nodes" { 207 | connection { 208 | user = "${var.aws_ami_user}" 209 | private_key = "${file("${var.aws_key_pair_file}")}" 210 | } 211 | 212 | ami = "${data.aws_ami.centos.id}" 213 | instance_type = "t2.medium" 214 | key_name = "${var.aws_key_pair_name}" 215 | subnet_id = "${data.aws_subnet_ids.automate.ids[1]}" 216 | vpc_security_group_ids = ["${aws_security_group.build_nodes.id}"] 217 | ebs_optimized = false 218 | count = 3 219 | 220 | root_block_device { 221 | delete_on_termination = true 222 | volume_size = 100 223 | volume_type = "gp2" 224 | } 225 | 226 | tags { 227 | Name = "${format("${var.tag_automate}_build_node_%02d_${random_id.automate_instance_id.hex}", count.index + 1)}" 228 | X-Dept = "${var.tag_dept}" 229 | X-Contact = "${var.tag_contact}" 230 | } 231 | 232 | provisioner "chef" { 233 | attributes_json = <<-EOF 234 | { 235 | "tags": "delivery-build-node", 236 | "chef_automate": { 237 | "fqdn": "${aws_instance.chef_automate.public_dns}" 238 | }, 239 | "chef_server": { 240 | "fqdn": "${aws_instance.chef_server.public_dns}" 241 | } 242 | } 243 | EOF 244 | 245 | environment = "_default" 246 | node_name = "build-node-${count.index + 1}" 247 | fetch_chef_certificates = true 248 | run_list = ["chef-services::install_build_nodes"] 249 | server_url = "https://${aws_instance.chef_server.public_dns}/organizations/delivery" 250 | user_name = "delivery-validator" 251 | user_key = "${data.template_file.delivery_validator.rendered}" 252 | client_options = ["trusted_certs_dir '/etc/chef/trusted_certs'"] 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /mount_data_volume: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | # metadata 4 | CURL_META_DATA="curl --retry 3 --silent --show-error --fail -L http://169.254.169.254/latest" 5 | INSTANCE_ID=$($CURL_META_DATA/meta-data/instance-id) 6 | AVAILABILITY_ZONE=$($CURL_META_DATA/meta-data/placement/availability-zone) 7 | REGION=${AVAILABILITY_ZONE%?} 8 | 9 | # die selinux die 10 | setenforce 0 11 | 12 | # install awscli - assuming role is set 13 | yum install -y lvm2 xfsprogs git tuned sysstat #sg3_utils python-setuptools 14 | 15 | # Set performance characteristics hopefully for faster install too 16 | tuned-adm profile virtual-guest 17 | echo never > /sys/kernel/mm/transparent_hugepage/enabled 18 | echo never > /sys/kernel/mm/transparent_hugepage/defrag 19 | /bin/systemctl start sysstat.service 20 | 21 | # Create LVM LV /dev/chef-vg/chef-lv 22 | pvcreate /dev/xvdb 23 | vgcreate chef-vg /dev/xvdb 24 | lvcreate -n chef-lv -l 80%VG chef-vg 25 | 26 | # create xfs 27 | mkfs.xfs /dev/chef-vg/chef-lv 28 | 29 | # mount 30 | mkdir -p /var/opt 31 | mount /dev/chef-vg/chef-lv /var/opt 32 | -------------------------------------------------------------------------------- /security.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group" "chef_server" { 2 | name = "chef_server_${random_id.automate_instance_id.hex}" 3 | description = "Terraform Automate Chef Server" 4 | vpc_id = "${var.automate_vpc}" 5 | 6 | tags { 7 | Name = "${var.tag_automate}_chef_server_security_group_${random_id.automate_instance_id.hex}" 8 | X-Dept = "${var.tag_dept}" 9 | X-Contact = "${var.tag_contact}" 10 | } 11 | } 12 | 13 | # SSH - all 14 | resource "aws_security_group_rule" "ingress_chef_server_allow_22_tcp_all" { 15 | type = "ingress" 16 | from_port = 22 17 | to_port = 22 18 | protocol = "tcp" 19 | cidr_blocks = ["0.0.0.0/0"] 20 | security_group_id = "${aws_security_group.chef_server.id}" 21 | } 22 | 23 | # HTTP (nginx) 24 | resource "aws_security_group_rule" "ingress_chef_server_allow_80_tcp" { 25 | type = "ingress" 26 | from_port = 80 27 | to_port = 80 28 | protocol = "tcp" 29 | cidr_blocks = ["0.0.0.0/0"] 30 | security_group_id = "${aws_security_group.chef_server.id}" 31 | } 32 | 33 | # HTTPS (nginx) 34 | resource "aws_security_group_rule" "ingress_chef_server_allow_443_tcp" { 35 | type = "ingress" 36 | from_port = 443 37 | to_port = 443 38 | protocol = "tcp" 39 | cidr_blocks = ["0.0.0.0/0"] 40 | security_group_id = "${aws_security_group.chef_server.id}" 41 | } 42 | 43 | # opscode push-jobs 44 | resource "aws_security_group_rule" "ingress_chef_server_allow_10000-10003_tcp" { 45 | type = "ingress" 46 | from_port = 10000 47 | to_port = 10003 48 | protocol = "tcp" 49 | cidr_blocks = ["0.0.0.0/0"] 50 | security_group_id = "${aws_security_group.chef_server.id}" 51 | } 52 | 53 | # Allow all Chef 54 | resource "aws_security_group_rule" "ingress_chef_server_allow_all_chef_automate" { 55 | type = "ingress" 56 | from_port = 0 57 | to_port = 0 58 | protocol = "-1" 59 | source_security_group_id = "${aws_security_group.chef_automate.id}" 60 | security_group_id = "${aws_security_group.chef_server.id}" 61 | } 62 | 63 | # Egress: ALL 64 | resource "aws_security_group_rule" "egress_chef_server_allow_0-65535_all" { 65 | type = "egress" 66 | from_port = 0 67 | to_port = 0 68 | protocol = "-1" 69 | cidr_blocks = ["0.0.0.0/0"] 70 | security_group_id = "${aws_security_group.chef_server.id}" 71 | } 72 | 73 | resource "aws_security_group" "chef_automate" { 74 | name = "chef_automate_${random_id.automate_instance_id.hex}" 75 | description = "Terraform Chef Automate Server" 76 | vpc_id = "${var.automate_vpc}" 77 | 78 | tags { 79 | Name = "${var.tag_automate}_chef_automate_security_group" 80 | X-Dept = "${var.tag_dept}" 81 | X-Contact = "${var.tag_contact}" 82 | } 83 | } 84 | 85 | # SSH - all 86 | resource "aws_security_group_rule" "ingress_chef_automate_allow_22_tcp_all" { 87 | type = "ingress" 88 | from_port = 22 89 | to_port = 22 90 | protocol = "tcp" 91 | cidr_blocks = ["0.0.0.0/0"] 92 | security_group_id = "${aws_security_group.chef_automate.id}" 93 | } 94 | 95 | # HTTP 96 | resource "aws_security_group_rule" "ingress_chef_automate_allow_80_tcp" { 97 | type = "ingress" 98 | from_port = 80 99 | to_port = 80 100 | protocol = "tcp" 101 | cidr_blocks = ["0.0.0.0/0"] 102 | security_group_id = "${aws_security_group.chef_automate.id}" 103 | } 104 | 105 | # HTTPS 106 | resource "aws_security_group_rule" "ingress_chef_automate_allow_443_tcp" { 107 | type = "ingress" 108 | from_port = 443 109 | to_port = 443 110 | protocol = "tcp" 111 | cidr_blocks = ["0.0.0.0/0"] 112 | security_group_id = "${aws_security_group.chef_automate.id}" 113 | } 114 | 115 | # Automate GIT 116 | resource "aws_security_group_rule" "ingress_chef_automate_allow_8989_tcp" { 117 | type = "ingress" 118 | from_port = 8989 119 | to_port = 8989 120 | protocol = "tcp" 121 | cidr_blocks = ["0.0.0.0/0"] 122 | security_group_id = "${aws_security_group.chef_automate.id}" 123 | } 124 | 125 | # Allow etcd communication 126 | resource "aws_security_group_rule" "ingress_chef_automate_allow_2379_tcp" { 127 | type = "ingress" 128 | from_port = 2379 129 | to_port = 2380 130 | protocol = "tcp" 131 | security_group_id = "${aws_security_group.chef_automate.id}" 132 | source_security_group_id = "${aws_security_group.chef_automate.id}" 133 | } 134 | 135 | # Allow elasticsearch clients 136 | resource "aws_security_group_rule" "ingress_chef_automate_allow_9200_to_9400_tcp" { 137 | type = "ingress" 138 | from_port = 9200 139 | to_port = 9400 140 | protocol = "tcp" 141 | security_group_id = "${aws_security_group.chef_automate.id}" 142 | source_security_group_id = "${aws_security_group.chef_automate.id}" 143 | } 144 | 145 | # Allow postgres connections 146 | resource "aws_security_group_rule" "ingress_chef_automate_allow_5432_tcp" { 147 | type = "ingress" 148 | from_port = 5432 149 | to_port = 5432 150 | protocol = "tcp" 151 | security_group_id = "${aws_security_group.chef_automate.id}" 152 | source_security_group_id = "${aws_security_group.chef_automate.id}" 153 | } 154 | 155 | # Allow leaderel connections 156 | resource "aws_security_group_rule" "ingress_chef_automate_allow_7331_tcp" { 157 | type = "ingress" 158 | from_port = 7331 159 | to_port = 7331 160 | protocol = "tcp" 161 | security_group_id = "${aws_security_group.chef_automate.id}" 162 | source_security_group_id = "${aws_security_group.chef_automate.id}" 163 | } 164 | 165 | # Allow all Chef Server 166 | resource "aws_security_group_rule" "ingress_chef_automate_allow_all_chef_server" { 167 | type = "ingress" 168 | from_port = 0 169 | to_port = 0 170 | protocol = "-1" 171 | source_security_group_id = "${aws_security_group.chef_server.id}" 172 | security_group_id = "${aws_security_group.chef_automate.id}" 173 | } 174 | 175 | # Egress: ALL 176 | resource "aws_security_group_rule" "egress_chef_automate_allow_0-65535_all" { 177 | type = "egress" 178 | from_port = 0 179 | to_port = 0 180 | protocol = "-1" 181 | cidr_blocks = ["0.0.0.0/0"] 182 | security_group_id = "${aws_security_group.chef_automate.id}" 183 | } 184 | 185 | resource "aws_security_group" "build_nodes" { 186 | name = "build_nodes_${random_id.automate_instance_id.hex}" 187 | description = "Terraform Build Nodes" 188 | vpc_id = "${var.automate_vpc}" 189 | 190 | tags { 191 | Name = "${var.tag_automate}_build_nodes_security_group" 192 | X-Dept = "${var.tag_dept}" 193 | X-Contact = "${var.tag_contact}" 194 | } 195 | } 196 | 197 | # Egress: ALL 198 | resource "aws_security_group_rule" "egress_build_nodes_allow_0-65535_all" { 199 | type = "egress" 200 | from_port = 0 201 | to_port = 0 202 | protocol = "-1" 203 | cidr_blocks = ["0.0.0.0/0"] 204 | security_group_id = "${aws_security_group.build_nodes.id}" 205 | } 206 | 207 | # SSH - all 208 | resource "aws_security_group_rule" "ingress_build_nodes_allow_22_tcp_all" { 209 | type = "ingress" 210 | from_port = 22 211 | to_port = 22 212 | protocol = "tcp" 213 | cidr_blocks = ["0.0.0.0/0"] 214 | security_group_id = "${aws_security_group.build_nodes.id}" 215 | } 216 | 217 | # Allow all Chef Automate 218 | resource "aws_security_group_rule" "ingress_build_nodes_allow_all_chef_server" { 219 | type = "ingress" 220 | from_port = 0 221 | to_port = 0 222 | protocol = "-1" 223 | source_security_group_id = "${aws_security_group.chef_automate.id}" 224 | security_group_id = "${aws_security_group.build_nodes.id}" 225 | } 226 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir .chef 4 | ssh-keygen -t rsa -N "" -f .chef/delivery-validator.pem 5 | openssl rsa -in .chef/delivery-validator.pem -pubout -out .chef/delivery-validator.pub 6 | -------------------------------------------------------------------------------- /testing.tf: -------------------------------------------------------------------------------- 1 | data "template_file" "chef_load_conf" { 2 | template = "${file("./chef_load.conf.tpl")}" 3 | 4 | vars { 5 | chef_server_fqdn = "${aws_instance.chef_server.public_dns}" 6 | automate_server_fqdn = "${aws_instance.chef_automate.public_dns}" 7 | } 8 | } 9 | 10 | resource "aws_instance" "chef_load" { 11 | connection { 12 | user = "${var.aws_ami_user}" 13 | private_key = "${file("${var.aws_key_pair_file}")}" 14 | } 15 | 16 | count = 0 17 | ami = "${data.aws_ami.centos.id}" 18 | instance_type = "${var.aws_instance_type}" 19 | key_name = "${var.aws_key_pair_name}" 20 | subnet_id = "${data.aws_subnet_ids.automate.ids[1]}" 21 | vpc_security_group_ids = ["${aws_security_group.chef_automate.id}"] 22 | associate_public_ip_address = true 23 | ebs_optimized = true 24 | 25 | root_block_device { 26 | delete_on_termination = true 27 | volume_size = 20 28 | volume_type = "gp2" 29 | 30 | #iops = 1000 31 | } 32 | 33 | ebs_block_device { 34 | device_name = "/dev/sdb" 35 | volume_type = "io1" 36 | iops = 5000 # iops = volume_size * 50 37 | volume_size = 100 38 | delete_on_termination = true 39 | } 40 | 41 | tags { 42 | Name = "${format("${var.tag_automate}_chef_load_%02d_${random_id.automate_instance_id.hex}", count.index + 1)}" 43 | X-Dept = "${var.tag_dept}" 44 | X-Contact = "${var.tag_contact}" 45 | } 46 | 47 | # Set hostname in separate connection. 48 | # Transient hostname doesn't set correctly in time otherwise. 49 | provisioner "remote-exec" { 50 | inline = [ 51 | "sudo hostnamectl set-hostname ${aws_instance.chef_automate.public_dns}", 52 | "sudo mkdir /etc/chef/", 53 | ] 54 | } 55 | 56 | # mount the EBS volume 57 | provisioner "file" { 58 | source = "mount_data_volume" 59 | destination = "/tmp/mount_data_volume" 60 | } 61 | 62 | provisioner "file" { 63 | content = "${data.template_file.delivery_validator.rendered}" 64 | destination = "/home/centos/delivery-validator.pem" 65 | } 66 | 67 | provisioner "file" { 68 | content = "${data.template_file.chef_load_conf.rendered}" 69 | destination = "/home/centos/chef_load.conf" 70 | } 71 | 72 | provisioner "file" { 73 | source = "./files/chef_load.service" 74 | destination = "/tmp/chef_load.service" 75 | } 76 | 77 | provisioner "remote-exec" { 78 | inline = [ 79 | "sudo mv /tmp/chef_load.service /etc/systemd/system/chef_load.service", 80 | ] 81 | } 82 | 83 | provisioner "remote-exec" { 84 | inline = [ 85 | "sudo bash -ex /tmp/mount_data_volume", 86 | "sudo yum install git -y", 87 | "cd && git clone https://github.com/jeremiahsnapp/chef-load.git", 88 | "wget https://github.com/chef/chef-load/releases/download/v1.0.0/chef-load_1.0.0_Linux_64bit -O chef-load-1.0.0", 89 | "chmod +x chef-load-1.0.0", 90 | "chmod 600 delivery-validator.pem", 91 | "knife ssl fetch https://${aws_instance.chef_server.public_dns}", 92 | ] 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" {} 2 | 3 | variable "aws_profile" { 4 | default = "default" 5 | } 6 | 7 | variable "aws_key_pair_name" {} 8 | 9 | variable "aws_key_pair_file" {} 10 | 11 | variable "chef-delivery-enterprise" { 12 | default = "terraform" 13 | } 14 | 15 | variable "chef-server-organization" { 16 | default = "terraform" 17 | } 18 | 19 | variable "automate_vpc" {} 20 | 21 | variable "tag_automate" { 22 | default = "terraform_automate" 23 | } 24 | 25 | variable "tag_dept" { 26 | default = "example_department" 27 | } 28 | 29 | variable "tag_contact" { 30 | default = "example_human" 31 | } 32 | 33 | variable "aws_build_node_instance_type" { 34 | default = "t2.medium" 35 | } 36 | 37 | variable "aws_instance_type" { 38 | default = "m4.xlarge" 39 | } 40 | 41 | variable "aws_ami_user" { 42 | default = "centos" 43 | } 44 | --------------------------------------------------------------------------------