Application Migration ServiceでActiveDirectoryメンバーサーバー同士を衝突させてみた!

記事タイトルとURLをコピーする

ジム通いを再開したりしなかったりしている前田です。
気になったことがあって検証したので、久しぶりにブログを書いてみました。興味のある方はぜひご覧になってみてください。

概要

MGNを用いてADメンバを移行する際、移行前後で同一のネットワークに存在しないよう注意が必要と言われています。
これは、AD内で同一のコンピュータアカウントが衝突し、不具合を引き起こすリスクがあるためです。
本ブログでは、あえてこの衝突を発生させて、どのような事象が起きるのかを検証したいと思います。

略語について

冗長になることを防ぐため、下記の用語は略語で表記します。

用語 説明
MGN Application Migration Serviceの略。
AD Active Directoryの略。
ドメコン ADのドメインコントローラの略。
ADメンバ ADドメコン配下に配置されるメンバーサーバーの略。

本記事で扱わない内容

  • MGNの仕様・使い方
  • ネットワーク構成の詳細
  • ADの詳細

結論

  • 移行直後は移行元、移行先双方でActiveDirectory認証が利用可能だった。
  • ドメコンが管理するDNSレコードでは、移行元、移行先どちらかのみが登録された(今回の検証では移行先サーバーが登録された)
  • セキュアチャネルのパスワードが更新されると、更新されなかったサーバー側ではセキュアチャネルが破損してActiveDirectory認証が利用できなくなった。

検証環境

  • VPCを2つ用意します。
  • ADドメコンとADメンバの役割のEC2をそれぞれ用意します。
  • MGNを用いてADメンバの移行を実施し、同一ネットワーク上で同じコンピュータアカウントを持つADメンバが存在するようにし、何が起こるのか確認します。

セットアップ手順

1.VPC&EC2構築

①下記のCloudFormationテンプレートを実行し、VPCをはじめとしたネットワーク環境とEC2を作成します。

クリックで詳細表示(network.yaml)

AWSTemplateFormatVersion: '2010-09-09'
Description: 'AWS MGN AD Conflict Experiment - Network Stack'

Parameters:
  EnvironmentName:
    Type: String
    Default: 'mgn-ad-experiment'
    Description: 'Environment name prefix for resources'

