Windows EC2 インスタンスのネットワークレベル認証(NLA)を SSM オートメーションドキュメント AWSSupport-TroubleshootRDP で無効化してみた

パラメータを指定してオートメーションを実行するだけの3分コースで NLA を無効化できました。

コンバンハ、千葉(幸)です。

EC2 インスタンス(Windows)にリモートデスクトップ接続を試みた際に、以下のエラーが表示されました。

NLA-error

接続しようとしているリモートコンピューターには、ネットワークレベル認証(NLA)が必要ですが、お使いのWindowsのドメインコントローラーに接続してNLAを実行することができません。リモートコンピューターの管理者であれば、[システムのプロパティ]ダイアログボックスの[リモート]タブにあるオプションを使用して、NLAを無効にできます。

プレミアムサポートのページでは、解決方法として以下が記載されています。

NLA エラーは、ドメイン認証情報が認証されていないためにインスタンスがドメインコントローラーへの接続を失った場合に発生することがよくあります。この問題を修正するには、AWS Systems Manager の AWSSupport-TroubleshootRDP オートメーションドキュメントを使用するか、インスタンスで NLA を無効にします。

今回は AWSSupport-TroubleshootRDP を使用して NLA を無効化してみます。

AWSSupport-TroubleshootRDP オートメーションドキュメントとは

AWSSupport-TroubleshootRDP は、AWS Systems Manager ドキュメントの一種です。Systems Manager ドキュメントは他の Systems Manager の機能と組み合わせて使用できるものであり、今回の AWSSupport-TroubleshootRDP は Systems Manager オートメーション向けに用意されたドキュメントです。

Systems Manager オートメーションドキュメントではコマンドが事前定義されており、ステップに則ってそれらが順番に実行されます。

AWSSupport-TroubleshootRDP では、対象のインスタンス上における RDP 関連の設定値の確認・変更についてのコマンドなどが定義されています。

Systems Manager マネージドインスタンス(Systems Manager エージェントのセットアップ、適切な権限を持つ IAM ロール、エンドポイントへの疎通などを満たして Systems Manager の管理下にあるインスタンス)への実行だけでなく、オフラインでの実行も可能です。

オフライン実行の場合、専用のレスキュー EC2 インスタンスが作成され、そのインスタンスからドキュメント実行対象インスタンスへのオペレーションが行われます。

詳細は以下を確認してください。

指定可能なパラメータ

以下のパラメータを設定可能です。

  • 実行対象のインスタンスID
  • 以下に対してチェックだけするか設定変更/操作も行うか
    • Windowsファイアウォール
    • RDPサービスの自動起動
    • RDPサービスの再起動
    • RDPに使用するポート
    • ネットワークレベル認証
    • fDenyTSConnections(リモートデスクトップ接続が有効になっているかどうか)
  • オフラインでのトラブルシュートも行うか
    • オフラインを有効化する場合以下を指定可
      • レスキューインスタンスを起動するサブネット
      • ログ出力対象のS3バケット
  • オートメーション実行に使用する IAM ロール
名前 タイプ 指定 説明 デフォルト値
InstanceId String 必須 RDP 設定のトラブルシューティングを行うインスタンスの ID -
Action String オプション 【Custom】項目に応じて Check か Fix かを行う【CheckAll】すべての項目で Check のみ行う 【FixAll】RDP のデフォルト設定を復元し、Windows ファイアウォールを無効にする Custom
AllowOffline String オプション (Fix の項目がある場合のみ)オンラインでのトラブルシューティングに失敗した場合や、対象がマネージドインスタンスではない場合にオフラインでの RDP修復を許可する場合に true を設定。注: オフラインの修復では、SSM オートメーションはインスタンスを停止し、操作を試行する前に AMI を作成する FALSE
Firewall String オプション Windows ファイアウォール(すべてのプロファイル)をチェックするか無効にする Check
RDPServiceStartupType String オプション RDPサービスの自動起動を有効化するか現在の設定のチェックのみをする Check
RDPServiceAction String オプション RDP サービス (TermService) のチェック、開始、再起動、または強制再起動を行う Check
RDPPortAction String オプション RDP 接続に使用されている現在のポートをチェックするか、RDP ポートを 3389 に戻してサービスを再起動する Check
NLASettingAction String オプション ネットワークレベル認証 (NLA) をチェックまたは無効にする Check
RemoteConnections String オプション fDenyTSConnections(リモート デスクトップ接続が有効になっているかどうか)設定をチェックするか有効化する Check
SubnetId String オプション (オフラインの場合のみ)オフラインのトラブルシューティングを実行するために使用されるEC2RescueインスタンスのサブネットID。サブネットIDが指定されていない場合、AWS Systems Manager Automationは新しいVPCを作成する。重要:サブネットは、InstanceIdと同じAZにあり、SSMエンドポイントへのアクセスを許可している必要がある SelectedInstanceSubnet
S3BucketName String オプション (オフラインの場合のみ)トラブルシューティングログをアップロードするS3バケット名。クロスアカウント不可。不要なアクセス権を持たせないようバケットポリシーを考慮すること -
AutomationAssumeRole String オプション Automation 実行のためのIAMロール。ロールが指定されていない場合、AWS Systems Manager Automationは、このドキュメントを実行するユーザーの権限を使用する -

