PowerShellを使ってWindowsのホスト名を簡単に変更する方法

standalone Windows PCのhostname変更は、PartOfDomain=falseを必須にし、old name・machine SID・workgroupを未存在backupへ保存してから行います。new nameの形式とcollisionを検査し、preview、actual Rename-Computer、別承認restart、fresh verificationを分離します。rollbackも独立したrename/restart/reverifyです。

目次

standalone baselineをCreateNew backupへ保存する

domain memberなら停止します。local built-in administrator SIDからmachine SIDを固定し、old nameとworkgroupをGUID付き未存在JSONへCreateNewで保存します。new nameは15文字以内の許可文字列で、DNS応答またはping応答があればcollisionとして停止します。

$ErrorActionPreference='Stop'
$cs=Get-CimInstance Win32_ComputerSystem -ErrorAction Stop
if([bool]$cs.PartOfDomain){throw 'domain member is out of scope; use the domain change-control workflow'}
$admin=@(Get-LocalUser -ErrorAction Stop|Where-Object {$_.SID.Value -match '-500$'})
if($admin.Count -ne 1){throw 'cannot derive one local machine SID'}
$oldSid=($admin[0].SID.Value -replace '-500$','')
$newName='LABPC02'
if($newName.Length -gt 15 -or $newName -notmatch '^[A-Za-z0-9](?:[A-Za-z0-9-]{0,13}[A-Za-z0-9])?$' -or $newName -ieq $cs.Name){throw 'new standalone hostname is invalid or unchanged'}
$dns=@(Resolve-DnsName -Name $newName -DnsOnly -ErrorAction SilentlyContinue)
$ping=Test-Connection -ComputerName $newName -Count 1 -Quiet -ErrorAction SilentlyContinue
if($dns.Count -gt 0 -or $ping){throw 'new hostname collision evidence found'}
$operationId=[guid]::NewGuid().ToString('N')
$backupDir='C:\ProgramData\ITtrip\StandaloneRename';$null=New-Item -ItemType Directory -Path $backupDir -Force -ErrorAction Stop
$statePath=Join-Path $backupDir "$operationId.json"
if(Test-Path -LiteralPath $statePath){throw 'backup path already exists'}
$state=[ordered]@{OperationId=$operationId;Stage='Planned';OldName=[string]$cs.Name;NewName=$newName;MachineSid=$oldSid;OldWorkgroup=[string]$cs.Domain;PartOfDomain=$false;CreatedUtc=(Get-Date).ToUniversalTime().ToString('o')}
$bytes=[Text.Encoding]::UTF8.GetBytes(($state|ConvertTo-Json -Depth 5));$stream=[IO.File]::Open($statePath,[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::None);try{$stream.Write($bytes,0,$bytes.Length);$stream.Flush($true)}finally{$stream.Dispose()}
[pscustomobject]@{StatePath=$statePath;State=$state;DnsCollision=$false;PingCollision=$false}

preview後にactual Rename-Computerを実行する

tokenにoperation、old/new name、SID、workgroupを含めます。承認後もPartOfDomain、old name、workgroupがbaseline一致することを確認し、Rename-Computerをterminating modeで実行します。

Rename-Computer -NewName $state.NewName -WhatIf
$token="RENAME-STANDALONE OP=$($state.OperationId) OLD=$($state.OldName) NEW=$($state.NewName) SID=$($state.MachineSid) WORKGROUP=$($state.OldWorkgroup)"
if((Read-Host "previewを確認し、renameを実行する場合は $token") -ne $token){throw 'rename not approved'}
$now=Get-CimInstance Win32_ComputerSystem -ErrorAction Stop
if([bool]$now.PartOfDomain -or [string]$now.Name -cne $state.OldName -or [string]$now.Domain -cne $state.OldWorkgroup){throw 'standalone baseline changed before rename'}
Rename-Computer -NewName $state.NewName -Force -ErrorAction Stop
$state.Stage='RenameIssued';$state.RenameIssuedUtc=(Get-Date).ToUniversalTime().ToString('o');$state|ConvertTo-Json -Depth 5|Set-Content -LiteralPath $statePath -Encoding UTF8 -ErrorAction Stop

restartを別承認にする

rename発行だけで自動restartしません。console/recovery accessとmaintenance windowを確認した別tokenでstateを進め、Restart-Computerを実行します。

$restartToken="RESTART-AFTER-RENAME OP=$($state.OperationId) NEW=$($state.NewName)"
if((Read-Host "console/recovery accessを確認し、再起動する場合は $restartToken") -ne $restartToken){throw 'rename issued; stateを保持して承認済みwindowで再起動してください'}
$state.Stage='RenameRestartApproved';$state|ConvertTo-Json -Depth 5|Set-Content -LiteralPath $statePath -Encoding UTF8 -ErrorAction Stop
Restart-Computer -Force -ErrorAction Stop

fresh sessionでhostname・CsName・SIDを確認する

再起動後にstateを読み、Win32 name、Get-ComputerInfo CsName、環境hostnameがnew nameへ一致することを確認します。machine SID、workgroup、PartOfDomain=falseも同時に読み戻します。

$statePath='PASTE_STATE_PATH'
$state=Get-Content -LiteralPath $statePath -Raw -ErrorAction Stop|ConvertFrom-Json
if($state.Stage -ne 'RenameRestartApproved'){throw 'fresh rename verification requires restart-approved state'}
$cs=Get-CimInstance Win32_ComputerSystem -ErrorAction Stop
$csName=(Get-ComputerInfo -Property CsName -ErrorAction Stop).CsName
$admin=@(Get-LocalUser -ErrorAction Stop|Where-Object {$_.SID.Value -match '-500$'})
$sid=($admin[0].SID.Value -replace '-500$','')
if([bool]$cs.PartOfDomain -or [string]$cs.Name -cne [string]$state.NewName -or [string]$csName -cne [string]$state.NewName -or [string]$env:COMPUTERNAME -cne [string]$state.NewName -or $sid -cne [string]$state.MachineSid -or [string]$cs.Domain -cne [string]$state.OldWorkgroup){throw 'hostname/CsName/SID/workgroup verification failed'}
$state.Stage='RenameVerified';$state|ConvertTo-Json -Depth 5|Set-Content -LiteralPath $statePath -Encoding UTF8 -ErrorAction Stop
[pscustomobject]@{Stage=$state.Stage;Hostname=$env:COMPUTERNAME;CsName=$csName;MachineSid=$sid;Workgroup=$cs.Domain;PartOfDomain=$cs.PartOfDomain}

standalone rollbackを別に実行する

verified stateとcurrent new name/SID/workgroupを再bindし、別tokenで保存済みold nameへRename-Computerします。通常変更と同様、rollback restartもさらに別承認です。

# standalone rollback: fresh sessionでbaseline stateを読み、別tokenでold nameへ戻す。
$statePath='PASTE_STATE_PATH'
$state=Get-Content -LiteralPath $statePath -Raw -ErrorAction Stop|ConvertFrom-Json
if($state.Stage -ne 'RenameVerified'){throw 'rollback requires verified renamed state'}
$cs=Get-CimInstance Win32_ComputerSystem -ErrorAction Stop
$admin=@(Get-LocalUser -ErrorAction Stop|Where-Object {$_.SID.Value -match '-500$'});$sid=($admin[0].SID.Value -replace '-500$','')
if([bool]$cs.PartOfDomain -or [string]$cs.Name -cne [string]$state.NewName -or [string]$cs.Domain -cne [string]$state.OldWorkgroup -or $sid -cne [string]$state.MachineSid){throw 'rollback precondition mismatch'}
$token="ROLLBACK-HOSTNAME OP=$($state.OperationId) CURRENT=$($state.NewName) RESTORE=$($state.OldName)"
if((Read-Host "old hostnameへ戻す場合は $token") -ne $token){throw 'rollback not approved'}
Rename-Computer -NewName $state.OldName -Force -ErrorAction Stop
$state.Stage='RollbackIssued';$state|ConvertTo-Json -Depth 5|Set-Content -LiteralPath $statePath -Encoding UTF8 -ErrorAction Stop
$statePath='PASTE_STATE_PATH';$state=Get-Content -LiteralPath $statePath -Raw -ErrorAction Stop|ConvertFrom-Json
if($state.Stage -ne 'RollbackIssued'){throw 'rollback restart requires issued state'}
$token="RESTART-AFTER-HOSTNAME-ROLLBACK OP=$($state.OperationId) OLD=$($state.OldName)"
if((Read-Host "rollback再起動を承認する場合は $token") -ne $token){throw 'rollback issued; stateを保持してください'}
$state.Stage='RollbackRestartApproved';$state|ConvertTo-Json -Depth 5|Set-Content -LiteralPath $statePath -Encoding UTF8 -ErrorAction Stop
Restart-Computer -Force -ErrorAction Stop

rollback後をfresh reverifyする

old name、CsName、environment hostname、machine SID、workgroup、PartOfDomain=falseの全fieldをfresh sessionでexact readbackします。一項目でも違えば完了としません。

$statePath='PASTE_STATE_PATH';$state=Get-Content -LiteralPath $statePath -Raw -ErrorAction Stop|ConvertFrom-Json
if($state.Stage -ne 'RollbackRestartApproved'){throw 'fresh rollback verification requires restart-approved state'}
$cs=Get-CimInstance Win32_ComputerSystem -ErrorAction Stop;$csName=(Get-ComputerInfo -Property CsName -ErrorAction Stop).CsName
$admin=@(Get-LocalUser -ErrorAction Stop|Where-Object {$_.SID.Value -match '-500$'});$sid=($admin[0].SID.Value -replace '-500$','')
if([bool]$cs.PartOfDomain -or [string]$cs.Name -cne [string]$state.OldName -or [string]$csName -cne [string]$state.OldName -or [string]$env:COMPUTERNAME -cne [string]$state.OldName -or $sid -cne [string]$state.MachineSid -or [string]$cs.Domain -cne [string]$state.OldWorkgroup){throw 'rollback exact readback failed'}
[pscustomobject]@{Stage='RollbackVerified';Hostname=$env:COMPUTERNAME;CsName=$csName;MachineSid=$sid;Workgroup=$cs.Domain;PartOfDomain=$cs.PartOfDomain}

公式情報・参考資料

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次