📝

Microsoft Active Directory + FSx for windows を CloudFormation で構築してみよう

に公開

はじめに

AWS のソリューションを使って、手軽にファイルサーバーを構築したい & サーバー管理はやりたくないと思い、AWS マネージドの環境を CloudFormation で構築する方法を考えてみました。
あくまで、個人検証用として作成しています。
商用利用の際は下記著作権含め十分検証してからご利用ください。

本記事で触れないこと

  • 各 AWS ソリューションの説明や使い方
  • CloudFormation テンプレート(yaml)の解説
  • 利用料金

AWS構成図

できる限りセキュリティは保証したかったので、プライベートサブネット上で構築しています。
実際は AWS Direct Connect や AWS Site-to-Site VPN を使って、接続することに
なると思います。
image.png

YAMLテンプレート

microsoft-ad+fsx-windows.yaml(折り畳んでます)
AWSTemplateFormatVersion: '2010-09-09'

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Network configuration
        Parameters:
          - AvailabilityZones
          - VPCCIDR
          - PrivateSubnet1CIDR
          - PrivateSubnet2CIDR
      - Label:
          default: Microsoft Active Directory configuration
        Parameters:
          - DomainDNSName
          - DomainNetBIOSName
          - DomainAdminPassword
          - ADScenarioType
      - Label:
          default: FSx for Windows File Server configuration
        Parameters:
          - NumberOfAZs
          - BackupRetention
          - DailyBackupTime
          - StorageType
          - StorageCapacity
          - ThroughputCapacity
          - FSxEncryptionKey
          - FSxExistingKeyID
          - FSxAllowedCIDR
          - WeeklyMaintenanceTime
    ParameterLabels:
      AvailabilityZones:
        default: Availability Zones
      ADScenarioType:
        default: Type of Active Directory deployment
      BackupRetention:
        default: Automated backup retention
      DailyBackupTime:
        default: Daily backup start time
      DomainAdminPassword:
        default: Domain admin password
      DomainDNSName:
        default: Domain DNS name
      DomainNetBIOSName:
        default: Domain NetBIOS name
      FSxEncryptionKey:
        default: Type of AWS KMS key used by Amazon FSx
      FSxExistingKeyID:
        default: AWS KMS key ID
      FSxAllowedCIDR:
        default: CIDR ranged allowed to connect to FSx file system
      NumberOfAZs:
        default: Number of Availability Zones
      StorageType:
        default: Storage type
      StorageCapacity:
        default: Storage size
      ThroughputCapacity:
        default: Throughput capacity of Amazon FSx file system
      PrivateSubnet1CIDR:
        default: Private subnet 1 CIDR
      PrivateSubnet2CIDR:
        default: Private subnet 2 CIDR
      VPCCIDR:
        default: VPC CIDR
      WeeklyMaintenanceTime:
        default: Weekly maintenance start time

Parameters:
  AvailabilityZones:
    Type: List<AWS::EC2::AvailabilityZone::Name>
  ADScenarioType:
    AllowedValues:
      - AWS Managed Microsoft AD(Enterprise Edition)
      - AWS Managed Microsoft AD(Standard Edition)
    Default: AWS Managed Microsoft AD(Standard Edition)
    Type: String
  BackupRetention:
    Default: 7
    Type: Number
  DailyBackupTime:
    Default: '01:00'
    Type: String
  DomainAdminPassword:
    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]))^.*
    MaxLength: '32'
    MinLength: '8'
    NoEcho: 'true'
    Type: String
  DomainDNSName:
    AllowedPattern: '[a-zA-Z0-9\-]+\..+'
    Default: example.com
    MaxLength: '255'
    MinLength: '2'
    Type: String
  DomainNetBIOSName:
    AllowedPattern: '[a-zA-Z0-9\-]+'
    Default: example
    MaxLength: '15'
    MinLength: '1'
    Type: String
  StorageType:
    AllowedValues:
      - 'SSD'
      - 'HDD'
    Default: 'SSD'
    Type: String
  StorageCapacity:
    Default: 32
    Type: Number
  ThroughputCapacity:
    Default: 8
    Type: Number
  FSxEncryptionKey:
    AllowedValues:
      - 'Default'
      - 'GenerateKey'
      - 'UseKey'
    Default: 'Default'
    Type: String
  FSxExistingKeyID:
    Default: ''
    Type: String
  FSxAllowedCIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.0.0/16
    Type: String
  NumberOfAZs:
    AllowedValues:
      - '1'
      - '2'
    Default: '1'
    Type: String
  PrivateSubnet1CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.0.0/19
    Type: String
  PrivateSubnet2CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.32.0/19
    Type: String
  VPCCIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.0.0/16
    Type: String
  WeeklyMaintenanceTime:
     Default: '7:02:00'
     Type: String