ステップ

ドキュメント実行の際には以下のステップに則って順番に処理が行われます。

ドキュメント実行の際に指定したパラメータによっては、すべてのステップが行われるわけではありません。(特に後半はオフライン実行に関するステップのため、オンライン実行の場合はスキップされます。)

ステップ名を見ても具体的な内容はまでは判別できないと思いますので、ここでは参考程度にご参照ください。

  • ステップ 1: assertInstanceIsWindows
  • ステップ 2: assertInstanceIsManagedInstance
  • ステップ 3: assertActionIsCustom
  • ステップ 4: manageFirewallProfiles
  • ステップ 5: manageRDPServiceSettings
  • ステップ 6: manageRDPSettings
  • ステップ 7: assertActionIsCheckAll
  • ステップ 8: checkFirewallProfiles
  • ステップ 9: checkRDPServiceSettings
  • ステップ 10: checkRDPSettings
  • ステップ 11: assertActionIsFixAll
  • ステップ 12: disableFirewallProfiles
  • ステップ 13: restoreDefaultRDPServiceSettings
  • ステップ 14: restoreDefaultRDPSettings
  • ステップ 15: assertAllowOffline
  • ステップ 16: assertActionIsFixAllForOfflineBranch
  • ステップ 17: assertSubnetId
  • ステップ 18: describeSourceInstance
  • ステップ 19: troubleshootRDPOffline
  • ステップ 20: troubleshootRDPOfflineWithSubnetId

ステップごとにインプットアウトプットの定義が可能である、と覚えておくと後の操作が分かりやすくなるかもしれません。

コンテンツ

2021年3月時点のバージョン3の内容は以下の通りです。

ボリュームがあるため、無理に全体を理解する必要はありません。大まかに流れを確認したい場合や、一部の処理の詳細を確認したい、といった場合にご確認ください。

