【问题标题】:Programmatically set EBS Volumes Windows Drive Letters using Terraform, Chef or Powershell使用 Terraform、Chef 或 Powershell 以编程方式设置 EBS 卷 Windows 驱动器号
【发布时间】:2018-04-05 21:05:49
【问题描述】:

我正在使用 terraform 和 chef 创建多个 aws ebs 卷并将它们附加到一个 EC2 实例。

问题是我希望能够为每个 ebs 卷指定一个特定的 Windows 驱动器号。问题是当 EC2 实例被实例化时,窗口只是给它连续的驱动器号(D、E、F 等)

某些驱动器的大小相同,因此我不一定能根据驱动器大小重命名。有谁知道用 terraform 或厨师做到这一点的方法。我的 google foo 没有找到任何东西。

当然,这必须为其他人提出?

我确实看到了使用 EC2Config Windows GUI 来设置它们的参考,但重点是自动化该过程,因为最终我希望厨师安装 SQL 服务器,并且某些数据预计会出现在某些驱动器号上。

这似乎可行 - 尽管我想知道是否没有更简单的方法。

function Convert-SCSITargetIdToDeviceName
{
param([int]$SCSITargetId)
If ($SCSITargetId -eq 0) {
    return "/dev/sda1"
}
$deviceName = "xvd"
If ($SCSITargetId -gt 25) {
    $deviceName += [char](0x60 + [int]($SCSITargetId / 26))
}
$deviceName += [char](0x61 + $SCSITargetId % 26)
return $deviceName
}

Get-WmiObject -Class Win32_DiskDrive | ForEach-Object {
$DiskDrive = $_
$Volumes = Get-WmiObject -Query "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='$($DiskDrive.DeviceID)'} WHERE AssocClass=Win32_DiskDriveToDiskPartition" | ForEach-Object {
    $DiskPartition = $_
    Get-WmiObject -Query "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='$($DiskPartition.DeviceID)'} WHERE AssocClass=Win32_LogicalDiskToPartition"
}
If ($DiskDrive.PNPDeviceID -like "*PROD_PVDISK*") {
    $BlockDeviceName = Convert-SCSITargetIdToDeviceName($DiskDrive.SCSITargetId)
    If ($BlockDeviceName -eq "xvdf") { $drive = gwmi win32_volume -Filter "DriveLetter = '$($Volumes.DeviceID)'"; Set-WmiInstance -input $drive -Arguments @{DriveLetter="D:"; Label="SQL Data"} };
    If ($BlockDeviceName -eq "xvdg") { $drive = gwmi win32_volume -Filter "DriveLetter = '$($Volumes.DeviceID)'"; Set-WmiInstance -input $drive -Arguments @{DriveLetter="L:"; Label="SQL Logs"} };
    If ($BlockDeviceName -eq "xvdh") { $drive = gwmi win32_volume -Filter "DriveLetter = '$($Volumes.DeviceID)'"; Set-WmiInstance -input $drive -Arguments @{DriveLetter="R:"; Label="Report Data"} };
    If ($BlockDeviceName -eq "xvdi") { $drive = gwmi win32_volume -Filter "DriveLetter = '$($Volumes.DeviceID)'"; Set-WmiInstance -input $drive -Arguments @{DriveLetter="T:"; Label="Temp DB"} };
    If ($BlockDeviceName -eq "xvdj") { $drive = gwmi win32_volume -Filter "DriveLetter = '$($Volumes.DeviceID)'"; Set-WmiInstance -input $drive -Arguments @{DriveLetter="M:"; Label="MSDTC"} };
    If ($BlockDeviceName -eq "xvdk") { $drive = gwmi win32_volume -Filter "DriveLetter = '$($Volumes.DeviceID)'"; Set-WmiInstance -input $drive -Arguments @{DriveLetter="B:"; Label="Backups"} };
} ElseIf ($DiskDrive.PNPDeviceID -like "*PROD_AMAZON_EC2_NVME*") {
    $BlockDeviceName = Get-EC2InstanceMetadata "meta-data/block-device-mapping/ephemeral$($DiskDrive.SCSIPort - 2)"
    If ($BlockDeviceName -eq "xvdf") { $drive = gwmi win32_volume -Filter "DriveLetter = '$($Volumes.DeviceID)'"; Set-WmiInstance -input $drive -Arguments @{DriveLetter="D:"; Label="SQL Data"} };
    If ($BlockDeviceName -eq "xvdg") { $drive = gwmi win32_volume -Filter "DriveLetter = '$($Volumes.DeviceID)'"; Set-WmiInstance -input $drive -Arguments @{DriveLetter="L:"; Label="SQL Logs"} };
    If ($BlockDeviceName -eq "xvdh") { $drive = gwmi win32_volume -Filter "DriveLetter = '$($Volumes.DeviceID)'"; Set-WmiInstance -input $drive -Arguments @{DriveLetter="R:"; Label="Report Data"} };
    If ($BlockDeviceName -eq "xvdi") { $drive = gwmi win32_volume -Filter "DriveLetter = '$($Volumes.DeviceID)'"; Set-WmiInstance -input $drive -Arguments @{DriveLetter="T:"; Label="Temp DB"} };
    If ($BlockDeviceName -eq "xvdj") { $drive = gwmi win32_volume -Filter "DriveLetter = '$($Volumes.DeviceID)'"; Set-WmiInstance -input $drive -Arguments @{DriveLetter="M:"; Label="MSDTC"} };
    If ($BlockDeviceName -eq "xvdk") { $drive = gwmi win32_volume -Filter "DriveLetter = '$($Volumes.DeviceID)'"; Set-WmiInstance -input $drive -Arguments @{DriveLetter="B:"; Label="Backups"} };
} Else {
    write-host "Couldn't find disks";
}
}