Conditions:
  UseEnterpriseEdition: !Equals
    - !Ref 'ADScenarioType'
    - 'AWS Managed Microsoft AD(Enterprise Edition)'
  IsTwoAz: !Equals
    - !Ref 'NumberOfAZs'
    - '2'
  HasKey: !Equals
    - 'UseKey'
    - !Ref 'FSxEncryptionKey'
  CreateKey: !Equals
    - 'GenerateKey'
    - !Ref 'FSxEncryptionKey'
  UseNonDefault: !Not [!Equals [!Ref 'FSxEncryptionKey', 'Default']]

Resources:
  # VPCの作成
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref 'VPCCIDR'
      InstanceTenancy: default
      EnableDnsSupport: true
      EnableDnsHostnames: true

  # プライベートサブネットの作成
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      CidrBlock: !Ref 'PrivateSubnet1CIDR'
      AvailabilityZone: !Select ['0', !Ref 'AvailabilityZones']

  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      CidrBlock: !Ref 'PrivateSubnet2CIDR'
      AvailabilityZone: !Select ['1', !Ref 'AvailabilityZones']

  # ルートテーブルの作成
  PrivateSubnet1RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'

  PrivateSubnet2RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'

  # ルートテーブルとサブネットの紐付け
  PrivateSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PrivateSubnet1'
      RouteTableId: !Ref 'PrivateSubnet1RouteTable'

  PrivateSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PrivateSubnet2'
      RouteTableId: !Ref 'PrivateSubnet2RouteTable'

  # Managed AD 関連
  MicrosoftAD:
    Type: AWS::DirectoryService::MicrosoftAD
    Properties:
      Name: !Ref DomainDNSName
      Edition: !If [UseEnterpriseEdition, 'Enterprise', 'Standard']
      ShortName: !Ref DomainNetBIOSName
      Password: !Ref DomainAdminPassword
      VpcSettings:
        SubnetIds:
          - !Ref PrivateSubnet1
          - !Ref PrivateSubnet2
        VpcId: !Ref VPC

  DHCPOptions:
    Type: AWS::EC2::DHCPOptions
    Properties:
      DomainName: !Ref DomainDNSName
      DomainNameServers: !GetAtt MicrosoftAD.DnsIpAddresses

  VPCDHCPOptionsAssociation:
    Type: AWS::EC2::VPCDHCPOptionsAssociation
    Properties:
      VpcId: !Ref VPC
      DhcpOptionsId: !Ref DHCPOptions

  DomainMemberSG:
    Type: AWS::EC2::SecurityGroup
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: F1000
            reason: "Standard Amazon practice"
    Properties:
      GroupDescription: Domain Members
      VpcId: !Ref VPC

  DomainMembersIngressRDP:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref DomainMemberSG
      IpProtocol: tcp
      FromPort: 3389
      ToPort: 3389
      SourceSecurityGroupId: !Ref DomainMemberSG

  DomainMembersIngressWinRMHTTP:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref DomainMemberSG
      IpProtocol: tcp
      FromPort: 5985
      ToPort: 5985
      SourceSecurityGroupId: !Ref DomainMemberSG

  DomainMembersIngressWinRMHTTPS:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref DomainMemberSG
      IpProtocol: tcp
      FromPort: 5986
      ToPort: 5986
      SourceSecurityGroupId: !Ref DomainMemberSG

  # FSx for Windows File Storage 関連
  FSxKMSKey:
    Condition: CreateKey
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Type: AWS::KMS::Key
    Properties:
      EnableKeyRotation: true
      KeyPolicy:
        Version: 2012-10-17
        Id: !Ref AWS::StackName
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
            Action: kms:*
            Resource: '*'
          - Effect: Allow
            Principal:
              AWS: '*'
            Action:
              - kms:Encrypt
              - kms:Decrypt
              - kms:ReEncrypt*
              - kms:GenerateDataKey*
              - kms:CreateGrant
              - kms:ListGrants
              - kms:DescribeKey
            Resource: '*'
            Condition:
              StringEquals:
                kms:ViaService: !Sub fsx.${AWS::Region}.amazonaws.com
                kms:CallerAccount: !Ref AWS::AccountId
    Metadata:
      cfn-lint:
        config:
          ignore_checks:
            - EIAMPolicyActionWildcard
          ignore_reasons:
            - EIAMPolicyActionWildcard: Intent assumed for initial migration.

  FSxKeyAlias:
    Condition: CreateKey
    Type: AWS::KMS::Alias
    Properties:
      AliasName: !Sub alias/${AWS::StackName}
      TargetKeyId: !Ref FSxKMSKey

  FSxSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref VPC
      GroupDescription: Security Group for FSx for Windows File Storage Access
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 53
          ToPort: 53
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: udp
          FromPort: 53
          ToPort: 53
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: tcp
          FromPort: 88
          ToPort: 88
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: udp
          FromPort: 88
          ToPort: 88
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: udp
          FromPort: 123
          ToPort: 123
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: tcp
          FromPort: 135
          ToPort: 135
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: udp
          FromPort: 389
          ToPort: 389
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: tcp
          FromPort: 389
          ToPort: 389
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: udp
          FromPort: 445
          ToPort: 445
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: tcp
          FromPort: 445
          ToPort: 445
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: udp
          FromPort: 464
          ToPort: 464
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: tcp
          FromPort: 464
          ToPort: 464
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: tcp
          FromPort: 636
          ToPort: 636
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: tcp
          FromPort: 3268
          ToPort: 3268
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: tcp
          FromPort: 3269
          ToPort: 3269
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: tcp
          FromPort: 5985
          ToPort: 5985
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: tcp
          FromPort: 9389
          ToPort: 9389
          CidrIp: !Ref FSxAllowedCIDR
        - IpProtocol: tcp
          FromPort: 49152
          ToPort: 65535
          CidrIp: !Ref FSxAllowedCIDR

  WindowsFSx:
    Type: AWS::FSx::FileSystem
    Properties:
      FileSystemType: WINDOWS
      KmsKeyId: !If
        - UseNonDefault
        - !If
            - HasKey
            - !Ref FSxExistingKeyID
            - !Ref FSxKMSKey
        - !Ref AWS::NoValue
      StorageType: !Ref StorageType
      StorageCapacity: !Ref StorageCapacity
      SubnetIds:
        - !Ref PrivateSubnet1
        - !If [IsTwoAz, !Ref 'PrivateSubnet2', !Ref 'AWS::NoValue']
      SecurityGroupIds:
        - !Ref DomainMemberSG
        - !Ref FSxSecurityGroup
      WindowsConfiguration:
        ActiveDirectoryId:
          !Ref MicrosoftAD
        WeeklyMaintenanceStartTime:
          !Ref WeeklyMaintenanceTime
        DailyAutomaticBackupStartTime:
          !Ref DailyBackupTime
        AutomaticBackupRetentionDays:
          !Ref BackupRetention
        DeploymentType: !If [IsTwoAz, 'MULTI_AZ_1', 'SINGLE_AZ_1']
        PreferredSubnetId:
          !Ref PrivateSubnet1
        ThroughputCapacity:
          !Ref ThroughputCapacity