折り畳み
{
  "schemaVersion": "0.3",
  "description": "The AWSSupport-TroubleshootRDP automation document allows the user to check or modify common settings on the target instance which may impact Remote Desktop Protocol (RDP) connections, such as the RDP port, Network Layer Authentication (NLA) and Windows Firewall profiles. Optionally, changes can be applied offline by stopping and starting the instance, if the user explicitly allows for offline remediation. By default, the document reads and outputs the values of the settings. IMPORTANT: Changes to the RDP settings, RDP service and Windows Firewall profiles should be carefully reviewed before running this document.",
  "assumeRole": "{{ AutomationAssumeRole }}",
  "parameters": {
    "InstanceId": {
      "type": "String",
      "description": "(Required) The ID of the instance to troubleshoot the RDP settings of.",
      "allowedPattern": "^[m]{0,1}i-[a-z0-9]{8,17}$"
    },
    "Action": {
      "description": "(Optional) [Custom] Use the values from Firewall, RDPServiceStartupType, RDPServiceAction, RDPPortAction, NLASettingAction and RemoteConnections to manage the settings. [CheckAll] Read the values of the settings without changing them. [FixAll] Restore RDP default settings, and disable the Windows Firewall.",
      "type": "String",
      "allowedValues": [
        "CheckAll",
        "FixAll",
        "Custom"
      ],
      "default": "Custom"
    },
    "AllowOffline": {
      "type": "String",
      "description": "(Optional) Fix only - Set it to true if you allow an offline RDP remediation in case the online troubleshooting fails, or the provided instance is not a managed instance. Note: For the offline remediation, SSM Automation stops the instance, and creates an AMI before attempting any operations.",
      "default": "False",
      "allowedValues": [
        "True",
        "False"
      ]
    },
    "Firewall": {
      "type": "String",
      "description": "(Optional) Check or disable the Windows firewall (all profiles).",
      "default": "Check",
      "allowedValues": [
        "Check",
        "Disable"
      ]
    },
    "RDPServiceStartupType": {
      "type": "String",
      "description": "(Optional) Check or set the RDP service to automatically start when Windows boots.",
      "default": "Check",
      "allowedValues": [
        "Check",
        "Auto"
      ]
    },
    "RDPServiceAction": {
      "type": "String",
      "description": "(Optional) Check, start, restart, or force-restart the RDP service (TermService).",
      "default": "Check",
      "allowedValues": [
        "Check",
        "Start",
        "Restart",
        "Force-Restart"
      ]
    },
    "RDPPortAction": {
      "type": "String",
      "description": "(Optional) Check the current port used for RDP connections, or modify the RDP port back to 3389 and restart the service.",
      "default": "Check",
      "allowedValues": [
        "Check",
        "Modify"
      ]
    },
    "NLASettingAction": {
      "type": "String",
      "description": "(Optional) Check or disable Network Layer Authentication (NLA).",
      "default": "Check",
      "allowedValues": [
        "Check",
        "Disable"
      ]
    },
    "RemoteConnections": {
      "type": "String",
      "description": "(Optional) An action to perform on the fDenyTSConnections setting: Check, Enable.",
      "default": "Check",
      "allowedValues": [
        "Check",
        "Enable"
      ]
    },
    "SubnetId": {
      "type": "String",
      "description": "(Optional) Offline only - The subnet ID for the EC2Rescue instance used to perform the offline troubleshooting. If no subnet ID is specified, AWS Systems Manager Automation will create a new VPC. IMPORTANT: The subnet must be in the same Availability Zone as InstanceId, and it must allow access to the SSM endpoints.",
      "default": "SelectedInstanceSubnet",
      "allowedPattern": "^$|^subnet-[a-z0-9]{8,17}$|SelectedInstanceSubnet"
    },
    "S3BucketName": {
      "description": "(Optional) Offline only - S3 bucket name in your account where you want to upload the troubleshooting logs. Make sure the bucket policy does not grant unnecessary read/write permissions to parties that do not need access to the collected logs.",
      "allowedPattern": "^$|^[_a-zA-Z0-9][-._a-zA-Z0-9]{2,62}$",
      "type": "String",
      "default": ""
    },
    "AutomationAssumeRole": {
      "type": "String",
      "description": "(Optional) The IAM role for this execution. If no role is specified, AWS Systems Manager Automation will use the permissions of the user that executes this document.",
      "default": ""
    }
  },
  "mainSteps": [
    {
      "name": "assertInstanceIsWindows",
      "action": "aws:assertAwsResourceProperty",
      "onFailure": "Abort",
      "inputs": {
        "Service": "ec2",
        "Api": "DescribeInstances",
        "InstanceIds": [
          "{{ InstanceId }}"
        ],
        "PropertySelector": "$.Reservations[0].Instances[0].Platform",
        "DesiredValues": [
          "windows"
        ]
      },
      "isCritical": "true"
    },
    {
      "name": "assertInstanceIsManagedInstance",
      "action": "aws:assertAwsResourceProperty",
      "onFailure": "step:assertAllowOffline",
      "inputs": {
        "Service": "ssm",
        "Api": "DescribeInstanceInformation",
        "InstanceInformationFilterList": [
          {
            "key": "InstanceIds",
            "valueSet": [
              "{{ InstanceId }}"
            ]
          }
        ],
        "PropertySelector": "$.InstanceInformationList[0].PingStatus",
        "DesiredValues": [
          "Online"
        ]
      },
      "isCritical": "false",
      "nextStep": "assertActionIsCustom"
    },
    {
      "name": "assertActionIsCustom",
      "action": "aws:assertAwsResourceProperty",
      "onFailure": "step:assertActionIsCheckAll",
      "inputs": {
        "Service": "ssm",
        "Api": "GetAutomationExecution",
        "AutomationExecutionId": "{{ automation:EXECUTION_ID }}",
        "PropertySelector": "AutomationExecution.Parameters.Action[0]",
        "DesiredValues": [
          "Custom"
        ]
      },
      "isCritical": "false",
      "nextStep": "manageFirewallProfiles"
    },
    {
      "name": "manageFirewallProfiles",
      "action": "aws:runCommand",
      "onFailure": "Continue",
      "inputs": {
        "DocumentName": "AWS-RunPowerShellScript",
        "InstanceIds": [
          "{{ InstanceId }}"
        ],
        "Parameters": {
          "commands": [
            "Function Invoke-FirewallAction {",
            "",
            "    param (",
            "        [Parameter(Mandatory = $true)]",
            "        [ValidateSet(\"Check\", \"Disable\")] ",
            "        [String]$FirewallAction",
            "    )",
            "",
            "    Try {",
            "",
            "        If ( $FirewallAction -eq \"Check\") {",
            "",
            "            & \"${env:SYSTEMROOT}\\system32\\netsh.exe\" advfirewall show allprofiles ",
            "",
            "        }",
            "        else {",
            "",
            "            #Turn off firewall (Domain, Private, Public profile)",
            "            & \"${env:SYSTEMROOT}\\system32\\netsh.exe\" advfirewall set allprofiles state off > $null",
            "            Write-Host \"All Windows firewall profiles have been disabled (Domain, Private, Public).\"",
            "",
            "        }",
            "    }",
            "    Catch {",
            "",
            "        throw \"Unable to apply action $FirewallAction to firewall. Reason: \" + $_.Exception.Message",
            "",
            "    }",
            "",
            "}",
            "",
            "Try {",
            "",
            "    #Input variables ",
            "    $documentActionInput = \"{{ Action }}\"",
            "    $firewallInput = \"{{ Firewall }}\"",
            "",
            "    switch ($documentActionInput) {",
            "        \"Custom\" { $firewallAction = $firewallInput }",
            "        \"CheckAll\" { $firewallAction = \"Check\" }",
            "        \"FixAll\" { $firewallAction = \"Disable\" }",
            "        default { throw \"Unexpected input.\"} ",
            "    }",
            "",
            "    #Firewall Action",
            "    Invoke-FirewallAction -FirewallAction $firewallAction",
            "",
            "}",
            "Catch {",
            "",
            "    Write-Host $_.Exception.Message",
            "    Exit 1",
            "",
            "}"
          ]
        }
      },
      "isCritical": "true",
      "nextStep": "manageRDPServiceSettings"
    },
    {
      "name": "manageRDPServiceSettings",
      "action": "aws:executeAutomation",
      "onFailure": "Continue",
      "inputs": {
        "DocumentName": "AWSSupport-ManageWindowsService",
        "RuntimeParameters": {
          "InstanceId": [
            "{{ InstanceId }}"
          ],
          "WindowsServiceName": [
            "TermService"
          ],
          "StartupType": [
            "{{ RDPServiceStartupType }}"
          ],
          "ServiceAction": [
            "{{ RDPServiceAction }}"
          ]
        }
      },
      "isCritical": "true",
      "nextStep": "manageRDPSettings"
    },
    {
      "name": "manageRDPSettings",
      "action": "aws:executeAutomation",
      "onFailure": "Abort",
      "inputs": {
        "DocumentName": "AWSSupport-ManageRDPSettings",
        "RuntimeParameters": {
          "InstanceId": [
            "{{ InstanceId }}"
          ],
          "RDPPortAction": [
            "{{ RDPPortAction }}"
          ],
          "RDPPort": [
            "3389"
          ],
          "NLASettingAction": [
            "{{ NLASettingAction }}"
          ],
          "RemoteConnections": [
            "{{ RemoteConnections }}"
          ]
        }
      },
      "isEnd": "true"
    },
    {
      "name": "assertActionIsCheckAll",
      "action": "aws:assertAwsResourceProperty",
      "onFailure": "step:assertActionIsFixAll",
      "inputs": {
        "Service": "ssm",
        "Api": "GetAutomationExecution",
        "AutomationExecutionId": "{{ automation:EXECUTION_ID }}",
        "PropertySelector": "$.AutomationExecution.Parameters.Action[0]",
        "DesiredValues": [
          "CheckAll"
        ]
      },
      "isCritical": "false",
      "nextStep": "checkFirewallProfiles"
    },
    {
      "name": "checkFirewallProfiles",
      "action": "aws:runCommand",
      "onFailure": "Continue",
      "inputs": {
        "DocumentName": "AWS-RunPowerShellScript",
        "InstanceIds": [
          "{{ InstanceId }}"
        ],
        "Parameters": {
          "commands": [
            "Function Invoke-FirewallAction {",
            "",
            "    param (",
            "        [Parameter(Mandatory = $true)]",
            "        [ValidateSet(\"Check\", \"Disable\")] ",
            "        [String]$FirewallAction",
            "    )",
            "",
            "    Try {",
            "",
            "        If ( $FirewallAction -eq \"Check\") {",
            "",
            "            & \"${env:SYSTEMROOT}\\system32\\netsh.exe\" advfirewall show allprofiles ",
            "",
            "        }",
            "        else {",
            "",
            "            #Turn off firewall (Domain, Private, Public profile)",
            "            & \"${env:SYSTEMROOT}\\system32\\netsh.exe\" advfirewall set allprofiles state off > $null",
            "            Write-Host \"All Windows firewall profiles have been disabled (Domain, Private, Public).\"",
            "",
            "        }",
            "    }",
            "    Catch {",
            "",
            "        throw \"Unable to apply action $FirewallAction to firewall. Reason: \" + $_.Exception.Message",
            "",
            "    }",
            "",
            "}",
            "",
            "Try {",
            "",
            "    #Input variables ",
            "    $documentActionInput = \"{{ Action }}\"",
            "    $firewallInput = \"{{ Firewall }}\"",
            "",
            "    switch ($documentActionInput) {",
            "        \"Custom\" { $firewallAction = $firewallInput }",
            "        \"CheckAll\" { $firewallAction = \"Check\" }",
            "        \"FixAll\" { $firewallAction = \"Disable\" }",
            "        default { throw \"Unexpected input.\"} ",
            "    }",
            "",
            "    #Firewall Action",
            "    Invoke-FirewallAction -FirewallAction $firewallAction",
            "",
            "}",
            "Catch {",
            "",
            "    Write-Host $_.Exception.Message",
            "    Exit 1",
            "",
            "}"
          ]
        }
      },
      "isCritical": "true",
      "nextStep": "checkRDPServiceSettings"
    },
    {
      "name": "checkRDPServiceSettings",
      "action": "aws:executeAutomation",
      "onFailure": "Continue",
      "inputs": {
        "DocumentName": "AWSSupport-ManageWindowsService",
        "RuntimeParameters": {
          "InstanceId": [
            "{{ InstanceId }}"
          ],
          "WindowsServiceName": [
            "TermService"
          ],
          "StartupType": [
            "Check"
          ],
          "ServiceAction": [
            "Check"
          ]
        }
      },
      "isCritical": "true",
      "nextStep": "checkRDPSettings"
    },
    {
      "name": "checkRDPSettings",
      "action": "aws:executeAutomation",
      "onFailure": "Abort",
      "inputs": {
        "DocumentName": "AWSSupport-ManageRDPSettings",
        "RuntimeParameters": {
          "InstanceId": [
            "{{ InstanceId }}"
          ],
          "RDPPortAction": [
            "Check"
          ],
          "NLASettingAction": [
            "Check"
          ],
          "RemoteConnections": [
            "Check"
          ]
        }
      },
      "isEnd": "true"
    },
    {
      "name": "assertActionIsFixAll",
      "action": "aws:assertAwsResourceProperty",
      "onFailure": "Abort",
      "inputs": {
        "Service": "ssm",
        "Api": "GetAutomationExecution",
        "AutomationExecutionId": "{{ automation:EXECUTION_ID }}",
        "PropertySelector": "$.AutomationExecution.Parameters.Action[0]",
        "DesiredValues": [
          "FixAll"
        ]
      },
      "nextStep": "disableFirewallProfiles"
    },
    {
      "name": "disableFirewallProfiles",
      "action": "aws:runCommand",
      "onFailure": "Continue",
      "inputs": {
        "DocumentName": "AWS-RunPowerShellScript",
        "InstanceIds": [
          "{{ InstanceId }}"
        ],
        "Parameters": {
          "commands": [
            "Function Invoke-FirewallAction {",
            "",
            "    param (",
            "        [Parameter(Mandatory = $true)]",
            "        [ValidateSet(\"Check\", \"Disable\")] ",
            "        [String]$FirewallAction",
            "    )",
            "",
            "    Try {",
            "",
            "        If ( $FirewallAction -eq \"Check\") {",
            "",
            "            & \"${env:SYSTEMROOT}\\system32\\netsh.exe\" advfirewall show allprofiles ",
            "",
            "        }",
            "        else {",
            "",
            "            #Turn off firewall (Domain, Private, Public profile)",
            "            & \"${env:SYSTEMROOT}\\system32\\netsh.exe\" advfirewall set allprofiles state off > $null",
            "            Write-Host \"All Windows firewall profiles have been disabled (Domain, Private, Public).\"",
            "",
            "        }",
            "    }",
            "    Catch {",
            "",
            "        throw \"Unable to apply action $FirewallAction to firewall. Reason: \" + $_.Exception.Message",
            "",
            "    }",
            "",
            "}",
            "",
            "Try {",
            "",
            "    #Input variables ",
            "    $documentActionInput = \"{{ Action }}\"",
            "    $firewallInput = \"{{ Firewall }}\"",
            "",
            "    switch ($documentActionInput) {",
            "        \"Custom\" { $firewallAction = $firewallInput }",
            "        \"CheckAll\" { $firewallAction = \"Check\" }",
            "        \"FixAll\" { $firewallAction = \"Disable\" }",
            "        default { throw \"Unexpected input.\"} ",
            "    }",
            "",
            "    #Firewall Action",
            "    Invoke-FirewallAction -FirewallAction $firewallAction",
            "",
            "}",
            "Catch {",
            "",
            "    Write-Host $_.Exception.Message",
            "    Exit 1",
            "",
            "}"
          ]
        }
      },
      "isCritical": "true",
      "nextStep": "restoreDefaultRDPServiceSettings"
    },
    {
      "name": "restoreDefaultRDPServiceSettings",
      "action": "aws:executeAutomation",
      "onFailure": "Continue",
      "inputs": {
        "DocumentName": "AWSSupport-ManageWindowsService",
        "RuntimeParameters": {
          "InstanceId": [
            "{{ InstanceId }}"
          ],
          "WindowsServiceName": [
            "TermService"
          ],
          "StartupType": [
            "Auto"
          ],
          "ServiceAction": [
            "Start"
          ]
        }
      },
      "isCritical": "true",
      "nextStep": "restoreDefaultRDPSettings"
    },
    {
      "name": "restoreDefaultRDPSettings",
      "action": "aws:executeAutomation",
      "onFailure": "Abort",
      "inputs": {
        "DocumentName": "AWSSupport-ManageRDPSettings",
        "RuntimeParameters": {
          "InstanceId": [
            "{{ InstanceId }}"
          ],
          "RDPPortAction": [
            "Modify"
          ],
          "RDPPort": [
            "3389"
          ],
          "NLASettingAction": [
            "Disable"
          ],
          "RemoteConnections": [
            "Enable"
          ]
        }
      },
      "isEnd": "true"
    },
    {
      "name": "assertAllowOffline",
      "action": "aws:assertAwsResourceProperty",
      "onFailure": "Abort",
      "inputs": {
        "Service": "ssm",
        "Api": "GetAutomationExecution",
        "AutomationExecutionId": "{{ automation:EXECUTION_ID }}",
        "PropertySelector": "$.AutomationExecution.Parameters.AllowOffline[0]",
        "DesiredValues": [
          "True"
        ]
      },
      "nextStep": "assertActionIsFixAllForOfflineBranch"
    },
    {
      "name": "assertActionIsFixAllForOfflineBranch",
      "action": "aws:assertAwsResourceProperty",
      "onFailure": "Abort",
      "inputs": {
        "Service": "ssm",
        "Api": "GetAutomationExecution",
        "AutomationExecutionId": "{{ automation:EXECUTION_ID }}",
        "PropertySelector": "$.AutomationExecution.Parameters.Action[0]",
        "DesiredValues": [
          "FixAll"
        ]
      },
      "nextStep": "assertSubnetId"
    },
    {
      "name": "assertSubnetId",
      "action": "aws:assertAwsResourceProperty",
      "onFailure": "step:troubleshootRDPOffline",
      "inputs": {
        "Service": "ssm",
        "Api": "GetAutomationExecution",
        "AutomationExecutionId": "{{ automation:EXECUTION_ID }}",
        "PropertySelector": "$.AutomationExecution.Parameters.SubnetId[0]",
        "DesiredValues": [
          "SelectedInstanceSubnet"
        ]
      },
      "isCritical": "false",
      "nextStep": "describeSourceInstance"
    },
    {
      "name": "describeSourceInstance",
      "action": "aws:executeAwsApi",
      "onFailure": "Abort",
      "inputs": {
        "Service": "ec2",
        "Api": "DescribeInstances",
        "InstanceIds": [
          "{{ InstanceId }}"
        ]
      },
      "outputs": [
        {
          "Name": "SubnetId",
          "Selector": "$.Reservations[0].Instances[0].NetworkInterfaces[0].SubnetId",
          "Type": "String"
        }
      ],
      "nextStep": "troubleshootRDPOfflineWithSubnetId"
    },
    {
      "name": "troubleshootRDPOffline",
      "action": "aws:executeAutomation",
      "onFailure": "Abort",
      "inputs": {
        "RuntimeParameters": {
          "UnreachableInstanceId": [
            "{{ InstanceId }}"
          ],
          "SubnetId": [
            "{{ SubnetId }}"
          ],
          "LogDestination": [
            "{{ S3BucketName }}"
          ],
          "AssumeRole": [
            "{{ AutomationAssumeRole }}"
          ]
        },
        "DocumentName": "AWSSupport-ExecuteEC2Rescue"
      },
      "isEnd": "true"
    },
    {
      "name": "troubleshootRDPOfflineWithSubnetId",
      "action": "aws:executeAutomation",
      "onFailure": "Abort",
      "inputs": {
        "RuntimeParameters": {
          "UnreachableInstanceId": [
            "{{ InstanceId }}"
          ],
          "SubnetId": [
            "{{ describeSourceInstance.SubnetId }}"
          ],
          "LogDestination": [
            "{{ S3BucketName }}"
          ],
          "AssumeRole": [
            "{{ AutomationAssumeRole }}"
          ]
        },
        "DocumentName": "AWSSupport-ExecuteEC2Rescue"
      },
      "isEnd": "true"
    }
  ],
  "outputs": [
    "manageFirewallProfiles.Output",
    "manageRDPServiceSettings.Output",
    "manageRDPSettings.Output",
    "checkFirewallProfiles.Output",
    "checkRDPServiceSettings.Output",
    "checkRDPSettings.Output",
    "disableFirewallProfiles.Output",
    "restoreDefaultRDPServiceSettings.Output",
    "restoreDefaultRDPSettings.Output",
    "troubleshootRDPOffline.Output",
    "troubleshootRDPOfflineWithSubnetId.Output"
  ]
}

