safeguard-discovery.psm1

# Helper
function Test-SafeguardSession
{
    [CmdletBinding()]
    param (
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

    if (-not (Get-Module safeguard-ps)) { Import-Module safeguard-ps }
    if (Get-Module safeguard-ps)
    {
        if ($SafeguardSession)
        {
            $true
        }
        else
        {
            Write-Verbose "safeguard-ps is installed, but it is not connected to Safeguard"
            $false
        }
    }
    else
    {
        Write-Verbose "safeguard-ps is not installed"
        $false
    }
}


<#
.SYNOPSIS
Get login credentials from Safeguard or locally if not available.
 
.DESCRIPTION
If connected to Safeguard (using Connect-Safeguard) this cmdlet will look for an open access request that
matches the provided network address. If found it will check-out the password and use that for login
credentials. If not found then it will prompt the user for the credentials at the console.
 
.PARAMETER NetworkAddress
Name or network address of an existing asset in Safeguard for which there is an open access request.
 
.PARAMETER AccountName
Name of the account to match against open access requests in case there are more than one for the asset.
 
.INPUTS
None.
 
.OUTPUTS
System.Management.Automation.PSCredential.
 
.EXAMPLE
Get-SgDiscConnectionCredential Hyperv.test.env
 
#>

function Get-SgDiscConnectionCredential
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$false)]
        [string]$NetworkAddress,
        [Parameter(Mandatory=$false)]
        [string]$AccountName
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }
    
    $local:Credential = $null
    if (Test-SafeguardSession)
    {
        if (-Not $NetworkAddress)
        {
            $NetworkAddress = (Read-Host "NetworkAddress")
        }

        $local:Requests = Get-SafeguardMyRequest
        if ($AccountName)
        {
            $local:Requests = $local:Requests | Where-Object {
                ($_.AssetNetworkAddress -eq $NetworkAddress -or $_.AssetName -eq $NetworkAddress) -and $_.AccountName -eq $AccountName }
        }
        else
        {
            $local:Requests = $local:Requests | Where-Object {
                $_.AssetNetworkAddress -eq $NetworkAddress -or $_.AssetName -eq $NetworkAddress }
        }
        
        $local:count = $local:Requests | Measure-Object
        if ($local:count.Count -lt 1)
        {
            Write-Verbose "Unable to find an open access request with name or network address equal to '$NetworkAddress'"
            if ($AccountName) { Write-Verbose "Where account name also equals '$AccountName" }
        }
        elseif ($local:count.Count -gt 1)
        {
            Write-Verbose "Found $($local:count.Count) open access requests with name or network address equal to '$NetworkAddress'"
            if ($AccountName) { Write-Verbose "Where account name also equals '$AccountName" }
        }
        else
        {
            if ($local:Requests[0].State -ne "RequestAvailable" -and $local:Requests[0].State -ne "PasswordCheckedOut")
            {
                Write-Verbose "Access request state is '$($local:Requests[0].State)', not 'RequestAvailable' or 'PasswordCheckedOut'"
            }
            else
            {
                $local:Credential = (New-Object PSCredential -ArgumentList $local:Requests[0].AccountName,(ConvertTo-SecureString -AsPlainText -Force `
                                        (Get-SafeguardAccessRequestPassword $local:Requests[0].Id)))
            }
        }
    }
    else
    {
        Write-Verbose "No safeguard-ps connection, cannot use it for credentials"
    }

    if (-not $local:Credential)
    {
        Write-Host "Credentials for ${NetworkAddress}"
        if (-not $AccountName)
        {
            $AccountName = (Read-Host "AccountName")
        }
        $local:Password = (Read-Host "Password" -AsSecureString)
        $local:Credential = (New-Object PSCredential -ArgumentList $AccountName,$local:Password)
    }

    $local:Credential
}

<#
.SYNOPSIS
Import discovered accounts to an asset in Safeguard.
 
.DESCRIPTION
This cmdlet may be used to add accounts to an asset in Safeguard that were discovered using other cmdlets in this module.
 
You must be connected to Safeguard using the Connect-Safeguard cmdlet before using this function.
 
.PARAMETER NetworkAddress
Network address or name of an existing asset in Safeguard to add accounts to
 
.PARAMETER DiscoveredAccounts
Array of discovered accounts containing an AccountName and a Description to be imported to Safeguard.
 
.INPUTS
None.
 
.OUTPUTS
System.Management.Automation.PSObject.
 
.EXAMPLE
Get-SgDiscOracleAccount | Import-SgDiscDiscoveredAccount oracle.test.env
 
.EXAMPLE
Import-SgDiscDiscoveredAccount oracle.test.env $DiscoveredAccounts
#>

function Import-SgDiscDiscoveredAccount
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true,Position=0)]
        [string]$NetworkAddress,
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [PSObject[]]$DiscoveredAccounts
    )

    begin {
        if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
        if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

        if (Test-SafeguardSession)
        {
            $local:Assets = @(Get-SafeguardAsset $NetworkAddress -Fields AssetPartitionName,Id,Name,NetworkAddress)
            if ($local:Assets.Count -lt 1)
            {
                throw "Unable to find an asset matching '$NetworkAddress'"
            }
            elseif ($local:Assets.Count -gt 1)
            {
                throw "Found $($local:Assets.Count) assets matching '$NetworkAddress"
            }
        }
        else
        {
            throw "You must connect to Safeguard using safeguard-ps to use this cmdlet, run Connect-Safeguard"
        }
    }
    process {
        $DiscoveredAccounts | ForEach-Object {
            if (-not $_.AccountName)
            {
                Write-Host -ForegroundColor Yellow ($_ | Out-String)
                throw "Discovered account has no AccountName field"
            }
            try
            {
                Write-Verbose "Checking for existence of '$($_.AccountName)' on '$NetworkAddress'"
                $local:Account = (Get-SafeguardAssetAccount $NetworkAddress $_.AccountName)
            }
            catch {}
            if ($local:Account)
            {
                Write-Host -ForegroundColor Green "Discovered account '$($_.AccountName)' already exists"
            }
            else
            {
                if ($_.Description)
                {
                    $local:Description = $_.Description
                }
                else
                {
                    $local:Description = "safeguard-discovery -- no additional information"
                }
                if ($_.DomainName)
                {
                    $local:Account = (New-SafeguardAssetAccount $local:Assets[0].Id -NewAccountName $_.AccountName -DomainName $_.DomainName `
                                        -Description $local:Description)
                }
                elseif ($_.DistinguishedName)
                {
                    $local:Account = (New-SafeguardAssetAccount $local:Assets[0].Id -NewAccountName $_.AccountName -DistinguishedName $_.DistinguishedName `
                                        -Description $local:Description)
                }
                else
                {
                    $local:Account = (New-SafeguardAssetAccount $local:Assets[0].Id -NewAccountName $_.AccountName `
                                        -Description $local:Description)
                }
                New-Object PSObject -Property ([ordered]@{
                    AssetId = $local:Account.AssetId;
                    AssetName = $local:Account.AssetName;
                    Id = $local:Account.Id
                    Name = $local:Account.Name;
                    DomainName = $local:Account.DomainName;
                    DistinguishedName = $local:Account.DistinguishedName;
                    PlatformDisplayName = $local:Account.PlatformDisplayName;
                })
            }
            $local:Account = $null
        }
    }
    end {}
}


