AutomatedLab.Utils.psm1

#Region '.\public\Get-CustomRole.ps1' -1

function Get-CustomRole {
    <#
    .SYNOPSIS
    List available custom roles
     
    .EXAMPLE
    Get-CustomRole
    #>

    [CmdletBinding()]
    Param()

    end {
        $LabSource = Get-LabSourcesLocation
        $RolePath = Join-Path $LabSource -ChildPath 'CustomRoles'

        (Get-ChildItem $RolePath -Directory).Name
    }
}
#EndRegion '.\public\Get-CustomRole.ps1' 19
#Region '.\public\Get-LabConfiguration.ps1' -1

function Get-LabConfiguration {
    <#
    .SYNOPSIS
    Returns a configuration object
        
    .PARAMETER Name
    The name of the configuration to return
     
    .EXAMPLE
    Get-LabConfiguration -Name Example
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory)]
        [String]
        $Name
    )

    end {
        Import-Configuration -Name $Name -CompanyName $env:USERNAME
    }
}
#EndRegion '.\public\Get-LabConfiguration.ps1' 23
#Region '.\public\Get-LabConfigurationPath.ps1' -1

function Get-LabConfigurationPath {
    <#
    .SYNOPSIS
    Returns the path to the lab configuration
         
    .PARAMETER Name
    The lab to return
     
    .EXAMPLE
    Get-LabCOnfiguration -Name MyLab
     
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory)]
        [String]
        $Name
    )

    end {
        Get-ConfigurationPath -Name $Name -CompanyName $env:USERNAME -Scope User
    }
}
#EndRegion '.\public\Get-LabConfigurationPath.ps1' 24
#Region '.\public\New-CustomRole.ps1' -1

function New-CustomRole {
    <#
    .SYNOPSIS
    Creates a new Custom Role in AutomatedLab
     
    .PARAMETER Name
    The name of the custom role
     
    .PARAMETER InitScript
    If you already have the role script written, provide it with -InitScript
     
    .PARAMETER AdditionalFiles
    Provide the file path of any additional files the role requires to function
     
    .PARAMETER InitUrl
    This is a url to a PowerShell hosted online, e.g a gist or repository.
 
    .EXAMPLE
    New-CustomRole -Name SampleRole
 
    Create a new role called SampleRole. It will be bootstrapped for you.
 
    .EXAMPLE
    New-CustomRole -Name SampleRole -InitScript C:\scripts\role_scripts\SampleRole.ps1
 
    Create a new role called SampleRole, and use an existing InitScript
 
    .EXAMPLE
    New-CustomRole -Name SampleRole -AdditionalFiles C:\temp\cert.pfx,C:\temp\my.lic
 
    Create a new role called SampleRole and provide some additonal files it requires
 
    .EXAMPLE
    New-CustomRole -Name SampleRole -InitUrl https://fabrikam.com/roles/SampleRole/role.ps1
 
    Creates a new role called SampleRole and downloads the role script from a url and saves it as SampleRole.ps1
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory)]
        [String]
        $Name,

        [Parameter()]
        [ValidateScript({ 
                if ((Test-Path $_) -and ((Get-Item $_).Extension -eq ".ps1")) {
                    $true
                } 
                else {
                    throw "The init script either doesn't exist or must be a .ps1 file!"
                }
            })]
        [String]
        $InitScript,

        [Parameter()]
        [string]
        $InitUrl,

        [Parameter()]
        [ValidateScript({
                $af = $_
                $af | ForEach-Object { Test-Path $_ }
            })]
        [String[]]
        $AdditionalFiles
    )

    end {

        $LabSourcesLocation = Get-LabSourcesLocation
        $rolePath = Join-Path (Join-Path $LabSourcesLocation -ChildPath 'CustomRoles') -ChildPath $Name
        
        if (-not (Test-Path $rolePath)) {
            $null = New-Item $rolePath -ItemType Directory

           
            if ($InitScript) {
                # If user provides an init script, put it in the role folder
                Copy-Item $InitScript -Destination "$rolePath\$Name.ps1"
            }

            elseif($InitUrl) {
                # When provided a url it downloads the scipt contents and saves it as the role script
                $Script = Join-Path $rolePath -ChildPath "$($Name).ps1"
                $contents = [System.Net.WebClient]::New().DownloadString($InitUrl)
                $contents | Out-File $Script
            }

            else {
                # Otherwise we just create a blank role script
                $null = New-Item -Path $rolePath -Name "$($Name).ps1" -ItemType File
            }
            
            # Copy any additional files to the role
            if ($AdditionalFiles) {
                Copy-Item (Resolve-Path $AdditionalFiles) -Destination $rolePath
            }
        }

        else {
            throw 'Role already exists. Please choose a different name.'
        }
    }
}
#EndRegion '.\public\New-CustomRole.ps1' 106
#Region '.\public\New-HostsFileEntry.ps1' -1

