SimpleEnv.psm1

Write-Verbose 'Importing from [C:\Users\chanc_000\Source\SimpleEnv\SimpleEnv\classes]'
Write-Verbose 'Importing from [C:\Users\chanc_000\Source\SimpleEnv\SimpleEnv\private]'
Write-Verbose 'Importing from [C:\Users\chanc_000\Source\SimpleEnv\SimpleEnv\public]'
# .\Pre-Module.ps1


$Script:Configuration = @{
    ConfigFilePath = "$Env:USERPROFILE/SimpleENV.json"
}


function ClearEnvironment
{
    $script:Environment = @{
        SimpleEnvVersion = ''
        EnvironmentInfo  = @{ }
        Servers          = @()
    }
    
    $Environment.SimpleEnvVersion = [string]$MyInvocation.MyCommand.Module.Version
}

ClearEnvironment







# .\classes\SimpleEnvServer.ps1

class SimpleEnvServer
{
    [string] $Name
    [string] $ComputerName
    [string] $Environment
    [string[]] $Roles = @()
    [string[]] $Tags = @()
    [hashtable] $Properties = @{ }
}

# .\public\Add-SimpleEnvServer.ps1

function Add-SimpleEnvServer
{
    <#
    .SYNOPSIS
    Adds a SimpleEnvServer object to the loaded environment, and optionally save the changes back to the environment config.
 
    .PARAMETER ServerObject
    The [SimpleEnvServer] object to add.
 
    .PARAMETER Save
    Save changes back to the environment config file at the end of pipeline exectution.
 
    .EXAMPLE
    PS> (1..4) | %{New-SimpleEnvServer -Name "TestServer_$_"} | Add-SimpleEnvServer -Save
        Creates new SimpleEnv Servers named: Test_1,Test_2,Test_3,Test_4 and adds them to the loaded environment,
        then saves the changes back to the backing JSON file.
     
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'low')]    
    param (
        
        # SimpleEnvServer object
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object]
        $ServerObject,

        
        #
        [Parameter(Mandatory = $false)]
        [switch]
        $Save
    )
    

    process
    {
        $server = [SimpleEnvServer]$ServerObject
        if ($PSCmdlet.ShouldProcess($server.Name, "Add to the existing environment?"))
        {
            $script:Environment.Servers += $server
            
        }
    }
    end
    {
        
        if ($Save.IsPresent -and $PSCmdlet.ShouldProcess( "Save changes to the existing environment?") )
        {
            $SaveArgs = @{
                Verbose = $Verbose
            }
            
            Save-SimpleEnv @SaveArgs
        }
    }

}



# .\public\Clear-SimpleEnv.ps1
function Clear-SimpleEnv
{
    <#
.Description
Resets the in-memory SimpleEnv to a blank environment.
 
.example
PS> Clear-SimpleEnv
 
#>

    [CmdletBinding()]
    param ()
    Write-Verbose "Setting environment to default."
    ClearEnvironment
}
# .\public\Export-SimpleEnv.ps1

function Export-SimpleEnv
{
    <#
    .description
     
    Writes the current SimpleEnv to a JSON file (with utf8 encoding).
 
    .parameter FilePath
    The Filepath to write too.
 
    .PARAMETER Force
 
    Overwrites existing file without confirmation
 
    .example
    PS> Export-SimpleEnv -FilePath '~\desktop\MyEnvironment.SimpleEnv.json'
 
    #>

    [CmdletBinding()]
    param (
        # Path to Json file
        [Parameter(Mandatory = $true,
            ParameterSetName = "json",
            ValueFromPipelineByPropertyName = $true
        )]
        [alias("path")]
        [string]
        $FilePath,

        #
        [Parameter()]
        [switch]
        $Force
    )

    $FileParams = @{
        FilePath = $FilePath 
        Encoding = 'utf8'
    }

    if ($Force.IsPresent)
    {
        $FileParams['Force'] = $true
    }
    else
    {
        $FileParams['NoClobber'] = $true
    }

    $script:Environment | ConvertTo-Json -Depth 5 | Out-File @FileParams
}