YAMLパラメーターの解説

  • Network configuration
パラメーター名 解説 備考
AvailabilityZones 利用するAZを2つ選択 Microsoft Active Directory
はAZが2つ必要
VPCCIDR VPCのCIDR
PrivateSubnet1CIDR PrivateSubnet1のCIDR
PrivateSubnet2CIDR PrivateSubnet2のCIDR
  • Microsoft Active Directory configuration
パラメーター名 解説 備考
DomainDNSName ルートドメイン
DomainNetBIOSName NetBios名
DomainAdminPassword Adminのパスワード 半角英数字
(大文字は最低1文字必要)
ADScenarioType ADのタイプを選択 Enterprise or Standard
  • FSx for Windows File Server configuration
パラメーター名 解説 備考
NumberOfAZs FSxをシングルにするか
マルチにするか
1を選択した場合、上記
構成図のAZ2にFSxは
作成されません。
BackupRetention バックアップを保持する日数 最大保持期間は35日
0は自動バックアップが
無効になります。
DailyBackupTime バックアップを実行する時間
StorageType HDD or SSD シングルAZの場合
HDDは利用不可
StorageCapacity ストレージ容量 32 GiB - 65,536 GiB
ThroughputCapacity FSxのスループット 8 - 2048
FSxEncryptionKey 保存時の暗号化 Default:AWS KMS
GenerateKey:キーを作成
UseKey:既存のキーを使用
FSxExistingKeyID AWS KMS Key ID UseKeyを選択の場合、
AWS KMS Key ID を指定します
FSxAllowedCIDR FSxにアクセスを許可する
CIDR ブロックを指定します
WeeklyMaintenanceTime 毎週のメンテナンスを実行
するための希望開始時刻