function New-HostsFileEntry {
    <#
    .SYNOPSIS
    Adds an entry to the HOSTS file for the given ip address and hostname
         
    .PARAMETER IPAddress
    The ip address to add
     
    .PARAMETER Hostname
    The hostname to add
     
    .PARAMETER Note
    An optional note about the entry
 
    .EXAMPLE
    New-HostsFileEntry -IpAddress 127.0.0.1 -Hostname widget.fabrikam.com
     
    .EXAMPLE
    New-HostsFileEntry -IpAddress 10.10.10.100 -Hostname widget.fabrikam.com -Note 'this is my fancy widget server'
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory)]
        [String]
        $IPAddress,

        [Parameter(Mandatory)]
        [String]
        $Hostname,

        [Parameter()]
        [String]
        $Note
    )
begin {
    if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
        throw "This function requires administrator privileges. Please elevate your terminal, and try this command again."
    }
}
    end {
        $entry = '{0} {1}' -f $IPAddress, $Hostname

        if ($Note) {
            $entry = "$entry #{0}" -f $Note
        }

        $hostFile = 'C:\Windows\system32\drivers\etc\hosts'
        $entry | Out-File -FilePath $hostFile -Encoding utf8 -Append
    }
}

#EndRegion '.\public\New-HostsFileEntry.ps1' 52
#Region '.\public\New-LabConfiguration.ps1' -1

function New-LabConfiguration {
    <#
    .SYNOPSIS
    Creates a new lab configuration
     
    .DESCRIPTION
    Long description
     
    .PARAMETER Name
    The name for the configuration
     
    .PARAMETER Definition
    A .ps1 file you wish to save with the configuration
     
    .PARAMETER Parameters
    A hashtable of Parameters that will be passed to the Definition when executed
     
    .PARAMETER Url
    A url to a PowerShell script you wish to include as the definition
     
    .EXAMPLE
    $conf = @{
    Name = 'Example'
    Definition = 'C:\temp\sample.ps1'
    Parameters = @{
        Animal = 'Dog'
        Breed = 'Lab'
        }
    }
 
    New-LabConfiguration @conf
     
    .EXAMPLE
    $conf = @{
    Name = 'Example'
    Url = 'https://files.fabrikam.com/myscript.ps1'
    Parameters = @{
        Animal = 'Dog'
        Breed = 'Lab'
       }
    }
 
    New-LabConfiguration @conf
 
 
    #>

    [CmdletBinding(DefaultParameterSetName = 'default')]
    Param(
        [Parameter(Mandatory, ParameterSetName = 'default')]
        [Parameter(Mandatory, ParameterSetName = 'Git')]
        [String]
        $Name,

        [Parameter(Mandatory, ParameterSetName = 'default')]
        [String]
        $Definition,

        [Parameter(ParameterSetName = 'Git')]
        [Parameter(ParameterSetName = 'default')]
        [Hashtable]
        $Parameters,

        [Parameter(Mandatory, ParameterSetName = 'Git')]
        [String]
        $Url
    )

    end {

        $ConfigurationBase = Join-Path $env:LOCALAPPDATA -ChildPath 'PowerShell'
        $slug = Join-Path $env:USERNAME -ChildPath $Name

        $Configuration = Join-Path $ConfigurationBase -ChildPath $slug

        if (-not $Parameters) {
            $Parameters = @{}
        }

        #Add the name
        $Parameters.Add('Name', $Name)
        
        switch ($PSCmdlet.ParameterSetName) {
            'Git' {
                $Definition = Join-Path $Configuration -ChildPath 'Definition.ps1' 
            }
            default {
                $Definition = Resolve-Path $Definition
            }
        }

        @{
            Definition = $Definition
            Parameters = $Parameters
        } | Export-Configuration -CompanyName $env:USERNAME -Name $Name -Scope User

        # The configuration has to exist on disk before we can use it to build the path
        # where the definition will be saved when downloading from a Url.
        # So we postpone processing until we have exported the configuration with the correct
        # value, and then just drop the file there.
        if ($url) {
            [System.Net.WebClient]::new().DownloadFile($Url, $Definition)       
        }         

    }
}
#EndRegion '.\public\New-LabConfiguration.ps1' 106
#Region '.\public\Remove-LabConfiguration.ps1' -1