# .\public\Get-SimpleEnv.ps1
function Get-SimpleEnv
{
    <#
.Synopsis
Get the entire stored simpleEnv as an object.
 
.Parameter Full
 
Get the entire stored simpleEnv as an object.
 
 
.EXAMPLE
 
PS> Get-SimpleEnv
 
Get the entire stored simpleEnv as an object.
 
.EXAMPLE
 
PS> Get-SimpleEnv -Server
 
Get the list of all server
 
 
#>



    [CmdletBinding(DefaultParameterSetName = 'Full')]
    param (
        #
        [Parameter(Mandatory = $true, ParameterSetName = 'Full')]
        [switch]
        $Full,

        [Parameter(Mandatory = $true, ParameterSetName = 'servers')]
        [switch]
        $Servers#,

        # [Parameter(Mandatory = $false, ParameterSetName = 'info')]
        # [switch]
        # $EnvironmentInfo
    )

    begin
    {

    }

    process
    {
        if ($PSCmdlet.ParameterSetName -eq 'Full')
        {
            $script:Environment

        }
        elseif ($PSCmdlet.ParameterSetName -eq 'servers')
        {
            $script:Environment.Servers

        }
        elseif ($PSCmdlet.ParameterSetName -eq 'info')
        {
            $script:Environment.EnvironmentInfo
        }
    }

    end
    {
    }
}

# .\public\Get-SimpleEnvConfiguration.ps1

function Get-SimpleEnvConfiguration
{
    <#
.description
 
Returns the current SimpleEnv configuration as a hashtable.
 
.PARAMETER All
 
Returns the full configuration
 
.PARAMETER FilePath
 
Returns only the FilePath of the backing JSON file.
 
 
.example
 
PS> Get-SimpleEnvConfiguration
 
 
#>

    [CmdletBinding(DefaultParameterSetName = 'all')]
    
    [OutPutType([hashtable], ParameterSetName = 'all')]
    [OutPutType([string], ParameterSetName = 'filepath')]

    param (


        [Parameter(ParameterSetName = 'all')]
        [switch]
        $All,


        [Parameter(ParameterSetName = 'filepath')]
        [switch]
        $FilePath

    )
    
    if ($PSCmdlet.ParameterSetName -eq 'all')
    {
        return $Script:Configuration 
    }
    else
    {
        if ($FilePath.IsPresent)
        {
            $Script:Configuration.ConfigFilePath
        }
    }    
}

# .\public\Get-SimpleEnvServer.ps1
function Get-SimpleEnvServer
{
    <#
    .description
    Retrieves the specified servers from the loaded SimpleEnv.
 
    .parameter Name
    Selects servers with a matching name, wildcard patterns are supported.
 
    .parameter Role
    Select Servers that have a Role that matches the wildcard pattern.
 
    .parameter Tag
    Select Servers that have a tag that matches the wildcard pattern.
 
   .parameter Environment
    Selects servers with a matching Environment, wildcard patterns are supported.
 
    .parameter All
    Return all servers.
 
    .parameter JustComputerName
    Return only the ComputerName(s) of the selected servers.
 
    .example
    PS> Get-SimpleEnvServer
 
    Name ComputerName Tags Properties
    ---- -------- ---- ----------
    DevOps DevOps.test.env.com {dev, 2019, infrastructure, ops} {DistinguishedName}
    Dev-box Dev-box.test.env.com {dev, 2012r2, developer} {DistinguishedName}
 
    .example
    PS> Get-Server -Name *ops
 
    Name ComputerName Tags Properties
    ---- -------- ---- ----------
    DevOps DevOps.test.env.com {dev, 2019, infrastructure, ops} {DistinguishedName}
 
 
    .example
    PS> Get-Server -Tag 2012*
 
    Name ComputerName Tags Properties
    ---- -------- ---- ----------
    Dev-box Dev-box.test.env.com {dev, 2012r2, developer} {DistinguishedName}
 
 
    .example
    PS> Get-Server -Tag 2012* -JustComputerName
 
    Dev-box.test.env.com
 
 
 
    #>

    [CmdletBinding(DefaultParameterSetName = 'all')]
    param (
        #
        [Parameter(Mandatory = $false, ParameterSetName = 'filtered', Position = 0)]
        [ValidateNotNullOrEmpty()]
        [SupportsWildcards()]
        [string]
        $Name,

        #
        [Parameter(Mandatory = $false, ParameterSetName = 'filtered')]
        [ValidateNotNullOrEmpty()]
        [SupportsWildcards()]
        [string]
        $Role,

        #
        [Parameter(Mandatory = $false, ParameterSetName = 'filtered')]
        [ValidateNotNullOrEmpty()]
        [SupportsWildcards()]
        [string]
        $Tag,

        #
        [Parameter(Mandatory = $false, ParameterSetName = 'filtered')]
        [ValidateNotNullOrEmpty()]
        [SupportsWildcards()]
        [string]
        $Environment,

        # Parameter help description
        [Parameter(Mandatory = $false, ParameterSetName = 'all')]
        [Parameter(Mandatory = $false, ParameterSetName = 'filtered')]
        [Alias('DnsName')]
        [switch]
        $JustComputerName,

        # Parameter help description
        [Parameter(Mandatory = $false, ParameterSetName = 'all')]
        [switch]
        $All

    )

    begin
    {
        if ($script:Environment.Servers.count -eq 0)
        {
            Write-Warning -Message ("No servers in environment [{0}] Try importing another configuration, or adding a server." -f $Script:Configuration.ConfigFilePath)
        }

        [bool] $SkipName = (-not $PSBoundParameters.ContainsKey('Name'))
        [bool] $SkipTags = (-not $PSBoundParameters.ContainsKey('tag'))
        [bool] $SkipRoles = (-not $PSBoundParameters.ContainsKey('role'))
        [bool] $SkipEnv = (-not $PSBoundParameters.ContainsKey('Environment'))

        filter OutputFormat
        {

            if ($JustComputerName.IsPresent)
            {
                $Psitem.ComputerName
            }
            else
            {
                $Psitem
            }
        }
    }

    Process
    {
        $Servers = if ($PSCmdlet.ParameterSetName -like 'all')
        {
            $script:Environment.Servers
        }
        else
        {
            $script:Environment.Servers |
            Where-Object { ($SkipName -or ($_.Name -like $Name )) } |
            Where-Object { ($SkipTags -or ($_.Tags -like $Tag )) } |
            Where-Object { ($SkipRoles -or ($_.Roles -like $Role )) } |
            Where-Object { ($SkipEnv -or ($_.Environment -like $Environment )) }
        }
        Write-Verbose "Found $($Servers.count) servers."
        $Servers | OutputFormat

    }
    end
    {
    }
}


