text: from aws_cdk import core
  import aws_cdk.aws_directoryservice as _ds
  import aws_cdk.aws_workspaces as _ws
  import aws_cdk.aws_ec2 as _ec2
  import aws_cdk.aws_iam as _iam
  import aws_cdk.aws_ssm as _ssm
  import aws_cdk.aws_route53 as _r53
  import aws_cdk.aws_lambda as _lambda
  import aws_cdk.aws_cloudformation as _cf
  import aws_cdk.aws_secretsmanager as _sm
  
  
  class AWSManagedAD(core.Stack):
  
      def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
          super().__init__(scope, id, **kwargs)
  
          # Global variables to import cdk contexts from cdk.json
          _vpcID = self.node.try_get_context("VpcId")
          _sm_password = self.node.try_get_context("Secret_domain_password_arn")
          _dname = self.node.try_get_context("Domain_name")
          _subnet1 = self.node.try_get_context("Subnet1")
          _subnet2 = self.node.try_get_context("Subnet2")
          _sm_ec2keypair = self.node.try_get_context("Secret_keypair_arn")
          _ec2instance = self.node.try_get_context("Instance_type")
  
          # Import Vpc from the existing one in the AWS Account
          Vpc = _ec2.Vpc.from_lookup(self,"ImportVPC",vpc_id = _vpcID)
          Subnet1 = _ec2.Subnet.from_subnet_attributes(
              self,"subnetfromADManagedAD",
              subnet_id = _subnet1[0],
              availability_zone = _subnet1[1]
          )
  
          # Import a Secret Manager Secret for Domain Password
          secret_adpassword = _sm.Secret.from_secret_arn(
              self, "AdPasswordSecretStore",
              secret_arn = _sm_password
          )
  
          #Import a Secret Manager Secre for EC2 KeyPair
          secret_ec2keypair = _sm.Secret.from_secret_arn(
              self, "ImportEC2KeyPairSecretStore",
              secret_arn = _sm_ec2keypair
          )
  
          # Create an AWS Managed AD Service in STANDARD Version
          ad = _ds.CfnMicrosoftAD(
              self,"ManagedAD",
              name = _dname,
              password = secret_adpassword.secret_value_from_json("Key").to_string(),
              edition = "Standard",
              vpc_settings =
                  { "vpcId": _vpcID,
                    "subnetIds": [ _subnet1[0], _subnet2[0] ]
                  }
  	    )
  
          self.directory = ad
  
          # Create r53 hosted Zone for DNS DomainName
          hostedzone = _r53.HostedZone(
              self, "HostedZoneforAD",
              zone_name = _dname,
              vpcs = [Vpc]
          )
  
          # Get the DNS IPs from AWS Managed AD
          targetip = _r53.RecordTarget(values = ad.attr_dns_ip_addresses)
  
          # Create A Record on Route 53 to point to AWS Managed AD IPs to later EC2 to join Domain
          r53Arecord = _r53.ARecord(
              self, "RecordAforAD",
              target = targetip,
              zone = hostedzone
          )
  
          # Create Policy to EC2JoinDomain Role
          ec2ssmpolicy = _iam.PolicyDocument(
              statements = [
                  _iam.PolicyStatement(
                      actions = [
                      "ssm:DescribeAssociation",
                      "ssm:GetDocument",
                      "ssm:DescribeDocument",
                      "ssm:GetManifest",
                      "ssm:GetParameters",
                      "ssm:ListAssociations",
                      "ssm:ListInstanceAssociations",
                      "ssm:UpdateAssociationStatus",
                      "ssm:UpdateInstanceAssociationStatus",
                      "ssm:UpdateInstanceInformation"
                      ],
                      resources = [ "*" ]
                  ),
                  _iam.PolicyStatement(
                      actions = [
                      "ssmmessages:CreateControlChannel",
                      "ssmmessages:CreateDataChannel",
                      "ssmmessages:OpenControlChannel",
                      "ssmmessages:OpenDataChannel"
                      ],
                      resources = [ "*" ]
                  ),
                  _iam.PolicyStatement(
                      actions = [
                      "ec2messages:AcknowledgeMessage",
                      "ec2messages:DeleteMessage",
                      "ec2messages:FailMessage",
                      "ec2messages:GetEndpoint",
                      "ec2messages:GetMessages",
                      "ec2messages:SendReply"
                      ],
                      resources = [ "*" ]
                  ),
                  _iam.PolicyStatement(
                      actions = [
                      "ec2:DescribeInstanceStatus"
                      ],
                      resources = [ "*" ]
                  ),
                  _iam.PolicyStatement(
                      actions = [
                      "secretsmanager:GetSecretValue"
                      ],
                      resources = [ "{}".format(_sm_password) ]
                  )
              ]
          )
  
          # Create role "EC2JoinDomain" to apply on Windows EC2JoinDomain (EC2)
          ssmrole = _iam.Role(
              self,"SSMRoleforEC2",
              assumed_by = _iam.ServicePrincipal('ec2.amazonaws.com'),
              inline_policies = { "EC2SSMPolicy": ec2ssmpolicy },
              role_name = "EC2JoinDomain"
          )
  
          # Create Policy to workspaces_DefaultRole Role
          wsdefaultpolicy = _iam.PolicyDocument(
              statements = [
                  _iam.PolicyStatement(
                      actions = [
                      "ec2:CreateNetworkInterface",
                      "ec2:DeleteNetworkInterface",
                      "ec2:DescribeNetworkInterfaces"
                      ],
                      resources = [ "*" ]
                  ),
                  _iam.PolicyStatement(
                      actions = [
                      "workspaces:RebootWorkspaces",
                      "workspaces:RebuildWorkspaces",
                      "workspaces:ModifyWorkspaceProperties"
                      ],
                      resources = [ "*" ]
                  )
              ]
          )
  
          # Create role workspaces_DefaultRole for later WorkSpaces API usage
          wsrole = _iam.Role(
              self, "WorkSpacesDefaultRole",
              assumed_by = _iam.ServicePrincipal('workspaces.amazonaws.com'),
              inline_policies = { "WorkSpacesDefaultPolicy": wsdefaultpolicy },
              role_name = "workspaces_DefaultRole"
          )
  
          # Create a security group for RDP access on Windows EC2JoinDomain (EC2)
          rdpsg = _ec2.SecurityGroup(
              self, "SGForRDP",
              vpc = Vpc,
              description = "The Secrurity Group from local environment to Windows EC2 Instance"
          )
  
          rdpsg.add_ingress_rule(
              peer = _ec2.Peer.ipv4("192.168.1.1/32"),
              connection = _ec2.Port.tcp(3389)
          )
  
          # Create Windows EC2JoinDomain (EC2) as AD Admin server
          adadminEC2 = _ec2.Instance(
              self, "WindowsEC2",
              instance_type = _ec2.InstanceType(instance_type_identifier = _ec2instance),
              machine_image = _ec2.MachineImage.latest_windows(
                  version = _ec2.WindowsVersion.WINDOWS_SERVER_2016_ENGLISH_FULL_BASE
              ),
              vpc = Vpc,
              key_name = secret_ec2keypair.secret_value_from_json("Key").to_string(),
              role = ssmrole,
              security_group = rdpsg,
              vpc_subnets = _ec2.SubnetSelection(
                  subnets = [ Subnet1 ]
              )
          )
  
          adadminEC2.instance.add_depends_on(ad)
  
          # Create a SSM Parameter Store for Domain Name
          domain = _ssm.StringParameter(
              self, "ADDomainName",
              parameter_name = "ad_join_domain_name",
              string_value = ad.name
          )
  
          # Create a SSM Parameter Store for Domain User
          aduser = _ssm.StringParameter(
              self, "ADDomainUser",
              parameter_name = "ad_join_domain_user",
              string_value = "Admin"
          )
  
          domain.node.add_dependency(ad)
          aduser.node.add_dependency(ad)
  
          # Create SSM Document to join Window EC2 into AD
          ssmdocument = _ssm.CfnDocument(
              self, "SSMDocumentJoinAD",
              document_type = "Command",
              name = "SSMDocumentJoinAD",
              content =
              {
                  "description": "Run a PowerShell script to domain join a Windows instance securely",
                  "schemaVersion": "2.0",
                  "mainSteps": [
                      {
                          "action": "aws:runPowerShellScript",
                          "name": "runPowerShellWithSecureString",
                          "inputs": {
                              "runCommand": [
                              "# Example PowerShell script to domain join a Windows instance securely",
                              "# Adopt the document from AWS Blog Join a Microsoft Active Directory Domain with Parameter Store and Amazon EC2 Systems Manager Documents",
                              "",
                              "$ErrorActionPreference = 'Stop'",
                              "",
                              "try{",
                              "    # Parameter names",
                              "    # $dnsParameterStore = ([System.Net.Dns]::GetHostAddresses({}).IPAddressToString[0])".format(domain.parameter_name),
                              "    $domainNameParameterStore = \"{}\"".format(domain.parameter_name),
                              "    $domainJoinUserNameParameterStore = \"{}\"".format(aduser.parameter_name),
                              "    $domainJoinPasswordParameterStore = \"{}\"".format(secret_adpassword.secret_arn),
                              "",
                              "    # Retrieve configuration values from parameters",
                              "    $ipdns = ([System.Net.Dns]::GetHostAddresses(\"{}\").IPAddressToString[0])".format(_dname),
                              "    $domain = (Get-SSMParameterValue -Name $domainNameParameterStore).Parameters[0].Value",
                              "    $username = $domain + \"\\\" + (Get-SSMParameterValue -Name $domainJoinUserNameParameterStore).Parameters[0].Value",
                              "    $password = ((Get-SECSecretValue -SecretId $domainJoinPasswordParameterStore ).SecretString | ConvertFrom-Json ).Key | ConvertTo-SecureString -asPlainText -Force ",
                              "",
                              "    # Create a System.Management.Automation.PSCredential object",
                              "    $credential = New-Object System.Management.Automation.PSCredential($username, $password)",
                              "",
                              "    # Determine the name of the Network Adapter of this machine",
                              "    $networkAdapter = Get-WmiObject Win32_NetworkAdapter -Filter \"AdapterType = 'Ethernet 802.3'\"",
                              "    $networkAdapterName = ($networkAdapter | Select-Object -First 1).NetConnectionID",
                              "",
                              "    # Set up the IPv4 address of the AD DNS server as the first DNS server on this machine",
                              "    netsh.exe interface ipv4 add dnsservers name=$networkAdapterName address=$ipdns index=1",
                              "",
                              "    # Join the domain and reboot",
                              "    Add-Computer -DomainName $domain -Credential $credential",
                              "    Restart-Computer -Force",
                              "}",
                              "catch [Exception]{",
                              "    Write-Host $_.Exception.ToString()",
                              "    Write-Host 'Command execution failed.'",
                              "    $host.SetShouldExit(1)",
                              "}"
                              ]
                          }
                     }
                 ]
              }
          )
  
          # Create SSM Associate to trigger SSM doucment to let Windows EC2JoinDomain (EC2) join Domain
          ssmjoinad = _ssm.CfnAssociation(
              self,"WindowJoinAD",
              name = ssmdocument.name,
              targets = [{
                  "key": "InstanceIds",
                  "values": [ adadminEC2.instance_id ]
              }]
          )
  
          ssmjoinad.add_depends_on(ssmdocument)
  
          # Create a Policy for Lambda Role
          lambdapolicy = _iam.PolicyDocument(
              statements = [
                  _iam.PolicyStatement(
                      actions = [ "logs:CreateLogGroup" ],
                      resources = [ "arn:aws:logs:{}:{}:*".format(self.region,self.account) ]
                  ),
                  _iam.PolicyStatement(
                      actions = [
                      "logs:CreateLogStream",
                      "logs:PutLogEvents"
                      ],
                      resources = [ "arn:aws:logs:{}:{}:log-group:/aws/lambda/*".format(self.region,self.account) ]
                  ),
                  _iam.PolicyStatement(
                      actions = [
                      "workspaces:RegisterWorkspaceDirectory",
                      "workspaces:DeregisterWorkspaceDirectory",
                      "ds:DescribeDirectories",
                      "ds:AuthorizeApplication",
                      "ds:UnauthorizeApplication",
                      "iam:GetRole",
                      "ec2:DescribeInternetGateways",
                      "ec2:DescribeVpcs",
                      "ec2:DescribeRouteTables",
                      "ec2:DescribeSubnets",
                      "ec2:DescribeNetworkInterfaces",
                      "ec2:DescribeAvailabilityZones",
                      "ec2:CreateSecurityGroup",
                      "ec2:CreateTags"
                      ],
                      resources = [ "*" ]
                  )
              ]
          )
  
          # Creare a IAM Role for Lambda
          lambdarole = _iam.Role(
              self,"LambdaRoleForRegisterDS",
              assumed_by = _iam.ServicePrincipal('lambda.amazonaws.com'),
              inline_policies = { "LambdaActicateDS": lambdapolicy },
              role_name = "LambdaActivateDirectoryService"
          )
  
          # Create a Lambda function to Register Directory Service on WorkSpaces
          dslambda = _lambda.Function(
              self, "LambdaStackForDSFunction",
              runtime = _lambda.Runtime.PYTHON_3_7,
              handler = "workspaceds.handler",
              role = lambdarole,
              code=_lambda.Code.asset('lambda'),
              environment={
                  "DIRECTORY_ID": ad.ref
              },
              timeout = core.Duration.seconds(120)
          )
          # Create a customResource to trigger Lambda function after Lambda function is created
          _cf.CfnCustomResource(
              self, "InvokeLambdaFunction",
              service_token = dslambda.function_arn
          )
  
      # Return AWS Managed AD Directory Service ID for WorkSpaces creation
      def get_ad(self):
          return self.directory
  