Packer.Images/scripts/Update-OvfConfiguration.ps1
djpbessems d1413040fe
Some checks failed
continuous-integration/drone/push Build is failing
Append new nodes into XML parent node; Fix capacity comparison.
2021-05-10 23:13:04 +02:00

355 lines
16 KiB
PowerShell

#Requires -Modules 'powershell-yaml'
[CmdletBinding()]
Param(
[Parameter(Mandatory)]
[ValidateScript({
If (Test-Path($_)) {
$True
} Else {
Throw "'$_' is not a valid filename (within working directory '$PWD'), or access denied; aborting."
}
})]
[string]$OVFFile,
[hashtable]$Parameter
)
$GetContentSplat = @{
Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', ".yml")
Raw = $True
}
$RawContent = Get-Content @GetContentSplat
$ConvertFromYamlSplat = @{
Yaml = $RawContent
AllDocuments = $True
}
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
# Check if the respective .yml file declared substitutions which need to be parsed
If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) {
ForEach ($Pattern in $YamlDocuments[-1].Variables) {
$RawContent = $RawContent -replace "\{\{ ($($Pattern.Name)) \}\}", [string](Invoke-Expression -Command $Pattern.Expression)
}
# Perform conversion to Yaml again, now with parsed file contents
$ConvertFromYamlSplat = @{
Yaml = $RawContent
AllDocuments = $True
}
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
$OVFConfig = $YamlDocuments[0..($YamlDocuments.Count - 2)]
}
Else {
$OVFConfig = $YamlDocuments
}
$SourceFile = Get-Item -Path $OVFFile
$GetContentSplat = @{
Path = $SourceFile.FullName
}
$XML = [xml](Get-Content @GetContentSplat)
$NS = [System.Xml.XmlNamespaceManager]$XML.NameTable
[void]$NS.AddNamespace('ns', $XML.DocumentElement.xmlns)
[void]$NS.AddNamespace('ovf', $XML.DocumentElement.ovf)
ForEach ($ReferencedDisk in $OVFConfig.Disks.Referenced) {
$XMLDisk = $XML.SelectSingleNode("//ns:DiskSection/ns:Disk[@ovf:diskId='$($ReferencedDisk.Id)']", $NS)
If ($XMLDisk.Count -eq 1) {
$FactorMap = @{
'KB' = [Math]::Pow(2,10)
'MB' = [Math]::Pow(2,20)
'GB' = [Math]::Pow(2,30)
'TB' = [Math]::Pow(2,40)
'PB' = [Math]::Pow(2,50)
}
If (($FactorMap.Keys -contains $ReferencedDisk.UnitSize)) {
# Convert new capacity to same scale as existing capacity
If ($XMLDisk.capacityAllocationUnits -match 'byte \* 2\^(\d+)') {
$NewSize = [Math]::Truncate($ReferencedDisk.Capacity * $FactorMap[$ReferencedDisk.UnitSize] / [Math]::Pow(2,$Matches[1]))
}
ElseIf ($XMLDisk.capacityAllocationUnits -eq 'byte') {
$NewSize = $ReferencedDisk.Capacity * $FactorMap[$ReferencedDisk.UnitSize]
}
Else {
Throw "Unexpected value '$($XMLDisk.capacityAllocationUnits)' for property 'capacityAllocationUnits'; aborting."
}
# Determine if specified capacity is equal or larger than current & allocated capacity
If ($XMLDisk.populatedSize -le $NewSize -and $XMLDisk.capacity -le $NewSize) {
[void]$XMLDisk.SetAttribute('ovf:capacity', $NewSize)
Write-Host "Updated 'Disk' with new property value"
}
}
Else {
# Invalid UnitSize; skipping modification of existing disk
Continue
}
}
}
ForEach ($DynamicDisk in $OVFConfig.Disks.Dynamic) {
# Determine next free available diskId
$XMLDisks = $XML.SelectNodes("//ns:DiskSection/ns:Disk[contains(@ovf:diskId,'vmdisk')]", $NS)
$DiskId = $XMLDisks.Count + 1
While ($XMLDisks.DiskId -contains "vmdisk$($DiskId)") {
$DiskId++
}
$XMLDisk = $XML.CreateElement('Disk', $XML.DocumentElement.xmlns)
$XMLDiskAttrUnits = $XML.CreateAttribute('capacityAllocationUnits', $XML.DocumentElement.ovf)
Switch ($DynamicDisk.UnitSize) {
'KB' {
$Powers = 10
}
'MB' {
$Powers = 20
}
'GB' {
$Powers = 30
}
'TB' {
$Powers = 40
}
'PB' {
$Powers = 50
}
default {
# Invalid UnitSize; skipping modification of existing disk
Continue
}
}
$XMLDiskAttrUnits.Value = "byte * 2^$($Powers)"
$XMLDiskAttrFormat.CreateAttribute('format', $XML.DocumentElement.ovf)
$XMLDiskAttrFormat.Value = 'http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized'
$XMLDiskId.CreateAttribute('diskId', $XML.DocumentElement.ovf)
$XMLDiskId.Value = "vmdisk$($DiskId)"
$XMLDiskAttrCapacity.CreateAttribute('capacity', $XML.DocumentElement.ovf)
$XMLDiskAttrCapacity.Value = '${{vmconfig.disksize.{0}}}' -f $DiskId
$XMLDiskAttrPopulated.CreateAttribute('populatedSize', $XML.DocumentElement.ovf)
$XMLDiskAttrPopulated.Value = 0
[void]$XMLDisk.Attributes.Append($XMLDiskAttrUnits)
[void]$XMLDisk.Attributes.Append($XMLDiskAttrFormat)
[void]$XMLDisk.Attributes.Append($XMLDiskId)
[void]$XMLDisk.Attributes.Append($XMLDiskAttrCapacity)
[void]$XMLDisk.Attributes.Append($XMLDiskAttrPopulated)
[void]$XML.SelectSingleNode('//ns:DiskSection', $NS).AppendChild($XMLDisk)
$OVFConfig.PropertyCategories[0].ProductProperties += @{
Key = "vmconfig.disksize.$($DiskId)"
Type = If ([boolean]$DynamicDisk.Constraints.Minimum -or [boolean]$DynamicDisk.Constraints.Maximum) {
"Int($($DynamicDisk.Constraints.Minimum)..$($DynamicDisk.Constraints.Maximum))"
}
Else {
'Int'
}
Label = "Disk $($DiskId) size*"
Description = "$($DynamicDisk.Description) (in $($DynamicDisk.UnitSize))".Trim()
DefaultValue = "$($DynamicDisk.Constraints.Minimum)"
Configurations = '*'
UserConfigurable = 'true'
}
}
Write-Host "Inserted $($OVFConfig.Disks.Dynamic.Count) new nodes into 'DiskSection'"
If ($OVFConfig.DeploymentConfigurations.Count -gt 0) {
$XMLSection = $XML.CreateElement('DeploymentOptionSection', $XML.DocumentElement.xmlns)
$XMLSectionInfo = $XML.CreateElement('Info', $XML.DocumentElement.xmlns)
$XMLSectionInfo.InnerText = 'Deployment Type'
[void]$XMLSection.AppendChild($XMLSectionInfo)
ForEach ($Configuration in $OVFConfig.DeploymentConfigurations) {
$XMLConfig = $XML.CreateElement('Configuration', $XML.DocumentElement.xmlns)
$XMLConfigAttrId = $XML.CreateAttribute('id', $XML.DocumentElement.ovf)
$XMLConfigAttrId.Value = $Configuration.Id
$XMLConfigLabel = $XML.CreateElement('Label', $XML.DocumentElement.xmlns)
$XMLConfigLabel.InnerText = $Configuration.Label
$XMLConfigDescription = $XML.CreateElement('Description', $XML.DocumentElement.xmlns)
$XMLConfigDescription.InnerText = $Configuration.Description
[void]$XMLConfig.Attributes.Append($XMLConfigAttrId)
[void]$XMLConfig.AppendChild($XMLConfigLabel)
[void]$XMLConfig.AppendChild($XMLConfigDescription)
[void]$XMLSection.AppendChild($XMLConfig)
}
[void]$XML.SelectSingleNode('//ns:Envelope', $NS).InsertAfter($XMLSection, $XML.SelectSingleNode('//ns:NetworkSection', $NS))
Write-Host "Inserted 'DeploymentOptionSection' with $($Configuration.Count) nodes"
}
$XMLAttrTransport = $XML.CreateAttribute('transport', $XML.DocumentElement.ovf)
$XMLAttrTransport.Value = 'com.vmware.guestInfo'
[void]$XML.SelectSingleNode('//ns:VirtualHardwareSection', $NS).Attributes.Append($XMLAttrTransport)
ForEach ($ExtraConfig in $OVFConfig.AdvancedOptions) {
$XMLExtraConfig = $XML.CreateElement('vmw:ExtraConfig', $XML.DocumentElement.vmw)
$XMLExtraConfigAttrRequired = $XML.CreateAttribute('required', $XML.DocumentElement.ovf)
$XMLExtraConfigAttrRequired.Value = "$([boolean]$ExtraConfig.Required)".ToLower()
$XMLExtraConfigAttrKey = $XML.CreateAttribute('key', $XML.DocumentElement.vmw)
$XMLExtraConfigAttrKey.Value = $ExtraConfig.Key
$XMLExtraConfigAttrValue = $XML.CreateAttribute('value', $XML.DocumentElement.vmw)
$XMLExtraConfigAttrValue.Value = $ExtraConfig.Value
[void]$XMLExtraConfig.Attributes.Append($XMLExtraConfigAttrRequired)
[void]$XMLExtraConfig.Attributes.Append($XMLExtraConfigAttrKey)
[void]$XMLExtraConfig.Attributes.Append($XMLExtraConfigAttrValue)
[void]$XML.SelectSingleNode('//ns:VirtualHardwareSection', $NS).AppendChild($XMLExtraConfig)
}
Write-Host "Added $($OVFConfig.AdvancedOptions.Count) 'vmw:ExtraConfig' nodes"
$XMLProductSection = $XML.SelectSingleNode('//ns:ProductSection', $NS)
If ($XMLProductSection -eq $Null) {
$XMLProductSection = $XML.CreateElement('ProductSection', $XML.DocumentElement.xmlns)
[void]$XML.SelectSingleNode('//ns:VirtualSystem', $NS).AppendChild($XMLProductSection)
Write-Host "Inserted 'ProductSection'"
} Else {
ForEach ($Child in $XMLProductSection.SelectNodes('//ns:ProductSection/child::*', $NS)) {
[void]$Child.ParentNode.RemoveChild($Child)
}
Write-Host "Destroyed pre-existing children in 'ProductSection'"
}
$XMLProductSectionInfo = $XML.CreateElement('Info', $XML.DocumentElement.xmlns)
$XMLProductSectionInfo.InnerText = 'Information about the installed software'
[void]$XMLProductSection.AppendChild($XMLProductSectionInfo)
Write-Host "Inserted new 'Info' into 'ProductSection'"
ForEach ($Category in $OVFConfig.PropertyCategories) {
If ($Category.Name -ne '') {
$XMLCategory = $XML.CreateElement('Category', $XML.DocumentElement.xmlns)
$XMLCategory.InnerText = $Category.Name
[void]$XMLProductSection.AppendChild($XMLCategory)
Write-Host "Inserted new 'Category' into 'ProductSection'"
}
ForEach ($Property in $Category.ProductProperties) {
$XMLProperty = $XML.CreateElement('Property', $XML.DocumentElement.xmlns)
$XMLPropertyAttrKey = $XML.CreateAttribute('key', $XML.DocumentElement.ovf)
$XMLPropertyAttrKey.Value = $Property.Key
$XMLPropertyAttrType = $XML.CreateAttribute('type', $XML.DocumentElement.ovf)
Switch -regex ($Property.Type) {
'^boolean' {
$XMLPropertyAttrType.Value = 'boolean'
}
'^int' {
$XMLPropertyAttrType.Value = 'uint8'
$Qualifiers = @()
If ($Property.Type -match '^int\((\d*)\.\.(\d*)\)') {
If ($Matches[1]) {
$Qualifiers += "MinValue($($Matches[1]))"
}
If ($Matches[2]) {
$Qualifiers += "MaxValue($($Matches[2]))"
}
$XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.ovf)
$XMLPropertyAttrQualifiers.Value = $Qualifiers -join ' '
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
}
}
'^ip' {
$XMLPropertyAttrType.Value = 'string'
$XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.vmw)
$XMLPropertyAttrQualifiers.Value = 'Ip'
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
}
'^password' {
$XMLPropertyAttrType.Value = 'string'
$XMLPropertyAttrPassword = $XML.CreateAttribute('password', $XML.DocumentElement.ovf)
$XMLPropertyAttrPassword.Value = 'true'
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrPassword)
$Qualifiers = @()
If ($Property.Type -match '^password\((\d*)\.\.(\d*)\)') {
If ($Matches[1]) {
$Qualifiers += "MinLen($($Matches[1]))"
}
If ($Matches[2]) {
$Qualifiers += "MaxLen($($Matches[2]))"
}
$XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.ovf)
$XMLPropertyAttrQualifiers.Value = $Qualifiers -join ' '
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
}
}
'^string' {
$XMLPropertyAttrType.Value = 'string'
$Qualifiers = @()
If ($Property.Type -match '^string\((\d*)\.\.(\d*)\)') {
If ($Matches[1]) {
$Qualifiers += "MinLen($($Matches[1]))"
}
If ($Matches[2]) {
$Qualifiers += "MaxLen($($Matches[2]))"
}
$XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.ovf)
$XMLPropertyAttrQualifiers.Value = $Qualifiers -join ' '
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
} ElseIf ($Property.Type -match '^string\[(.*)\]') {
$XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.ovf)
$XMLPropertyAttrQualifiers.Value = "ValueMap{$($Matches[1] -replace '","', '", "')}"
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
}
}
}
$XMLPropertyAttrUserConfigurable = $XML.CreateAttribute('userConfigurable', $XML.DocumentElement.ovf)
$XMLPropertyAttrUserConfigurable.Value = "$([boolean]$Property.UserConfigurable)".ToLower()
$XMLPropertyAttrValue = $XML.CreateAttribute('value', $XML.DocumentElement.ovf)
If ($Property.Type -eq 'boolean') {
$XMLPropertyAttrValue.Value = "$([boolean]$Property.DefaultValue)".ToLower()
} Else {
$XMLPropertyAttrValue.Value = $Property.DefaultValue
}
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrKey)
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrType)
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrUserConfigurable)
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrValue)
If ($Property.Label) {
$XMLPropertyLabel = $XML.CreateElement('Label', $XML.DocumentElement.xmlns)
$XMLPropertyLabel.InnerText = $Property.Label
[void]$XMLProperty.AppendChild($XMLPropertyLabel)
}
If ($Property.Description) {
$XMLPropertyDescription = $XML.CreateElement('Description', $XML.DocumentElement.xmlns)
$XMLPropertyDescription.InnerText = $Property.Description
[void]$XMLProperty.AppendChild($XMLPropertyDescription)
}
If (($Property.Configurations.Count -eq 1) -and ($Property.Configurations -eq '*')) {
$XMLPropertyAttrConfiguration = $XML.CreateAttribute('configuration', $XML.DocumentElement.ovf)
$XMLPropertyAttrConfiguration.Value = $OVFConfig.DeploymentConfigurations.Id -join ' '
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrConfiguration)
} ElseIf ($Property.Configurations.Count -gt 0) {
$XMLPropertyAttrConfiguration = $XML.CreateAttribute('configuration', $XML.DocumentElement.ovf)
$XMLPropertyAttrConfiguration.Value = $Property.Configurations -join ' '
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrConfiguration)
}
If ($Property.Value.Count -eq 1) {
$XMLPropertyAttrValue = $XML.CreateAttribute('value', $XML.DocumentElement.ovf)
$XMLPropertyAttrValue.Value = $Property.Value
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrValue)
} ElseIf ($Property.Value.Count -gt 1) {
ForEach ($Value in $Property.Value) {
$XMLValue = $XML.CreateElement('Value', $XML.DocumentElement.xmlns)
$XMLValueAttrValue = $XML.CreateAttribute('value', $XML.DocumentElement.ovf)
$XMLValueAttrValue.Value = $Value
$XMLValueAttrConfiguration = $XML.CreateAttribute('configuration', $XML.DocumentElement.ovf)
$XMLValueAttrConfiguration.Value = $Value
[void]$XMLValue.Attributes.Append($XMLValueAttrValue)
[void]$XMLValue.Attributes.Append($XMLValueAttrConfiguration)
[void]$XMLProperty.AppendChild($XMLValue)
}
}
[void]$XMLProductSection.AppendChild($XMLProperty)
}
Write-Host "Inserted $($Category.ProductProperties.Count) new node(s) into 'ProductSection'"
}
$XML.Save($SourceFile.FullName)