Whistler.psm1

Set-Variable -Name "DefaultCharSet" `
    -Value "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23465789#%&'()*+,-./[\]^_{}~" `
    -Option ReadOnly `
    -Visibility Private 
 
Function Convert-SecretToPassword() {
    <#
    .SYNOPSIS
    Given a Secret object, return just the secret's value.
    #>

    param([parameter(ValueFromPipeline=$true,Mandatory=$true)]$Secret,
        [securestring]$Key,
        [switch]$AsPlainText)

    Write-Debug "Attempting to convert secret's value to a password"
    If ($Secret) {
        If ($Key) {
            $SecureSecret = ConvertTo-SecureString -String $Secret.SecretValueText -SecureKey $Key
        } else {
            $SecureSecret = $Secret.SecretValue
        }
        If ($AsPlainText) {
            return ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
                [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR(
                    $SecureSecret
                )
            ))
        } else {
            return $SecureSecret
        }
    }
}

Function Get-TooManyPassword() {
<#
.SYNOPSIS
Get a password out of an Azure Key Vault
.DESCRIPTION
Given the name of a secret, look for the first Secret & version in the Key Vault, and return its Value
.PARAMETER Name
Name of the secret value in tye key vault
.PARAMETER Key
Key used to encrypt password stored in the vault if stored cryptically
.PARAMETER AsPlainText
If flagged, will return the clear text value of the Secret instead of the SecureString version
.EXAMPLE
PS> Get-TooManyPassword -Name "MyPWD"
Returns the value of the Secret MyPWD in the current Key Vault as a SecureString
.LINK
Get-TooManySecret
Get-AzKeyVaultSecret
#>
 
[CmdletBinding()]
[Alias("Get-Password","gpwd")]
param(
    [securestring]$Key,
    [switch]$AsPlainText)
    DynamicParam {
        $RuntimeParamDic  = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $StandardProps = @('Mandatory','ValueFromPipeline','ValueFromPipelineByPropertyName','ParameterSetName','Position')
        $Attrib = [ordered]@{
            'Name' = @{
                'AttribType' = [string]
                'Mandatory' = $true
                'ValueFromPipeline' = $true
                'ValueFromPipelineByPropertyName' = $true
                'ParameterSetName' = '__AllParameterSets'
                'Position' = 1
                'ValidSet' = (Get-TooManySecretList -UseVault)
            }
        }
        
        ForEach ($AttribName in $Attrib.Keys) {
            #[string]$AttribName = $Key.ToString()
            $ThisAttrib = New-Object System.Management.Automation.ParameterAttribute
            ForEach ($Prop in $StandardProps) {
                If ($null -ne $Attrib.$AttribName.$Prop) {
                    $ThisAttrib.$Prop = $Attrib.$AttribName.$Prop
                }
            }
            $ThisCollection = New-Object  System.Collections.ObjectModel.Collection[System.Attribute]
            $ThisCollection.Add($ThisAttrib)

            If ($Attrib.$AttribName.ValidSet) {
                $ThisValidation = New-Object  System.Management.Automation.ValidateSetAttribute($Attrib.$AttribName.ValidSet)
                $ThisCollection.Add($ThisValidation)
            }

            $ThisRuntimeParam  = New-Object System.Management.Automation.RuntimeDefinedParameter($AttribName,  $Attrib.$AttribName.AttribType, $ThisCollection)
            $RuntimeParamDic.Add($AttribName,  $ThisRuntimeParam)
        }

        return  $RuntimeParamDic
      
    }

Process {
    $Name = $PSBoundParameters.Name
    $Secret = Get-TooManySecret -Name $Name -ExcludeMetadata | Select-Object -First 1
    $Secret | Convert-SecretToPassword -AsPlainText:$AsPlainText -Key $Key 
}

}

Function Set-TooManyPassword() {
<#
.SYNOPSIS
Set the password to a new value
.DESCRIPTION
Using either a SecureString or plain text, add a new version to a Secret in an Azure Key Vault. If a secret does
not exist with this name, a new one is created. A SecureString or clear String value will be returned. The
type returned is determined by the AsPlainText switch; if selected, a clear String is returned; otherwise, a
SecureString object is returned
.PARAMETER Name
TODO: More about parameter needed
.PARAMETER Value
TODO: More about parameter needed
.PARAMETER SecureValue
TODO: More about parameter needed
.PARAMETER KeyVault
TODO: More about parameter needed
.PARAMETER Key
A 128-bit, 192-bit, or 256-bit key used to encrypted the value within the key vault. Must be in SecureString format.
.PARAMETER AsPlainText
If flagged, will return the clear text value of the Secret instead of the SecureString version
.PARAMETER DisablePrevious
TODO: More about parameter needed
.EXAMPLE
PS> Set-TooManyPassword -Name "MyPWD" -Value "MyNewPWD"
Adds a new version to the MyPWD secret as MyNewPWD and returns that MyNewPWD in a SecureString object
.OUTPUTS
SecureString
String
.LINK
Get-TooManySecret
Get-AzKeyVaultSecret
#>
 
[CmdletBinding(DefaultParameterSetname="PlainText")]
[Alias("Set-Password","spwd")]
param([parameter(Mandatory=$true)][ValidatePattern("^[0-9a-zA-Z-]+$")][string]$Name,
        [parameter(ParameterSetName="PlainText",Mandatory=$true)][string]$Value,
        [parameter(ParameterSetName="SecureString",Mandatory=$true)][SecureString]$SecureValue,
        [Microsoft.Azure.Commands.KeyVault.Models.PSKeyVaultIdentityItem]$KeyVault = (Get-TooManyKeyVault),
        [SecureString]$Key,
        [switch]$AsPlainText,
        [switch]$DisablePrevious
        )
    
    If ($DisablePrevious) {
        $OldSecret = Get-TooManySecret -Name $Name -ExcludeMetadata
        if ($OldSecret) {
            $OldSecret | Update-AzKeyVaultSecret -Enable:$false
        }
    }

    switch ($PSCmdlet.ParameterSetName) {
        "PlainText" { 
            $Secure = ConvertTo-SecureString -String $Value -AsPlainText -Force
            Set-TooManyPassword -Name $Name -SecureValue $Secure -AsPlainText:$AsPlainText -DisablePrevious:$DisablePrevious -KeyVault $KeyVault -Key $Key
         }
        Default {
            If ($Key) {
                #If a seed key for value is supplied, convert the secure string to an encrypted value using the string
                $Encrypted = ConvertFrom-SecureString -SecureString $SecureValue -SecureKey $key
                #Then convert that encrypted value back to a SecureString for storage in the vault
                $SecureEncrypted = ConvertTo-SecureString -String $Encrypted -AsPlainText -Force
                $SecureValue = $SecureEncrypted
            }
            $Secret = Set-AzKeyVaultSecret -VaultName $KeyVault.VaultName -SecretValue  $SecureValue -Name $Name -NotBefore (Get-Date)
            If ($script:TMSSecretList.Names -notcontains $Name) {
                $script:TMSSecretList.Names += $Name
            }
            $Secret | Convert-SecretToPassword -AsPlainText:$AsPlainText
        }
    }    
}

Function New-TooManyPassword() {
    [Alias("New-Password","newpwd")]
    param([parameter(ValueFromPipeline=$true,Mandatory=$true)][ValidatePattern("^[0-9a-zA-Z-]+$")][string]$Name,
        [switch]$ReturnPlainText,
        [switch]$DisablePrevious)

    Set-TooManyPassword -Name $Name -SecureValue (Get-RandomPassword) -DisablePrevious:$DisablePrevious -AsPlainText:$ReturnPlainText

}


Function Get-TooManySecret() {
    [CmdletBinding(DefaultParameterSetName="Filtered")]
    [Alias("Get-Secret")]
    param(
        [parameter(Position=2)]
          [Microsoft.Azure.Commands.KeyVault.Models.PSKeyVaultIdentityItem]$KeyVault = (Get-TooManyKeyVault), 
        [parameter(Position=1,ParameterSetName="Filtered",ValueFromPipelineByPropertyName=$True)]
          [string]$Filter='*',
        [parameter(Position=3,ParameterSetName="Named",ValueFromPipelineByPropertyName=$True)][string]$Version,
        [parameter(ParameterSetName="Filtered")][switch]$RegEx,
        [parameter(ParameterSetName="Filtered")][switch]$Like=(-not $RegEx),
        [switch]$ExcludeMetadata,
        [switch]$IncludeVersions
    )
    DynamicParam {
        $RuntimeParamDic  = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $StandardProps = @('Mandatory','ValueFromPipeline','ValueFromPipelineByPropertyName','ParameterSetName','Position')
        $Attrib = [ordered]@{
            'Name' = @{
                'AttribType' = [string]
                'Mandatory' = $true
                'ValueFromPipeline' = $true
                'ValueFromPipelineByPropertyName' = $true
                'ParameterSetName' = 'Named'
                'Position' = 1
                'ValidSet' = (Get-TooManySecretList -UseVault)
            }
        }
        
        ForEach ($AttribName in $Attrib.Keys) {
            #[string]$AttribName = $Key.ToString()
            $ThisAttrib = New-Object System.Management.Automation.ParameterAttribute
            ForEach ($Prop in $StandardProps) {
                If ($null -ne $Attrib.$AttribName.$Prop) {
                    $ThisAttrib.$Prop = $Attrib.$AttribName.$Prop
                }
            }
            $ThisCollection = New-Object  System.Collections.ObjectModel.Collection[System.Attribute]
            $ThisCollection.Add($ThisAttrib)

            If ($Attrib.$AttribName.ValidSet) {
                $ThisValidation = New-Object  System.Management.Automation.ValidateSetAttribute($Attrib.$AttribName.ValidSet)
                $ThisCollection.Add($ThisValidation)
            }

            $ThisRuntimeParam  = New-Object System.Management.Automation.RuntimeDefinedParameter($AttribName,  $Attrib.$AttribName.AttribType, $ThisCollection)
            $RuntimeParamDic.Add($AttribName,  $ThisRuntimeParam)
        }

        return  $RuntimeParamDic
      
    }
        
Begin {

    If ($RegEx -or $Like) {
        $Secrets = $KeyVault | Get-AzKeyVaultSecret
    }
}

Process {
    switch ($PSCmdlet.ParameterSetName) {

        'Filtered' {
            If ($RegEx) {
                $FilteredSecrets = $Secrets | Where-Object { $_.Name -match $Filter }
            } else {
                $FilteredSecrets = $Secrets | Where-Object { $_.Name -like $Filter }
            }
            ForEach ($Secret in $FilteredSecrets) {
                $DetailedSecret = $KeyVault | Get-AzKeyVaultSecret -Name $Secret.Name -IncludeVersions:$IncludeVersions
                If ($ExcludeMetadata) {
                    $DetailedSecret
                } elseif ($IncludeVersions) {
                    $DetailedSecret | Add-TooManyMeta -Force
                } else  {
                    #Add-TooManyMeta -Secret $DetailedSecret -Force
                    $DetailedSecret | Add-TooManyMeta -Force
                }
            }
        } 
        Default {
            $Name = $PSBoundParameters.Name

            If ($Version -eq "") {
                $Secret = $KeyVault | Get-AzKeyVaultSecret -Name $Name -IncludeVersions:$IncludeVersions
            } else {
                $Secret = $KeyVault | Get-AzKeyVaultSecret -Name $Name -Version $Version
            }

            If ($ExcludeMetadata) {
                $Secret 
            } elseif ($Secret) {
                if ($IncludeVersions) {
                    $Secret | Add-TooManyMeta -Force 
                } else {
                    $Secret | Add-TooManyMeta -Force
                }
            }
        }
    }
}
End {

}
}

Function Test-TooManySecret {
    Param(
        [parameter(Mandatory=$true)]
        [string]$Name
    )

    ((Get-TooManySecretList) -contains $name)
}

Function Set-TooManySecret() {
    [Alias('Set-Secret')]
    param(
        [parameter(ParameterSetName="ByObject",ValueFromPipeline=$true,Mandatory=$true,Position=1)][PSObject]$Secret,
        [parameter(ParameterSetName="ByName",Mandatory=$true,Position=1)][ValidatePattern("^[0-9a-zA-Z-]+$")][string]$Name,
        [string]$URL,
        [string]$Username,
        [string]$Server,
        [string]$Domain,
        [string]$FQDN,
        [string]$UPN,
        [SecureString]$SecureValue,
        [switch]$DisablePrevious,
        [switch]$PassThru,
        [switch]$ImportTags,
        [switch]$Force,
        [hashtable]$Property=@{}
    )
Process {
    $SetProperties = @{}
    ForEach ($Prop in $Property.Keys) { $SetProperties.$Prop = $Property.$Prop}

    ForEach ($ParamName in $PSCmdlet.MyInvocation.BoundParameters.Keys) {
        $SetProperties.$ParamName = $PSCmdlet.MyInvocation.BoundParameters.$ParamName
    }

    If ($Secret) {
        $Name = $Secret.Name
        $SetProperties.SecretID = $Secret.Id
        $MetaResult = Set-TooManyMeta -InputObject $Secret -Property $SetProperties -ImportTags:$ImportTags
    } else {
        If (Test-TooManySecret -Name $name) {
            $Secret = Get-TooManySecret -Name $Name
        } elseif ($Force) {
            $Secret = New-TooManySecret -Name $name -SecureValue $SecureValue -PassThru
        }

        If ($Secret) {
            $SetProperties.SecretID = $Secret.Id
            $MetaResult = Set-TooManyMeta -Name $Name -Property $SetProperties
        }
    }

    If ($Secret) {
        If ($SecureValue) {
            Set-TooManyPassword -SecureValue $SecureValue -Name $Name -DisablePrevious:$DisablePrevious | Out-Null
        }

        $SecretUpdate = $Secret | Update-TooManySecret

        If ($PassThru) {
            Get-TooManySecret -Name $Name
        }
    }
}

}

Function Update-TooManySecret() {
    [Alias("Update-Secret")]
    param([parameter(ValueFromPipeline=$true,Mandatory=$true)][Microsoft.Azure.Commands.KeyVault.Models.PSKeyVaultSecret]$Secret)

Process {
    $Secret | Update-AzKeyVaultSecret
}

}

Function Update-TooManySecretList {
    [CmdletBinding(DefaultParameterSetName='ViaMeta')]
    [Alias('Update-SecretList')]
    param(
        [Microsoft.Azure.Commands.KeyVault.Models.PSKeyVaultIdentityItem]$KeyVault = (Get-TooManyKeyVault),
        [parameter(ParameterSetName="ViaMeta")][switch]$IncludeMetadata,
        [parameter(ParameterSetName="ViaKeyVault",Mandatory=$true)][switch]$UseVault
    )
Process {
    If (-not $script:TMSSecretList) {
        $script:TMSSecretList = [PSCustomObject]@{
            Names = @()
            Meta = @()
            LastUpdateTime = (Get-Date)
        }
        $script:TMSSecretList | Add-Member ScriptProperty Age { ((Get-Date) - $this.LastUpdateTime).TotalSeconds }
    }
    switch ($PSCmdlet.ParameterSetName) {
        'ViaKeyVault' {
            If ($UseVault) {
                $script:TMSSecretList.Names = $KeyVault | Get-AzKeyVaultSecret | ForEach-Object { $_.Name }
                $script:TMSSecretList.Meta = @()
                $script:TMSSecretList.LastUpdateTime = (Get-Date)
            }
        }
        Default {
            $script:TMSSecretList.Meta = Get-TooManyMetaList -IncludeMetadata:$IncludeMetadata
            $script:TMSSecretList.Names = $script:TMSSecretList.Meta | ForEach-Object { $_.Name }
            $script:TMSSecretList.LastUpdateTime = (Get-Date)
        }
    }
}
}
Function Get-TooManySecretList() {
    [CmdletBinding(DefaultParameterSetName='ViaMeta')]
    [Alias("Get-SecretList")]
    param(
        [Microsoft.Azure.Commands.KeyVault.Models.PSKeyVaultIdentityItem]$KeyVault = (Get-TooManyKeyVault),
        [parameter(ParameterSetName="ViaMeta")][switch]$IncludeMetadata,
        [parameter(ParameterSetName="ViaKeyVault",Mandatory=$true)][switch]$UseVault,
        [int]$MaxAge = 3600
    )

Process {
    If (-not $Script:TMSSecretList -or ($Script:TMSSecretList.Age -ge $MaxAge)) {
        switch ($PSCmdlet.ParameterSetName) {
            'ViaKeyVault' { Update-TooManySecretList -KeyVault $KeyVault -UseVault:$UseVault }
            Default { Update-TooManySecretList -KeyVault $KeyVault -IncludeMetadata:$IncludeMetadata }
        }
    }
    If ($IncludeMetadata) {
        $Script:TMSSecretList.Meta
    } else {
        $Script:TMSSecretList.Names
    }
}

}

Function New-TooManySecret() {
    [CmdletBinding()]
    [Alias("New-Secret")]
    param(
        [parameter(Mandatory=$true)]
        [ValidatePattern("^[0-9a-zA-Z-]+$")]
        [string]$Name,
        [SecureString]$SecureValue,
        [SecureString]$Key,
        [switch]$PassThru
    )
Process {
    try {
        $ExistingSecret = Get-TooManySecret -Name $Name 
    } catch {

    }
    If ($ExistingSecret) {
        Write-Warning "Secret named [$Name] already exists."
    } else {
        If ($SecureValue) {
            Set-TooManyPassword -Name $Name -SecureValue $SecureValue -Key $Key | Out-Null
        } else {
            New-TooManyPassword -Name $Name | Out-Null
        }
        If ($PassThru) { Get-TooManySecret -Name $Name }
    }
}
}

Function Get-TooManySecretProperty() {

}

Function Set-TooManySecretyProperty() {

}

Function Get-RandomPassword() {
    param([char[]]$CharSet=$DefaultCharSet,
        [int]$MinLength=15,
        [int]$MaxLength=30,
        [switch]$AsPlainText,
        [switch]$AllANSI,
        [switch]$NoConsecutive)

    Write-Debug "Charset: $charset"
    If ($AllANSI) {
        $CharSet += 33..126 | ForEach-Object{ [char]$_ }
        $CharSet = $CharSet | Select-Object -Unique
    }

    If ($AsPlainText) {
        $Length = Get-Random -Minimum $MinLength -Maximum $MaxLength+1
        $LastChar = ""
        ( 1..$Length | ForEach-Object {
            do { 
                $ThisChar = Get-Random -InputObject $charSet -Count 1
            } until ((-not $NoConsecutive) -or ($thisChar -cne $lastChar))
            If ($NoConsecutive) { $lastChar = $thisChar }
            $thisChar
        } ) -join ""
        #(Get-Random -InputObject $charSet -Count (Get-Random -Minimum $MinLength -Maximum $MaxLength)) -join ""
    } else {
        ConvertTo-SecureString -String ((Get-Random -InputObject $charSet -Count (Get-Random -Minimum $MinLength -Maximum $MaxLength)) -join "") -AsPlainText -Force
    }

}

Function Convert-TooManyKey() {
<#
.SYNOPSIS
Convert a key to a secure string for encrypting values.
.DESCRIPTION
Changes a 128-bit, 192-bit, or 256-bit key into a secure string to be used for encrypting secret values in
the key vault. By default, all Secrets are stored securely with limited access in Azure. However, for those
people that require an added level to not be easily viewed in Azure, the secret can be stored with an Encrpted
value within the Secret in Azure
.PARAMETER StringKey
An string of either 16, 24, or 32 ASCII characters long
.PARAMETER ByteKey
An array of bytes either 16, 24, or 32 in length
.PARAMETER CharKey
An array of ASCII characters either 16, 24, or 32 in length
.PARAMETER SecureKey
A SecureString holding a string of 16, 24, or 32 ASCII characters long
.EXAMPLE
PS> Convert-TooManyKey -StringKey "h|nD#iCDp8\}(Jqw"
Converts the given string to a secure string to be used for a key
.EXAMPLE
PS> Convert-TooManyKey -ByteKey @(104, 124, 110, 68, 35, 105, 67, 68, 112, 56, 92, 125, 40, 74, 113, 119)
Converts the given byte array to a secure string to be used for a key
.OUTPUTS
SecureString
String
.LINK
Get-TooManyPassword
Set-TooManyPassword
#>
 
    [CmdletBinding(DefaultParameterSetname="BySecureString")]
    [Alias('Convert-Key')]
    Param(
        [parameter(ParameterSetName="ByString",Mandatory=$true,Position=1)][string]$StringKey,
        [parameter(ParameterSetName="ByBytes",Mandatory=$true,Position=1)][byte[]]$ByteKey,
        [parameter(ParameterSetName="ByChars",Mandatory=$true,Position=1)][char[]]$CharKey,
        [parameter(ParameterSetName="BySecureString",Mandatory=$true,Position=1)][securestring]$SecureKey
    )
    $ValidKeyLengths = @(16,24,32)

    Switch ($PSCmdlet.ParameterSetName) {
        "ByBytes" {
            Convert-TooManyKey -StringKey ([char[]]$ByteKey -join "")
        }
        "ByString" {
            If ($ValidKeyLengths -contains $StringKey.Length) {
                $SecureKey = ConvertTo-SecureString -String $StringKey -AsPlainText -Force
            } else {
                Write-Error ("Key length invalid. Must be in one of these lengths: {0}-bits" -f (($ValidKeyLengths | ForEach-Object{ $_ * 8 }) -join "-bits, "))
            }
        }
        "ByChars" {
            Convert-TooManyKey -StringKey ($CharKey -join "")
        }
        Default {}
    }
    Return $SecureKey
}