Project MAT for Shift(第 2 部分)– 网络

欢迎阅读 MAT4Shift 博客系列的下一篇博文。第一篇文章的链接如下,供您参考。在本系列的第 2 部分中,我们将深入探讨在平台间移动 VM 时如何执行网络设置迁移,以及为什么要这么做。

 

当我们开始与 NetApp 团队合作集成 MAT 和 Shift 时,正是恰逢其时。Mark 凭借其在处理实际迁移方面的经验开发了一系列新功能,以后将会囊括在其脚本的未来版本中。MAT 的优点在于它采用一次性进程并添加了“命令和控件”,因此管理员可以通过自动方式多次管理进程的执行。他知道问题在于在不永久影响第一个虚拟磁盘平台的情况下,从一个虚拟磁盘平台迁移到另一个虚拟磁盘平台只是成功的一半。他记下进程以便了解每种 VM 配置并将其写入 SQL,当他采取下一步并构建 VM 时,他使用该信息来确保内存、处理器数量以及其他值保持不变。由于复杂性(读取:十分耗时),网络不在本次范围之内。

当我们与 Glenn 会晤时,我曾考虑使用 PowerShell 脚本来收集每一个网络细节,采用 XML 格式将其存储在 VM 内,并设置一个已在下次启动时将重新配置每个 NIC 的计划的任务。唯一的问题是它需要一些更新的 cmdlet,这意味着我们需要在每个 VM 内引入一个必备组件,这令我十分担忧。当 Glenn 拥有一个奇思妙想时,我恰好打算尝试在 WMI 调用中重新构建所有内容。NetSH 拥有一个“转储”参数,其可使用每一个 NIC 的配置生成一个文本文件,还有另一个参数,可用于从已存储的文件导入配置。这非常完美,因为这在理论上为我们提供了向后与 1999 (Windows 2000 Server) 的兼容性。

我想要狂欢,就像在 1990 年一样… 不必在意。

这留给我们三个问题,我们很高兴地宣布已经解决了这些问题。

  • NetSH 需要网络适配器名称以保持不变,以使它了解如何导入设置。我们指出可以使用 MAC 地址作为权威参考。由于两个平台均可通过主机设置该值,因此没有问题。与 VLAN 一起添加到脚本。一举两得!
  • NetSH 不包含 DNS 客户端信息 – 在与 Glenn 通话一小时的过程中我们讨论了 WMI 的 MSDN 参考,我们将其构建到脚本中。由于脚本已经完成了某些难度较大的任务,我们必须至少为每个 NIC 拥有两个唯一值,这样可使其轻松确定每个客户端配置应对应的 NIC。
  • Ghost 适配器 - 在技术上,我们正在创建一种新设备,即使 MAC 相同。如果您体验过台式计算机的构建和重构,您可能会遇到“ghost 网络适配器”的情况,正如我所说的 NIC 不再物理存在但操作系统记得它曾经存在以及是如何配置它的。当您尝试为新的适配器提供同一 IP 时这会生成提示,因为操作系统正在尝试如果丢失的适配器在某一天找到回到机器中的路径时避免发生冲突。我们希望避免出现这种情况。我们认真考虑了大量 DLL 反射,以尝试在迁移前卸载设备。最终,Glenn 再次凭借天才的想法编写脚本,将 NIC 切换为 DHCP 并在发生配置转储后将其重命名,因此迁移后我们将看不到它。

我相信在他首次构建出该部分脚本的框架时他正与家人一起度假。真是好样的!

 

既然您已经了解了我们的思路,让我们看一下该脚本的三个不同部分,并深入探讨其工作原理。我所描述的流程为发现、存储和设置。

 

 

发现

查找 M4PS (MAT for Project Shift) 技术预览行,您将发现会连续发生以下两个操作 -

将 NIC 配置转储为文本并避免文件开头出现陌生字符,以防止清洁导入:

001 netsh dump | ?{$_ -notmatch "^\s|#"} | Out-File -FilePath "$env:TEMP\nicConfig.txt" -Encoding ascii

进行 WMI 调用以记录 DNS 信息:

001 002 gwmi win32_NetworkAdapter | where { $_.NetConnectionID } | Select-Object -Property Name, MacAddress,NetConnectionID | Export-Clixml -Path "$env:TEMP\nicConfig.xml" gwmi win32_networkadapterconfiguration | ? {$_.IPEnabled -eq "True"} | select MACAddress, DNSDomain, DNSDomainSuffixSearchOrder, DNSServerSearchOrder | Export-Clixml -Path "$ENV:TEMP\nicDNS.xml"

我知道还有大量的其他代码。我们稍微从内而外执行一些工作。上面这些命令需要从 VM 内部执行。为此,我们调用一个特殊的 PowerCLI cmdlet invoke-vmscript,只要您通过管理凭据,就允许 VMware 工具通过主机在 VM 内部执行代码。

微笑  保持一会儿。

 

实际上,在“发现”阶段,我们还嵌入了“存储”和“设置”阶段所需的脚本。所有这三个阶段的代码均已设计出并保存为 base64 方式的字符块,以便尽可能清洁地传递所有内容。这是您看到 $code 的原因,其中要运行的所有内容均已保存,包括一系列设置为 $restorecode 的命令,即使只有一个步骤也会进一步探究并在 VM 内动态编码“设置”阶段。网络配置迁移就像一个洋葱,需要层层进行!

 

 

存储