Resources:
  # ========================================
  # 移行元VPC (Source VPC)
  # ========================================
  SourceVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/24
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-source-vpc'
        - Key: Environment
          Value: !Ref EnvironmentName

  # 移行元VPC - パブリックサブネット 1a
  SourcePublicSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SourceVPC
      CidrBlock: 10.0.0.192/28
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-source-public-1a'
        - Key: Environment
          Value: !Ref EnvironmentName

  # 移行元VPC - プライベートサブネット 1a
  SourcePrivateSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SourceVPC
      CidrBlock: 10.0.0.0/26
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-source-private-1a'
        - Key: Environment
          Value: !Ref EnvironmentName

  # 移行元VPC - プライベートサブネット 1c
  SourcePrivateSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SourceVPC
      CidrBlock: 10.0.0.64/26
      AvailabilityZone: ap-northeast-1c
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-source-private-1c'
        - Key: Environment
          Value: !Ref EnvironmentName

  # 移行元VPC - VPCエンドポイント専用サブネット 1a
  SourceVPCESubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SourceVPC
      CidrBlock: 10.0.0.144/28
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-source-vpce-1a'
        - Key: Environment
          Value: !Ref EnvironmentName

  # 移行元VPC - VPCエンドポイント専用サブネット 1c
  SourceVPCESubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SourceVPC
      CidrBlock: 10.0.0.176/28
      AvailabilityZone: ap-northeast-1c
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-source-vpce-1c'
        - Key: Environment
          Value: !Ref EnvironmentName

  # ========================================
  # 移行先VPC (Target VPC)
  # ========================================
  TargetVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.1.0/24
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-vpc'
        - Key: Environment
          Value: !Ref EnvironmentName

  # 移行先VPC - パブリックサブネット 1a
  TargetPublicSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref TargetVPC
      CidrBlock: 10.0.1.192/28
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-public-1a'
        - Key: Environment
          Value: !Ref EnvironmentName

  # 移行先VPC - プライベートサブネット 1a
  TargetPrivateSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref TargetVPC
      CidrBlock: 10.0.1.0/26
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-private-1a'
        - Key: Environment
          Value: !Ref EnvironmentName

  # 移行先VPC - MGNステージングサブネット 1c
  TargetStagingSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref TargetVPC
      CidrBlock: 10.0.1.64/26
      AvailabilityZone: ap-northeast-1c
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-staging-1c'
        - Key: Environment
          Value: !Ref EnvironmentName

  # 移行先VPC - VPCエンドポイント専用サブネット 1a
  TargetVPCESubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref TargetVPC
      CidrBlock: 10.0.1.144/28
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-vpce-1a'
        - Key: Environment
          Value: !Ref EnvironmentName

  # 移行先VPC - VPCエンドポイント専用サブネット 1c
  TargetVPCESubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref TargetVPC
      CidrBlock: 10.0.1.176/28
      AvailabilityZone: ap-northeast-1c
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-vpce-1c'
        - Key: Environment
          Value: !Ref EnvironmentName

  # ========================================
  # インターネットゲートウェイ - 移行元VPC
  # ========================================
  SourceInternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-source-igw'
        - Key: Environment
          Value: !Ref EnvironmentName

  SourceIGWAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref SourceVPC
      InternetGatewayId: !Ref SourceInternetGateway

  # NAT Gateway - 移行元VPC
  SourceNATGatewayEIP:
    Type: AWS::EC2::EIP
    DependsOn: SourceIGWAttachment
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-source-nat-eip'
        - Key: Environment
          Value: !Ref EnvironmentName

  SourceNATGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt SourceNATGatewayEIP.AllocationId
      SubnetId: !Ref SourcePublicSubnet1a
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-source-nat'
        - Key: Environment
          Value: !Ref EnvironmentName

  # ========================================
  # インターネットゲートウェイ - 移行先VPC
  # ========================================
  TargetInternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-igw'
        - Key: Environment
          Value: !Ref EnvironmentName

  TargetIGWAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref TargetVPC
      InternetGatewayId: !Ref TargetInternetGateway

  # NAT Gateway - 移行先VPC
  TargetNATGatewayEIP:
    Type: AWS::EC2::EIP
    DependsOn: TargetIGWAttachment
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-nat-eip'
        - Key: Environment
          Value: !Ref EnvironmentName

  TargetNATGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt TargetNATGatewayEIP.AllocationId
      SubnetId: !Ref TargetPublicSubnet1a
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-nat'
        - Key: Environment
          Value: !Ref EnvironmentName

  # ========================================
  # VPC Peering Connection
  # ========================================
  VPCPeeringConnection:
    Type: AWS::EC2::VPCPeeringConnection
    Properties:
      VpcId: !Ref SourceVPC
      PeerVpcId: !Ref TargetVPC
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-vpc-peering'
        - Key: Environment
          Value: !Ref EnvironmentName

  # ========================================
  # ルートテーブル - 移行元VPC パブリックサブネット
  # ========================================
  SourcePublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref SourceVPC
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-source-public-rt'
        - Key: Environment
          Value: !Ref EnvironmentName

  # インターネットへのルート
  SourcePublicRoute:
    Type: AWS::EC2::Route
    DependsOn: SourceIGWAttachment
    Properties:
      RouteTableId: !Ref SourcePublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref SourceInternetGateway

  # パブリックサブネット関連付け
  SourcePublicSubnet1aRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SourcePublicSubnet1a
      RouteTableId: !Ref SourcePublicRouteTable

  # ========================================
  # ルートテーブル - 移行元VPC プライベートサブネット
  # ========================================
  SourcePrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref SourceVPC
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-source-private-rt'
        - Key: Environment
          Value: !Ref EnvironmentName

  # インターネットへのルート (NAT Gateway経由)
  SourcePrivateInternetRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref SourcePrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref SourceNATGateway

  # 移行先VPCへのルート (VPC Peering経由)
  SourceToTargetRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref SourcePrivateRouteTable
      DestinationCidrBlock: 10.0.1.0/24
      VpcPeeringConnectionId: !Ref VPCPeeringConnection

  # サブネット関連付け - 1a
  SourcePrivateSubnet1aRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SourcePrivateSubnet1a
      RouteTableId: !Ref SourcePrivateRouteTable

  # サブネット関連付け - 1c
  SourcePrivateSubnet1cRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SourcePrivateSubnet1c
      RouteTableId: !Ref SourcePrivateRouteTable

  # ========================================
  # ルートテーブル - 移行元VPC VPCEサブネット
  # ========================================
  SourceVPCERouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref SourceVPC
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-source-vpce-rt'
        - Key: Environment
          Value: !Ref EnvironmentName

  # VPCEサブネット関連付け - 1a
  SourceVPCESubnet1aRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SourceVPCESubnet1a
      RouteTableId: !Ref SourceVPCERouteTable

  # VPCEサブネット関連付け - 1c
  SourceVPCESubnet1cRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SourceVPCESubnet1c
      RouteTableId: !Ref SourceVPCERouteTable

  # ========================================
  # ルートテーブル - 移行先VPC パブリックサブネット
  # ========================================
  TargetPublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref TargetVPC
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-public-rt'
        - Key: Environment
          Value: !Ref EnvironmentName

  # インターネットへのルート
  TargetPublicRoute:
    Type: AWS::EC2::Route
    DependsOn: TargetIGWAttachment
    Properties:
      RouteTableId: !Ref TargetPublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref TargetInternetGateway

  # パブリックサブネット関連付け
  TargetPublicSubnet1aRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref TargetPublicSubnet1a
      RouteTableId: !Ref TargetPublicRouteTable

  # ========================================
  # ルートテーブル - 移行先VPC プライベートサブネット
  # ========================================
  TargetPrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref TargetVPC
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-private-rt'
        - Key: Environment
          Value: !Ref EnvironmentName

  # インターネットへのルート (NAT Gateway経由)
  TargetPrivateInternetRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref TargetPrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref TargetNATGateway

  # 移行元VPCへのルート (VPC Peering経由)
  TargetToSourceRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref TargetPrivateRouteTable
      DestinationCidrBlock: 10.0.0.0/24
      VpcPeeringConnectionId: !Ref VPCPeeringConnection

  # サブネット関連付け - 1a
  TargetPrivateSubnet1aRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref TargetPrivateSubnet1a
      RouteTableId: !Ref TargetPrivateRouteTable

  # サブネット関連付け - ステージング 1c
  TargetStagingSubnet1cRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref TargetStagingSubnet1c
      RouteTableId: !Ref TargetPrivateRouteTable

  # ========================================
  # ルートテーブル - 移行先VPC VPCEサブネット
  # ========================================
  TargetVPCERouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref TargetVPC
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-vpce-rt'
        - Key: Environment
          Value: !Ref EnvironmentName

  # 移行元VPCへのルート (VPC Peering経由) - Route 53 Resolver用
  TargetVPCEToSourceRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref TargetVPCERouteTable
      DestinationCidrBlock: 10.0.0.0/24
      VpcPeeringConnectionId: !Ref VPCPeeringConnection

  # VPCEサブネット関連付け - 1a
  TargetVPCESubnet1aRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref TargetVPCESubnet1a
      RouteTableId: !Ref TargetVPCERouteTable

  # VPCEサブネット関連付け - 1c
  TargetVPCESubnet1cRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref TargetVPCESubnet1c
      RouteTableId: !Ref TargetVPCERouteTable

  # ========================================
  # セキュリティグループ - ドメインコントローラー
  # ========================================
  DomainControllerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${EnvironmentName}-dc-sg'
      GroupDescription: 'Security group for Active Directory Domain Controller'
      VpcId: !Ref SourceVPC
      SecurityGroupIngress:
        # DNS
        - IpProtocol: tcp
          FromPort: 53
          ToPort: 53
          CidrIp: 10.0.0.0/24
          Description: 'DNS TCP from Source VPC'
        - IpProtocol: tcp
          FromPort: 53
          ToPort: 53
          CidrIp: 10.0.1.0/24
          Description: 'DNS TCP from Target VPC'
        - IpProtocol: udp
          FromPort: 53
          ToPort: 53
          CidrIp: 10.0.0.0/24
          Description: 'DNS UDP from Source VPC'
        - IpProtocol: udp
          FromPort: 53
          ToPort: 53
          CidrIp: 10.0.1.0/24
          Description: 'DNS UDP from Target VPC'
        # Kerberos
        - IpProtocol: tcp
          FromPort: 88
          ToPort: 88
          CidrIp: 10.0.0.0/24
          Description: 'Kerberos TCP from Source VPC'
        - IpProtocol: tcp
          FromPort: 88
          ToPort: 88
          CidrIp: 10.0.1.0/24
          Description: 'Kerberos TCP from Target VPC'
        - IpProtocol: udp
          FromPort: 88
          ToPort: 88
          CidrIp: 10.0.0.0/24
          Description: 'Kerberos UDP from Source VPC'
        - IpProtocol: udp
          FromPort: 88
          ToPort: 88
          CidrIp: 10.0.1.0/24
          Description: 'Kerberos UDP from Target VPC'
        # RPC
        - IpProtocol: tcp
          FromPort: 135
          ToPort: 135
          CidrIp: 10.0.0.0/24
          Description: 'RPC from Source VPC'
        - IpProtocol: tcp
          FromPort: 135
          ToPort: 135
          CidrIp: 10.0.1.0/24
          Description: 'RPC from Target VPC'
        # LDAP
        - IpProtocol: tcp
          FromPort: 389
          ToPort: 389
          CidrIp: 10.0.0.0/24
          Description: 'LDAP TCP from Source VPC'
        - IpProtocol: tcp
          FromPort: 389
          ToPort: 389
          CidrIp: 10.0.1.0/24
          Description: 'LDAP TCP from Target VPC'
        - IpProtocol: udp
          FromPort: 389
          ToPort: 389
          CidrIp: 10.0.0.0/24
          Description: 'LDAP UDP from Source VPC'
        - IpProtocol: udp
          FromPort: 389
          ToPort: 389
          CidrIp: 10.0.1.0/24
          Description: 'LDAP UDP from Target VPC'
        # SMB
        - IpProtocol: tcp
          FromPort: 445
          ToPort: 445
          CidrIp: 10.0.0.0/24
          Description: 'SMB from Source VPC'
        - IpProtocol: tcp
          FromPort: 445
          ToPort: 445
          CidrIp: 10.0.1.0/24
          Description: 'SMB from Target VPC'
        # LDAPS
        - IpProtocol: tcp
          FromPort: 636
          ToPort: 636
          CidrIp: 10.0.0.0/24
          Description: 'LDAPS from Source VPC'
        - IpProtocol: tcp
          FromPort: 636
          ToPort: 636
          CidrIp: 10.0.1.0/24
          Description: 'LDAPS from Target VPC'
        # Global Catalog
        - IpProtocol: tcp
          FromPort: 3268
          ToPort: 3269
          CidrIp: 10.0.0.0/24
          Description: 'Global Catalog from Source VPC'
        - IpProtocol: tcp
          FromPort: 3268
          ToPort: 3269
          CidrIp: 10.0.1.0/24
          Description: 'Global Catalog from Target VPC'
        # Dynamic RPC
        - IpProtocol: tcp
          FromPort: 49152
          ToPort: 65535
          CidrIp: 10.0.0.0/24
          Description: 'Dynamic RPC from Source VPC'
        - IpProtocol: tcp
          FromPort: 49152
          ToPort: 65535
          CidrIp: 10.0.1.0/24
          Description: 'Dynamic RPC from Target VPC'
        # RDP (管理用)
        - IpProtocol: tcp
          FromPort: 3389
          ToPort: 3389
          CidrIp: 10.0.0.0/24
          Description: 'RDP from Source VPC'
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
          Description: 'Allow all outbound traffic'
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-dc-sg'
        - Key: Environment
          Value: !Ref EnvironmentName

  # ========================================
  # セキュリティグループ - メンバーサーバー (移行元)
  # ========================================
  MemberServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${EnvironmentName}-member-sg'
      GroupDescription: 'Security group for Member Server'
      VpcId: !Ref SourceVPC
      SecurityGroupIngress:
        # RDP (管理用)
        - IpProtocol: tcp
          FromPort: 3389
          ToPort: 3389
          CidrIp: 10.0.0.0/24
          Description: 'RDP from Source VPC'
        # ICMP (疎通確認用)
        - IpProtocol: icmp
          FromPort: -1
          ToPort: -1
          CidrIp: 10.0.0.0/24
          Description: 'ICMP from Source VPC'
        - IpProtocol: icmp
          FromPort: -1
          ToPort: -1
          CidrIp: 10.0.1.0/24
          Description: 'ICMP from Target VPC'
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
          Description: 'Allow all outbound traffic'
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-member-sg'
        - Key: Environment
          Value: !Ref EnvironmentName

  # ========================================
  # セキュリティグループ - 移行先メンバーサーバー
  # ========================================
  TargetMemberServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${EnvironmentName}-target-member-sg'
      GroupDescription: 'Security group for Target Member Server'
      VpcId: !Ref TargetVPC
      SecurityGroupIngress:
        # RDP (管理用)
        - IpProtocol: tcp
          FromPort: 3389
          ToPort: 3389
          CidrIp: 10.0.0.0/24
          Description: 'RDP from Source VPC'
        - IpProtocol: tcp
          FromPort: 3389
          ToPort: 3389
          CidrIp: 10.0.1.0/24
          Description: 'RDP from Target VPC'
        # ICMP (疎通確認用)
        - IpProtocol: icmp
          FromPort: -1
          ToPort: -1
          CidrIp: 10.0.0.0/24
          Description: 'ICMP from Source VPC'
        - IpProtocol: icmp
          FromPort: -1
          ToPort: -1
          CidrIp: 10.0.1.0/24
          Description: 'ICMP from Target VPC'
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
          Description: 'Allow all outbound traffic'
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-member-sg'
        - Key: Environment
          Value: !Ref EnvironmentName

  # ========================================
  # セキュリティグループ - MGNレプリケーション
  # ========================================
  MGNReplicationSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${EnvironmentName}-mgn-replication-sg'
      GroupDescription: 'Security group for MGN Replication Server'
      VpcId: !Ref TargetVPC
      SecurityGroupIngress:
        # MGNレプリケーショントラフィック
        - IpProtocol: tcp
          FromPort: 1500
          ToPort: 1500
          CidrIp: 10.0.0.0/24
          Description: 'MGN Replication from Source VPC'
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
          Description: 'HTTPS for MGN API communication'
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-mgn-replication-sg'
        - Key: Environment
          Value: !Ref EnvironmentName

  # ========================================
  # セキュリティグループ - VPCエンドポイント (移行元VPC)
  # ========================================
  SourceVPCEndpointSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${EnvironmentName}-source-vpce-sg'
      GroupDescription: 'Security group for VPC Endpoints in Source VPC'
      VpcId: !Ref SourceVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 10.0.0.0/24
          Description: 'HTTPS from Source VPC'
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
          Description: 'Allow all outbound traffic'
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-source-vpce-sg'
        - Key: Environment
          Value: !Ref EnvironmentName

  # ========================================
  # セキュリティグループ - VPCエンドポイント (移行先VPC)
  # ========================================
  TargetVPCEndpointSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${EnvironmentName}-target-vpce-sg'
      GroupDescription: 'Security group for VPC Endpoints in Target VPC'
      VpcId: !Ref TargetVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 10.0.1.0/24
          Description: 'HTTPS from Target VPC'
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
          Description: 'Allow all outbound traffic'
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-target-vpce-sg'
        - Key: Environment
          Value: !Ref EnvironmentName

  # ========================================
  # VPCエンドポイント - 移行元VPC
  # ========================================
  # EC2 VPCエンドポイント
  SourceEC2Endpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ec2'
      VpcId: !Ref SourceVPC
      SubnetIds:
        - !Ref SourceVPCESubnet1a
      SecurityGroupIds:
        - !Ref SourceVPCEndpointSecurityGroup
      PrivateDnsEnabled: true

  # MGN VPCエンドポイント
  SourceMGNEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Sub 'com.amazonaws.${AWS::Region}.mgn'
      VpcId: !Ref SourceVPC
      SubnetIds:
        - !Ref SourceVPCESubnet1a
      SecurityGroupIds:
        - !Ref SourceVPCEndpointSecurityGroup
      PrivateDnsEnabled: true

  # S3 Gateway VPCエンドポイント
  SourceS3GatewayEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Gateway
      ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'
      VpcId: !Ref SourceVPC
      RouteTableIds:
        - !Ref SourcePrivateRouteTable

  # ========================================
  # VPCエンドポイント - 移行先VPC
  # ========================================
  # EC2 VPCエンドポイント
  TargetEC2Endpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ec2'
      VpcId: !Ref TargetVPC
      SubnetIds:
        - !Ref TargetVPCESubnet1a
      SecurityGroupIds:
        - !Ref TargetVPCEndpointSecurityGroup
      PrivateDnsEnabled: true

  # MGN VPCエンドポイント
  TargetMGNEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Sub 'com.amazonaws.${AWS::Region}.mgn'
      VpcId: !Ref TargetVPC
      SubnetIds:
        - !Ref TargetVPCESubnet1a
      SecurityGroupIds:
        - !Ref TargetVPCEndpointSecurityGroup
      PrivateDnsEnabled: true

  # S3 Gateway VPCエンドポイント
  TargetS3GatewayEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Gateway
      ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'
      VpcId: !Ref TargetVPC
      RouteTableIds:
        - !Ref TargetPrivateRouteTable

  # ========================================
  # Route 53 Resolver Endpoint
  # ========================================
  # Route 53 Resolver Outbound Endpoint用のセキュリティグループ
  ResolverOutboundSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${EnvironmentName}-resolver-outbound-sg'
      GroupDescription: 'Security group for Route 53 Resolver Outbound Endpoint'
      VpcId: !Ref TargetVPC
      SecurityGroupEgress:
        - IpProtocol: udp
          FromPort: 53
          ToPort: 53
          CidrIp: 10.0.0.0/24
          Description: 'Allow DNS queries to Source VPC (UDP)'
        - IpProtocol: tcp
          FromPort: 53
          ToPort: 53
          CidrIp: 10.0.0.0/24
          Description: 'Allow DNS queries to Source VPC (TCP)'
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-resolver-outbound-sg'
        - Key: Environment
          Value: !Ref EnvironmentName

  # Route 53 Resolver Outbound Endpoint (VPCエンドポイント用サブネットに配置)
  ResolverOutboundEndpoint:
    Type: AWS::Route53Resolver::ResolverEndpoint
    Properties:
      Name: !Sub '${EnvironmentName}-outbound-endpoint'
      Direction: OUTBOUND
      IpAddresses:
        - SubnetId: !Ref TargetVPCESubnet1a
        - SubnetId: !Ref TargetVPCESubnet1c
      SecurityGroupIds:
        - !Ref ResolverOutboundSecurityGroup
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-outbound-endpoint'
        - Key: Environment
          Value: !Ref EnvironmentName

  # Resolver Rule: example.local を Domain Controller に転送
  ResolverRule:
    Type: AWS::Route53Resolver::ResolverRule
    Properties:
      Name: !Sub '${EnvironmentName}-forward-to-dc'
      DomainName: 'example.local'
      RuleType: FORWARD
      ResolverEndpointId: !Ref ResolverOutboundEndpoint
      TargetIps:
        - Ip: '10.0.0.50'
          Port: 53
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-forward-to-dc'
        - Key: Environment
          Value: !Ref EnvironmentName

  # Resolver Rule Association: Target VPC に関連付け
  ResolverRuleAssociation:
    Type: AWS::Route53Resolver::ResolverRuleAssociation
    Properties:
      Name: !Sub '${EnvironmentName}-rule-association'
      ResolverRuleId: !Ref ResolverRule
      VPCId: !Ref TargetVPC

