1

I'm using some scripts based on http://blog.brianbeach.com/2014/07/setting-hostname-in-syspreped-ami.html to set the Host Name of a new windows instance created from an AMI to be the Name tag of the instance. Instead of HKLM:\System\Setup pointing to windeploy.exe, it runs a script which runs this:

$InstanceName = 'WebServerNew'

Try 
{
    Start-Transcript -Path D:\WebServerUtility\SysPrep\Windeploy.log -Append
    Write-Host "Discovering instance identity from meta-data web service"
    $InstanceId = (Invoke-RestMethod 'http://169.254.169.254/latest/meta-data/instance-id').ToString()
    $AvailabilityZone = (Invoke-RestMethod 'http://169.254.169.254/latest/meta-data/placement/availability-zone').ToString()
    $Region = $AvailabilityZone.Substring(0,$AvailabilityZone.Length-1)

    Write-Host "Getting Tags for the instance"
    $Tags = Get-EC2Tag -Filters @{Name='resource-id';Value=$InstanceId} -Region $Region
    $InstanceName = ($Tags | Where-Object {$_.Key -eq 'Name'}).Value
    Write-Host "`tFound Instance Name: $InstanceName"    
}
Catch
{
    Write-Host $_
    $InstanceName = 'WebServerError'
}

try
{ 
    If($InstanceName -ne $null) {
          Write-Host "Setting the machine name to $InstanceName"
          $AnswerFilePath = "C:\Windows\Panther\unattend.xml"
          $AnswerFile = [xml](Get-Content -Path $AnswerFilePath) 
          $ns = New-Object System.Xml.XmlNamespaceManager($AnswerFile.NameTable)
          $ns.AddNamespace("ns", $AnswerFile.DocumentElement.NamespaceURI)
          $ComputerName = $AnswerFile.SelectSingleNode('/ns:unattend/ns:settings[@pass="specialize"]/ns:component[@name="Microsoft-Windows-Shell-Setup"]/ns:ComputerName', $ns)
          $ComputerName.InnerText = $InstanceName
          $AnswerFile.Save($AnswerFilePath)
    }
}
Catch
{
    Write-Host $_
}
Finally
{
    Stop-Transcript
}

and THEN it calls WinDeploy.exe to finish the specialization.

The problem is that the line

    Write-Host "Discovering instance identity from meta-data web service"
    $InstanceId = (Invoke-RestMethod 'http://169.254.169.254/latest/meta-data/instance-id').ToString()

fails with "Unable to connect to the remote server".

Any idea why it would be unable to connect to that?

enter image description here

John Rotenstein
  • 241,921
  • 22
  • 380
  • 470
Ben Curthoys
  • 741
  • 7
  • 20

4 Answers4

3

You need to add this to the top of the script:

Start-Service -Name 'Dhcp'
route add 169.254.169.254 MASK 255.255.255.255 0.0.0.0

The DHCP Client is stopped when the script is triggered. That why you don't have IP.

And you also need to add a route to the meta-data service. Sysprep remove those routes.

2

looks weird.. . URL looks correct.. Lets try to debug this. Let's isolate the problem and see if its script issue or something else.

  1. try telnet 169.254.169.254 80

if it says connected or not

  1. Also try http://169.254.169.254/latest/meta-data/instance-id on browser and see the output..

Also, try to run the script after system has initialized... Probably the time during which script is running; this local IP is not initialized.

Every time system starts ; EC2 Adds a custom route to the primary network adapter to enable the following IP addresses when multiple NICs are attached: 169.254.169.254. And this script of yours is getting executed before NICs are attached. Hence the problem.

Deepak Singhal
  • 10,568
  • 11
  • 59
  • 98
1

Your script works perfectly fine for me.

The issue is that something on your instance is blocking access to that IP Address, such as a firewall.

Check your system configuration to see whether Windows Firewall is blocking access, or any other security software is installed that is blocking it.

Trying Invoke-RestMethod 'http://google.com' would be a good test, too.

John Rotenstein
  • 241,921
  • 22
  • 380
  • 470
  • It works fine when the instance is running. It's just in the start-up script that it fails. I'll try disabling the Windows Firewall and creating a new image. I'll add a call to get Google too and see if that works or fails. – Ben Curthoys Jul 25 '17 at 16:10
  • Yep, works for me too. @BenCurthoys, perhaps your script executes before the interface is up; you may not even have an IP address at the time of execution and thus cannot reach the metadata server. Perhaps you could try to enter a retry loop. – Jedi Jul 25 '17 at 17:57
1

DHCP is started but Interface is not allocated IP intermittently and below is the workaround to get this done

Function Get-IP{
    $global:initializedIP=netsh interface ip show address $interfaceName | where { $_ -match "IP Address"} | %{ $_ -replace "^.*IP Address:\W*", ""}
    Write-host "`tInitialized IP: $initializedIP"
}

Function InitializeNetwork{
    $global:interfaceName = netsh interface ipv4 show interfaces | where { $_ -match "Ethernet"}| %{ $_ -replace "^.*Connected\W*",""}  
    write-host "`tInterface name: $interfaceName"
    $status = Invoke-Command -Command { netsh interface ipv4 set interface $interfaceName mtu=1460 }
    Write-host "`tInitializing $interfaceName : $status"
    Write-Host "`tStarting Service: DHCP"
    Start-Service -Name 'Dhcp'    
    $r_status = Invoke-Command -Command { route /p add 169.254.169.254 mask 255.255.255.255 0.0.0.0 if 6 metric 1}
    Write-Host "`tAdding metadata route $r_status"
}    
  
Try {
    Start-Transcript -Path C:\Temp\Windeploy.log -Append
    Write-Host "[Meta-Data] Discovering instance identity from meta-data web service"
    Set-Variable -Name "interfaceName" -value "Ethernet 2" -scope global
    Set-Variable -Name "initializedIP" -value "169.254.169.254" -scope global
    Write-Host "[Network] Initializing default network"
    InitializeNetwork
    Get-IP
    While (-not ($initializedIP -like "10.*.*.*"))
    {
        Write-Host "`tGetting IP for $interfaceName"
        Invoke-Command -Command {ipconfig /renew $interfaceName}    
        Get-IP    
    }
    
    $Global:InstanceInfo = Invoke-WebRequest 'http://169.254.169.254/latest/dynamic/instance-identity/document/' -UseBasicParsing | ConvertFrom-Json
    $AccountId = $InstanceInfo.accountId
    $AWSRegion = $InstanceInfo.region
    $InstanceId = $InstanceInfo.instanceId
    Write-Host "[Instance-Tags] Getting Tags for the instance"    
    $Tags = Get-EC2Tag -Filters @{Name='resource-id';Value=$InstanceId} -Region $AWSRegion
    Write-Host "`t$Tags"

    #Generate windows hostname with lowercase letters/numbers only if meta-data not available
    Write-Host "[ComputerName] Auto generate the default computer name." 
    $instanceName = "WIN-" + -join ((48..57) + (97..122) | Get-Random -Count 11 | % {[char]$_})
    Write-Host "`tAuto Instance Name: $InstanceName"
    If ($null -eq $Tags.Where( { $_.Key -eq "hostname" }).Value){
        $instanceName="{0}{1}" -F $Tags.Where( {($_.Key).ToLower() -eq "appcode" }).Value, $InstanceId.Remove(0,($InstanceId.Length - 9))
    }else{
        $InstanceName = ($Tags | Where-Object {$_.Key -eq 'hostname'}).Value
    }
    Write-Host "`tFound Instance Name: $InstanceName"
    If($InstanceName) {
        Write-Host "[Unattend] Setting the machine name to $InstanceName"
        $AnswerFilePath = "C:\Windows\Panther\Unattend.xml"
        $AnswerFile = [xml](Get-Content -Path $AnswerFilePath)
        $ns = New-Object System.Xml.XmlNamespaceManager($answerFile.NameTable)
        $ns.AddNamespace("ns", $AnswerFile.DocumentElement.NamespaceURI)
        $ComputerName = $AnswerFile.SelectSingleNode('/ns:unattend/ns:settings[@pass="specialize"]/ns:component[@name="Microsoft-Windows-Shell-Setup"]/ns:ComputerName', $ns)
        $ComputerName.InnerText = $InstanceName
        $AnswerFile.Save($AnswerFilePath)
        Write-host "[Unattend] ComputerName is added."
    }else{
        Write-host "[Unattend] Unable to Set Instance Name"
    }
    Write-host "[BootMGR] Setting 0 Timeout to Boot Manager"
    Invoke-Command -Command { bcdedit.exe /set "{bootmgr}" timeout 0 }    
}
Catch {
    Write-Host $_
}
Finally
{   
    Write-host "[Start-up] Starting OOBE Setup"
    Start-Process C:\windows\system32\oobe\windeploy.exe -Wait
    Stop-Transcript
}
Rahul Trivedi
  • 124
  • 1
  • 9