【问题讨论】:

  • 您知道如何手动实现吗?如果您可以分享您找到的手动过程(“使用 EC2Config Windows GUI”),有人可能会帮助您将其翻译成 Terraform 或 Chef,但目前这个问题包括 AWS 问题 Terraform/Chef 问题,因此不太可能有人知道这两个部分的答案。
  • docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/… 有说明,但它指出结果可能并不总是符合预期。
  • 所以主题行有 terraform,但我也提到了 chef 和 powershell,因为我真的不确定最好的方法。是否使用 terraform(我现在想可能不是) - 它可能需要 powershell 和/或 chef。
  • 我已经编辑了原始问题以包含一个(非工作)powershell 示例。它获取驱动器,但不会设置驱动器号或标签。我相信那是因为驱动器和卷是不同的。我只是不确定如何将一个映射到另一个。
  • 现在是一个工作示例,尽管我仍然想知道这是否是最好的方法。看起来相当复杂。

标签: windows amazon-ec2 chef-infra terraform drive-letter


【解决方案1】:

如果您考虑此链接中的表格: https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2-windows-volumes.html

您可以在 EBS 上看到,第一行是:

Bus Number 0, Target ID 0, LUN 0 /dev/sda1
Bus Number 0, Target ID 1, LUN 0 xvdb

磁盘 0 (/dev/sda1) 始终由 EC2 为您设置为 C:

所以你知道当你运行“New-Partition -DiskNumber 1 -UseMaximumSize -IsActive -AssignDriveLetter”你会得到 D: 给它。

因此,如果您使用 Builders 中的以下卷使用 Packer 配置 AMI 映像(本示例中只有两个,但您可以做很多):

        "launch_block_device_mappings": [{
        "device_name": "/dev/sda1",
        "volume_size": 30,
        "volume_type": "gp2",
        "delete_on_termination": true
    },
    {
        "device_name": "xvdb",
        "volume_size": 30,
        "volume_type": "gp2",
        "delete_on_termination": true    
    }]

..您可以计划一下,知道 xvd[b] 实际上是要映射的内容后面的两个字母。

然后使用 Terraform 启动这个多卷 AMI 的 EC2 实例,并将其放在 aws_instance 资源的 user_data 部分中:

    user_data = <<EOF
    <powershell>
    Initialize-Disk -Number 1 -PartitionStyle "MBR"
    New-Partition -DiskNumber 1 -UseMaximumSize -IsActive -AssignDriveLetter
    Format-Volume -DriveLetter d -Confirm:$FALSE
    Set-Partition -DriveLetter D -NewDriveLetter S
    </powershell>
    EOF