# ========================================
# Outputs
# ========================================
Outputs:
  SourceVpcId:
    Description: 'Source VPC ID'
    Value: !Ref SourceVPC
    Export:
      Name: !Sub '${EnvironmentName}-source-vpc-id'

  SourcePrivateSubnet1aId:
    Description: 'Source Private Subnet 1a ID'
    Value: !Ref SourcePrivateSubnet1a
    Export:
      Name: !Sub '${EnvironmentName}-source-private-subnet-1a-id'

  SourcePrivateSubnet1cId:
    Description: 'Source Private Subnet 1c ID'
    Value: !Ref SourcePrivateSubnet1c
    Export:
      Name: !Sub '${EnvironmentName}-source-private-subnet-1c-id'

  TargetVpcId:
    Description: 'Target VPC ID'
    Value: !Ref TargetVPC
    Export:
      Name: !Sub '${EnvironmentName}-target-vpc-id'

  TargetPrivateSubnet1aId:
    Description: 'Target Private Subnet 1a ID'
    Value: !Ref TargetPrivateSubnet1a
    Export:
      Name: !Sub '${EnvironmentName}-target-private-subnet-1a-id'

  TargetStagingSubnet1cId:
    Description: 'Target Staging Subnet 1c ID'
    Value: !Ref TargetStagingSubnet1c
    Export:
      Name: !Sub '${EnvironmentName}-target-staging-subnet-1c-id'

  VPCPeeringConnectionId:
    Description: 'VPC Peering Connection ID'
    Value: !Ref VPCPeeringConnection
    Export:
      Name: !Sub '${EnvironmentName}-vpc-peering-id'

  DomainControllerSecurityGroupId:
    Description: 'Domain Controller Security Group ID'
    Value: !Ref DomainControllerSecurityGroup
    Export:
      Name: !Sub '${EnvironmentName}-dc-sg-id'

  MemberServerSecurityGroupId:
    Description: 'Member Server Security Group ID'
    Value: !Ref MemberServerSecurityGroup
    Export:
      Name: !Sub '${EnvironmentName}-member-sg-id'

  TargetMemberServerSecurityGroupId:
    Description: 'Target Member Server Security Group ID'
    Value: !Ref TargetMemberServerSecurityGroup
    Export:
      Name: !Sub '${EnvironmentName}-target-member-sg-id'

  MGNReplicationSecurityGroupId:
    Description: 'MGN Replication Security Group ID'
    Value: !Ref MGNReplicationSecurityGroup
    Export:
      Name: !Sub '${EnvironmentName}-mgn-replication-sg-id'

  ResolverOutboundEndpointId:
    Description: 'Route 53 Resolver Outbound Endpoint ID'
    Value: !Ref ResolverOutboundEndpoint
    Export:
      Name: !Sub '${EnvironmentName}-resolver-outbound-endpoint-id'

  ResolverRuleId:
    Description: 'Route 53 Resolver Rule ID'
    Value: !Ref ResolverRule
    Export:
      Name: !Sub '${EnvironmentName}-resolver-rule-id'

  ResolverOutboundSecurityGroupId:
    Description: 'Route 53 Resolver Outbound Security Group ID'
    Value: !Ref ResolverOutboundSecurityGroup
    Export:
      Name: !Sub '${EnvironmentName}-resolver-outbound-sg-id'

クリックで詳細表示(compute.yaml)

AWSTemplateFormatVersion: '2010-09-09'
Description: 'AWS MGN AD Conflict Experiment - Compute Stack'

Parameters:
  EnvironmentName:
    Type: String
    Default: 'mgn-ad-experiment'
    Description: 'Environment name prefix for resources'

  LatestWindowsAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: '/aws/service/ami-windows-latest/Windows_Server-2022-Japanese-Full-Base'
    Description: 'Latest Windows Server 2022 AMI ID from SSM Parameter Store'

  KeyPairName:
    Type: String
    Default: ''
    Description: 'EC2 key pair name for Fleet Manager RDP connection (Optional: leave empty if not using Fleet Manager)'

Conditions:
  HasKeyPair: !Not [!Equals [!Ref KeyPairName, '']]

Resources:
  # ========================================
  # IAMロール - SSM管理用
  # ========================================
  SSMInstanceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${EnvironmentName}-ssm-instance-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore'
        - 'arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy'
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-ssm-instance-role'
        - Key: Environment
          Value: !Ref EnvironmentName

  # ========================================
  # IAMロール - MGNエージェント用
  # ========================================
  MGNAgentRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${EnvironmentName}-mgn-agent-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore'
        - 'arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy'
        - 'arn:aws:iam::aws:policy/AWSApplicationMigrationAgentPolicy'
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-mgn-agent-role'
        - Key: Environment
          Value: !Ref EnvironmentName

  # ========================================
  # インスタンスプロファイル - SSM管理用
  # ========================================
  SSMInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: !Sub '${EnvironmentName}-ssm-instance-profile'
      Roles:
        - !Ref SSMInstanceRole

  # ========================================
  # インスタンスプロファイル - MGNエージェント用
  # ========================================
  MGNAgentInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: !Sub '${EnvironmentName}-mgn-agent-instance-profile'
      Roles:
        - !Ref MGNAgentRole

  # ========================================
  # ドメインコントローラーEC2インスタンス
  # ========================================
  DomainControllerInstance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t3.medium
      ImageId: !Ref LatestWindowsAmiId
      IamInstanceProfile: !Ref SSMInstanceProfile
      KeyName: !If [HasKeyPair, !Ref KeyPairName, !Ref 'AWS::NoValue']
      SubnetId: !ImportValue
        'Fn::Sub': '${EnvironmentName}-source-private-subnet-1a-id'
      SecurityGroupIds:
        - !ImportValue
            'Fn::Sub': '${EnvironmentName}-dc-sg-id'
      PrivateIpAddress: 10.0.0.50
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeType: gp3
            VolumeSize: 50
            DeleteOnTermination: true
            Encrypted: true
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-domain-controller'
        - Key: Environment
          Value: !Ref EnvironmentName
        - Key: Role
          Value: 'DomainController'

  # ========================================
  # メンバーサーバーEC2インスタンス
  # ========================================
  MemberServerInstance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t3.small
      ImageId: !Ref LatestWindowsAmiId
      IamInstanceProfile: !Ref MGNAgentInstanceProfile
      KeyName: !If [HasKeyPair, !Ref KeyPairName, !Ref 'AWS::NoValue']
      SubnetId: !ImportValue
        'Fn::Sub': '${EnvironmentName}-source-private-subnet-1a-id'
      SecurityGroupIds:
        - !ImportValue
            'Fn::Sub': '${EnvironmentName}-member-sg-id'
      PrivateIpAddress: 10.0.0.51
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeType: gp3
            VolumeSize: 30
            DeleteOnTermination: true
            Encrypted: true
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-member-server'
        - Key: Environment
          Value: !Ref EnvironmentName
        - Key: Role
          Value: 'MemberServer'