当我们开始脱离螺旋逆向操作时,数据已被存储为完成的上述命令。您会看到两个 XML 文件和 txt 文件,所有内容均写入 TEMP 目录。

此操作完成后(记住,此时我们仍在 VMware VM 内),我们还需要存储将在下次重新启动时启动以读取数据文件并在 Hyper-V VM 上执行操作的值。这是“设置”阶段的准备工作。在正常环境下,您可能使用诸如 PowerShell 远程处理来与 VM 内的操作系统进行通信,但请注意,我们实际上正在重构网络配置,因此尝试从外部执行是不可能的。在网络恢复在线状态之前,不可能进行通信。

为此,我们利用量子计算的强大功能来通过将消息存储在替代维度中进行通信。Winking smile不,实际上我们使用注册表(这是由于兼容性!)。如果您熟悉“Run Once”,下面的内容应该也十分熟悉。

001 002 003 004 005 006 007 008 009 010 $WinAccount,$winPassword,$WinDomain = Get-Content "$env:TEMP\nicCreds.txt" Remove-Item "$env:TEMP\nicCreds.txt" $RunOnceKey = "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce" $WinLogonKey ="HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" set-itemproperty $RunOnceKey "ConfigureServer" "$env:TEMP\nicRestore.cmd" set-itemproperty $WinLogonKey "DefaultUserName" $WinAccount set-itemproperty $WinLogonKey "DefaultPassword" $WinPassword set-itemproperty $WinLogonKey "AutoAdminLogon" "1" set-itemproperty $WinLogonKey "AutoLogonCount" "2" set-itemproperty $WinLogonKey "DefaultDomainName" $WinDomain

完成这一步之前还有最后一件事!我提到 Glenn 找到一种避免新旧 NIC 发生 IP 冲突的方式。请查看这一系列的命令,其中他使用静态 IP 查找 NIC,将它们设置为 DHCP,然后将所有 NIC 重命名为其原始名称加“_VMware”。

001 002 003 004 005 gwmi win32_NetworkAdapterConfiguration | ? {! $_.DHCPEnabled -and $_.IPAddress } | %{ $_.EnableDHCP() } $NetworkConnections = (New-Object -com shell.application).Namespace(0x31) Foreach ($NIC in (gwmi win32_NetworkAdapter | where { $_.NetConnectionID }|Select-Object -ExpandProperty NetConnectionID)) { $NetworkConnections.Items() |Where-Object {$_.Name -eq $NIC} |ForEach-Object { $_.Name="$($_.Name)_VMware"} }

请记住,到目前为止,VMware VM 内仍会发生任何事情!脚本就像一个必须能够在没有外部通信的情况下自行操作直到其使命完成的特殊操作团队一样。

 

 

设置

随着“设置”阶段的启动,我们即将完成任务。环境仍在 VM 内,并在不能与外部进行通信的情况下操作,但我们现在位于 Hyper-V 上。当 VM 启动时,RunOnce 注册表项将会导致操作系统自动登录并还原配置。这是再次设置 RunOnce 项时动态混淆的命令系列,所有内容均保持清洁。

当我们阅读下面的行时 –

需要一段时间等待情况稳定(所有 VM 在不同的平台上启动后)。

001 Start-Sleep -Seconds 30;$na = (New-Object -com shell.application).Namespace(0x31)

接下来,设置 VM 名称并导入 DNS 和 NetSH 数据。

001 002 003 004 Foreach ($n in (Import-Clixml -Path "$env:TEMP\nicConfig.xml")){$na.Items()|?{$_.Name -eq $(gwmi win32_networkadapter -F "MACAddress='$($N.MACAddress)'").NetConnectionID}|%{ $_.Name="$($_.Name)_old" }} Foreach ($n in (Import-Clixml -Path "$env:TEMP\nicConfig.xml")){$na.Items()|?{$_.Name -eq $(gwmi win32_networkadapter -F "MACAddress='$($N.MACAddress)'").NetConnectionID}|%{ $_.Name=$n.NetConnectionID }} Foreach ($n in (Import-Clixml -Path "$env:TEMP\nicDNS.xml")){([wmiclass]'Win32_NetworkAdapterConfiguration').SetDNSSuffixSearchOrder($n.DNSDomainSuffixSearchOrder);gwmi win32_networkadapterconfiguration -F "MACAddress='$($N.MACAddress)'" |%{$_.SetDNSServerSearchOrder($n.DNSServerSearchOrder);$_.SetDNSDomain($n.DNSDomain)}} netsh -f "$env:TEMP\nicConfig.txt"

该流程完成后会关机,以便 VM 不保持登录状态。

001 C:\Windows\system32\shutdown.exe -s -t 5 -f

 

总结

完成之前,我想感谢 Mark 和 Glenn 让我参与这项工作,感谢 Mark 在博文 1 中的溢美之词,与他们一起合作十分愉快。照片中的三个人坐在会议桌旁讨论问题,并在发现问题的所在时立刻寻找脚本解决方案的 PowerShell 狂热爱好者,正是我们。在我们在尝试中断 VM 的过程中启动更多且更大的 VM 时看到我们的劳动成果会更好。它不但永远不会闪烁,而且我们始终需要优化脚本进程几秒钟,实际上感觉像很长一段时间。当它呈现出来时,进程最慢的部分之一是删除 VMware 工具。我们将该过程称为“鱼尾锤”在工作。

有关更多信息,请查看 Mark 的博客或从 TechNet 库中下载 MAT4Shift:
https://gallery.technet.microsoft.com/MAT-powered-by-Shift-343f242d