'Call to 169.254.169.254/latest/meta-data/instance-id fails in AWS deployment script

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



Solution 1:[1]

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.

Solution 2:[2]

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.

Solution 3:[3]

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
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 John Rotenstein
Solution 3 Rahul Trivedi