Set-Alias -Name 'Get-Server' -Value Get-SimpleEnvServer

# .\public\Import-SimpleEnv.ps1


function Import-SimpleEnv
{
    <#
.SYNOPSIS
 
Imports the specified Json file into the currently loaded SimpleEnv, however changes are still persisted back to the oridginal file.
 
.PARAMETER FilePath
 
Path to the SimpleENV json file to import.
 
.PARAMETER ClearExisting
 
Remove existing servers before importing, instead of combining the environments.
 
.EXAMPLE
PS> Import-SimpleEnv -FilePath Test.simpleenv.json -ClearExisting
Should only have the servers in 'Test.simpleenv.json' loaded.
 
#>


    [CmdletBinding()]
    param (
        # Path to Json file
        [Parameter(Mandatory = $true,
            ParameterSetName = "json",
            ValueFromPipelineByPropertyName = $true
        )]
        [alias("path")]
        [string]
        $FilePath,

        #
        [Parameter()]
        [switch]
        $ClearExisting
    )
    
    begin
    {
        #$ImportProperties = [SimpleEnvServer].DeclaredProperties.Name

        if ($ClearExisting.IsPresent)
        {
            $script:Environment.Servers.Clear()
        
        }
        function ObjToShallowhashtable($object)
        {

            if ($null -ne $object)
            {
                $props = $object
                $PropTable = @{ }
                $Keys = $props | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
                $Keys | ForEach-Object {
                    $PropTable.$_ = $props.$_
                }                
                $PropTable
            }
            else
            {
                return @{ }
            }
        }
    }
    
    process
    {
        $temp = Get-Content -Path $Filepath -Raw | ConvertFrom-Json
        # Reset-SimpleEnv

        $temp.Servers | ForEach-Object {
            $Srv = $_
            if ($null -ne $Srv.Properties)
            {
                $srv.Properties = ObjToShallowhashtable -object $Srv.Properties
            }
        }

        $Temp.Servers | New-SimpleEnvServer | Add-SimpleEnvServer

        if ($null -ne $temp.EnvironmentInfo)
        {
            $script:Environment.EnvironmentInfo = ObjToShallowhashtable -object $Temp.EnvironmentInfo
        }
        elseif ($null -ne $temp.MetaData)
        {
            $script:Environment.EnvironmentInfo = ObjToShallowhashtable -object $Temp.Metadata
        }
    }
    
    end
    {
    }
}
# .\public\New-SimpleEnvServer.ps1