ここまで見てきた内容は、 AWSSupport-TroubleshootRDP の詳細画面から確認可能です。 Systems Manager ドキュメントの画面からドキュメント名で検索をかけることで遷移できます。

AWS_Systems_Manager_-_Documents

やってみた(チェックのみ)

実際に AWSSupport-TroubleshootRDP を実行してみます。まずは設定変更を伴わずにチェックのみを行うパターンでやってみます。

参考にするページは以下です。

ドキュメントの実行画面への遷移

AWS マネジメントコンソールを開いている状態で以下のページにアクセスすることで、実行画面に遷移します。

https://console.aws.amazon.com/systems-manager/automation/execute/AWSSupport-TroubleshootRDP

実行パターンはいくつかありますが、今回はシンプルな実行を選択します。

AWS_Systems_Manager_-_Automation

パラメータの入力

先ほどの画面を下にスクロールすると、パラメータの入力箇所があります。

まずはInstanceIdで実行対象のインスタンスを選択します。

AWS_Systems_Manager_-_Automation-4822307

今回はマネージドインスタンスである EC2 インスタンスを選択しました。「シンプルな実行」の場合、一度に1台のみ指定可能です。

続いて他のパラメータを指定します。

今回はデフォルトのままにします。ざっくり、以下の指定となります。

  • すべてチェックのみ
  • オフライン実行なし