Set-Partition -DriveLetter D -NewDriveLetter S 行用于将已知的顺序驱动器重命名为您习惯的任何字母。就我而言,他们想要 D: as S: - 只需重复这一行即可将 E: 重命名为 X: 或任何您需要的名称。

希望这会有所帮助。

更新: 还有另一种方法(Server 2016 up),当我发现 Sysprep 核对所有被烘焙到 AMI 映像中的映射时,我发现了这种方法。

您必须在 C:\ProgramData\Amazon\EC2-Windows\Launch\Config 中提供 DriveLetterMappingConfig.json 文件才能进行映射。文件格式为:

{
  "driveLetterMapping": [
    {
      "volumeName": "sample volume",
      "driveLetter": "H"
    }
  ]
}

...只是,默认情况下,我的驱动器没有卷名;他们是空白的。所以回到 1980 年的旧“标签”命令。将 D: 驱动器标记为 volume2。所以文件看起来像:

{
  "driveLetterMapping": [
    {
      "volumeName": "volume2",
      "driveLetter": "S"
    }
  ]
}

运行 C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeDisks.ps1 测试此方法有效(D: 变为 S:)

所以现在,回到 Packer,我还需要在 C:\ProgramData\Amazon\EC2-Windows\Launch\Config 中使用此 DriveLetterMappingConfig.json 文件预置映像,以确保我在 AMI 上执行的所有驱动器工作S: 在实例上以 S: 的形式返回。 (我将文件和我们要安装在盒子上的所有其他垃圾一起放在 S3 存储桶中。)

我将磁盘内容放入 .ps1 并从配置器调用它:

{ “类型”:“powershell”, “脚本”:“./setup_two_drive_names_c_and_s.ps1”
},

上面的.ps1在哪里:

# Do volume config of the two drives
write-host "Setting up drives..."
Initialize-Disk -Number 1 -PartitionStyle "MBR"
New-Partition -DiskNumber 1 -UseMaximumSize -IsActive -AssignDriveLetter
Format-Volume -DriveLetter d -Confirm:$FALSE
label c: "volume1"
label d: "volume2"
Set-Partition -DriveLetter D -NewDriveLetter S

# Now insert DriveLetterMappingConfig.json file into C:\ProgramData\Amazon\EC2-Windows\Launch\Config to ensure instance starts with correct drive mappings
Write-Host "S3 Download: DriveLetterMappingConfig.json"
Read-S3Object -BucketName ********* -Key DriveLetterMappingConfig.json -File 'c:\temp\DriveLetterMappingConfig.json'
Write-Host "Copying DriveLetterMappingConfig.json to C:\ProgramData\Amazon\EC2-Windows\Launch\Config..."
Copy-Item "c:\temp\DriveLetterMappingConfig.json" -Destination "C:\ProgramData\Amazon\EC2-Windows\Launch\Config\DriveLetterMappingConfig.json" -Force
Write-Host "Set Initialze Disks to run on every boot..."
C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeDisks.ps1 -Schedule

是的,没有理由给 c 贴上标签:但我很开心......

带有“-Schedule”参数的最后一行表示每次启动都会发生这种情况。

【讨论】:

  • 使用 LaunchConfig 您无法分辨哪个驱动器是哪个字母。而且您不能设置多个驱动器。
【解决方案2】:

我需要一个带有 4 个大小相同的驱动器的 Windows Server 2016,但我不在乎哪个块设备变成了哪个驱动器号。以下是我采取的步骤(使用 Packer)来获得这个:

首先,在模板的构建器区域中,根据需要添加任意数量的块设备(在我的例子中 - launch_block_device_mapping 下的 4 个条目)。然后,在配置列表中运行以下命令:

  1. 使用任何 Windows 2016 Amazon 实例上可用的脚本初始化磁盘;这将使每个磁盘联机,向其中添加一个分区,将分区扩展到最大可能大小,对其进行格式化并为其分配一个 Windows 驱动器号。

    {
        "type": "powershell",        
        "inline": [
            "C:\\ProgramData\\Amazon\\EC2-Windows\\Launch\\Scripts\\InitializeDisks.ps1"        
        ]
    }
    

    注意事项:

    如果添加 '-Schedule' 参数,此时不会初始化磁盘,因为此选项只会将脚本添加到计划在下次启动时运行一次的任务实例(之后它被停用)。

    驱动器号按字母顺序分配,从 D 开始(因为 C 是为根驱动器保留的)。

    卷附加到实例的顺序与块设备名称无关,不会有一对一的对应关系(xvdb 不会成为 D: \驱动,xvdc不会变成E:\等)

  2. 为已初始化磁盘的每个驱动器号分配您想要的标签。

    {
        "type": "powershell",
        "inline": [
            "write-output \"Label partitions after initializing disks\"",
            "label C: \"OS\"",
            "label D: \"Programs\"",
            "label E: \"Data\"",
            "label F: \"Backup\"",
            ...
        ]
    }
    

    注意:另一个可能的选项是在运行磁盘初始化脚本之前直接在 DriveLetterMapping.json 文件(适用于任何 Windows 2016 Amazon AMI)中添加标签(我无法完成这项工作)。

  3. 在添加您可能需要的任何其他配置器后(例如,激活 Windows 组件、安装应用程序或检查 Windows 更新),作为配置器列表中的最后一个条目,请确保添加了实例初始化和 SysPrep 脚本

    {
        "type": "powershell",
        "inline": [
            "C:/ProgramData/Amazon/EC2-Windows/Launch/Scripts/InitializeInstance.ps1 -Schedule",
            "C:/ProgramData/Amazon/EC2-Windows/Launch/Scripts/SysprepInstance.ps1 -NoShutdown"
        ]
    }
    

    注意:最后一步特定于 EC2Launch,并适用于 Windows 2016 及以后的版本。对于旧版本(如 Windows 2012),语法有所不同,它基于 EC2Config。

从该配置中获得 AMI 后,从该配置启动的任何实例的驱动器号都应符合要求。

如果驱动器号及其标签未按预期映射,您还可以尝试使用实例的用户数据强制重新标记驱动器。就在您启动它之前,可以轻松地以明文形式传递一个 powershell 脚本;下面只是一个可能的例子:

<powershell>
write-output "Force re-map of drive letters based on labels, after disk initialization"
# remove drive letters, but keep labels
Get-Volume -Drive D | Get-Partition | Remove-PartitionAccessPath -accesspath "D`:\"
Get-Volume -Drive E | Get-Partition | Remove-PartitionAccessPath -accesspath "E`:\"
Get-Volume -Drive F | Get-Partition | Remove-PartitionAccessPath -accesspath "F`:\"
# add drive letters based on labels
get-volume | where filesystemlabel -match "Programs" | Get-Partition | Set-Partition -NewDriveLetter D
get-volume | where filesystemlabel -match "Data" | Get-Partition | Set-Partition -NewDriveLetter E
get-volume | where filesystemlabel -match "Backup" | Get-Partition | Set-Partition -NewDriveLetter F
</powershell>