ドメイン参加に関する制約

  • Active Directory の種類
    Standard Edition または Enterprise Edition を選択可能
  • パスワードポリシー
    最小8文字、最大32文字
    英大文字・小文字・数字・記号を含む必要あり
  • ドメイン名の形式
    FQDN形式(例: example.com)
    NetBIOS名は最大15文字
  • CIDR制約
    FSxAllowedCIDR は /16〜/28 の範囲で指定
  • ポート要件(セキュリティグループ)
    FSxがADに参加するため以下のポートが必要です。
    TCP/UDP: 53, 88, 389, 445, 464
    TCP: 135, 636, 3268, 3269, 5985, 9389, 49152-65535
    UDP: 123

CloudFormation 実行時のエラー集

AZが2つ選択されていませんので、2つ選択してください。

FSx をシングルAZで構築する場合、StorageType を HDD として作成することができません。
(つまり FSx をシングルAZで構築する場合は、SSD 1択です!)
HDD を利用したい場合は、マルチAZとしてください。

throughputCapacity は 8 ~ 2048 の範囲を選択してください。

StorageCapacity は 32 ~ 65,536 の範囲を選択してください。

Windows11 で検証

  1. AWS Client VPN で Private Subnet に接続
  2. 作業PCの hosts に FSx の DNS名(以降「fs-hoge.example.com」とする) と IPアドレスを追加
  3. エクスプローラーで「\\fs-hoge.example.com」に接続
  4. ログイン画面が表示されますので、ユーザー名(ルートドメインが「example.com」の場合、「Admin@example.com」です。)と、設定したパスワードを入力し接続
  5. エクスプローラーの「share」フォルダにアクセスし、ファイルのアップロードなど試してください
  6. あとはADでユーザーを追加したり、FSxで共有フォルダを作成したりお好きに

まとめ

AWS でファイルサーバー検証を行う場合、サクッと構築できるかなと思います。
個人で検証する場合は、AWS Client VPN を利用すると良さそうです。
(別の機会にまとめてみます)
この記事が、誰かの役に立てば幸いです。

参考サイト

参考サイトとの変更点

  • パブリックサブネットは作成しません
  • NAT gateway、RD Gateway および AutoScalling group は作成しません
  • 2つのプライベートサブネット構成に固定しました
  • Active Directory domain controller および Domain controller security group は作成しません
株式会社グローバルネットコア 有志コミュニティ(β)

Discussion