AWS_Systems_Manager_-_Automation-4822351

最後に画面下部で [ 実行 ] を押下することで、実行が開始されます。

AWS_Systems_Manager_-_Automation-4822389

実行結果の確認

実行が開始されると、以下の画面に遷移します。

AWS_Systems_Manager_-_Automation-4822611

[ 出力 ] 部を展開すると、各ステップにおけるアウトプットが一覧で確認できます。

アウトプットが定義されていないステップもあるため、その場合はここには現れてきません。また、今回実行されなかったステップ(青字)については、「アウトプットなし」の表記となっています。

(ステップ4が文字化けしてしまってますね、、)

AWS_Systems_Manager_-_Automation-4822698-5033522

「全体的なステータス」が 成功になっていればオートメーションの実行は完了しています。今回の例では、ステップ6より後はすべて「保留中」となり、スキップされます。

AWS_Systems_Manager_-_Automation-4822891

ステップの詳細を確認

個々のステップの詳細を確認することもできます。ここでは、ステップ6の詳細を確認してみます。

インプットとして、オートメーション実行時に指定したパラメータのいくつかが反映されています。

AWS_Systems_Manager_-_Automation-4823385

アウトプットとして、チェックの結果が出力されています。

AWS_Systems_Manager_-_Automation-4823459

