Skip to content
This repository was archived by the owner on Jul 11, 2023. It is now read-only.

Commit 510a6ad

Browse files
committed
Active Directory with seamless Windows EC2 join (from ASG)
1 parent e27ed8e commit 510a6ad

10 files changed

+556
-0
lines changed

examples/ad-asg/Makefile

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
.PHONY: init ssh-key plan-vpc plan-subnets plan-gateway plan apply destroy clean
2+
3+
.DEFAULT_GOAL = help
4+
5+
# Hardcoding value of 3 minutes when we check if the plan file is stale
6+
STALE_PLAN_FILE := `find "tf.out" -mmin -3 | grep -q tf.out`
7+
8+
## Check if tf.out is stale (Older than 2 minutes)
9+
check-plan-file:
10+
@if ! ${STALE_PLAN_FILE} ; then \
11+
echo "ERROR: Stale tf.out plan file (older than 3 minutes)!"; \
12+
exit 1; \
13+
fi
14+
15+
## Runs terraform get and terraform init for env
16+
init:
17+
@terraform get
18+
@terraform init
19+
20+
## Create ssh key
21+
ssh-key:
22+
@ssh-keygen -q -N "" -b 4096 -C "SSH key for vpc-scenario-1 example" -f ./id_rsa
23+
24+
## use 'terraform plan' to 'target' the vpc in the vpc module
25+
plan-vpc:
26+
@terraform plan \
27+
-target="module.vpc.module.vpc" \
28+
-out=tf.out
29+
30+
## use 'terraform plan' to 'target' the public subnets in the vpc module
31+
plan-subnets:
32+
@terraform plan \
33+
-target="module.vpc.module.public-subnets" \
34+
-out=tf.out
35+
36+
## use 'terraform plan' to 'target' the public gateway in the vpc module
37+
plan-gateway:
38+
@terraform plan \
39+
-target="module.vpc.module.public-gateway" \
40+
-out=tf.out
41+
42+
## use 'terraform plan' to map out updates to apply
43+
plan:
44+
@terraform plan -out=tf.out
45+
46+
## use 'terraform apply' to apply updates in a 'tf.out' plan file
47+
apply: check-plan-file
48+
@terraform apply tf.out
49+
50+
## use 'terraform destroy' to remove all resources from AWS
51+
destroy:
52+
@terraform destroy
53+
54+
## rm -rf all files and state
55+
clean:
56+
@rm -f tf.out
57+
@rm -f id_rsa
58+
@rm -f id_rsa.pub
59+
@rm -f terraform.tfvars
60+
@rm -f terraform.*.backup
61+
@rm -f terraform.tfstate
62+
63+
## Show help screen.
64+
help:
65+
@echo "Please use \`make <target>' where <target> is one of\n\n"
66+
@awk '/^[a-zA-Z\-\_0-9]+:/ { \
67+
helpMessage = match(lastLine, /^## (.*)/); \
68+
if (helpMessage) { \
69+
helpCommand = substr($$1, 0, index($$1, ":")); \
70+
helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \
71+
printf "%-30s %s\n", helpCommand, helpMessage; \
72+
} \
73+
} \
74+
{ lastLine = $$0 }' $(MAKEFILE_LIST)

