在 AWS 上使用 Terraform 安装 Windows HotFix

Install Windows HotFix using Terraform on AWS

我有一个非常简单的 PowerShell 脚本,可将生成的测试文件从 Windows 2008 R2 数据中心服务器(干净的 AWS 实例)上传到 AWS S3 存储桶。如果我 运行 使用 Terraform(remote-exec 配置器)在服务器上远程执行脚本,则脚本在 S3 上传时会失败并显示 WhosebugException。当我直接在服务器上 运行 脚本时,它 运行 没问题并上传了文件。

我已经尝试了不同的文件大小,14.5MB 似乎是 WhosebugException 发生之前的最大可用大小。当我将 RDP 连接到服务器并直接 运行 脚本时,几乎任何大小都可以正常工作。我已经测试了 200MB,它工作正常。

知道为什么会这样或者我能做些什么来解决这个问题吗?我实际需要上传的文件是50MB。

以下是重现问题的基本部分。 terraform.tf 文件:

resource "aws_instance" "windows" {
  count                       = "1"
  ami                         = "ami-e935fc94" #base win 2008 R2 datacenter
  instance_type               = "t2.micro"

  connection {
    type     = "winrm"
    user     = "<username>"
    password = "<password>"
    timeout  = "30m"
  }

  provisioner "file" {
    source      = "windows/upload.ps1"
    destination = "C:\scripts\upload.ps1"
  }

  provisioner "remote-exec" {
    inline = [
      "powershell.exe -File C:\scripts\upload.ps1"
    ]
  }
}

PowerShell 脚本非常简单。 upload.ps1:

$f = new-object System.IO.FileStream C:\Temp\test.dat, Create, ReadWrite
$f.SetLength(40MB) # change this to 14.5MB and it works!
$f.Close()
Write-S3Object -BucketName "mybucket" -Folder "C:\Temp" -KeyPrefix "20180322" -SearchPattern "*.dat"

从 Terraform(remote-exec 配置器启动脚本时收到的错误:

aws_instance.windows (remote-exec): Process is terminated due to WhosebugException.

运行 upload.ps1 来自服务器本身的 RDP 工作正常,包括更大的文件(测试最大 200MB)。

这里是版本信息:

Microsoft Windows Server 2008 R2 Datacenter
Powershell Version: 3.0
AWS Tools for Windows PowerShell, Version 3.3.245.0
Amazon Web Services SDK for .NET, Core Runtime Version 3.3.21.15

这个问题是由 Windows bug 引起的。对于标准 Windows 服务器来说,这一切都很好——您可以修补并继续。但是,使用 Terraform 的 AWS 自动化让事情变得更加棘手。

理想的解决方案将允许 1) 使用基本 AMI,2) 将修补程序应用于自身,以及 3) 然后 运行 WinRM remote-exec,所有这些都来自 Terraform。另一种解决方案是创建一个安装了修补程序的 AMI,并让 Terraform 使用该 AMI 生成实例。但是,您将无法维护 AMI。

通常,我使用过滤器获取 Microsoft-provided 基本 AMI:

data "aws_ami" "windows2008" {
  most_recent = true

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  filter {
    name   = "name"
    values = ["Windows_Server-2008-R2_SP1-English-64Bit-Base*",]
  }

  owners = ["801119661308", "amazon"]
}

然后我使用该 AMI 创建 AWS 实例:

resource "aws_instance" "windows" {
  count                       = "1"
  ami                         = "${data.aws_ami.windows2008.id}"
  ...
}

但是, 基本 AMI 没有安装 hotfix 允许您避免此 WinRM/Windows 错误。这是它变得棘手。

您可以使用用户数据脚本来执行 multi-phase 设置。在实例的第一次启动(阶段 1)中,我们将阻止实例,以便 remote-exec 在我们准备好之前不会进入。然后,我们将下载并安装修补程序并重新启动(感谢 Niklas Akerlund, and Techibee). On the second boot (in method described here),我们将取消阻止实例(启用 WinRM)以便 remote-exec 可以连接。

这是我的 userdata/PowerShell 脚本:

$StateFile = "C:\Temp\userdata_state.txt"
If(-Not (Test-Path -Path $StateFile))
{
  # PHASE 1

  # Close the instance to WinRM connections until instance is ready (probably already closed, but just in case)
  Start-Process -FilePath "winrm" -ArgumentList "set winrm/config/service/auth @{Basic=`"false`"}" -Wait

  # Set the admin password for WinRM connections
  $Admin = [adsi]("WinNT://./Administrator, user")
  $Admin.psbase.invoke("SetPassword", "${tfi_rm_pass}")

  # Create state file so after reboot it will know
  New-Item -Path $StateFile -ItemType "file" -Force

  # Make it so that userdata will run again after reboot
  $EC2SettingsFile="C:\Program Files\Amazon\Ec2ConfigService\Settings\Config.xml"
  $Xml = [xml](Get-Content $EC2SettingsFile)
  $XmlElement = $Xml.get_DocumentElement()
  $XmlElementToModify = $XmlElement.Plugins

  Foreach ($Element in $XmlElementToModify.Plugin)
  {
      If ($Element.name -eq "Ec2HandleUserData")
      {
          $Element.State="Enabled"
      }
  }
  $Xml.Save($EC2SettingsFile)

  # Download and install hotfix

  # Download self-extractor
  $DownloadUrl = "https://hotfixv4.trafficmanager.net/Windows%207/Windows%20Server2008%20R2%20SP1/sp2/Fix467402/7600/free/463984_intl_x64_zip.exe"
  $HotfixDir = "C:\hotfix"
  $HotfixFile = "$HotfixDir\KB2842230.exe"
  mkdir $HotfixDir
  (New-Object System.Net.WebClient).DownloadFile($DownloadUrl, $HotfixFile)

  # Extract self-extractor
  Add-Type -AssemblyName System.IO.Compression.FileSystem
  [System.IO.Compression.ZipFile]::ExtractToDirectory($HotfixFile, $HotfixDir)

  # Install - NOTE: wusa returns immediately, before install completes, so you must check process to see when it finishes
  Get-Item "$HotfixDir\*.msu" | Foreach { wusa ""$_.FullName /quiet /norestart"" ; While (@(Get-Process wusa -ErrorAction SilentlyContinue).Count -ne 0) { Start-Sleep 3 } }

  # Reboot
  Restart-Computer
}
Else 
{
  # PHASE 2

  # Open WinRM for remote-exec
  Start-Process -FilePath "winrm" -ArgumentList "quickconfig -q"
  Start-Process -FilePath "winrm" -ArgumentList "set winrm/config/service @{AllowUnencrypted=`"true`"}" -Wait
  Start-Process -FilePath "winrm" -ArgumentList "set winrm/config/service/auth @{Basic=`"true`"}" -Wait
  Start-Process -FilePath "winrm" -ArgumentList "set winrm/config @{MaxTimeoutms=`"1900000`"}"
}