Skip to content

Instantly share code, notes, and snippets.

@rkhan99e
Created April 3, 2024 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rkhan99e/47d37218ab24d5ce241bf7bc57c2138e to your computer and use it in GitHub Desktop.
Save rkhan99e/47d37218ab24d5ce241bf7bc57c2138e to your computer and use it in GitHub Desktop.
# Input Parameters
param(
[Parameter(Mandatory=$false)]
[string]$InputFile,
[Parameter(Mandatory=$false)]
[string]$OutputFile
)
# Ref: https://www.powershellgallery.com/packages/PSYaml/1.0.2/Content/Public%5CConvertTo-Yaml.ps1
function ConvertTo-Yaml
{
<#
.SYNOPSIS
creates a YAML description of the data in the object
.DESCRIPTION
This produces YAML from any object you pass to it. It isn't suitable for the huge objects produced by some of the cmdlets such as Get-Process, but fine for simple objects
.EXAMPLE
$array=@()
$array+=Get-Process wi* | Select-Object-Object Handles,NPM,PM,WS,VM,CPU,Id,ProcessName
ConvertTo-YAML $array
.PARAMETER Object
the object that you want scripted out
.PARAMETER Depth
The depth that you want your object scripted to
.PARAMETER Nesting Level
internal use only. required for formatting
#>
[OutputType('System.String')]
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
[AllowNull()]
$inputObject,
[parameter(Position = 1, Mandatory = $false, ValueFromPipeline = $false)]
[int]$depth = 16,
[parameter(Position = 2, Mandatory = $false, ValueFromPipeline = $false)]
[int]$NestingLevel = 0,
[parameter(Position = 3, Mandatory = $false, ValueFromPipeline = $false)]
[int]$XMLAsInnerXML = 0
)
BEGIN { }
PROCESS
{
# if it is null return null
If ( !($inputObject) )
{
$p += 'null'
return $p
}
if ($NestingLevel -eq 0) { '---' }
$padding = [string]' ' * $NestingLevel # lets just create our left-padding for the block
try
{
$Type = $inputObject.GetType().Name # we start by getting the object's type
if ($Type -ieq 'Object[]')
{
#what it really is
$Type = "$($inputObject.GetType().BaseType.Name)"
}
#report the leaves in terms of object type
if ($depth -ilt $NestingLevel)
{
$Type = 'OutOfDepth'
}
elseif ($Type -ieq 'XmlDocument' -or $Type -ieq 'XmlElement')
{
if ($XMLAsInnerXML -ne 0)
{
$Type = 'InnerXML'
}
else
{
$Type = 'XML'
}
} # convert to PS Alias
# prevent these values being identified as an object
if (@('boolean', 'byte', 'byte[]', 'char', 'datetime', 'decimal', 'double', 'float', 'single', 'guid', 'int', 'int32',
'int16', 'long', 'int64', 'OutOfDepth', 'RuntimeType', 'PSNoteProperty', 'regex', 'sbyte', 'string',
'timespan', 'uint16', 'uint32', 'uint64', 'uri', 'version', 'void', 'xml', 'datatable', 'Dictionary`2',
'SqlDataReader', 'datarow', 'ScriptBlock', 'type') -notcontains $type)
{
if ($Type -ieq 'OrderedDictionary')
{
$Type = 'HashTable'
}
elseif ($Type -ieq 'PSCustomObject')
{
$Type = 'PSObject'
}
elseif ($Type -ieq 'List`1')
{
$Type = 'Array'
}
elseif ($inputObject -is "Array")
{
$Type = 'Array'
} # whatever it thinks it is called
elseif ($inputObject -is "HashTable")
{
$Type = 'HashTable'
} # for our purposes it is a hashtable
elseif (!($inputObject | Get-Member -membertype Properties | Select-Object name | Where-Object name -like 'Keys'))
{
$Type = 'generic'
} #use dot notation
elseif (($inputObject | Get-Member -membertype Properties | Select-Object name).count -gt 1)
{
$Type = 'Object'
}
}
write-verbose "$($padding)Type:='$Type', Object type:=$($inputObject.GetType().Name), BaseName:=$($inputObject.GetType().BaseType.Name) "
switch ($Type)
{
'ScriptBlock'{ "{$($inputObject.ToString())}" }
'InnerXML' { "|`r`n" + ($inputObject.OuterXMl.Split("`r`n") | ForEach-Object{ "$padding$_`r`n" }) }
'DateTime' { $inputObject.ToString('s') } # s=SortableDateTimePattern (based on ISO 8601) using local time
'Byte[]' {
$string = [System.Convert]::ToBase64String($inputObject)
if ($string.Length -gt 100)
{
# right, we have to format it to YAML spec.
'!!binary "\' + "`r`n" # signal that we are going to use the readable Base64 string format
#$bits = @()
$length = $string.Length
$IndexIntoString = 0
$wrap = 100
while ($length -gt $IndexIntoString + $Wrap)
{
$padding + $string.Substring($IndexIntoString, $wrap).Trim() + "`r`n"
$IndexIntoString += $wrap
}
if ($IndexIntoString -lt $length)
{
$padding + $string.Substring($IndexIntoString).Trim() + "`r`n"
}
else
{
"`r`n"
}
}
else
{
'!!binary "' + $($string -replace '''', '''''') + '"'
}
}
'Boolean' {
"$(&{
if ($inputObject -eq $true) { 'true' }
else { 'false' }
})"
}
'string' {
$String = "$inputObject"
if ($string -match '[\r\n]' -or $string.Length -gt 80)
{
# right, we have to format it to YAML spec.
$folded = ">`r`n" # signal that we are going to use the readable 'newlines-folded' format
$string.Split("`n") | ForEach-Object {
$length = $_.Length
$IndexIntoString = 0
$wrap = 80
while ($length -gt $IndexIntoString + $Wrap)
{
$BreakPoint = $wrap
$earliest = $_.Substring($IndexIntoString, $wrap).LastIndexOf(' ')
$latest = $_.Substring($IndexIntoString + $wrap).IndexOf(' ')
if (($earliest -eq -1) -or ($latest -eq -1))
{
$BreakPoint = $wrap
}
elseif ($wrap - $earliest -lt ($latest))
{
$BreakPoint = $earliest
}
else
{
$BreakPoint = $wrap + $latest
}
if (($wrap - $earliest) + $latest -gt 30)
{
$BreakPoint = $wrap # in case it is a string without spaces
}
$folded += $padding + $_.Substring($IndexIntoString, $BreakPoint).Trim() + "`r`n"
$IndexIntoString += $BreakPoint
}
if ($IndexIntoString -lt $length)
{
$folded += $padding + $_.Substring($IndexIntoString).Trim() + "`r`n`r`n"
}
else
{
$folded += "`r`n`r`n"
}
}
$folded
}
else
{
"'$($string -replace '''', '''''')'"
}
}
'Char' { "([int]$inputObject)" }
{
@('byte', 'decimal', 'double', 'float', 'single', 'int', 'int32', 'int16', `
'long', 'int64', 'sbyte', 'uint16', 'uint32', 'uint64') -contains $_
}
{ "$inputObject" } # rendered as is without single quotes
'PSNoteProperty' { "$(ConvertTo-YAML -inputObject $inputObject.Value -depth $depth -NestingLevel ($NestingLevel + 1))" }
'Array' { "$($inputObject | Foreach-Object { "`r`n$padding- $(ConvertTo-YAML -inputObject $_ -depth $depth -NestingLevel ($NestingLevel + 1))" })" }
'HashTable'{
("$($inputObject.GetEnumerator() | Foreach-Object {
"`r`n$padding $($_.Name): " +
(ConvertTo-YAML -inputObject $_.Value -depth $depth -NestingLevel ($NestingLevel + 1))
})")
}
'Dictionary`2'{
("$($inputObject.GetEnumerator() | Foreach-Object {
"`r`n$padding $($_.Key): " +
(ConvertTo-YAML -inputObject $_.Value -depth $depth -NestingLevel ($NestingLevel + 1))
})")
}
'PSObject' { ("$($inputObject.PSObject.Properties | Foreach-Object { "`r`n$padding $($_.Name): " + (ConvertTo-YAML -inputObject $_ -depth $depth -NestingLevel ($NestingLevel + 1)) })") }
'generic' { "$($inputObject.Keys | Foreach-Object { "`r`n$padding $($_): $(ConvertTo-YAML -inputObject $inputObject.$_ -depth $depth -NestingLevel ($NestingLevel + 1))" })" }
'Object' { ("$($inputObject | Get-Member -membertype properties | Select-Object-Object name | Foreach-Object { "`r`n$padding $($_.name): $(ConvertTo-YAML -inputObject $inputObject.$($_.name) -depth $NestingLevel -NestingLevel ($NestingLevel + 1))" })") }
'XML' { ("$($inputObject | Get-Member -membertype properties | Where-Object-object { @('xml', 'schema') -notcontains $_.name } | Select-Object-Object name | Foreach-Object { "`r`n$padding $($_.name): $(ConvertTo-YAML -inputObject $inputObject.$($_.name) -depth $depth -NestingLevel ($NestingLevel + 1))" })") }
'DataRow' { ("$($inputObject | Get-Member -membertype properties | Select-Object-Object name | Foreach-Object { "`r`n$padding $($_.name): $(ConvertTo-YAML -inputObject $inputObject.$($_.name) -depth $depth -NestingLevel ($NestingLevel + 1))" })") }
<#
'SqlDataReader'{ $all = $inputObject.FieldCount
while ($inputObject.Read()) {for ($i = 0; $i -lt $all; $i++)
{"`r`n$padding $($Reader.GetName($i)): $(ConvertTo-YAML -inputObject $($Reader.GetValue($i)) -depth $depth -NestingLevel ($NestingLevel+1))"}}
#>
default { "'$inputObject'" }
}
}
catch
{
write-error "Error'$($_)' in script $($_.InvocationInfo.ScriptName) $($_.InvocationInfo.Line.Trim()) (line $($_.InvocationInfo.ScriptLineNumber)) char $($_.InvocationInfo.OffsetInLine) executing $($_.InvocationInfo.MyCommand) on $type object '$($inputObject)' Class: $($inputObject.GetType().Name) BaseClass: $($inputObject.GetType().BaseType.Name) "
}
finally { }
}
END { }
}
if (-not $InputFile) {
Write-Host "Input file path is missing. Please provide the input file path using the --InputFile parameter." -ForegroundColor Red
return
}
if (-not $OutputFile) {
Write-Host "Output file path is missing. Please provide the output file path using the --OutputFile parameter." -ForegroundColor Red
return
}
# Read the input JSON file
$json = Get-Content $InputFile | ConvertFrom-Json
# Convert the JSON to YAML format
$yaml = @()
# Process the Application object
$yaml += ConvertTo-Yaml $json.Application
# Process the SamlCertificates array
foreach ($cert in $json.SamlCertificates) {
$yaml += ConvertTo-Yaml $cert
}
# Process the ClientSecrets array
foreach ($secret in $json.ClientSecrets) {
$yaml += ConvertTo-Yaml $secret
}
# Process the AppRoleGrants array
foreach ($assignment in $json.AppRoleGrants) {
$yaml += ConvertTo-Yaml $assignment
}
# Process the GroupAssignments array
foreach ($assignment in $json.GroupAssignments) {
$yaml += ConvertTo-Yaml $assignment
}
# Process the ScopeGrants object
$yaml += ConvertTo-Yaml $json.ScopeGrants
# Write the YAML output to the specified file
$yaml | Set-Content $OutputFile
Write-Host "Conversion is Completed."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment