AWSTemplateFormatVersion: '2010-09-09'
Description: 'This example template describes an installation of Microsoft Active
  Directory set up on a single EC2 instance. The domain administrator password is
  the one retrieved from the instance. To add members to the domain: first, make sure
  members are launched in domain member security group created by this template; then,
  configure members to use this instance''s private IP address as the DNS server.
  **WARNING** This template creates a Windows instance and related resources. You
  will be billed for the AWS resources used if you create a stack from this template.'
Parameters:
  DomainDNSName:
    AllowedPattern: '[a-zA-Z0-9]+\..+'
    Description: 'Fully qualified domain name (FQDN) of the forest root domain, for
      example: corp.example.com'
    MaxLength: '25'
    MinLength: '3'
    Type: String
  DomainNetBIOSName:
    AllowedPattern: '[a-zA-Z0-9]+'
    Description: 'NetBIOS name of the domain, up to 15 characters, for users of earlier
      versions of Windows, for example: CORP'
    MaxLength: '15'
    MinLength: '1'
    Type: String
  ImageID:
    AllowedPattern: ami-[a-z0-9]+
    ConstraintDescription: please specify an AMI ID.
    Description: ID of the Microsoft Windows-based AMI to use for the EC2 instance.
    Type: String
  InstanceType:
    AllowedValues:
    - t1.micro
    - t2.micro
    - t2.small
    - t2.medium
    - m1.small
    - m1.medium
    - m1.large
    - m1.xlarge
    - m2.xlarge
    - m2.2xlarge
    - m2.4xlarge
    - m3.medium
    - m3.large
    - m3.xlarge
    - m3.2xlarge
    - m4.large
    - m4.xlarge
    - m4.2xlarge
    - m4.4xlarge
    - m4.10xlarge
    - c1.medium
    - c1.xlarge
    - c3.large
    - c3.xlarge
    - c3.2xlarge
    - c3.4xlarge
    - c3.8xlarge
    - c4.large
    - c4.xlarge
    - c4.2xlarge
    - c4.4xlarge
    - c4.8xlarge
    - g2.2xlarge
    - r3.large
    - r3.xlarge
    - r3.2xlarge
    - r3.4xlarge
    - r3.8xlarge
    - i2.xlarge
    - i2.2xlarge
    - i2.4xlarge
    - i2.8xlarge
    - d2.xlarge
    - d2.2xlarge
    - d2.4xlarge
    - d2.8xlarge
    - hi1.4xlarge
    - hs1.8xlarge
    - cr1.8xlarge
    - cc2.8xlarge
    - cg1.4xlarge
    ConstraintDescription: must be a valid EC2 instance type.
    Default: t2.micro
    Description: Amazon EC2 instance type to use.
    Type: String
  KeyName:
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
    Description: Name of an existing EC2 KeyPair to use.
    Type: AWS::EC2::KeyPair::KeyName
  RestoreModePassword:
    AllowedPattern: (?=^.{6,255}$)((?=.*\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))^.*
    Description: 'Password for a separate Administrator account when the domain controller
      is in Restore Mode: must be at least 8 characters and containing letters, numbers
      and symbols.'
    MaxLength: '32'
    MinLength: '8'
    NoEcho: 'True'
    Type: String
  SourceCidrForRDP:
    AllowedPattern: ^([0-9]+\.){3}[0-9]+\/[0-9]+$
    Description: IPv4 CIDR from which you are likely to connect to instances via RDP.
    MaxLength: '18'
    MinLength: '9'
    Type: String
Resources:
  DomainController:
    Type: AWS::EC2::Instance
    Metadata:
      AWS::CloudFormation::Init:
        config:
          commands:
            1-run-dcpromo:
              command: !Sub 'C:\cfn\RunCommand.bat "dcpromo /unattend /ReplicaOrNewDomain:Domain
                /NewDomain:Forest /NewDomainDNSName:${DomainDNSName} /ForestLevel:4
                /DomainNetbiosName:${DomainNetBIOSName} /DomainLevel:4 /InstallDNS:Yes
                /ConfirmGc:Yes /CreateDNSDelegation:No /DatabasePath:"C:\Windows\NTDS"
                /LogPath:"C:\Windows\NTDS" /SYSVOLPath:"C:\Windows\SYSVOL" /SafeModeAdminPassword=${RestoreModePassword}
                /RebootOnCompletion:Yes"'
              waitAfterCompletion: forever
            2-signal-success:
              command: !Sub ['cfn-signal.exe -e 0 "${Param1}"', {Param1: !Base64 {
                    Ref: DomainControllerWaitHandle}}]
          files:
            C:\cfn\RunCommand.bat:
              content: '%~1

                IF %ERRORLEVEL% GTR 10 ( exit /b 1 ) else ( exit /b 0 )'
            C:\cfn\cfn-hup.conf:
              content: !Sub '[main]

                stack=${AWS::StackId}

                region=${AWS::Region}

                '
            C:\cfn\hooks.d\cfn-auto-reloader.conf:
              content: !Sub '[cfn-auto-reloader-hook]

                triggers=post.update

                path=Resources.DomainController.Metadata.AWS::CloudFormation::Init

                action=cfn-init.exe -v -s ${AWS::StackId} -r DomainController --region
                ${AWS::Region}

                '
          services:
            windows:
              cfn-hup:
                enabled: 'true'
                ensureRunning: 'true'
                files:
                - C:\cfn\cfn-hup.conf
                - C:\cfn\hooks.d\cfn-auto-reloader.conf
    Properties:
      ImageId: !Ref 'ImageID'
      InstanceType: !Ref 'InstanceType'
      KeyName: !Ref 'KeyName'
      SecurityGroups:
      - !Ref 'DomainControllerSecurityGroup'
      Tags:
      - Key: Name
        Value: !Sub '${AWS::StackName}-DomainController'
      UserData: !Base64
        Fn::Sub: '<script>

          cfn-init.exe -v -s ${AWS::StackId} -r DomainController  --region ${AWS::Region}

          </script>'
  DomainControllerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Domain Controller
      SecurityGroupIngress:
      - FromPort: '123'
        IpProtocol: udp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '123'
      - FromPort: '135'
        IpProtocol: tcp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '135'
      - FromPort: '138'
        IpProtocol: udp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '138'
      - FromPort: '1024'
        IpProtocol: tcp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '65535'
      - FromPort: '389'
        IpProtocol: tcp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '389'
      - FromPort: '389'
        IpProtocol: udp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '389'
      - FromPort: '636'
        IpProtocol: tcp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '636'
      - FromPort: '3268'
        IpProtocol: tcp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '3268'
      - FromPort: '3269'
        IpProtocol: tcp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '3269'
      - FromPort: '53'
        IpProtocol: tcp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '53'
      - FromPort: '53'
        IpProtocol: udp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '53'
      - FromPort: '88'
        IpProtocol: tcp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '88'
      - FromPort: '88'
        IpProtocol: udp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '88'
      - FromPort: '445'
        IpProtocol: tcp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '445'
      - FromPort: '445'
        IpProtocol: udp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '445'
      - FromPort: '135'
        IpProtocol: udp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '135'
      - CidrIp: !Ref 'SourceCidrForRDP'
        FromPort: '3389'
        IpProtocol: tcp
        ToPort: '3389'
      - FromPort: '-1'
        IpProtocol: icmp
        SourceSecurityGroupName: !Ref 'DomainMemberSecurityGroup'
        ToPort: '-1'
      Tags:
      - Key: Name
        Value: !Sub '${AWS::StackName}-DomainControllerSecurityGroup'
  DomainControllerWaitCondition:
    Type: AWS::CloudFormation::WaitCondition
    DependsOn: DomainController
    Properties:
      Handle: !Ref 'DomainControllerWaitHandle'
      Timeout: '3600'
  DomainControllerWaitHandle:
    Type: AWS::CloudFormation::WaitConditionHandle
  DomainMemberSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Domain Members
      SecurityGroupIngress: []
      Tags:
      - Key: Name
        Value: !Sub '${AWS::StackName}-DomainMemberSecurityGroup'
  DomainMemberSecurityGroupIngressSourceCidrForRDP:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      CidrIp: !Ref 'SourceCidrForRDP'
      FromPort: '3389'
      GroupName: !Ref 'DomainMemberSecurityGroup'
      IpProtocol: tcp
      ToPort: '3389'
Outputs:
  DNSIPAddress:
    Description: Private IP address of the example EC2 instance running Active Directory.
    Value: !GetAtt 'DomainController.PrivateIp'
  DomainControllerDNSName:
    Description: Public DNS name of Active Directory server for RDP connection.
    Value: !GetAtt 'DomainController.PublicDnsName'
  DomainMemberSecurityGroupID:
    Description: ID of the Domain Member Security Group.
    Export:
      Name: !Sub '${AWS::StackName}-DomainMemberSecurityGroupID'
    Value: !GetAtt 'DomainMemberSecurityGroup.GroupId'
  InstanceID:
    Description: ID of the EC2 instance.
    Value: !Ref 'DomainController'

