This repository has been archived by the owner on Jul 11, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 97
Active Directory with seamless Windows EC2 join (from ASG) #253
Open
psibi
wants to merge
2
commits into
master
Choose a base branch
from
ad-asg
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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,74 @@ | ||
.PHONY: init ssh-key plan-vpc plan-subnets plan-gateway plan apply destroy clean | ||
|
||
.DEFAULT_GOAL = help | ||
|
||
# Hardcoding value of 3 minutes when we check if the plan file is stale | ||
STALE_PLAN_FILE := `find "tf.out" -mmin -3 | grep -q tf.out` | ||
|
||
## Check if tf.out is stale (Older than 2 minutes) | ||
check-plan-file: | ||
@if ! ${STALE_PLAN_FILE} ; then \ | ||
echo "ERROR: Stale tf.out plan file (older than 3 minutes)!"; \ | ||
exit 1; \ | ||
fi | ||
|
||
## Runs terraform get and terraform init for env | ||
init: | ||
@terraform get | ||
@terraform init | ||
|
||
## Create ssh key | ||
ssh-key: | ||
@ssh-keygen -q -N "" -b 4096 -C "SSH key for vpc-scenario-1 example" -f ./id_rsa | ||
|
||
## use 'terraform plan' to 'target' the vpc in the vpc module | ||
plan-vpc: | ||
@terraform plan \ | ||
-target="module.vpc.module.vpc" \ | ||
-out=tf.out | ||
|
||
## use 'terraform plan' to 'target' the public subnets in the vpc module | ||
plan-subnets: | ||
@terraform plan \ | ||
-target="module.vpc.module.public-subnets" \ | ||
-out=tf.out | ||
|
||
## use 'terraform plan' to 'target' the public gateway in the vpc module | ||
plan-gateway: | ||
@terraform plan \ | ||
-target="module.vpc.module.public-gateway" \ | ||
-out=tf.out | ||
|
||
## use 'terraform plan' to map out updates to apply | ||
plan: | ||
@terraform plan -out=tf.out | ||
|
||
## use 'terraform apply' to apply updates in a 'tf.out' plan file | ||
apply: check-plan-file | ||
@terraform apply tf.out | ||
|
||
## use 'terraform destroy' to remove all resources from AWS | ||
destroy: | ||
@terraform destroy | ||
|
||
## rm -rf all files and state | ||
clean: | ||
@rm -f tf.out | ||
@rm -f id_rsa | ||
@rm -f id_rsa.pub | ||
@rm -f terraform.tfvars | ||
@rm -f terraform.*.backup | ||
@rm -f terraform.tfstate | ||
|
||
## Show help screen. | ||
help: | ||
@echo "Please use \`make <target>' where <target> is one of\n\n" | ||
@awk '/^[a-zA-Z\-\_0-9]+:/ { \ | ||
helpMessage = match(lastLine, /^## (.*)/); \ | ||
if (helpMessage) { \ | ||
helpCommand = substr($$1, 0, index($$1, ":")); \ | ||
helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \ | ||
printf "%-30s %s\n", helpCommand, helpMessage; \ | ||
} \ | ||
} \ | ||
{ lastLine = $$0 }' $(MAKEFILE_LIST) |
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,156 @@ | ||
# Active Directory with seamless Windows EC2 join (from ASG) | ||
|
||
The terraform code is built on top of | ||
[vpc-scenario1](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Scenario1.html) | ||
with two additional private subnets and a NAT gateway on a public | ||
subnet. This example demonstrate how an Windows EC2 instance present | ||
in | ||
[ASG](https://docs.aws.amazon.com/autoscaling/ec2/userguide/AutoScalingGroup.html) | ||
seamlessly joins an Active directory when it gets newly spawned. The | ||
only difference between this example and the [ad-ec2](../ad-ec2) is | ||
that this example uses ASG. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we include all of the EC2/ASG/windows/linux variants in one single example? and do the "across VPCs" in a second example? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel having different example more clearly explains the differences. But I don't have strong opinion on that. |
||
|
||
## Environment creation and deployment | ||
|
||
To use this example set up AWS credentials and then run the commands in the | ||
following order: | ||
|
||
``` | ||
make ssh-key | ||
make init | ||
make plan-vpc | ||
make apply | ||
make plan-subnets | ||
make apply | ||
make plan-gateway | ||
make apply | ||
make plan | ||
make apply | ||
``` | ||
|
||
## Execution | ||
|
||
Once you run the above commands, you will get an output like this: | ||
|
||
``` shellsession | ||
... | ||
module.nat-gateway.aws_route_table_association.private-rta[0]: Refreshing state... [id=rtbassoc-0be4f2c71ef12e768] | ||
module.nat-gateway.aws_route_table_association.private-rta[1]: Refreshing state... [id=rtbassoc-08a1f878abab73841] | ||
aws_ssm_association.associate_ssm: Refreshing state... [id=996ff9a8-0931-4000-85aa-d01ef536f5a7] | ||
|
||
|
||
Outputs: | ||
|
||
asg-name = test-ad-project-asg-cluster20190919093341776000000005 | ||
microsoft-ad_dns_ip_addresses = [ | ||
"10.23.21.134", | ||
"10.23.22.45", | ||
] | ||
microsoft-ad_dns_name = dev.fpcomplete.local | ||
``` | ||
|
||
## Testing | ||
|
||
You need to test that the Windows EC2 instance actually joined the | ||
Active directory. There are two ways to test it: | ||
|
||
* RDP to your instance and verify | ||
* RDP using Active Directory authentication | ||
|
||
### Method 1 | ||
|
||
On a Linux client machine, something like | ||
[remmina](https://remmina.org) can be used to RDP into your Windows | ||
EC2 instance. You need to fill three information in the Remmina client | ||
to successfully RDP: | ||
|
||
* Server: You can go and find the instance IP address using the | ||
`asg-name` from the above output. This can be done either via AWS | ||
Console or use the `aws` cli tool. | ||
* User name: Administrator | ||
* User password: The password you used with the variable named | ||
`admin_password` in `variables.tf`. | ||
|
||
![Remmina settings](./assets/remmina-settings1.png) | ||
|
||
Note that if you try to take the password from the AWS Console using | ||
your SSH private key, that won't work as it has been overridden using | ||
[bootstrap.win.txt](./bootstrap.win.txt). | ||
|
||
Once you connect into the instance, you need to check the properties | ||
of your machine there: | ||
|
||
![System Properties](./assets/system-properties.png) | ||
|
||
If you have a `Domain:` entry there, then that means the instance has | ||
successfully joined the Active directory. Instead, if you have an | ||
entry that starts with `Workgroup:` then your device is not joined to an | ||
Active Directory. | ||
|
||
### Method 2 | ||
|
||
In this method, you again try to RDP via the Active directory | ||
credentials. When you create a directory with AWS Managed Microsoft | ||
AD, it will create a directory administrator account with the user | ||
name `Admin` and the specified password (which you supplied through | ||
terraform). Let's again use Remmina to fill the following four | ||
information: | ||
|
||
* Server: You can go and find the instance IP address using the | ||
`asg-name` from the above output. This can be done either via AWS | ||
Console or use the `aws` cli tool. | ||
* User name: Admin | ||
* User password: The password you used with the variable named | ||
`active_directory_password` in `variables.tf`. | ||
* Domain: The domain name which you passed in the `locals.tf`. For | ||
this example, it is `dev.fpcomplete.local`. | ||
|
||
![Remmina settings](./assets/remmina-settings2.png) | ||
|
||
If it's able to successfully connect to the instance, you can confirm | ||
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: | ||
|
||
* Start the "CMD" program. | ||
* Type "set user". | ||
* You will receive a output from the above command. Look at the line | ||
start with `USERDOMAIN:` entry. If it contains your computer's name, | ||
then you're logged in to the computer. If it contains the Active | ||
Directory's name, you're logged in to the Active Directory. In our | ||
case this is the output we receive which confirms that we are logged | ||
in via AD: | ||
|
||
``` shellsession | ||
C:\Users\Admin>set user | ||
USERDNSDOMAIN=DEV.FPCOMPLETE.LOCAL | ||
USERDOMAIN=dev | ||
USERDOMAIN_ROAMINGPROFILE=dev | ||
USERNAME=Admin | ||
USERPROFILE=C:\Users\Admin | ||
``` | ||
|
||
## Destruction | ||
|
||
To destroy the test environment run the following commands: | ||
|
||
``` | ||
$ make destroy | ||
$ make clean | ||
``` | ||
|
||
## Debugging | ||
|
||
The script execution using `user_data` is usually hard to debug. In | ||
our [bootstrap script](./bootstrap.win.txt), we use | ||
[Start-Transcript](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.host/start-transcript?view=powershell-6) | ||
to create a record of the powershell session to a text file. For the | ||
above launched instances, it is present in the following location: | ||
|
||
``` | ||
C:\Users\Administrators\Documents | ||
``` | ||
|
||
## Reference | ||
|
||
* [AWS docs on AWS Managed Microsoft AD](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_getting_started.html) | ||
* [AWS docs on Joining an EC2 instance](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_join_instance.html) | ||
* [AWS docs on Systems manager and AD](https://aws.amazon.com/premiumsupport/knowledge-center/ec2-systems-manager-dx-domain/) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,38 @@ | ||
<powershell> | ||
Start-Transcript | ||
|
||
# Set administrator password | ||
net user Administrator "${admin_password}" | ||
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 | ||
|
||
# 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 | ||
|
||
# Associate SSM document for domain joining | ||
$iid = (New-Object System.Net.WebClient).DownloadString("http://169.254.169.254/latest/meta-data/instance-id") | ||
New-SSMAssociation -InstanceId $iid -Name "${ssm_document_name}" | ||
</powershell> |
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,5 @@ | ||
locals { | ||
stage = "dev" | ||
base_domain = "fpcomplete.local" | ||
domain = "${local.stage}.${local.base_domain}" | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would VPC Scenario 2 work here too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ketzacoatl It will work, but it will require some slight modifications.