-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1147 from gruntwork-io/feature/windows-instance-e…
…xample Implement Windows ec2 instance example
- Loading branch information
Showing
9 changed files
with
364 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# Windows Instance Example | ||
|
||
This folder provides a Packer template that can be used to build an Amazon Machine Image (AMI) of a Windows 2016 Server that comes pre-installed with: | ||
|
||
- The [Chocolately package manager](https://chocolatey.org/why-chocolatey) which makes it easy to install additional software packages onto Windows | ||
- Git | ||
- Python 3 | ||
|
||
In addition, this folder provides an example of how to launch a Windows instance based off this AMI that can be connected to via a Remote Desktop Protocol (RDP) client for the purposes of testing software or experimentation. | ||
|
||
This setup is ideal for "hot-reloading" code that you're actively developing and testing it against the Windows server. You can develop your code in your usual environment, perhaps a Mac or Linux laptop, yet see your changes reflected on the Windows server in seconds, by sharing a folder from your development machine with the Windows server via the RDP client. | ||
|
||
## Quick start | ||
|
||
Pre-requistes: | ||
|
||
- [Packer version v1.8.1 or newer](https://github.com/hashicorp/packer) | ||
- [Terraform v1.0 or newer](https://github.com/hashicorp/terraform) | ||
- An AWS account with valid security credentials | ||
|
||
First, we'll build the AMI for the Windows Instance. Change into the packer directory: | ||
|
||
`cd packer` | ||
|
||
In order to build an Amazon Machine Image with Packer, you'll need to export your AWS account credentials. You can export your AWS credentials as the environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. | ||
|
||
For more information on authenticating to your AWS account from the command line, see our blog post [Authenticating to AWS with Environment Variables](https://blog.gruntwork.io/authenticating-to-aws-with-environment-variables-e793d6f6d02e). | ||
|
||
With your credentials properly exported, you can now run the packer build: | ||
|
||
`packer build build.pkr.hcl` | ||
|
||
This may take upwards of 25 minutes to complete, but generally completes in about 5 minutes. Keep an eye on your EC2 dashboard and ensure that you have selected the correct region and that you are on the AMI view. Once your AMI status has changed from "Pending" to "Available", you can copy your AMI ID. | ||
|
||
Create a new file named `terraform.tfvars` in this same directory and enter the following variables: | ||
|
||
```hcl | ||
ami_id = "<the AMI ID you copied in the previous step>" | ||
region = "us-east-1" | ||
root_volume_size = 100 | ||
``` | ||
Save the file. | ||
|
||
You're now ready to run terraform plan and check the output before proceeding: | ||
|
||
`terraform plan` | ||
|
||
Take a look at the plan output and ensure everything looks correct. You should see a single EC2 instance being created along with supporting resources such as a security group and security group rules. | ||
|
||
Once you're satisfied that the plan looks good, run terraform apply to create the infrastructure: | ||
|
||
`terraform apply --auto-approve` | ||
|
||
Once your resources apply successfully you'll see a similar output message containing the public IPv4 address of your Windows instance: | ||
|
||
`instance_ip = "35.84.139.82"` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
# LAUNCH THE WINDOWS INSTANCE | ||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
terraform { | ||
# This module is now only being tested with Terraform 1.1.x. However, to make upgrading easier, we are setting 1.0.0 as the minimum version. | ||
required_version = ">= 1.0.0" | ||
required_providers { | ||
aws = { | ||
source = "hashicorp/aws" | ||
version = "< 4.0" | ||
} | ||
} | ||
} | ||
|
||
# --------------------------------------------------------------------------------------------------------------------- | ||
# CONFIGURE OUR AWS CONNECTION | ||
# --------------------------------------------------------------------------------------------------------------------- | ||
|
||
provider "aws" { | ||
# The AWS region in which all resources will be created | ||
region = var.region | ||
} | ||
|
||
# --------------------------------------------------------------------------------------------------------------------- | ||
# DEPLOY INTO THE DEFAULT VPC AND SUBNETS | ||
# To keep this example simple, we are deploying into the Default VPC and its subnets. In real-world usage, you should | ||
# deploy into a custom VPC and private subnets. | ||
# --------------------------------------------------------------------------------------------------------------------- | ||
|
||
data "aws_vpc" "default" { | ||
default = true | ||
} | ||
|
||
data "aws_subnet_ids" "all" { | ||
vpc_id = data.aws_vpc.default.id | ||
} | ||
|
||
# --------------------------------------------------------------------------------------------------------------------- | ||
# CREATE A SECURITY GROUP TO ALLOW ACCESS TO THE RDS INSTANCE | ||
# --------------------------------------------------------------------------------------------------------------------- | ||
|
||
resource "aws_security_group" "windows_instance" { | ||
name = var.name | ||
vpc_id = data.aws_vpc.default.id | ||
} | ||
|
||
resource "aws_security_group_rule" "allow_rdp" { | ||
type = "ingress" | ||
security_group_id = aws_security_group.windows_instance.id | ||
|
||
from_port = "3389" | ||
to_port = "3389" | ||
protocol = "tcp" | ||
cidr_blocks = ["0.0.0.0/0"] | ||
} | ||
|
||
resource "aws_security_group_rule" "allow_egress" { | ||
type = "egress" | ||
security_group_id = aws_security_group.windows_instance.id | ||
|
||
from_port = 0 | ||
to_port = 0 | ||
protocol = "-1" | ||
cidr_blocks = ["0.0.0.0/0"] | ||
} | ||
|
||
# --------------------------------------------------------------------------------------------------------------------- | ||
# LAUNCH THE WINDOWS INSTANCE | ||
# --------------------------------------------------------------------------------------------------------------------- | ||
|
||
resource "aws_instance" "instance" { | ||
ami = var.ami | ||
instance_type = var.instance_type | ||
vpc_security_group_ids = [aws_security_group.windows_instance.id] | ||
|
||
tags = { | ||
Name = var.instance_type | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
output "windows_instance_public_ip" { | ||
description = "The IPv4 address of the Windows instance. Enter this value into your RDP client when connecting to your instance." | ||
value = aws_instance.instance.public_ip | ||
} |
52 changes: 52 additions & 0 deletions
52
examples/terraform-aws-ec2-windows-example/packer/build.pkr.hcl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
variable "instance_type" { | ||
type = string | ||
description = "The EC2 instance size / type to launch" | ||
} | ||
|
||
variable "region" { | ||
type = string | ||
description = "The AWS region to deploy the Windows instance into" | ||
} | ||
|
||
|
||
data "amazon-ami" "windows_server_2016" { | ||
filters = { | ||
name = "Windows_Server-2016-English-Full-Base-*" | ||
root-device-type = "ebs" | ||
virtualization-type = "hvm" | ||
} | ||
most_recent = true | ||
owners = ["801119661308"] | ||
region = var.region | ||
} | ||
|
||
locals { | ||
build_version = "${legacy_isotime("2006.01.02.150405")}" | ||
} | ||
|
||
source "amazon-ebs" "windows_server_2016" { | ||
ami_name = "WIN2016-CUSTOM-${local.build_version}" | ||
associate_public_ip_address = true | ||
communicator = "winrm" | ||
instance_type = var.instance_type | ||
region = var.region | ||
source_ami = "${data.amazon-ami.windows_server_2016.id}" | ||
user_data_file = "${path.root}/scripts/bootstrap_windows.txt" | ||
winrm_timeout = "15m" | ||
winrm_password = "SuperS3cr3t!!!!" | ||
winrm_username = "Administrator" | ||
|
||
} | ||
|
||
build { | ||
sources = ["source.amazon-ebs.windows_server_2016"] | ||
|
||
# Install Chocolatey package manager, then install any Chocolatey packages defined in scripts/install_packages.ps1 | ||
provisioner "powershell" { | ||
scripts = ["${path.root}/scripts/install_chocolatey.ps1", "${path.root}/scripts/install_packages.ps1"] | ||
} | ||
|
||
provisioner "windows-restart" { | ||
restart_timeout = "35m" | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
examples/terraform-aws-ec2-windows-example/packer/scripts/bootstrap_windows.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<powershell> | ||
# This script is adapted from: https://learn.hashicorp.com/tutorials/packer/aws-windows-image?in=packer/integrations | ||
|
||
# Set administrator password | ||
net user Administrator SuperS3cr3t!!!! | ||
wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE | ||
|
||
# First, make sure WinRM can't be connected to | ||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=block | ||
|
||
# Delete any existing WinRM listeners | ||
winrm delete winrm/config/listener?Address=*+Transport=HTTP 2>$Null | ||
winrm delete winrm/config/listener?Address=*+Transport=HTTPS 2>$Null | ||
|
||
# Disable group policies which block basic authentication and unencrypted login | ||
|
||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Client -Name AllowBasic -Value 1 | ||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Client -Name AllowUnencryptedTraffic -Value 1 | ||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service -Name AllowBasic -Value 1 | ||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service -Name AllowUnencryptedTraffic -Value 1 | ||
|
||
|
||
# Create a new WinRM listener and configure | ||
winrm create winrm/config/listener?Address=*+Transport=HTTP | ||
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="0"}' | ||
winrm set winrm/config '@{MaxTimeoutms="7200000"}' | ||
winrm set winrm/config/service '@{AllowUnencrypted="true"}' | ||
winrm set winrm/config/service '@{MaxConcurrentOperationsPerUser="12000"}' | ||
winrm set winrm/config/service/auth '@{Basic="true"}' | ||
winrm set winrm/config/client/auth '@{Basic="true"}' | ||
|
||
# Configure UAC to allow privilege elevation in remote shells | ||
$Key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' | ||
$Setting = 'LocalAccountTokenFilterPolicy' | ||
Set-ItemProperty -Path $Key -Name $Setting -Value 1 -Force | ||
|
||
# Configure and restart the WinRM Service; Enable the required firewall exception | ||
Stop-Service -Name WinRM | ||
Set-Service -Name WinRM -StartupType Automatic | ||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new action=allow localip=any remoteip=any | ||
Start-Service -Name WinRM | ||
</powershell> | ||
|
1 change: 1 addition & 0 deletions
1
examples/terraform-aws-ec2-windows-example/packer/scripts/install_chocolatey.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) |
2 changes: 2 additions & 0 deletions
2
examples/terraform-aws-ec2-windows-example/packer/scripts/install_packages.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
choco install -y python3 | ||
choco install -y git |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# --------------------------------------------------------------------------------------------------------------------- | ||
# ENVIRONMENT VARIABLES | ||
# Define these secrets as environment variables | ||
# --------------------------------------------------------------------------------------------------------------------- | ||
|
||
# AWS_ACCESS_KEY_ID | ||
# AWS_SECRET_ACCESS_KEY | ||
|
||
# --------------------------------------------------------------------------------------------------------------------- | ||
# REQUIRED PARAMETERS | ||
# You must provide a value for each of these parameters. | ||
# --------------------------------------------------------------------------------------------------------------------- | ||
|
||
variable "region" { | ||
description = "The AWS region in which all resources will be created" | ||
type = string | ||
default = "us-west-2" | ||
} | ||
|
||
variable "ami" { | ||
description = "The ID of the AMI to run on the Windows instance." | ||
type = string | ||
} | ||
|
||
# --------------------------------------------------------------------------------------------------------------------- | ||
# OPTIONAL PARAMETERS | ||
# These parameters have reasonable defaults. | ||
# --------------------------------------------------------------------------------------------------------------------- | ||
|
||
variable "name" { | ||
description = "The name of the Windows instance" | ||
type = string | ||
default = "windows_test_instance" | ||
} | ||
|
||
variable "instance_type" { | ||
description = "The instance type to deploy." | ||
type = string | ||
default = "t3.small" | ||
} | ||
|
||
variable "root_volume_size" { | ||
description = "The size in GiB of the root volume. Must match the root volume size of the target AMI." | ||
type = number | ||
default = 30 | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package test | ||
|
||
import ( | ||
"fmt" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/gruntwork-io/terratest/modules/aws" | ||
"github.com/gruntwork-io/terratest/modules/packer" | ||
"github.com/gruntwork-io/terratest/modules/random" | ||
"github.com/gruntwork-io/terratest/modules/terraform" | ||
test_structure "github.com/gruntwork-io/terratest/modules/test-structure" | ||
) | ||
|
||
func TestWindowsInstance(t *testing.T) { | ||
// Uncomment any of the following to skip that section during the test | ||
//os.Setenv("SKIP_setup", "true") | ||
//os.Setenv("SKIP_build_ami", "true") | ||
//os.Setenv("SKIP_deploy", "true") | ||
//os.Setenv("SKIP_validate", "true") | ||
//os.Setenv("SKIP_cleanup", "true") | ||
|
||
workingDir := filepath.Join(".", "stages", t.Name()) | ||
testBasePath := test_structure.CopyTerraformFolderToTemp(t, "..", "examples/terraform-aws-ec2-windows-example") | ||
|
||
test_structure.RunTestStage(t, "setup", func() { | ||
uniqueID := random.UniqueId() | ||
region := aws.GetRandomRegion(t, []string{}, []string{}) | ||
roleName := fmt.Sprintf("%s-test-role", uniqueID) | ||
|
||
instanceType := aws.GetRecommendedInstanceType(t, region, []string{"t2.micro, t3.micro", "t2.small", "t3.small"}) | ||
test_structure.SaveString(t, workingDir, "region", region) | ||
test_structure.SaveString(t, workingDir, "uniqueID", uniqueID) | ||
test_structure.SaveString(t, workingDir, "instanceType", instanceType) | ||
test_structure.SaveString(t, workingDir, "roleName", roleName) | ||
}) | ||
|
||
test_structure.RunTestStage(t, "build_ami", func() { | ||
region := test_structure.LoadString(t, workingDir, "region") | ||
instanceType := test_structure.LoadString(t, workingDir, "instanceType") | ||
roleName := test_structure.LoadString(t, workingDir, "roleName") | ||
|
||
varsMap := make(map[string]string) | ||
|
||
varsMap["instance_type"] = instanceType | ||
varsMap["region"] = region | ||
packerOptions := &packer.Options{ | ||
Template: filepath.Join(testBasePath, "packer/build.pkr.hcl"), | ||
Vars: varsMap, | ||
} | ||
|
||
amiID := packer.BuildArtifact(t, packerOptions) | ||
|
||
test_structure.SaveString(t, workingDir, "amiID", amiID) | ||
|
||
terratestOptions := &terraform.Options{ | ||
TerraformDir: testBasePath, | ||
Vars: make(map[string]interface{}), | ||
} | ||
|
||
terratestOptions.Vars["ami"] = amiID | ||
terratestOptions.Vars["region"] = region | ||
terratestOptions.Vars["iam_role_name"] = roleName | ||
test_structure.SaveTerraformOptions(t, workingDir, terratestOptions) | ||
}) | ||
|
||
defer test_structure.RunTestStage(t, "cleanup", func() { | ||
terratestOptions := test_structure.LoadTerraformOptions(t, workingDir) | ||
terraform.Destroy(t, terratestOptions) | ||
}) | ||
|
||
test_structure.RunTestStage(t, "deploy", func() { | ||
terratestOptions := test_structure.LoadTerraformOptions(t, workingDir) | ||
terraform.InitAndApply(t, terratestOptions) | ||
}) | ||
|
||
} |