<#
.SYNOPSIS
Import discovered assets to an asset partition in Safeguard.
 
.DESCRIPTION
This cmdlet may be used to add assets to an asset partition in Safeguard that were discovered using other cmdlets in this module.
 
You must be connected to Safeguard using the Connect-Safeguard cmdlet before using this function.
 
.PARAMETER AssetPartition
Name of an existing asset partition in Safeguard
 
.PARAMETER DiscoveredAssets
Array of discovered accounts containing an AccountName and a Description to be imported to Safeguard.
 
.INPUTS
None.
 
.OUTPUTS
System.Management.Automation.PSObject.
 
.EXAMPLE
Get-SgDiscHypervAsset Hyperv.test.env | Import-SgDiscDiscoveredAsset Macrocosm
 
.EXAMPLE
Import-SgDiscDiscoveredAsset Macrocosm $DiscoveredAssets
#>

function Import-SgDiscDiscoveredAsset
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true,Position=0)]
        [string]$AssetPartition,
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [PSObject[]]$DiscoveredAssets
    )

    begin {
        if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
        if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

        if (Test-SafeguardSession)
        {
            $local:AssetPartitions = @(Get-SafeguardAssetPartition $AssetPartition)
            if ($local:AssetPartitions.Count -lt 1)
            {
                throw "Unable to find an asset partition matching '$AssetPartition'"
            }
            elseif ($local:AssetPartitions.Count -gt 1)
            {
                throw "Found $($local:AssetPartitions.Count) assets matching '$AssetPartition"
            }
        }
        else
        {
            throw "You must connect to Safeguard using safeguard-ps to use this cmdlet, run Connect-Safeguard"
        }
    }
    process {
        $DiscoveredAssets | ForEach-Object {

            if (-not $_.AssetName)
            {
                Write-Host -ForegroundColor Yellow ($_ | Out-String)
                throw "Discovered asset has no AssetName field"
            }
            try
            {
                $local:AssetName = $_.AssetName.Replace("`"","")
                Write-Verbose "Checking for existence of '$local:AssetName'"
                $local:Asset = (Get-SafeguardAsset $local:AssetName)
            }
            catch {}
            if ($local:Asset)
            {
                Write-Host -ForegroundColor Green "Discovered asset '$local:AssetName' already exists"
            }
            else
            {
                if ($_.Description)
                {
                    $local:Description = $_.Description
                }
                else
                {
                    $local:Description = "safeguard-discovery -- no additional information"
                }

                if ($_.IpAddress)
                {
                    $count = $_.IpAddress | Measure-Object

                    if ($count.Count -eq 1)
                    {
                        $local:NetworkAddress = $_.IpAddress
                    }
                    elseif ($count.Count -gt 1)
                    {
                
                    }
                }
                else
                {
                    $local:NetworkAddress = $local:AssetName
                }

                if (-not $_.OperatingSystem)
                {
                    $local:Platform = 500 # Other
                }
                elseif ($_.OperatingSystem -like "*Windows*")
                {
                    $local:Platform = 548 # Windows
                }
                elseif ($_.OperatingSystem -like "*Mac*")
                {
                    $local:Platform = 525 # Mac
                }
                else
                {
                    $local:Platform = 521 # Linux
                }

                $CredentialType = "None" # No service account
                
                try
                {
                    $local:Asset = (New-SafeguardAsset -AssetPartitionId $local:AssetPartitions[0].Id -DisplayName $local:AssetName -NetworkAddress $local:NetworkAddress -Description $local:Description -Platform $local:Platform -ServiceAccountCredentialType $CredentialType -NoSshHostKeyDiscovery)
                    New-Object PSObject -Property ([ordered]@{
                        Id = $local:Asset.Id
                        Name = $local:Asset.Name;
                        Description = $local:Asset.Description;
                        NetworkAddress = $local:Asset.NetworkAddress;
                        AssetPartitionId = $local:Asset.AssetPartitionId;
                        AssetPartitionName = $local:Asset.AssetPartitionName;
                        PlatformId = $local:Asset.PlatformId;
                        PlatformDisplayName = $local:Asset.PlatformDisplayName;
                    })
                } 
                catch 
                {
                    throw "Failed to add discovered asset '$local:AssetName' to Safeguard. Reason: $_.Message"
                }
            }
            $local:Asset = $null
        }
    }
    end {}
}