Current RDP Port: 3389.
Network Level Authentication is enabled on this machine.
Remote Desktop connections are enabled on this machine.

冒頭で引用したエラーは NLA を無効化することで解決が見込めますが、現時点では NLA が有効であることがわかりました。

やってみた(NLAの無効化)

AWSSupport-TroubleshootRDP の使い方が分かったところで、本題の NLA 無効化を実施してみます。

と言っても、先ほど実行した内容のパラメータを一部変えるだけです。

パラメータの指定画面で、NLASettingActionDisableに指定します。

AWS_Systems_Manager_-_Automation-4823627

その他は先ほどの手順と同様にして、実行します。

オートメーションの実行ステータスが成功に遷移したことを確認し、ステップ6の詳細を確認します。(なお、私の環境では 1分程度で成功ステータスに遷移しました。)

AWS_Systems_Manager_-_Automation-4823808

Current RDP Port: 3389.
Network Level Authentication disabled.
Remote Desktop connections are enabled on this machine.

インプットのNLASettingActionDisableになっており、アウトプットで NLA が無効化されたことが確認できました。

この状態で再度リモートデスクトップ接続を試みると、問題なく接続が可能でした。

終わりに

SSM オートメーションドキュメント AWSSupport-TroubleshootRDP を使用し、NLA の無効化を行ってみました。

