Automating Windows Workloads in AWS using Systems Manager and PowerShell DSC with Terragrunt Part1.
Javier Sepúlveda
Posted on April 19, 2024
Cloud people!
In this opportunity, I want to share a scenary for automated and cover workloads in windows and runned automations based on powershell dsc with systems manager.
Check github repository for the code
Requirements
Step 1.
The first step is configuring the network layer, it is using the vpc module of registry, all information about this module in this link.
For effects practices only two subnets are created, one private subnet and one public subnet, this demo not need more.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.2"
name = local.name
cidr = local.vpc_cidr
azs = local.azs
public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 6, k)]
private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 6, k + 10)]
enable_nat_gateway = true
create_igw = true
enable_dns_hostnames = true
single_nat_gateway = true
manage_default_network_acl = true
default_network_acl_tags = { Name = "${local.name}-default" }
manage_default_route_table = true
default_route_table_tags = { Name = "${local.name}-default" }
manage_default_security_group = true
default_security_group_tags = { Name = "${local.name}-default" }
tags = local.tags
}
Step 2.
The second step is related to create security group, in this case the open ports are 3389 and 80, and like step 1 it is using the security group module of registry, all information about this module in this link.
module "security_group_bastion" {
source = "terraform-aws-modules/security-group/aws"
version = "5.1.0"
name = "windows security group"
description = "windows security group"
vpc_id = var.vpc_id
ingress_with_cidr_blocks = [
{
from_port = 3389
to_port = 3389
protocol = "tcp"
description = "rdp ports"
cidr_blocks = "172.16.0.0/16"
},
{
from_port = 3389
to_port = 3389
protocol = "tcp"
description = "rdp ports"
cidr_blocks = "0.0.0.0/0"
},
{
from_port = 5986
to_port = 5986
protocol = "tcp"
description = "winrm"
cidr_blocks = "0.0.0.0/0"
},
{
from_port = 80
to_port = 80
protocol = "tcp"
description = "http"
cidr_blocks = "0.0.0.0/0"
},
]
egress_with_cidr_blocks = [
{
description = "Allowing outbound traffic"
to_port = 0
protocol = "-1"
cidr_blocks = "0.0.0.0/0"
from_port = 0
},
]
}
Step 3.
This step is to deploy the ec2 instance and use the userdata for to prepare the instance with the necessary. But for this simple scenary all modules are installed by default so not is neccesary install modules in userdata process. If you need other modules, you can find in this link the modules, I hope cover other modules in the future.
userdata
<powershell>
New-NetFirewallRule -DisplayName 'Allow local VPC' -Direction Inbound -LocalAddress 172.16.0.0/16 -LocalPort Any -Action Allow
# Instalar y cargar el proveedor NuGet
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
Import-PackageProvider -Name NuGet -Force
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
#DSC module IIS
Install-Module -Name WebAdministrationDsc -RequiredVersion 4.1.0 -Confirm:$false -Force
</powershell>
If you need install by example an module dsc different you need to add in the userdata these installation.
Something like this:
Install-Module -Name ComputerManagementDsc -RequiredVersion 9.0.0
-Force -Confirm:$false
Important: As best practice, always specific the module version because variables or functions can be changed between version and your configuration can be affected, additional your instance always need to have installed the module that is using your configuration.
Windows Ec2
module "ec2-instance" {
source = "terraform-aws-modules/ec2-instance/aws"
version = "5.6.0"
name = "windows-server"
ami = "ami-00d990e7e5ece7974" #Microsoft Windows Server 2022 Base
instance_type = "t3.medium"
subnet_id = element(var.public_subnet_ids, 0)
key_name = "clustersql2"
monitoring = true
vpc_security_group_ids = [var.security_group_id]
associate_public_ip_address = true
create_iam_instance_profile = true
iam_role_description = "IAM role for EC2 instance"
iam_role_policies = {
AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
user_data = base64encode("${path.module}/userdata/bastion_user_data.ps1")
user_data_replace_on_change = true
tags = local.tags
}
Modules by default with windows.
Step 4.
In this step it is necessary add the script in some part for add to the instance, for this case the script will be add in the s3 bucket for in the next step the automation of systems manager can be downloaded and execute locally in ec2. it is using the s3module of registry, all information about this module in this link.
module "s3-bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
version = "4.1.2"
bucket = "s3-script-dsc-${random_string.script_suffix.result}"
force_destroy = true
}
module "s3-bucket_object" {
source = "terraform-aws-modules/s3-bucket/aws//modules/object"
version = "4.1.2"
bucket = module.s3-bucket.s3_bucket_id
key = "dsc.ps1"
file_source = "../../scripts/dsc.ps1"
}
resource "random_string" "script_suffix" {
length = 6
special = false
lower = true
upper = false
}
DSC uses a Pull and Push mode, for this scenario it is using the push mode, with a pull mode this step would not be necessary.
Review script
This script automated the deploy of a website in IIS, create a folder with a index.html and add new content, additional stoped the default website.
configuration website {
Import-DscResource -ModuleName WebAdministrationDsc -ModuleVersion 4.1.0
Node $env:COMPUTERNAME
{
WindowsFeature IIS
{
Ensure = 'Present'
Name = 'Web-Server'
}
WindowsFeature AspNet45
{
Ensure = 'Present'
Name = 'Web-Asp-Net45'
}
WebSite DefaultSite
{
Ensure = 'Present'
Name = 'Default Web Site'
State = 'Stopped'
ServerAutoStart = $false
PhysicalPath = 'C:\inetpub\wwwroot'
DependsOn = '[WindowsFeature]IIS'
}
File WebContent
{
Ensure = 'Present'
DestinationPath = 'C:\segoja7\www\index.html'
Recurse = $true
Type = 'File'
Contents = 'cloud people website using dsc'
DependsOn = '[WindowsFeature]AspNet45'
}
WebSite NewWebsite
{
Ensure = 'Present'
Name = 'segoja7'
State = 'Started'
ServerAutoStart = $true
PhysicalPath = 'C:\segoja7\www\'
DependsOn = '[File]WebContent'
}
}
}
website -OutputPath ".\website"
Start-DscConfiguration -Path ".\website" -Wait -Force -ComputerName $env:COMPUTERNAME
You can use the command, for know the submodule structure
Get-DscResource -Name WindowsFeature -Syntax
With the script in s3, The next step is create automation for run in windows ec2 instance.
Step 5.
In this step it is created an automation and a document, the document is associated in the automation and the automation is associated with an association for trigger the automations when this is created.
resource "aws_ssm_document" "dsc-automation" {
name = "dsc-automation"
document_type = "Automation"
document_format = "YAML"
content = templatefile("${path.module}/documents/automation.yaml", {
instance_id = var.instance_id
Assume_role = aws_iam_role.dsc-automationssm-role.arn
})
}
resource "aws_ssm_document" "dsc-script" {
name = "dsc-script"
document_type = "Command"
document_format = "YAML"
content = templatefile("${path.module}/documents/dsc-script.yaml", {
names3bucket = var.s3_bucket_id
})
}
resource "aws_ssm_association" "dsc-association" {
name = aws_ssm_document.dsc-automation.name
association_name = aws_ssm_document.dsc-automation.name
# apply_only_at_cron_interval = true
parameters = {
Instance = var.instance_id
}
}
resource "aws_iam_role" "dsc-automationssm-role" {
name = "wsfc-automationssm-role"
assume_role_policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Principal" : {
"Service" : [
"ssm.amazonaws.com"
]
},
"Action" : "sts:AssumeRole",
"Condition" : {
"StringEquals" : {
"aws:SourceAccount" : "${data.aws_caller_identity.current.account_id}"
},
"ArnLike" : {
"aws:SourceArn" : "arn:aws:ssm:*:${data.aws_caller_identity.current.account_id}:automation-execution/*"
}
}
}
]
}
)
}
resource "aws_iam_role_policy_attachment" "dsc-policy-attachment" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole"
role = aws_iam_role.dsc-automationssm-role.name
}
When the automation is created with terraform, this is executed using the instance ID as target.
Step 6.
It is time for validate the automation, that bassically executed the document dsc-script the step 5, the document make a download of the script located in s3 bucket, later this is executed from C:\Scripts\dsc.ps1.
Step 7.
With the script executed it is time for check the site web using the public dns of ec2 instance.
Conclusion, DSC is a great tool for obtain a state desired of a machine in windows there are many things for cover about of DSC powershell I hope in other oportunity write other blog related to this tool.
If you have any questions, please leave them in the comments!
Successful!!
Posted on April 19, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
April 19, 2024