# ========================================
# Outputs
# ========================================
Outputs:
  DomainControllerInstanceId:
    Description: 'Domain Controller Instance ID'
    Value: !Ref DomainControllerInstance
    Export:
      Name: !Sub '${EnvironmentName}-dc-instance-id'

  DomainControllerPrivateIP:
    Description: 'Domain Controller Private IP Address'
    Value: !GetAtt DomainControllerInstance.PrivateIp
    Export:
      Name: !Sub '${EnvironmentName}-dc-private-ip'

  MemberServerInstanceId:
    Description: 'Member Server Instance ID'
    Value: !Ref MemberServerInstance
    Export:
      Name: !Sub '${EnvironmentName}-member-instance-id'

  MemberServerPrivateIP:
    Description: 'Member Server Private IP Address'
    Value: !GetAtt MemberServerInstance.PrivateIp
    Export:
      Name: !Sub '${EnvironmentName}-member-private-ip'

  SSMInstanceRoleArn:
    Description: 'SSM Instance Role ARN'
    Value: !GetAtt SSMInstanceRole.Arn
    Export:
      Name: !Sub '${EnvironmentName}-ssm-instance-role-arn'

  MGNAgentRoleArn:
    Description: 'MGN Agent Role ARN'
    Value: !GetAtt MGNAgentRole.Arn
    Export:
      Name: !Sub '${EnvironmentName}-mgn-agent-role-arn'

2.ドメコン設定

①下記スクリプトを実行してADのドメコンを設定します。

クリックで詳細表示

$DomainName = "example.local"
$SafeModePassword = "P@ssw0rd123!"
# ========================================

# エラー時に停止
$ErrorActionPreference = "Stop"

# ログ関数
function Write-Log {
    param([string]$Message, [string]$Level = "INFO")
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logMessage = "[$timestamp] [$Level] $Message"
    Write-Host $logMessage
    Add-Content -Path "C:\ad-setup.log" -Value $logMessage
}

Write-Log "========================================" "INFO"
Write-Log "Active Directory ドメインコントローラー セットアップ開始" "INFO"
Write-Log "========================================" "INFO"
Write-Log "ドメイン名: $DomainName" "INFO"

# NetBIOS名を生成
$NetBIOSName = ($DomainName -split '\.')[0].ToUpper()
Write-Log "NetBIOS名: $NetBIOSName" "INFO"

# ステップ1: AD DS役割のインストール確認
Write-Log "ステップ1: AD DS役割のインストール状態を確認中..." "INFO"
$addsFeature = Get-WindowsFeature -Name AD-Domain-Services

if ($addsFeature.Installed) {
    Write-Log "AD DS役割は既にインストールされています。" "INFO"
} else {
    Write-Log "AD DS役割をインストール中..." "INFO"
    try {
        Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools
        Write-Log "AD DS役割のインストールが完了しました。" "INFO"
    } catch {
        Write-Log "AD DS役割のインストールに失敗しました: $_" "ERROR"
        throw
    }
}

# ステップ2: 既存のドメインコントローラーかどうかを確認
Write-Log "ステップ2: 既存のドメインコントローラー状態を確認中..." "INFO"
try {
    $domainRole = (Get-WmiObject -Class Win32_ComputerSystem).DomainRole
    
    if ($domainRole -ge 4) {
        Write-Log "このサーバーは既にドメインコントローラーです。" "WARN"
        Write-Log "現在のドメイン: $((Get-ADDomain).DNSRoot)" "INFO"
        Write-Log "セットアップをスキップします。" "INFO"
        exit 0
    }
} catch {
    Write-Log "ドメインコントローラー状態の確認中にエラーが発生しました: $_" "WARN"
}

# ステップ3: 新しいフォレストとドメインの作成
Write-Log "ステップ3: 新しいフォレストとドメインを作成中..." "INFO"
Write-Log "警告: サーバーは自動的に再起動されます。" "WARN"