function Remove-LabConfiguration {
    <#
    .SYNOPSIS
    Removes a lab configuration
     
    .PARAMETER Name
    The configuration to remove
     
    .EXAMPLE
    Remove-LabConfiguration -Name TestConfig
    #>

    [CmdletBinding(ConfirmImpact =  'Medium',SupportsShouldProcess)]
    Param(
        [Parameter(Mandatory,ValueFromPipeline)]
        [String]
        $Name
    )

    if($PSCmdlet.ShouldProcess($Name,'Remove the lab configuration')){
        Get-ConfigurationPath -Name $Name -CompanyName $env:USERNAME -Scope User | Remove-Item -Recurse -Force
    }
}
#EndRegion '.\public\Remove-LabConfiguration.ps1' 23
#Region '.\public\Start-Lab.ps1' -1

function Start-Lab {
    <#
    .SYNOPSIS
    Starts a Lab from the configuration
     
    .DESCRIPTION
     
     
    .PARAMETER Name
    The lab to build
     
    .PARAMETER AdditionalParameters
    Any additonal parameter to pass to the lab. Will get added to configuration parameters.
     
    .EXAMPLE
    Start-Lab -Name Example
 
    .EXAMPLE
 
    Start-Lab -Name Example -AdditionalParameters @{ Car = 'Corvette'}
     
    #>

    [Cmdletbinding()]
    Param(
        [Parameter(Mandatory)]
        [String]
        $Name,

        [Parameter()]
        [Hashtable]
        $AdditionalParameters
    )

    end {

        $configuration = Get-LabConfiguration -Name $Name
        $parameters = $configuration['Parameters']

        if ($AdditionalParameters) {
            $AdditionalParameters.GetEnumerator() | ForEach-Object {
                $parameters[$_.Key] = $_.Value
            }
        }

        try {
            Write-Warning "Attempting to start lab: $Name"
            Import-Lab -Name $Name -ErrorAction Stop
            Get-LabVM | Start-LabVM
        }
        catch {
            Write-Warning "Lab $Name doesn't exist, creating and starting..."
            & $configuration['Definition'] @parameters
        }
    }
}
#EndRegion '.\public\Start-Lab.ps1' 56
#Region '.\public\Stop-Lab.ps1' -1

function Stop-Lab {
    <#
    .SYNOPSIS
    Stops a running lab
     
    .DESCRIPTION
    Long description
     
    .PARAMETER Name
    The lab to stop
     
    .EXAMPLE
    Stop-Lab -Name Example
 
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory)]
        [String]
        $Name
    )

    try {

        Import-Lab -Name $Name -ErrorAction Stop
        Get-LabVM | Stop-LabVM
    }
    catch {
        Write-Error -Message 'Lab was not found. Use Start-Lab to start or build first' -Exception ([System.IO.FileNotFoundException]::New())
    }
}
#EndRegion '.\public\Stop-Lab.ps1' 32
#Region '.\Suffix.ps1' -1

# Initialize here
#EndRegion '.\Suffix.ps1' 2