Deploy VPC architecture and Microsoft Active Directory domain controllers - AWS Systems Manager

Deploy VPC architecture and Microsoft Active Directory domain controllers

To increase efficiency and standardize common tasks, you might choose to automate deployments. This is useful if you regularly deploy the same architecture across multiple accounts and AWS Regions. Automating architecture deployments can also reduce the potential for human error that can occur when deploying architecture manually. AWS Systems Manager Automation actions can help you accomplish this. Automation is a capability of AWS Systems Manager.

The following example AWS Systems Manager runbook performs these actions:

  • Retrieves the latest Windows Server 2016 Amazon Machine Image (AMI) using Systems Manager Parameter Store to use when launching the EC2 instances that will be configured as domain controllers. Parameter Store is a capability of AWS Systems Manager.

  • Uses the aws:executeAwsApi automation action to call several AWS API operations to create the VPC architecture. The domain controller instances are launched in private subnets, and connect to the internet using a NAT gateway. This allows the SSM Agent on the instances to access the requisite Systems Manager endpoints.

  • Uses the aws:waitForAwsResourceProperty automation action to confirm the instances launched by the previous action are Online for AWS Systems Manager.

  • Uses the aws:runCommand automation action to configure the instances launched as Microsoft Active Directory domain controllers.

YAML
--- description: Custom Automation Deployment Example schemaVersion: '0.3' parameters: AutomationAssumeRole: type: String default: '' description: >- (Optional) The ARN of the role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to run this runbook. mainSteps: - name: getLatestWindowsAmi action: aws:executeAwsApi onFailure: Abort inputs: Service: ssm Api: GetParameter Name: >- /aws/service/ami-windows-latest/Windows_Server-2016-English-Full-Base outputs: - Name: amiId Selector: $.Parameter.Value Type: String nextStep: createSSMInstanceRole - name: createSSMInstanceRole action: aws:executeAwsApi onFailure: Abort inputs: Service: iam Api: CreateRole AssumeRolePolicyDocument: >- {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":["ec2.amazonaws.com"]},"Action":["sts:AssumeRole"]}]} RoleName: sampleSSMInstanceRole nextStep: attachManagedSSMPolicy - name: attachManagedSSMPolicy action: aws:executeAwsApi onFailure: Abort inputs: Service: iam Api: AttachRolePolicy PolicyArn: 'arn:aws:iam::aws:policy/service-role/AmazonSSMManagedInstanceCore' RoleName: sampleSSMInstanceRole nextStep: createSSMInstanceProfile - name: createSSMInstanceProfile action: aws:executeAwsApi onFailure: Abort inputs: Service: iam Api: CreateInstanceProfile InstanceProfileName: sampleSSMInstanceRole outputs: - Name: instanceProfileArn Selector: $.InstanceProfile.Arn Type: String nextStep: addSSMInstanceRoleToProfile - name: addSSMInstanceRoleToProfile action: aws:executeAwsApi onFailure: Abort inputs: Service: iam Api: AddRoleToInstanceProfile InstanceProfileName: sampleSSMInstanceRole RoleName: sampleSSMInstanceRole nextStep: createVpc - name: createVpc action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: CreateVpc CidrBlock: 10.0.100.0/22 outputs: - Name: vpcId Selector: $.Vpc.VpcId Type: String nextStep: getMainRtb - name: getMainRtb action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: DescribeRouteTables Filters: - Name: vpc-id Values: - '{{ createVpc.vpcId }}' outputs: - Name: mainRtbId Selector: '$.RouteTables[0].RouteTableId' Type: String nextStep: verifyMainRtb - name: verifyMainRtb action: aws:assertAwsResourceProperty onFailure: Abort inputs: Service: ec2 Api: DescribeRouteTables RouteTableIds: - '{{ getMainRtb.mainRtbId }}' PropertySelector: '$.RouteTables[0].Associations[0].Main' DesiredValues: - 'True' nextStep: createPubSubnet - name: createPubSubnet action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: CreateSubnet CidrBlock: 10.0.103.0/24 AvailabilityZone: us-west-2c VpcId: '{{ createVpc.vpcId }}' outputs: - Name: pubSubnetId Selector: $.Subnet.SubnetId Type: String nextStep: createPubRtb - name: createPubRtb action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: CreateRouteTable VpcId: '{{ createVpc.vpcId }}' outputs: - Name: pubRtbId Selector: $.RouteTable.RouteTableId Type: String nextStep: createIgw - name: createIgw action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: CreateInternetGateway outputs: - Name: igwId Selector: $.InternetGateway.InternetGatewayId Type: String nextStep: attachIgw - name: attachIgw action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: AttachInternetGateway InternetGatewayId: '{{ createIgw.igwId }}' VpcId: '{{ createVpc.vpcId }}' nextStep: allocateEip - name: allocateEip action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: AllocateAddress Domain: vpc outputs: - Name: eipAllocationId Selector: $.AllocationId Type: String nextStep: createNatGw - name: createNatGw action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: CreateNatGateway AllocationId: '{{ allocateEip.eipAllocationId }}' SubnetId: '{{ createPubSubnet.pubSubnetId }}' outputs: - Name: natGwId Selector: $.NatGateway.NatGatewayId Type: String nextStep: verifyNatGwAvailable - name: verifyNatGwAvailable action: aws:waitForAwsResourceProperty timeoutSeconds: 150 inputs: Service: ec2 Api: DescribeNatGateways NatGatewayIds: - '{{ createNatGw.natGwId }}' PropertySelector: '$.NatGateways[0].State' DesiredValues: - available nextStep: createNatRoute - name: createNatRoute action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: CreateRoute DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: '{{ createNatGw.natGwId }}' RouteTableId: '{{ getMainRtb.mainRtbId }}' nextStep: createPubRoute - name: createPubRoute action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: CreateRoute DestinationCidrBlock: 0.0.0.0/0 GatewayId: '{{ createIgw.igwId }}' RouteTableId: '{{ createPubRtb.pubRtbId }}' nextStep: setPubSubAssoc - name: setPubSubAssoc action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: AssociateRouteTable RouteTableId: '{{ createPubRtb.pubRtbId }}' SubnetId: '{{ createPubSubnet.pubSubnetId }}' - name: createDhcpOptions action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: CreateDhcpOptions DhcpConfigurations: - Key: domain-name-servers Values: - '10.0.100.50,10.0.101.50' - Key: domain-name Values: - sample.com outputs: - Name: dhcpOptionsId Selector: $.DhcpOptions.DhcpOptionsId Type: String nextStep: createDCSubnet1 - name: createDCSubnet1 action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: CreateSubnet CidrBlock: 10.0.100.0/24 AvailabilityZone: us-west-2a VpcId: '{{ createVpc.vpcId }}' outputs: - Name: firstSubnetId Selector: $.Subnet.SubnetId Type: String nextStep: createDCSubnet2 - name: createDCSubnet2 action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: CreateSubnet CidrBlock: 10.0.101.0/24 AvailabilityZone: us-west-2b VpcId: '{{ createVpc.vpcId }}' outputs: - Name: secondSubnetId Selector: $.Subnet.SubnetId Type: String nextStep: createDCSecGroup - name: createDCSecGroup action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: CreateSecurityGroup GroupName: SampleDCSecGroup Description: Security Group for Sample Domain Controllers VpcId: '{{ createVpc.vpcId }}' outputs: - Name: dcSecGroupId Selector: $.GroupId Type: String nextStep: authIngressDCTraffic - name: authIngressDCTraffic action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: AuthorizeSecurityGroupIngress GroupId: '{{ createDCSecGroup.dcSecGroupId }}' IpPermissions: - FromPort: -1 IpProtocol: '-1' IpRanges: - CidrIp: 0.0.0.0/0 Description: Allow all traffic between Domain Controllers nextStep: verifyInstanceProfile - name: verifyInstanceProfile action: aws:waitForAwsResourceProperty maxAttempts: 5 onFailure: Abort inputs: Service: iam Api: ListInstanceProfilesForRole RoleName: sampleSSMInstanceRole PropertySelector: '$.InstanceProfiles[0].Arn' DesiredValues: - '{{ createSSMInstanceProfile.instanceProfileArn }}' nextStep: iamEventualConsistency - name: iamEventualConsistency action: aws:sleep inputs: Duration: PT2M nextStep: launchDC1 - name: launchDC1 action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: RunInstances BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: DeleteOnTermination: true VolumeSize: 50 VolumeType: gp2 - DeviceName: xvdf Ebs: DeleteOnTermination: true VolumeSize: 100 VolumeType: gp2 IamInstanceProfile: Arn: '{{ createSSMInstanceProfile.instanceProfileArn }}' ImageId: '{{ getLatestWindowsAmi.amiId }}' InstanceType: t2.micro MaxCount: 1 MinCount: 1 PrivateIpAddress: 10.0.100.50 SecurityGroupIds: - '{{ createDCSecGroup.dcSecGroupId }}' SubnetId: '{{ createDCSubnet1.firstSubnetId }}' TagSpecifications: - ResourceType: instance Tags: - Key: Name Value: SampleDC1 outputs: - Name: pdcInstanceId Selector: '$.Instances[0].InstanceId' Type: String nextStep: launchDC2 - name: launchDC2 action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: RunInstances BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: DeleteOnTermination: true VolumeSize: 50 VolumeType: gp2 - DeviceName: xvdf Ebs: DeleteOnTermination: true VolumeSize: 100 VolumeType: gp2 IamInstanceProfile: Arn: '{{ createSSMInstanceProfile.instanceProfileArn }}' ImageId: '{{ getLatestWindowsAmi.amiId }}' InstanceType: t2.micro MaxCount: 1 MinCount: 1 PrivateIpAddress: 10.0.101.50 SecurityGroupIds: - '{{ createDCSecGroup.dcSecGroupId }}' SubnetId: '{{ createDCSubnet2.secondSubnetId }}' TagSpecifications: - ResourceType: instance Tags: - Key: Name Value: SampleDC2 outputs: - Name: adcInstanceId Selector: '$.Instances[0].InstanceId' Type: String nextStep: verifyDCInstanceState - name: verifyDCInstanceState action: aws:waitForAwsResourceProperty inputs: Service: ec2 Api: DescribeInstanceStatus IncludeAllInstances: true InstanceIds: - '{{ launchDC1.pdcInstanceId }}' - '{{ launchDC2.adcInstanceId }}' PropertySelector: '$.InstanceStatuses[0].InstanceState.Name' DesiredValues: - running nextStep: verifyInstancesOnlineSSM - name: verifyInstancesOnlineSSM action: aws:waitForAwsResourceProperty timeoutSeconds: 600 inputs: Service: ssm Api: DescribeInstanceInformation InstanceInformationFilterList: - key: InstanceIds valueSet: - '{{ launchDC1.pdcInstanceId }}' - '{{ launchDC2.adcInstanceId }}' PropertySelector: '$.InstanceInformationList[0].PingStatus' DesiredValues: - Online nextStep: installADRoles - name: installADRoles action: aws:runCommand inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{ launchDC1.pdcInstanceId }}' - '{{ launchDC2.adcInstanceId }}' Parameters: commands: |- try { Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools } catch { Write-Error "Failed to install ADDS Role." } nextStep: setAdminPassword - name: setAdminPassword action: aws:runCommand inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{ launchDC1.pdcInstanceId }}' Parameters: commands: - net user Administrator "sampleAdminPass123!" nextStep: createForest - name: createForest action: aws:runCommand inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{ launchDC1.pdcInstanceId }}' Parameters: commands: |- $dsrmPass = 'sample123!' | ConvertTo-SecureString -asPlainText -Force try { Install-ADDSForest -DomainName "sample.com" -DomainMode 6 -ForestMode 6 -InstallDNS -DatabasePath "D:\NTDS" -SysvolPath "D:\SYSVOL" -SafeModeAdministratorPassword $dsrmPass -Force } catch { Write-Error $_ } try { Add-DnsServerForwarder -IPAddress "10.0.100.2" } catch { Write-Error $_ } nextStep: associateDhcpOptions - name: associateDhcpOptions action: aws:executeAwsApi onFailure: Abort inputs: Service: ec2 Api: AssociateDhcpOptions DhcpOptionsId: '{{ createDhcpOptions.dhcpOptionsId }}' VpcId: '{{ createVpc.vpcId }}' nextStep: waitForADServices - name: waitForADServices action: aws:sleep inputs: Duration: PT1M nextStep: promoteADC - name: promoteADC action: aws:runCommand inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - '{{ launchDC2.adcInstanceId }}' Parameters: commands: |- ipconfig /renew $dsrmPass = 'sample123!' | ConvertTo-SecureString -asPlainText -Force $domAdminUser = "sample\Administrator" $domAdminPass = "sampleAdminPass123!" | ConvertTo-SecureString -asPlainText -Force $domAdminCred = New-Object System.Management.Automation.PSCredential($domAdminUser,$domAdminPass) try { Install-ADDSDomainController -DomainName "sample.com" -InstallDNS -DatabasePath "D:\NTDS" -SysvolPath "D:\SYSVOL" -SafeModeAdministratorPassword $dsrmPass -Credential $domAdminCred -Force } catch { Write-Error $_ }
JSON
{ "description": "Custom Automation Deployment Example", "schemaVersion": "0.3", "assumeRole": "{{ AutomationAssumeRole }}", "parameters": { "AutomationAssumeRole": { "type": "String", "description": "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to run this runbook.", "default": "" } }, "mainSteps": [ { "name": "getLatestWindowsAmi", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ssm", "Api": "GetParameter", "Name": "/aws/service/ami-windows-latest/Windows_Server-2016-English-Full-Base" }, "outputs": [ { "Name": "amiId", "Selector": "$.Parameter.Value", "Type": "String" } ], "nextStep": "createSSMInstanceRole" }, { "name": "createSSMInstanceRole", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "iam", "Api": "CreateRole", "AssumeRolePolicyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}", "RoleName": "sampleSSMInstanceRole" }, "nextStep": "attachManagedSSMPolicy" }, { "name": "attachManagedSSMPolicy", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "iam", "Api": "AttachRolePolicy", "PolicyArn": "arn:aws:iam::aws:policy/service-role/AmazonSSMManagedInstanceCore", "RoleName": "sampleSSMInstanceRole" }, "nextStep": "createSSMInstanceProfile" }, { "name": "createSSMInstanceProfile", "action":"aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "iam", "Api": "CreateInstanceProfile", "InstanceProfileName": "sampleSSMInstanceRole" }, "outputs": [ { "Name": "instanceProfileArn", "Selector": "$.InstanceProfile.Arn", "Type": "String" } ], "nextStep": "addSSMInstanceRoleToProfile" }, { "name": "addSSMInstanceRoleToProfile", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "iam", "Api": "AddRoleToInstanceProfile", "InstanceProfileName": "sampleSSMInstanceRole", "RoleName": "sampleSSMInstanceRole" }, "nextStep": "createVpc" }, { "name": "createVpc", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "CreateVpc", "CidrBlock": "10.0.100.0/22" }, "outputs": [ { "Name": "vpcId", "Selector": "$.Vpc.VpcId", "Type": "String" } "nextStep": "getMainRtb" }, { "name": "getMainRtb", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "DescribeRouteTables", "Filters": [ { "Name": "vpc-id", "Values": ["{{ createVpc.vpcId }}"] } ] }, "outputs": [ { "Name": "mainRtbId", "Selector": "$.RouteTables[0].RouteTableId", "Type": "String" } ], "nextStep": "verifyMainRtb" }, { "name": "verifyMainRtb", "action": "aws:assertAwsResourceProperty", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "DescribeRouteTables", "RouteTableIds": ["{{ getMainRtb.mainRtbId }}"], "PropertySelector": "$.RouteTables[0].Associations[0].Main", "DesiredValues": ["True"] }, "nextStep": "createPubSubnet" }, { "name": "createPubSubnet", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "CreateSubnet", "CidrBlock": "10.0.103.0/24", "AvailabilityZone": "us-west-2c", "VpcId": "{{ createVpc.vpcId }}" }, "outputs":[ { "Name": "pubSubnetId", "Selector": "$.Subnet.SubnetId", "Type": "String" } ], "nextStep": "createPubRtb" }, { "name": "createPubRtb", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "CreateRouteTable", "VpcId": "{{ createVpc.vpcId }}" }, "outputs": [ { "Name": "pubRtbId", "Selector": "$.RouteTable.RouteTableId", "Type": "String" } ], "nextStep": "createIgw" }, { "name": "createIgw", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "CreateInternetGateway" }, "outputs": [ { "Name": "igwId", "Selector": "$.InternetGateway.InternetGatewayId", "Type": "String" } ], "nextStep": "attachIgw" }, { "name": "attachIgw", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "AttachInternetGateway", "InternetGatewayId": "{{ createIgw.igwId }}", "VpcId": "{{ createVpc.vpcId }}" }, "nextStep": "allocateEip" }, { "name": "allocateEip", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "AllocateAddress", "Domain": "vpc" }, "outputs": [ { "Name": "eipAllocationId", "Selector": "$.AllocationId", "Type": "String" } ], "nextStep": "createNatGw" }, { "name": "createNatGw", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "CreateNatGateway", "AllocationId": "{{ allocateEip.eipAllocationId }}", "SubnetId": "{{ createPubSubnet.pubSubnetId }}" }, "outputs":[ { "Name": "natGwId", "Selector": "$.NatGateway.NatGatewayId", "Type": "String" } ], "nextStep": "verifyNatGwAvailable" }, { "name": "verifyNatGwAvailable", "action": "aws:waitForAwsResourceProperty", "timeoutSeconds": 150, "inputs": { "Service": "ec2", "Api": "DescribeNatGateways", "NatGatewayIds": [ "{{ createNatGw.natGwId }}" ], "PropertySelector": "$.NatGateways[0].State", "DesiredValues": [ "available" ] }, "nextStep": "createNatRoute" }, { "name": "createNatRoute", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "CreateRoute", "DestinationCidrBlock": "0.0.0.0/0", "NatGatewayId": "{{ createNatGw.natGwId }}", "RouteTableId": "{{ getMainRtb.mainRtbId }}" }, "nextStep": "createPubRoute" }, { "name": "createPubRoute", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "CreateRoute", "DestinationCidrBlock": "0.0.0.0/0", "GatewayId": "{{ createIgw.igwId }}", "RouteTableId": "{{ createPubRtb.pubRtbId }}" }, "nextStep": "setPubSubAssoc" }, { "name": "setPubSubAssoc", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "AssociateRouteTable", "RouteTableId": "{{ createPubRtb.pubRtbId }}", "SubnetId": "{{ createPubSubnet.pubSubnetId }}" } }, { "name": "createDhcpOptions", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "CreateDhcpOptions", "DhcpConfigurations": [ { "Key": "domain-name-servers", "Values": ["10.0.100.50,10.0.101.50"] }, { "Key": "domain-name", "Values": ["sample.com"] } ] }, "outputs": [ { "Name": "dhcpOptionsId", "Selector": "$.DhcpOptions.DhcpOptionsId", "Type": "String" } ], "nextStep": "createDCSubnet1" }, { "name": "createDCSubnet1", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "CreateSubnet", "CidrBlock": "10.0.100.0/24", "AvailabilityZone": "us-west-2a", "VpcId": "{{ createVpc.vpcId }}" }, "outputs": [ { "Name": "firstSubnetId", "Selector": "$.Subnet.SubnetId", "Type": "String" } ], "nextStep": "createDCSubnet2" }, { "name": "createDCSubnet2", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "CreateSubnet", "CidrBlock": "10.0.101.0/24", "AvailabilityZone": "us-west-2b", "VpcId": "{{ createVpc.vpcId }}" }, "outputs": [ { "Name": "secondSubnetId", "Selector": "$.Subnet.SubnetId", "Type": "String" } ], "nextStep": "createDCSecGroup" }, { "name": "createDCSecGroup", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "CreateSecurityGroup", "GroupName": "SampleDCSecGroup", "Description": "Security Group for Example Domain Controllers", "VpcId": "{{ createVpc.vpcId }}" }, "outputs": [ { "Name": "dcSecGroupId", "Selector": "$.GroupId", "Type": "String" } ], "nextStep": "authIngressDCTraffic" }, { "name": "authIngressDCTraffic", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "AuthorizeSecurityGroupIngress", "GroupId": "{{ createDCSecGroup.dcSecGroupId }}", "IpPermissions": [ { "FromPort": -1, "IpProtocol": "-1", "IpRanges": [ { "CidrIp": "0.0.0.0/0", "Description": "Allow all traffic between Domain Controllers" } ] } ] }, "nextStep": "verifyInstanceProfile" }, { "name": "verifyInstanceProfile", "action": "aws:waitForAwsResourceProperty", "maxAttempts": 5, "onFailure": "Abort", "inputs": { "Service": "iam", "Api": "ListInstanceProfilesForRole", "RoleName": "sampleSSMInstanceRole", "PropertySelector": "$.InstanceProfiles[0].Arn", "DesiredValues": [ "{{ createSSMInstanceProfile.instanceProfileArn }}" ] }, "nextStep": "iamEventualConsistency" }, { "name": "iamEventualConsistency", "action": "aws:sleep", "inputs": { "Duration": "PT2M" }, "nextStep": "launchDC1" }, { "name": "launchDC1", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "RunInstances", "BlockDeviceMappings": [ { "DeviceName": "/dev/sda1", "Ebs": { "DeleteOnTermination": true, "VolumeSize": 50, "VolumeType": "gp2" } }, { "DeviceName": "xvdf", "Ebs": { "DeleteOnTermination": true, "VolumeSize": 100, "VolumeType": "gp2" } } ], "IamInstanceProfile": { "Arn": "{{ createSSMInstanceProfile.instanceProfileArn }}" }, "ImageId": "{{ getLatestWindowsAmi.amiId }}", "InstanceType": "t2.micro", "MaxCount": 1, "MinCount": 1, "PrivateIpAddress": "10.0.100.50", "SecurityGroupIds": [ "{{ createDCSecGroup.dcSecGroupId }}" ], "SubnetId": "{{ createDCSubnet1.firstSubnetId }}", "TagSpecifications": [ { "ResourceType": "instance", "Tags": [ { "Key": "Name", "Value": "SampleDC1" } ] } ] }, "outputs": [ { "Name": "pdcInstanceId", "Selector": "$.Instances[0].InstanceId", "Type": "String" } ], "nextStep": "launchDC2" }, { "name": "launchDC2", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "RunInstances", "BlockDeviceMappings": [ { "DeviceName": "/dev/sda1", "Ebs": { "DeleteOnTermination": true, "VolumeSize": 50, "VolumeType": "gp2" } }, { "DeviceName": "xvdf", "Ebs": { "DeleteOnTermination": true, "VolumeSize": 100, "VolumeType": "gp2" } } ], "IamInstanceProfile": { "Arn": "{{ createSSMInstanceProfile.instanceProfileArn }}" }, "ImageId": "{{ getLatestWindowsAmi.amiId }}", "InstanceType": "t2.micro", "MaxCount": 1, "MinCount": 1, "PrivateIpAddress": "10.0.101.50", "SecurityGroupIds": [ "{{ createDCSecGroup.dcSecGroupId }}" ], "SubnetId": "{{ createDCSubnet2.secondSubnetId }}", "TagSpecifications": [ { "ResourceType": "instance", "Tags": [ { "Key": "Name", "Value": "SampleDC2" } ] } ] }, "outputs": [ { "Name": "adcInstanceId", "Selector": "$.Instances[0].InstanceId", "Type": "String" } ], "nextStep": "verifyDCInstanceState" }, { "name": "verifyDCInstanceState", "action": "aws:waitForAwsResourceProperty", "inputs": { "Service": "ec2", "Api": "DescribeInstanceStatus", "IncludeAllInstances": true, "InstanceIds": [ "{{ launchDC1.pdcInstanceId }}", "{{ launchDC2.adcInstanceId }}" ], "PropertySelector": "$.InstanceStatuses[0].InstanceState.Name", "DesiredValues": [ "running" ] }, "nextStep": "verifyInstancesOnlineSSM" }, { "name": "verifyInstancesOnlineSSM", "action": "aws:waitForAwsResourceProperty", "timeoutSeconds": 600, "inputs": { "Service": "ssm", "Api": "DescribeInstanceInformation", "InstanceInformationFilterList": [ { "key": "InstanceIds", "valueSet": [ "{{ launchDC1.pdcInstanceId }}", "{{ launchDC2.adcInstanceId }}" ] } ], "PropertySelector": "$.InstanceInformationList[0].PingStatus", "DesiredValues": [ "Online" ] }, "nextStep": "installADRoles" }, { "name": "installADRoles", "action": "aws:runCommand", "inputs": { "DocumentName": "AWS-RunPowerShellScript", "InstanceIds": [ "{{ launchDC1.pdcInstanceId }}", "{{ launchDC2.adcInstanceId }}" ], "Parameters": { "commands": [ "try {", " Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools", "}", "catch {", " Write-Error \"Failed to install ADDS Role.\"", "}" ] } }, "nextStep": "setAdminPassword" }, { "name": "setAdminPassword", "action": "aws:runCommand", "inputs": { "DocumentName": "AWS-RunPowerShellScript", "InstanceIds": [ "{{ launchDC1.pdcInstanceId }}" ], "Parameters": { "commands": [ "net user Administrator \"sampleAdminPass123!\"" ] } }, "nextStep": "createForest" }, { "name": "createForest", "action": "aws:runCommand", "inputs": { "DocumentName": "AWS-RunPowerShellScript", "InstanceIds": [ "{{ launchDC1.pdcInstanceId }}" ], "Parameters": { "commands": [ "$dsrmPass = 'sample123!' | ConvertTo-SecureString -asPlainText -Force", "try {", " Install-ADDSForest -DomainName \"sample.com\" -DomainMode 6 -ForestMode 6 -InstallDNS -DatabasePath \"D:\\NTDS\" -SysvolPath \"D:\\SYSVOL\" -SafeModeAdministratorPassword $dsrmPass -Force", "}", "catch {", " Write-Error $_", "}", "try {", " Add-DnsServerForwarder -IPAddress \"10.0.100.2\"", "}", "catch {", " Write-Error $_", "}" ] } }, "nextStep": "associateDhcpOptions" }, { "name": "associateDhcpOptions", "action": "aws:executeAwsApi", "onFailure": "Abort", "inputs": { "Service": "ec2", "Api": "AssociateDhcpOptions", "DhcpOptionsId": "{{ createDhcpOptions.dhcpOptionsId }}", "VpcId": "{{ createVpc.vpcId }}" }, "nextStep": "waitForADServices" }, { "name": "waitForADServices", "action": "aws:sleep", "inputs": { "Duration": "PT1M" }, "nextStep": "promoteADC" }, { "name": "promoteADC", "action": "aws:runCommand", "inputs": { "DocumentName": "AWS-RunPowerShellScript", "InstanceIds": [ "{{ launchDC2.adcInstanceId }}" ], "Parameters": { "commands": [ "ipconfig /renew", "$dsrmPass = 'sample123!' | ConvertTo-SecureString -asPlainText -Force", "$domAdminUser = \"sample\\Administrator\"", "$domAdminPass = \"sampleAdminPass123!\" | ConvertTo-SecureString -asPlainText -Force", "$domAdminCred = New-Object System.Management.Automation.PSCredential($domAdminUser,$domAdminPass)", "try {", " Install-ADDSDomainController -DomainName \"sample.com\" -InstallDNS -DatabasePath \"D:\\NTDS\" -SysvolPath \"D:\\SYSVOL\" -SafeModeAdministratorPassword $dsrmPass -Credential $domAdminCred -Force", "}", "catch {", " Write-Error $_", "}" ] } } } ] }