対象がマネージドインスタンスであれば、セッションマネージャーで接続して直接コマンドを実行したり、Run Command でコマンドを実行することでも NLA の無効化は実現できます。

冒頭のプレミアムサポートページでも、 Run Command で以下を実行するパターンの記述があります。

reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v SecurityLayer /t REG_DWORD /d 0 /f
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v UserAuthentication /t REG_DWORD /d 0 /f
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v fAllowSecProtocolNegotiation /t REG_DWORD /d 0 /f

個人的には、事前に定義されたコマンドをそのまま実行してもらえるオートメーションの方が、コマンドミスの可能性も減らせて好きです。全体的な手間も、そこまで変わりません。

また、マネージドインスタンスでないインスタンスが対象の場合にも使用できる、という点が AWSSupport-TroubleshootRDP の強みだと思います。

AWS 側で公式で用意してくれている便利ツールなので、積極的に使っていきたいですね。

また、Linux や Windows で「到達不可能」なインスタンスの診断とトラブルシューティングを行ってくれるAWSSupport-ExecuteEC2Rescueというオートメーションドキュメントも用意されています。

EC2 インスタンスにうまく接続できない、となった場合には、「 SSM オートメーションドキュメントを活用できないかな?」と選択肢の一つとして思い出していただければと思います。

以上、千葉(幸)がお送りしました。