クラウドネイティブにWindows + AutoScaling + Active Directory(AD)に対応する

アイキャッチ AWS EC2

コンニチハ、千葉です。

Windows + AutoScaling環境でのActive Directoryを運用する場合の自動化について、EC2インスタンスが自動的にMicrosoft Active Directoryドメインに参加するように構成する方法で語られていました。

上記ブログではスケールイン時のADオブジェクト削除の実装についてはあまり語られていなかったので、それを含めて実装してみました。

構成のパターン

スケールアウト時のAD参加は、AutoScalingのユーザーデータ + SSMで実施します。スケールイン時のADオブジェクトの削除方法については、2パターン考えられます。

  1. EC2上のシャットダウン時のバッチとして、AD退避を実施
  2. AutoScalingのライフサイクルフックを利用し、SQSへキューを発行し、ADサーバ側でキュー取得しオブジェクトを削除

1の方が実装が簡単そうですが、今回は2で実施します。理由は、AutoScalingするEC2にバッチを仕込まなくてよく、ADサーバのみで完結するので運用の手間が少ないためです。また、EC2の予期せぬ停止(シャットダウンプロセスが発生しない停止)にも対応できます。

構成は以下です。

20160721-windows-autoscaling-ad-29

この構成では以下を意識しました。

  • EC2のカスタマイズをなるべく実施しない、スタマイズが少ない方法を選択。たとへばEC2の管理台数が100台ある場合に、全サーバに共通のシェルを配置するとします。変更があった場合とっても辛いです。そのためSSMを利用して、EC2にはシェルを配置しない方式を選択します。例外として、ADサーバは台数も少なく、変更の想定もすくないのでADオブジェクト削除シェルは配置しています。
  • AWS側で吸収できるものはAWSサービスを利用する - > SQS、AutoScaling Lyfecycle Hook、AD Connector + SSMを利用

やってみた

AD環境の構成

ADサーバ + AD Connectorの作成

テスト用にADサーバを構築します。参考最初のドメインコントローラを構築する – Active Directory on AWS(2)

次に、AD Connectorを構築します。これは、SSMでドメイン参加する上で必要な構成となります。

AMC > WorkSpaces > Derectories > Set up Drectory > AD Connectorを選択します。適宜設定を入力します。

20160721-windows-autoscaling-ad-1

AD Connectorが作成されました

20160721-windows-autoscaling-ad-2

インスタンスロールの作成

SSMドメイン参加用のIAMロールを作成します。AMC > IAM > ポリシー > ポリシー作成 > AWS 管理ポリシーをコピー

AmazonEC2RoleforSSMを選択します。

20160721-windows-autoscaling-ad-10

ssm:CreateAssociationを追記します。

20160721-windows-autoscaling-ad-11

EC2にSSMの実行権限を与えるために、インスタンスロールを作成しておきます。先ほど作成したIAMポリシーをアタッチします。

20160721-windows-autoscaling-ad-12

SSMドキュメントの作成

AD Connector経由でドメイン参加するためのSSMドキュメントを作成します。propertiesを適宜環境に合わせて修正します。このSSMは、AutoScalingのユーザーデータから実行され起動したインスタンスをドメイン参加させます。

{
        "schemaVersion": "1.0",
        "description": "Sample configuration to join an instance to a domain",
        "runtimeConfig": {
           "aws:domainJoin": {
               "properties": {
                  "directoryId": "d-1234567890",
                  "directoryName": "test.example.com",
                  "directoryOU": "OU=test,DC=example,DC=com",
                  "dnsIpAddresses": [
                     "198.51.100.1",
                     "198.51.100.2"
                  ]
               }
           }
        }
}

AMC > EC2 > ドキュメント > ドキュメントの作成を選択します。

20160721-windows-autoscaling-ad-3

試しにテストインスタンスを起動(先ほど作成したインスタンロールをアタッチします)しSSMを実行、ドメイン参加できるか確認してみます。

AMC > EC2 > マネージドインスタンス > コマンドを実行 を選択します。作成したドキュメントを選択、ドメイン参加対象のインスタンスを指定、実行をクリックします。

20160721-windows-autoscaling-ad-4

SSMの実行が成功しました