function New-SimpleEnvServer
{
    <#
.SYNOPSIS
 
Creates a new SimpleEnvServer object.
 
.PARAMETER Name
 
Friendly name for the server
 
.PARAMETER ComputerName
 
The Network addressable name for the computer.
This was changed from DnsName to provide better interoperability with built-in commands.
 
.PARAMETER Environment
 
A short environment identifier.
 
 
.PARAMETER Tags
 
A list of strings to tag the server with.
 
.PARAMETER Roles
 
A list of strings identifing the servers roles.
 
.PARAMETER Properties
 
A hashtable of properties / metadata to store about the server.
This hashtable should be no more than 2 layers deep, or it may not be fully serialized.
 
 
 
.Example
 
PS> New-SimpleEnvServer -Name "Test01"
 
Server DNS Name Environment Roles Tags Properties
------ -------- ----------- ----- ---- ----------
Test01 {} {} {}
 
 
 
#>


    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'low')]
    [OutputType([SimpleEnvServer])]
    param (
        
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [string]
        $Name,

        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [string]
        $Environment,
        
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [Alias('FullName')]
        [Alias('DnsName')]
        [string]
        $ComputerName,

        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [string[]]
        $Tags,

        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [string[]]
        $Roles,

        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [Alias('Metadata')]
        [hashtable]
        $Properties
    )
    
    begin
    {
        $ParametersToImport = [SimpleEnvServer].DeclaredProperties.Name
    }
    
    process
    {
        $Item = @{
            Name = $Name
        }

        $ParametersToImport | ForEach-Object {
            if ($PSBoundParameters.ContainsKey($_))
            {
                $Item[$_] = $PSBoundParameters[$_]
            }
        }
        if ($PSCmdlet.ShouldProcess("Create new SimpleEnvServer object"))
        {
            [SimpleEnvServer]$item
        }   

    }
    
    end
    {
        
    }
}
# .\public\Remove-SimpleEnvServer.ps1

function Remove-SimpleEnvServer
{
    <#
.synopsis
 
Remove a SimpleEnvServer object from the loaded environment, and optionally save the changes back to the environment config.
 
.parameter ServerObject
 
The [SimpleEnvServer] Object to remove.
 
.parameter Save
 
Save changes back to the environment config file at the end of pipeline exectution.
 
.example
PS> Get-Server -Tag Agent | Remove-SimpleEnvServer -Save
Removes all servers tagged 'Agent' and saves the changes.
 
#>


    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'low')]   
    param (
        #
        # inputobject
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        # [ValidateScript( { $_ -is [SimpleEnvServer] })]
        [object]
        $ServerObject,

        #
        [Parameter(Mandatory = $false)]
        [switch]
        $Save
    )
    

    process
    {
        $Server = [SimpleEnvServer]$ServerObject
        if ($PSCmdlet.ShouldProcess($Server.Name, "Remove from existing SimpleEnv ?"))
        {
            $script:Environment.Servers = $script:Environment.Servers | Where-Object { $_ -ne $Server } 
        }
    }
    end
    {
        if ($Save.IsPresent -and $PSCmdlet.ShouldProcess( "Save changes to the existing environment?") )
        {
            $SaveArgs = @{
                Verbose = $Verbose
            }
            
            Save-SimpleEnv @SaveArgs
        }
    }

}


# .\public\Save-SimpleEnv.ps1

function Save-SimpleEnv
{
    <#
     
.Description
Exports the current SimpleEnv configuration to the current Configuration File Path
 
.Example
PS> Save-SimpleEnv
#>


    [CmdletBinding()]

    $ExportArgs = @{
        FilePath = $Script:Configuration.ConfigFilePath
        Verbose  = $VerbosePreference
        Force    = $true
    }
    Export-SimpleEnv @ExportArgs
}

# .\public\Set-SimpleEnvConfiguration.ps1
function Set-SimpleEnvConfiguration
{
    <#
.SYNOPSIS
 
Set SimpleEnv Configuration options.
 
.PARAMETER FilePath
 
Sets the path of the backing JSON File, and attempts to import it if it exists.
If you want to overwrite the file without importing it, try Export-SimpleEnv instead.
 
.EXAMPLE
 
PS> Set-SimpleEnvConfiguration -FilePath 'myEnv.SimpleEnv.JSON'
Imports the environment from myEnv.SimpleEnv.JSON, and will persist changes back to it.
 
 
 
.LINK
Export-SimpleEnv
 
#>


    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'low')]
    param (
        #
        [Parameter(Mandatory = $true)]
        [string]
        $FilePath
    )
    
    if ($PSCmdlet.ShouldProcess($Filepath, "Set the source environment JSON file to:" ))
    {
        try
        {
            Write-Verbose "Importing Configuration from $Filepath"
            if (Test-Path $Filepath)
            {
                Import-SimpleEnv -Filepath $Filepath
            }
            $Script:Configuration.ConfigFilePath = $Filepath
            
        }
        catch
        {
            Write-Error "Unable to import $Filepath"
        }    
    }

    
}

# .\Post-Module.ps1
if (Test-Path -Path $Script:Configuration.ConfigFilePath)
{
    Import-SimpleEnv -Filepath $Script:Configuration.ConfigFilePath 
}