fix(ec2): report Platform=windows for Windows images and instances (#666)

DescribeImages and DescribeInstances only ever populated PlatformDetails,
which is a billing string covering every OS. The AWS SDKs and the Terraform
provider key off the separate Platform field, which is either "windows" or
absent, so a Windows AMI read as Linux.

Derive Platform from PlatformDetails rather than persisting a new AMI field,
so already-imported Windows AMIs are fixed without a re-import.
This commit is contained in:
Josh Siviour
2026-08-03 14:10:24 +10:00
committed by GitHub
parent 55b496cf9a
commit 0638ff8c47
4 changed files with 64 additions and 0 deletions
@@ -272,6 +272,7 @@ func (s *ImageServiceImpl) DescribeImages(ctx context.Context, input *ec2.Descri
Description: aws.String(amiMeta.Description),
Architecture: aws.String(amiMeta.Architecture),
PlatformDetails: aws.String(amiMeta.PlatformDetails),
Platform: utils.PlatformFromDetails(amiMeta.PlatformDetails),
CreationDate: aws.String(amiMeta.CreationDate.Format("2006-01-02T15:04:05.000Z")),
RootDeviceType: aws.String(amiMeta.RootDeviceType),
VirtualizationType: aws.String(amiMeta.Virtualization),
@@ -551,6 +551,10 @@ func (s *InstanceServiceImpl) PrepareRunInstances(ctx context.Context, input *ec
}
instance.BootMode = amiMeta.BootMode
// Stamped at launch so DescribeInstances keeps reporting the AMI's platform
// even if the image is later deregistered.
ec2Instance.Platform = utils.PlatformFromDetails(amiMeta.PlatformDetails)
// Terraform may pass subnet/SG via NetworkInterfaces[0]; lift to top-level.
if (input.SubnetId == nil || *input.SubnetId == "") &&
len(input.NetworkInterfaces) > 0 && input.NetworkInterfaces[0] != nil {
+22
View File
@@ -0,0 +1,22 @@
package utils
import "strings"
// PlatformWindows is the only value AWS ever puts in the ec2 Platform field.
const PlatformWindows = "windows"
// PlatformFromDetails derives the AWS Platform field from PlatformDetails,
// returning nil for anything that is not Windows.
//
// The two fields are not interchangeable: PlatformDetails is a billing string
// covering every OS ("Linux/UNIX", "Red Hat Enterprise Linux", "Windows",
// "Windows BYOL"), while Platform is either "windows" or absent. The AWS SDKs
// and the Terraform provider key off Platform to decide an image or instance is
// Windows, so a Windows AMI that omits it reads as Linux.
func PlatformFromDetails(platformDetails string) *string {
if !strings.HasPrefix(strings.ToLower(platformDetails), PlatformWindows) {
return nil
}
p := PlatformWindows
return &p
}
+37
View File
@@ -0,0 +1,37 @@
package utils
import "testing"
func TestPlatformFromDetails(t *testing.T) {
tests := []struct {
name string
platformDetails string
want string
}{
{name: "windows", platformDetails: "Windows", want: PlatformWindows},
{name: "windows byol", platformDetails: "Windows BYOL", want: PlatformWindows},
{name: "windows with sql", platformDetails: "Windows with SQL Server Standard", want: PlatformWindows},
{name: "lowercase windows", platformDetails: "windows", want: PlatformWindows},
{name: "linux", platformDetails: "Linux/UNIX", want: ""},
{name: "rhel", platformDetails: "Red Hat Enterprise Linux", want: ""},
{name: "empty", platformDetails: "", want: ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := PlatformFromDetails(tt.platformDetails)
if tt.want == "" {
if got != nil {
t.Fatalf("PlatformFromDetails(%q) = %q, want nil", tt.platformDetails, *got)
}
return
}
if got == nil {
t.Fatalf("PlatformFromDetails(%q) = nil, want %q", tt.platformDetails, tt.want)
}
if *got != tt.want {
t.Fatalf("PlatformFromDetails(%q) = %q, want %q", tt.platformDetails, *got, tt.want)
}
})
}
}