20160721-windows-autoscaling-ad-5

サーバに入って状態を見てみます。

20160721-windows-autoscaling-ad-6

ドメイン参加できています!

AutoScaling環境の構成

AutoScalingを作成

AMC > EC2 > 起動設定 > を起動設定の作成をクリックします。作成時のポイントは

  • ユーザーデータの指定
  • EC2インスタンスロールを割り当てる(SSMの権限を付与したインスタンスロール)

ユーザーデータには以下を指定します。

<powershell>
$SSMCmdName="join-domain-chiba.local"
Set-DefaultAWSRegion -Region ap-northeast-1
Set-Variable -name instance_id -value (Invoke-Restmethod -uri http://169.254.169.254/latest/meta-data/instance-id)
New-SSMAssociation -InstanceId $instance_id -Name $SSMCmdName
</powershell>

20160721-windows-autoscaling-ad-8

続いてAuto Scalingグループを作成します。スケーリングポリシーなどは環境に合わせて設定ください。

20160721-windows-autoscaling-ad-9

SQSを作成

ライフサイクルフックで停止するインスタンス情報を保存するキューとDLQ用のキューを作成します。

  • asg-domain-chiba-local
  • asg-domain-chiba-local-dlq

AMC > SQS > 新しいキューの作成 よりキューを作成します。

20160721-windows-autoscaling-ad-15

asg-domain-chiba-local-dlqは、DLQ(Dead Letter Queue)として登録します。ADオブジェクト削除シェルにて何度か施行しても削除できないキュー(何度受信しても成功しないキュー)をこのDLQに保存します。

DLQをどう利用するかというと、プログラムで対応できないキューとして、手動で削除対応を実施します。具体的には、AD上のオブジェクトとキューに記載のインスタンスを比較し、キューまたはADコンピュータオブジェクトに不整合が発生している状態なので手動での削除を実施します。

20160721-windows-autoscaling-ad-16

ライフサイクルフックを作成

ライフサイクルフックを設定します。ライフサイクルフックは、AutoScalingのスケールイン時にどのインスタンスが停止したかをSQSにキューイングするために利用します。

まずは、ライフサイクルフック用のIAMロールを作成します。IAMロール作成画面でAutoScaling Notification Accessを選択します。

20160721-windows-autoscaling-ad-17

ライフサイクルフックを設定します。AMCから設定はできないので、AWS CLIが利用できる環境(AdministratorAccessポリシーをアタッチ)を用意して実施します。

$LifecycleHookName="join-domain-chiba-local"
$AutoScalingGroupName="chiba.local"
$SqsArn="arn:aws:sqs:ap-northeast-1:XXXXXXXXXX:asg-domain-chiba-local"
$RoleArn="arn:aws:iam::XXXXXXXXXX:role/life-cycle-ad"

aws autoscaling put-lifecycle-hook --lifecycle-hook-name $LifecycleHookName --auto-scaling-group-name $AutoScalingGroupName --lifecycle-transition autoscaling:EC2_INSTANCE_TERMINATING --notification-target-arn $SqsArn --heartbeat-timeout 300 --role-arn $RoleArn

スケールイン時のADオブジェクトの削除

以下のスクリプトを、ADサーバで実行します。タスクスケジューラーで1分間隔で実行するようにします。

ADサーバのインスタンスロールに、以下の権限をアタッチします。

  • AmazonSQSReadOnlyAccess
  • AmazonSSMFullAccess
for($counter1 = 0 ; $counter1 -lt 29 ; $counter1++){
  # インスタンスIDの取得(キューの取得)
  $SQSUrl="https://sqs.ap-northeast-1.amazonaws.com/xxxxxxxxxxxxx/asg-domain-chiba-local"
  $SQSMessage=(Receive-SQSMessage -QueueUrl $SQSUrl)
  if(-Not $SQSMessage){exit}

  $SQSMessageBody=$SQSMessage.body | ConvertFrom-Json
  $TargetInstanceID=$SQSMessageBody.EC2InstanceId

  # コンピューター名の取得
  ## SSMの実行
  $SSMDocumentName="AWS-RunPowerShellScript"
  $runPSCommand=Send-SSMCommand -InstanceId $TargetInstanceID -DocumentName AWS-RunPowerShellScript -Parameter @{'commands'='hostname'}
  if(-Not $runPSCommand.CommandId){exit}

  for($counter = 0 ; $counter -lt 29 ; $counter++){
    start-sleep 10
    $runPSCommandResult=Get-SSMCommandInvocation -CommandId $runPSCommand.CommandId -Details $true | select -ExpandProperty CommandPlugins

    if($runPSCommandResult.Status.Value -eq "Success"){
       break
    }elseif($runPSCommandResult.Status -eq "Pending"){
      echo "waiting..."
    }elseif($counter -eq 29){
      exit
    }else{
      exit
    }
  }

  ## コンピューター名の取得(SSMの実行)
  $COMNAME=$runPSCommandResult.Output
  if(-Not $COMNAME){exit}

  # ADコンピュータオブジェクトの削除
  Remove-ADComputer "$COMNAME" -confirm:$false
  $NowDate=date
  echo "$NowDate Deleted:$TargetInstanceID,$COMNAME" >> 'C:\ad-object-delete.log'

  # オブジェクトが存在していなければキューを削除
  Get-ADComputer  "$COMNAME"

  if(-Not $?){
    Remove-SQSMessage -QueueUrl $SQSUrl -ReceiptHandle $SQSMessage.ReceiptHandle -confirm:$false
    start-sleep 5
  }
}

コマンドをタスクスケジューラに登録します。

20160721-windows-autoscaling-ad-18

20160721-windows-autoscaling-ad-19

20160721-windows-autoscaling-ad-24

登録コマンド

powershell -NoProfile -ExecutionPolicy Unrestricted C:\adObjectDelete.ps1

開始(オプション)

C:¥

アラームの作成

CloudWatchにてアラームの設定を行います。閾値については環境に合わせて変更ください。

  • ad-delete-objectのApproximateNumberOfMessagesVisibleが5以上の場合。これは、ADコンピュータオブジェクト削除シェルが動作していないことを検知するためのアラートです。
  • ad-delete-object-dlqのApproximateNumberOfMessagesVisibleが1以上の場合。これは、ADコンピュータオブジェクトとキューのデータに不整合が発生した状態を検知するためのアラートです。このアラートが発生した場合は状態を確認し、キューまたはADコンピュータオブジェクトを手動で削除します。

20160721-windows-autoscaling-ad-21

動作確認

以下の動作確認をしました。

  • AutoScaling対象のインスタンスがスケールアウト時にドメイン参加していること
  • AutoScaling対象のインスタンスがスケールイン時にドメインから削除されていること
  • ADコンピュータオブジェクト削除シェルを停止した場合にアラートが発生すること
  • でたらめなキューを入れた場合にアラートが発生すること

AutoScaling対象のインスタンスがスケールアウト時にドメイン参加していること

AutoScalingにてDesiredの値を増やします。起動してきたインスタンスがADに登録されているか確認します。3台追加してみました。

ADに登録されていることを確認できました。

20160721-windows-autoscaling-ad-26

AutoScaling対象のインスタンスがスケールイン時にドメインから削除されていること

AutoScalingにてDesiredの値を0にして、キューに入っていること、AD上からオブジェクトが削除されていることを確認します。

キューイングされています。

20160721-windows-autoscaling-ad-27

AD上からオブジェクトが削除されています!

20160721-windows-autoscaling-ad-28

ADコンピュータオブジェクト削除シェルを停止した場合にアラートが発生すること

タスクスケジューラを向こうにして、キューを6つためてみます

20160721-windows-autoscaling-ad-22

アラートになりました

20160721-windows-autoscaling-ad-23

でたらめなキューを入れた場合にアラートが発生すること

上記の状態(ダミーのキューを6ついれた入れた状態)でタスクを実行します。ダミーのキューなので、エラーになりDLQにキューが移動されアラートがあがります。

20160721-windows-autoscaling-ad-25

最後に

結構長くなりましたが、これでAutoScaling環境下でもAD参加が可能になります。このAD環境に、AutoSaclingGroupをどんどん追加していくことを想定すると、この部分の構成を自動化するとさらに効率化できそうです。

参考