try {
    # SafeModePasswordをSecureStringに変換
    $securePassword = ConvertTo-SecureString $SafeModePassword -AsPlainText -Force
    
    # AD DSフォレストのインストール
    Install-ADDSForest `
        -DomainName $DomainName `
        -DomainNetbiosName $NetBIOSName `
        -SafeModeAdministratorPassword $securePassword `
        -InstallDns:$true `
        -DomainMode "WinThreshold" `
        -ForestMode "WinThreshold" `
        -DatabasePath "C:\Windows\NTDS" `
        -LogPath "C:\Windows\NTDS" `
        -SysvolPath "C:\Windows\SYSVOL" `
        -NoRebootOnCompletion:$false `
        -Force:$true
    
    Write-Log "ドメインコントローラーの昇格が完了しました。" "INFO"
    Write-Log "サーバーは再起動されます..." "INFO"
    
} catch {
    Write-Log "ドメインコントローラーの昇格に失敗しました: $_" "ERROR"
    Write-Log "詳細なエラー情報:" "ERROR"
    Write-Log $_.Exception.Message "ERROR"
    throw
}

Write-Log "========================================" "INFO"
Write-Log "セットアップスクリプトが完了しました。" "INFO"
Write-Log "再起動後、以下のコマンドでドメインコントローラーの状態を確認してください:" "INFO"
Write-Log "  Get-ADDomain" "INFO"
Write-Log "  Get-ADForest" "INFO"
Write-Log "  Get-DnsServerZone" "INFO"
Write-Log "" "INFO"
Write-Log "再起動後、ドメイン管理者のパスワードをリセットしてください:" "INFO"
Write-Log "  Set-ADAccountPassword -Identity Administrator -Reset -NewPassword (ConvertTo-SecureString -AsPlainText 'P@ssw0rd123!' -Force)" "INFO"
Write-Log "========================================" "INFO"



②再起動後に、ドメイン管理者のパスワードをリセットします。 (ADメンバがドメイン参加する時に成功させるため。)

Set-ADAccountPassword -Identity Administrator -Reset -NewPassword (ConvertTo-SecureString -AsPlainText 'P@ssw0rd123!' -Force)" "INFO"

ドメインコントローラの役割が与えられていることが確認できます。

3.ADメンバのドメイン参加

①下記コマンドを実行してコンピュータ名を分かりやすく変更します。

Rename-Computer -NewName "MEMBER-01" -DomainCredential (Get-Credential) -Restart -Force

再起動後、コンピュータ名が変更されています。



②下記スクリプトを実行してドメインexample.localに参加します。

クリックで詳細表示

$DomainName = "example.local"
$DomainController = "10.0.0.50"
$DomainAdmin = "Administrator"
$DomainPassword = "P@ssw0rd123!"

# エラー時に停止
$ErrorActionPreference = "Stop"

# ログ関数
function Write-Log {
    param([string]$Message, [string]$Level = "INFO")
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logMessage = "[$timestamp] [$Level] $Message"
    Write-Host $logMessage
    Add-Content -Path "C:\domain-join.log" -Value $logMessage
}

Write-Log "========================================" "INFO"
Write-Log "Active Directory ドメイン参加スクリプト開始" "INFO"
Write-Log "========================================" "INFO"
Write-Log "ドメイン名: $DomainName" "INFO"
Write-Log "ドメインコントローラー: $DomainController" "INFO"

# NetBIOS名を生成
$NetBIOSName = ($DomainName -split '\.')[0].ToUpper()
$DomainAdminFull = "$NetBIOSName\$DomainAdmin"
Write-Log "ドメイン管理者: $DomainAdminFull" "INFO"

# ステップ1: 既にドメインに参加しているかを確認
Write-Log "ステップ1: 現在のドメイン参加状態を確認中..." "INFO"
try {
    $computerInfo = Get-ComputerInfo
    $currentDomain = $computerInfo.CsDomain
    
    if ($currentDomain -eq $DomainName) {
        Write-Log "このサーバーは既にドメイン '$DomainName' に参加しています。" "WARN"
        Write-Log "ドメイン参加をスキップします。" "INFO"
        exit 0
    } elseif ($currentDomain -ne "WORKGROUP") {
        Write-Log "このサーバーは別のドメイン '$currentDomain' に参加しています。" "WARN"
        Write-Log "先にドメインから離脱してください。" "ERROR"
        exit 1
    }
    
    Write-Log "現在の状態: WORKGROUP (ドメイン未参加)" "INFO"
} catch {
    Write-Log "ドメイン参加状態の確認中にエラーが発生しました: $_" "WARN"
}

# ステップ2: DNS設定の変更
Write-Log "ステップ2: DNS設定をドメインコントローラーに変更中..." "INFO"
try {
    # アクティブなネットワークアダプターを取得
    $adapter = Get-NetAdapter | Where-Object {$_.Status -eq "Up"} | Select-Object -First 1
    
    if ($null -eq $adapter) {
        Write-Log "アクティブなネットワークアダプターが見つかりません。" "ERROR"
        throw "No active network adapter found"
    }
    
    $interfaceAlias = $adapter.Name
    Write-Log "ネットワークアダプター: $interfaceAlias" "INFO"
    
    # 現在のDNS設定を確認
    $currentDns = Get-DnsClientServerAddress -InterfaceAlias $interfaceAlias -AddressFamily IPv4
    Write-Log "現在のDNSサーバー: $($currentDns.ServerAddresses -join ', ')" "INFO"
    
    # DNSサーバーをドメインコントローラーに設定
    Set-DnsClientServerAddress -InterfaceAlias $interfaceAlias -ServerAddresses $DomainController
    Write-Log "DNSサーバーを $DomainController に変更しました。" "INFO"
    
    # 変更後のDNS設定を確認
    Start-Sleep -Seconds 2
    $newDns = Get-DnsClientServerAddress -InterfaceAlias $interfaceAlias -AddressFamily IPv4
    Write-Log "新しいDNSサーバー: $($newDns.ServerAddresses -join ', ')" "INFO"
    
} catch {
    Write-Log "DNS設定の変更に失敗しました: $_" "ERROR"
    throw
}

# ステップ3: ドメインコントローラーへの接続確認
Write-Log "ステップ3: ドメインコントローラーへの接続を確認中..." "INFO"
try {
    # LDAP接続テスト (ポート389)
    Write-Log "LDAP接続テスト (ポート389)..." "INFO"
    $ldapTest = Test-NetConnection -ComputerName $DomainController -Port 389 -WarningAction SilentlyContinue
    if ($ldapTest.TcpTestSucceeded) {
        Write-Log "LDAP接続: 成功" "INFO"
    } else {
        Write-Log "LDAP接続: 失敗" "ERROR"
        throw "LDAP connection failed"
    }
    
    # Kerberos接続テスト (ポート88)
    Write-Log "Kerberos接続テスト (ポート88)..." "INFO"
    $kerberosTest = Test-NetConnection -ComputerName $DomainController -Port 88 -WarningAction SilentlyContinue
    if ($kerberosTest.TcpTestSucceeded) {
        Write-Log "Kerberos接続: 成功" "INFO"
    } else {
        Write-Log "Kerberos接続: 失敗" "ERROR"
        throw "Kerberos connection failed"
    }
    
    # DNS名前解決テスト
    Write-Log "DNS名前解決テスト..." "INFO"
    $dnsTest = Resolve-DnsName -Name $DomainName -ErrorAction SilentlyContinue
    if ($dnsTest) {
        Write-Log "DNS名前解決: 成功 ($($dnsTest[0].IPAddress))" "INFO"
    } else {
        Write-Log "DNS名前解決: 失敗" "WARN"
        Write-Log "ドメイン参加は続行しますが、問題が発生する可能性があります。" "WARN"
    }
    
} catch {
    Write-Log "ドメインコントローラーへの接続確認に失敗しました: $_" "ERROR"
    Write-Log "ネットワーク接続とセキュリティグループの設定を確認してください。" "ERROR"
    throw
}

# ステップ4: ドメイン参加の実行
Write-Log "ステップ4: ドメインに参加中..." "INFO"
Write-Log "警告: サーバーは自動的に再起動されます。" "WARN"

try {
    # 認証情報の作成
    $securePassword = ConvertTo-SecureString $DomainPassword -AsPlainText -Force
    $credential = New-Object System.Management.Automation.PSCredential($DomainAdminFull, $securePassword)
    
    # ドメイン参加
    Add-Computer -DomainName $DomainName -Credential $credential -Restart -Force
    
    Write-Log "ドメイン参加が完了しました。" "INFO"
    Write-Log "サーバーは再起動されます..." "INFO"
    
} catch {
    Write-Log "ドメイン参加に失敗しました: $_" "ERROR"
    Write-Log "詳細なエラー情報:" "ERROR"
    Write-Log $_.Exception.Message "ERROR"
    
    # DNS設定を元に戻す(オプション)
    Write-Log "DNS設定を元に戻しています..." "WARN"
    try {
        Set-DnsClientServerAddress -InterfaceAlias $interfaceAlias -ResetServerAddresses
        Write-Log "DNS設定を元に戻しました。" "INFO"
    } catch {
        Write-Log "DNS設定の復元に失敗しました: $_" "WARN"
    }
    
    throw
}

Write-Log "========================================" "INFO"
Write-Log "ドメイン参加スクリプトが完了しました。" "INFO"
Write-Log "再起動後、以下のコマンドでドメイン参加を確認してください:" "INFO"
Write-Log "  Get-ComputerInfo | Select-Object CsDomain, CsDomainRole" "INFO"
Write-Log "  Test-ComputerSecureChannel -Verbose" "INFO"
Write-Log "========================================" "INFO"



③下記コマンドを実行し、メンバーサーバーとして登録されていることが確認できました。

Get-ComputerInfo | Select-Object CsName, CsDomain, CsDomainRole



④その他、衝突後に備えて取得した情報が下記になります。
※ID5719の、ドメインにおいて認証が失敗したエラーは一時的なものです。

情報取得用スクリプト

# ========================================
# ベースライン情報収集スクリプト
# ========================================

$OutputFile = "C:\baseline-info.txt"

# ヘッダー
"========================================" | Out-File $OutputFile
"ベースライン情報(競合前)" | Out-File $OutputFile -Append
"収集日時: $(Get-Date)" | Out-File $OutputFile -Append
"========================================`n" | Out-File $OutputFile -Append

# 1. コンピューター情報
"`n=== 1. コンピューター情報 ===" | Out-File $OutputFile -Append
Get-ComputerInfo | Select-Object CsName, CsDomain, CsDomainRole | Out-File $OutputFile -Append

# 2. セキュアチャネル
"`n=== 2. セキュアチャネル ===" | Out-File $OutputFile -Append
"Test-ComputerSecureChannel: $(Test-ComputerSecureChannel)" | Out-File $OutputFile -Append

# 3. ネットワーク接続
"`n=== 3. ドメインコントローラーへの接続 ===" | Out-File $OutputFile -Append
$ldap = Test-NetConnection -ComputerName 10.0.0.50 -Port 389 -WarningAction SilentlyContinue
"LDAP (389): $($ldap.TcpTestSucceeded)" | Out-File $OutputFile -Append
$kerberos = Test-NetConnection -ComputerName 10.0.0.50 -Port 88 -WarningAction SilentlyContinue
"Kerberos (88): $($kerberos.TcpTestSucceeded)" | Out-File $OutputFile -Append

# 4. Kerberosチケット
"`n=== 4. Kerberosチケット ===" | Out-File $OutputFile -Append
klist | Out-File $OutputFile -Append

# 5. イベントログ
"`n=== 5. 最近のイベントログ ===" | Out-File $OutputFile -Append
Get-EventLog -LogName System -Source "NETLOGON" -Newest 5 | Select-Object TimeGenerated, EventID, Message | Out-File $OutputFile -Append

Write-Host "ベースライン情報を $OutputFile に保存しました。" -ForegroundColor Green

========================================
ベースライン情報(競合前)
収集日時: 01/29/2026 06:33:34
========================================


=== 1. コンピューター情報 ===

CsName    CsDomain      CsDomainRole
------    --------      ------------
MEMBER-01 example.local MemberServer



=== 2. セキュアチャネル ===
Test-ComputerSecureChannel: True

=== 3. ドメインコントローラーへの接続 ===
LDAP (389): True
Kerberos (88): True

=== 4. Kerberosチケット ===

現在のログオン ID: 0:0x57cb9

キャッシュされたチケット: (2)

#0>  クライアント: Administrator @ EXAMPLE.LOCAL
    サーバー: krbtgt/EXAMPLE.LOCAL @ EXAMPLE.LOCAL
    Kerberos チケットの暗号化の種類: AES-256-CTS-HMAC-SHA1-96
    チケットのフラグ 0x40e10000 -> forwardable renewable initial pre_authent name_canonicalize 
    開始時刻: 1/29/2026 6:33:11 (ローカル)
    終了時刻: 1/29/2026 16:33:11 (ローカル)
    更新期限: 2/5/2026 6:33:11 (ローカル)
    セッション キーの種類: AES-256-CTS-HMAC-SHA1-96
    キャッシュ フラグ: 0x1 -> PRIMARY 
    呼び出された Kdc: EC2AMAZ-GLAVESF.example.local

#1>  クライアント: Administrator @ EXAMPLE.LOCAL
    サーバー: host/member-01.example.local @ EXAMPLE.LOCAL
    Kerberos チケットの暗号化の種類: AES-256-CTS-HMAC-SHA1-96
    チケットのフラグ 0x40a10000 -> forwardable renewable pre_authent name_canonicalize 
    開始時刻: 1/29/2026 6:33:11 (ローカル)
    終了時刻: 1/29/2026 16:33:11 (ローカル)
    更新期限: 2/5/2026 6:33:11 (ローカル)
    セッション キーの種類: AES-256-CTS-HMAC-SHA1-96
    キャッシュ フラグ: 0 
    呼び出された Kdc: EC2AMAZ-GLAVESF.example.local

=== 5. 最近のイベントログ ===

TimeGenerated      EventID Message                                                                                     
-------------      ------- -------                                                                                     
2026/01/29 3:09:08    5719 次の理由のため、このコンピューターはドメイン EXAMPLE のドメイン コントローラーの...           

4. ADメンバへのMGNエージェントインストール

※MGN有効化、レプリケーションテンプレート、起動テンプレート、エージェントインストール用IAMユーザの準備などはAWSコンソール側の操作は、長くなるため省略します。

①下記コマンドを実行し、MGNエージェントインストーラをダウンロード。

curl -o .\AwsReplicationWindowsInstaller.exe https://aws-application-migration-service-ap-northeast-1.s3.ap-northeast-1.amazonaws.com/latest/windows/AwsReplicationWindowsInstaller.exe



②下記コマンドを実行し、MGNエージェントをインストール

C:\Users\Administrator.EXAMPLE\AwsReplicationWindowsInstaller.exe --region ap-northeast-1 --aws-access-key-id <エージェントインストール用IAMユーザアクセスキー> --aws-secret-access-key <エージェントインストール用IAMユーザシークレットキー> --no-replication



エージェントインストールが成功しました。

MGNのソースサーバー管理画面にも、ADメンバが表示されています。

5.レプリケーション開始

①レプリケーションを開始

②テストインスタンスを起動

プライベートIPには10.0.1.51を割り当てています。

6.ログイン~情報取得

Fleet Managerからドメインユーザでログインしようとしたところ、意外なことに移行元でも移行先でもログインできました。

移行元、移行先、ドメコンの情報を見ていきたいと思います。

移行元の情報取得

注目したいのはセキュアチャネルのステータスを示す、Test-ComputerSecureChannel: Trueの箇所です。
セキュアチャネルとは、ドメコンとメンバの間で確立する安全な通信パスのことです。
メンバがADに参加するとドメコンから内部的なパスワードが発行され、セキュアチャネルを確立します。 上記のパスワードはドメインユーザが利用するものとは異なり、人間が管理・使用することはありません。パスワードは定期的(デフォルト30日周期)で自動更新されます。

Test-ComputerSecureChannelTrueとなっているため、ドメコンによる認証は問題ないことが分かります。

========================================
ベースライン情報(競合後)
収集日時: 01/30/2026 09:39:27
========================================


=== 1. コンピューター情報 ===

CsName    CsDomain      CsDomainRole
------    --------      ------------
MEMBER-01 example.local MemberServer



=== 2. セキュアチャネル ===
Test-ComputerSecureChannel: True

=== 3. ドメインコントローラーへの接続 ===
LDAP (389): True
Kerberos (88): True

=== 4. Kerberosチケット ===

現在のログオン ID: 0:0x57cb9

キャッシュされたチケット: (3)

#0>  クライアント: Administrator @ EXAMPLE.LOCAL
    サーバー: krbtgt/EXAMPLE.LOCAL @ EXAMPLE.LOCAL
    Kerberos チケットの暗号化の種類: AES-256-CTS-HMAC-SHA1-96
    チケットのフラグ 0x40e10000 -> forwardable renewable initial pre_authent name_canonicalize 
    開始時刻: 1/30/2026 9:33:41 (ローカル)
    終了時刻: 1/30/2026 19:33:41 (ローカル)
    更新期限: 2/6/2026 9:33:41 (ローカル)
    セッション キーの種類: AES-256-CTS-HMAC-SHA1-96
    キャッシュ フラグ: 0x1 -> PRIMARY 
    呼び出された Kdc: EC2AMAZ-GLAVESF.example.local

#1>  クライアント: Administrator @ EXAMPLE.LOCAL
    サーバー: LDAP/EC2AMAZ-GLAVESF.example.local/example.local @ EXAMPLE.LOCAL
    Kerberos チケットの暗号化の種類: AES-256-CTS-HMAC-SHA1-96
    チケットのフラグ 0x40a50000 -> forwardable renewable pre_authent ok_as_delegate name_canonicalize 
    開始時刻: 1/30/2026 9:35:30 (ローカル)
    終了時刻: 1/30/2026 19:33:41 (ローカル)
    更新期限: 2/6/2026 9:33:41 (ローカル)
    セッション キーの種類: AES-256-CTS-HMAC-SHA1-96
    キャッシュ フラグ: 0 
    呼び出された Kdc: EC2AMAZ-GLAVESF.example.local

#2>  クライアント: Administrator @ EXAMPLE.LOCAL
    サーバー: host/member-01.example.local @ EXAMPLE.LOCAL
    Kerberos チケットの暗号化の種類: AES-256-CTS-HMAC-SHA1-96
    チケットのフラグ 0x40a10000 -> forwardable renewable pre_authent name_canonicalize 
    開始時刻: 1/30/2026 9:33:41 (ローカル)
    終了時刻: 1/30/2026 19:33:41 (ローカル)
    更新期限: 2/6/2026 9:33:41 (ローカル)
    セッション キーの種類: AES-256-CTS-HMAC-SHA1-96
    キャッシュ フラグ: 0 
    呼び出された Kdc: EC2AMAZ-GLAVESF.example.local

=== 5. 最近のイベントログ ===

TimeGenerated      EventID Message                                                                                                                                     
-------------      ------- -------                                                                                                                                     
2026/01/29 3:09:08    5719 次の理由のため、このコンピューターはドメイン EXAMPLE のドメイン コントローラーの...                                                                                                         

移行先の情報取得

移行先サーバーもTest-ComputerSecureChannel: Trueとなっていました。
移行先においてもセキュアチャネルが確立されているため、移行元、移行先どちらにもドメインユーザでログインできた…というわけですね。
ねこまるの AD フリーク様の記事より、同じコンピュータアカウントがADドメインに参加した時点でどちらかのセキュアチャネルが破損するものと思っていたため、これは意外でした。

ドメイン参加済みのドメイン メンバーのコンピューターで使用されているホスト名と、同じ名前のコンピューターをドメインに参加させた場合、セキュアチャネルの破損が発生します。同じ名前のコンピューターをドメイン参加させたタイミングにて、すでにあるコンピューター アカウントが上書きされ、パスワードも変更されます。そのため、既存のコンピューターとドメイン コントローラーとでパスワードの不整合が発生します。

========================================
ベースライン情報(競合後)
収集日時: 01/30/2026 09:37:18
========================================


=== 1. コンピューター情報 ===

CsName    CsDomain      CsDomainRole
------    --------      ------------
MEMBER-01 example.local MemberServer



=== 2. セキュアチャネル ===
Test-ComputerSecureChannel: True

=== 3. ドメインコントローラーへの接続 ===
LDAP (389): True
Kerberos (88): True

=== 4. Kerberosチケット ===

現在のログオン ID: 0:0x2f7402

キャッシュされたチケット: (0)

=== 5. 最近のイベントログ ===

TimeGenerated      EventID Message                                                                                     
-------------      ------- -------                                                                                     
2026/01/29 3:09:08    5719 次の理由のため、このコンピューターはドメイン EXAMPLE のドメイン コントローラーの...                                            

ドメコンの情報取得

DNSレコードにおいてはMEMBER-01.example.local10.0.1.51が対応しており、移行先がメンバとして認識されていました。 例えば上記ドメイン名を指定してRDPログインしようとした場合は、意図せず移行先にログインすることになってしまう…などの影響が考えられます。

実験_セキュアチャネルをリセットしてみる。

前述の通りセキュアチャネルのパスワードは自動更新されますが、その場合どうなるのでしょうか。 試しに移行元においてセキュアチャネルを手動リセットし、何が起こるかを確認したいと思います。

下記コマンドを実行してセキュアチャネルをリセット( = パスワードを更新)します。

移行先サーバーでドメインユーザによるログイン失敗。

移行先サーバーでセキュアチャネルが破損していることが確認できました。
前述の記事に記載されている内容が原因と考えられます。

イベントログにも、同名のコンピュータアカウントが存在することで認証失敗した旨が記載されていました。

ドメコンから見えるMEMBER-01.example.localは移行元サーバー10.0.0.51に変更されていました。

対策

下記のような対策が考えられますが、本ブログでは概要までとし、いずれご紹介できたらと思います。

  • 移行後サーバーのドメコンへの通信をNACLやセキュリティグループで制限してカットオーバーインスタンス(移行後サーバー)を起動し、下記いずれかを実行する
    • カットオーバーインスタンスにsysprepを実行して、コンピュータアカウントが重複しないようにする。
    • 移行元サーバーを停止した後にカットオーバーインスタンスの制限を解除し、ADドメインへ参加する。

結論(再掲)

  • 移行直後は移行元、移行先双方でActiveDirectory認証が利用可能だった。
  • ドメコンが管理するDNSレコードでは、移行元、移行先どちらかのみが登録された(今回の検証では移行先サーバーが登録された)
  • セキュアチャネルのパスワードが更新されると、更新されなかったサーバー側ではセキュアチャネルが破損してActiveDirectory認証が利用できなくなった。




この記事がどなたかの一助になれば幸いです。

前田 青秀(執筆記事の一覧)

2023年2月入社 技術4課改めCR課

AWS資格12冠

ジムに通い始めましたが、なるべく楽してマッチョになりたい…