examples/ad-asg/README.md

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# Active Directory with seamless Windows EC2 join (from ASG)
2+
3+
The terraform code is built on top of
4+
[vpc-scenario1](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Scenario1.html)
5+
with two additional private subnets and a NAT gateway on a public
6+
subnet. This example demonstrate how an Windows EC2 instance present
7+
in
8+
[ASG](https://docs.aws.amazon.com/autoscaling/ec2/userguide/AutoScalingGroup.html)
9+
seamlessly joins an Active directory when it gets newly spawned. The
10+
only difference between this example and the [ad-ec2](../ad-ec2) is
11+
that this example uses ASG.
12+
13+
## Environment creation and deployment
14+
15+
To use this example set up AWS credentials and then run the commands in the
16+
following order:
17+
18+
```
19+
make ssh-key
20+
make init
21+
make plan-vpc
22+
make apply
23+
make plan-subnets
24+
make apply
25+
make plan-gateway
26+
make apply
27+
make plan
28+
make apply
29+
```
30+
31+
## Execution
32+
33+
Once you run the above commands, you will get an output like this:
34+
35+
``` shellsession
36+
...
37+
module.nat-gateway.aws_route_table_association.private-rta[0]: Refreshing state... [id=rtbassoc-0be4f2c71ef12e768]
38+
module.nat-gateway.aws_route_table_association.private-rta[1]: Refreshing state... [id=rtbassoc-08a1f878abab73841]
39+
aws_ssm_association.associate_ssm: Refreshing state... [id=996ff9a8-0931-4000-85aa-d01ef536f5a7]
40+
41+
42+
Outputs:
43+
44+
asg-name = test-ad-project-asg-cluster20190919093341776000000005
45+
microsoft-ad_dns_ip_addresses = [
46+
"10.23.21.134",
47+
"10.23.22.45",
48+
]
49+
microsoft-ad_dns_name = dev.fpcomplete.local
50+
```
51+
52+
## Testing
53+
54+
You need to test that the Windows EC2 instance actually joined the
55+
Active directory. There are two ways to test it:
56+
57+
* RDP to your instance and verify
58+
* RDP using Active Directory authentication
59+
60+
Note that once the terraform apply is completed, it takes some minutes
61+
(approximately _ minutes in my experience) for the instance to join
62+
the Active directory.
63+
64+
### Method 1
65+
66+
On a Linux client machine, something like
67+
[remmina](https://remmina.org) can be used to RDP into your Windows
68+
EC2 instance. You need to fill three information in the Remmina client
69+
to successfully RDP:
70+
71+
* Server: You can go and find the instance IP address using the
72+
`asg-name` from the above output. This can be done either via AWS
73+
Console or use the `aws` cli tool.
74+
* User name: Administrator
75+
* User password: The password you used with the variable named
76+
`admin_password` in `variables.tf`.
77+
78+
![Remmina settings](./assets/remmina-settings1.png)
79+
80+
Note that if you try to take the password from the AWS Console using
81+
your SSH private key, that won't work as it has been overridden using
82+
[bootstrap.win.txt](./bootstrap.win.txt).
83+
84+
Once you connect into the instance, you need to check the properties
85+
of your machine there:
86+
87+
![System Properties](./assets/system-properties.png)
88+
89+
If you have a `Domain:` entry there, then that means the instance has
90+
successfully joined the Active directory. Instead, if you have an
91+
entry that starts with `Workgroup:` then your device is not joined to an
92+
Active Directory.
93+
94+
### Method 2
95+
96+
In this method, you again try to RDP via the Active directory
97+
credentials. When you create a directory with AWS Managed Microsoft
98+
AD, it will create a directory administrator account with the user
99+
name `Admin` and the specified password (which you supplied through
100+
terraform). Let's again use Remmina to fill the following four
101+
information:
102+
103+
* Server: You can go and find the instance IP address using the
104+
`asg-name` from the above output. This can be done either via AWS
105+
Console or use the `aws` cli tool.
106+
* User name: Admin
107+
* User password: The password you used with the variable named
108+
`active_directory_password` in `variables.tf`.
109+
* Domain: The domain name which you passed in the `locals.tf`. For
110+
this example, it is `dev.fpcomplete.local`.
111+
112+
![Remmina settings](./assets/remmina-settings2.png)
113+
114+
If it's able to successfully connect to the instance, you can confirm
115+
that the EC2 instance has actually joined the AD. You can further verify that you have actually logged in via Active directory through the following steps:
116+
117+
* Start the "CMD" program.
118+
* Type "set user".
119+
* You will receive a output from the above command. Look at the line
120+
start with `USERDOMAIN:` entry. If it contains your computer's name,
121+
then you're logged in to the computer. If it contains the Active
122+
Directory's name, you're logged in to the Active Directory. In our
123+
case this is the output we receive which confirms that we are logged
124+
in via AD:
125+
126+
``` shellsession
127+
C:\Users\Admin>set user
128+
USERDNSDOMAIN=DEV.FPCOMPLETE.LOCAL
129+
USERDOMAIN=dev
130+
USERDOMAIN_ROAMINGPROFILE=dev
131+
USERNAME=Admin
132+
USERPROFILE=C:\Users\Admin
133+
```
134+
135+
## Destruction
136+
137+
To destroy the test environment run the following commands:
138+
139+
```
140+
$ make destroy
141+
$ make clean
142+
```
143+
144+
## Debugging
145+
146+
The script execution using `user_data` is usually hard to debug. In
147+
our [bootstrap script](./bootstrap.win.txt), we use
148+
[Start-Transcript](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.host/start-transcript?view=powershell-6)
149+
to create a record of the powershell session to a text file. For the
150+
above launched instances, it is present in the following location:
151+
152+
```
153+
C:\Users\Administrators\Documents
154+
```
155+
156+
## Reference
157+
158+
* [AWS docs on AWS Managed Microsoft AD](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_getting_started.html)
159+
* [AWS docs on Joining an EC2 instance](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_join_instance.html)
160+
* [AWS docs on Systems manager and AD](https://aws.amazon.com/premiumsupport/knowledge-center/ec2-systems-manager-dx-domain/)
53.5 KB
Loading
54.7 KB
Loading
65 KB
Loading

examples/ad-asg/bootstrap.win.txt

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<powershell>
2+
Start-Transcript
3+
4+
# Set administrator password
5+
net user Administrator "${admin_password}"
6+
wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE
7+
8+
# First, make sure WinRM can't be connected to
9+
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=block
10+
11+
# Delete any existing WinRM listeners
12+
winrm delete winrm/config/listener?Address=*+Transport=HTTP 2>$Null
13+
winrm delete winrm/config/listener?Address=*+Transport=HTTPS 2>$Null
14+
15+
# Create a new WinRM listener and configure
16+
winrm create winrm/config/listener?Address=*+Transport=HTTP
17+
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="0"}'
18+
winrm set winrm/config '@{MaxTimeoutms="7200000"}'
19+
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
20+
winrm set winrm/config/service '@{MaxConcurrentOperationsPerUser="12000"}'
21+
winrm set winrm/config/service/auth '@{Basic="true"}'
22+
winrm set winrm/config/client/auth '@{Basic="true"}'
23+
24+
# Configure UAC to allow privilege elevation in remote shells
25+
$Key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
26+
$Setting = 'LocalAccountTokenFilterPolicy'
27+
Set-ItemProperty -Path $Key -Name $Setting -Value 1 -Force
28+
29+
# Configure and restart the WinRM Service; Enable the required firewall exception
30+
Stop-Service -Name WinRM
31+
Set-Service -Name WinRM -StartupType Automatic
32+
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new action=allow localip=any remoteip=any
33+
Start-Service -Name WinRM
34+
35+
# Associate SSM document for domain joining
36+
$iid = (New-Object System.Net.WebClient).DownloadString("http://169.254.169.254/latest/meta-data/instance-id")
37+
New-SSMAssociation -InstanceId $iid -Name "${ssm_document_name}"
38+
</powershell>

examples/ad-asg/locals.tf

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
locals {
2+
stage = "dev"
3+
base_domain = "fpcomplete.local"
4+
domain = "${local.stage}.${local.base_domain}"
5+
}

0 commit comments

Comments
 (0)