【讨论】:

    【解决方案3】:

    稍微复杂一点的解决方案。设置为此工作

    1. 用“DriveLetter”标记每个卷,并重视您要为其分配的内容。没有“:”
    2. 在 IAM 中授予 EC2 实例“ec2:DescribeTags”和“ec2:DescribeVolumes”权限
    3. 通过将以下脚本传递给用户数据或创建一个 ssm 文档并在启动后运行它来运行以下脚本
    Get-Disk|where-Object IsSystem -eq $False|Foreach-Object {
      if ( $_.PartitionStyle -Eq 'RAW') {
          Initialize-Disk -Number $_.Number –PartitionStyle MBR
          Set-Disk -Number $_.Number -IsOffline $False
          $VolumeId=$_.SerialNumber -replace "_[^ ]*$" -replace "vol", "vol-"
          $InstanceId = (Invoke-WebRequest -Uri "http://169.254.169.254/latest/meta-data/instance-id" -UseBasicParsing).Content
          $DriveLetter = Get-EC2Volume -Filter @{Name="volume-id";Values=$VolumeId},@{Name="attachment.instance-id";Values=$instanceId}  |ForEach-Object {$_.Tags}|where Key -eq "DriveLetter"|Select-Object -Property Value |foreach-Object {$_.Value}
          New-Partition -DiskNumber $_.Number -DriveLetter $DriveLetter –UseMaximumSize
          Format-Volume -DriveLetter $DriveLetter
        }
    

    【讨论】:

    • 在我看来这是真正的解决方案(只要它完全有效)。所有带有适当标签的驱动器都可以根据标签进行初始化和映射。不过,我还会添加一个带有标签的标签。荣誉。
    • 现在可能需要更新元数据 v2
    【解决方案4】:

    在 AWS windows server 2016 及更高版本上,您可以在创建 EC2 期间使用 userdata 中的以下行来初始化辅助卷

    C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeDisks.ps1
    

    更多信息在这里:

    亚马逊支持

    以上 AWS 脚本将磁盘初始化为仅 MBR 类型。 (我们不能使用 MBR 类型扩展大于 2tb 的卷)

    我的用例是初始化 GPT 类型的卷。

    所以我最终将以下脚本传递给 userdata 并将其发送到保存在 C 驱动器中的文件 (我已将 Manpreet Nehras 的建议提到框架用户脚本 https://stackoverflow.com/a/61530894/8227788

    $disks = Get-Disk|where-Object  partitionstyle -eq 'RAW' 
    foreach ($diski in $disks) {
          Initialize-Disk -Number $diski.Number
          Set-Disk -Number $diski.Number -IsOffline $False
          $VolumeId=$diski.SerialNumber -replace "_[^ ]*$" -replace "vol", "vol-"
          $InstanceId = (Invoke-WebRequest -Uri "http://169.254.169.254/latest/meta-data/instance-id" -UseBasicParsing).Content
          $DriveLetter = (Get-EC2Tag -Filter @{Name="resource-type";Value="volume"},@{Name="resource-id";Value=$VolumeId} | where-object Key -eq "driveletter").value
          New-Partition -DiskNumber $diski.Number -DriveLetter $DriveLetter –UseMaximumSize
          Format-Volume -DriveLetter $DriveLetter
        }
    Start-Sleep -s 120
    '@
    $initializescript | Out-File C:\initializescript.ps1
    Start-Sleep -s 30  
    
    Then, I called the above script through AWS SSM by creating and associating it with instance . Below is the code
    
    
    resource "aws_ssm_association" "initialize" {
      count = length(var.ec2_name)
      name        = var.ssm_document_name
      targets {
        key    = "InstanceIds"
        values = [element(aws_instance.ec2server[*].id, count.index)]
      }
        }
    
    
    resource "aws_ssm_document" "InitializeDrives" {
      name          = "initializedriveswindows"
      document_type = "Command"
    
      content = <<DOC
    {
      "schemaVersion": "2.2",
      "description": "Run command to initialize drives",
      "parameters": {
        "Message": {
          "type": "String",
          "description": "Run command to initialize drives",
          "default": "Run command to initialize drives"
        }
      },
      "mainSteps": [
        {
          "action": "aws:runPowerShellScript",
          "name": "powershell",
          "inputs": {
            "runCommand": [
              "C:\\initializescript.ps1",
              "Restart-Computer -Force"
            ]
          }
        }
      ]
    }
    DOC
    } ```
    
    I have included sleep time to avoid race issues.
    

    【讨论】:

      【解决方案5】:

      首先,我们强制执行一个安装约定,该约定简单地说明 对于非根卷 为您的设备使用 xvdDRIVE 约定,其中 DRIVE 与您要挂载到的驱动器号相同

         xvdd - D:
         xvde - E:
         xvdm = M:
      

      支持驱动器分配..包括“跳过级别”安装

      format the volumes .. with the drive letter or some other convention you like
        we run diskpart with an input file.. but basically
            format fs=ntfs label=D quick
      

      然后我们更新 DriveletterConfig.xml 或 DriveLetterConfig.json(取决于 ec2config 或 ec2launch)

          for xml looks like:
              <Mapping> <VolumeName>D</VolumeName> <DriveLetter>D:</DriveLetter></Mapping>
              <Mapping> <VolumeName>E</VolumeName> <DriveLetter>E:</DriveLetter> </Mapping>
              <Mapping> <VolumeName>M</VolumeName> <DriveLetter>M:</DriveLetter> </Mapping>  
          
      

      【讨论】:

        猜你喜欢
        • 2017-07-25
        • 1970-01-01
        • 2021-09-13
        • 1970-01-01
        • 2013-09-27
        • 2010-10-04
        • 1970-01-01
        • 2015-10-07
        • 2018-08-09
        相关资源
        最近更新 更多