lib/Authentication.ps1
## Define a TMSessions Variable to store connection details in New-Variable -Scope global -Name 'TMSessions' -Value @{ } -Force function New-TMSession { <# .SYNOPSIS Creates a connection to a TransitionManager instance .DESCRIPTION This function creates a connection to the specified TransitionManager instance using the provided credentials. This session can be used when calling other functions within the TransitionManager module .PARAMETER SessionName The name that will be used when referring to the created TMSession .PARAMETER Server The URI of the TransitionManager instance to connect to .PARAMETER Credential The credentials to be used twhen connecting to TransitionManager .PARAMETER Project The name of the project on the TransitionManager instance to enter into .PARAMETER TMVersion Used to override the API version used by other functions in the TransitionManager module .PARAMETER AllowInsecureSSL Boolean indicating whether or not an insecure SSL connection is allowed .PARAMETER Api Boolean indicating wheter or not to also login to the TransitionManager API where possible .PARAMETER Passthru Switch indicating that the newly created TMSession should be output .PARAMETER ProfileName The name of the stored TMProfile which contains the connection settings .EXAMPLE $Session = @{ SessionName = 'TMDDEV' Server = 'tmddev.transitionmanager.net' Credential = (Get-StoredCredential -Name 'ME') Project = 'RD - VMware HCX' } New-TMSession @Session .EXAMPLE Get-TMProfile -Name 'TMDDEV' | New-TMSession .EXAMPLE New-TMSession -ProfileName 'TMQA09' .OUTPUTS None #> [CmdletBinding()] [Alias('Connect-TMServer')] param( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Alias('Name')] [String]$SessionName = 'Default', [Parameter(Mandatory = $true, ParameterSetName = 'ByProperty', ValueFromPipelineByPropertyName = $true)] [String]$Server, [Parameter(Mandatory = $true, ParameterSetName = 'ByProperty', ValueFromPipelineByPropertyName = $true)] [PSCredential]$Credential, [Parameter(Mandatory = $false, Position = 1, ValueFromPipelineByPropertyName = $true)] [Alias('Project')] [String]$ProjectName, [Parameter(Mandatory = $false, ParameterSetName = 'ByProperty', ValueFromPipelineByPropertyName = $true)] [String]$TMVersion, [Parameter(Mandatory = $false, ParameterSetName = 'ByProperty', ValueFromPipelineByPropertyName = $true)] [Bool]$AllowInsecureSSL = $false, [Parameter(Mandatory = $false, ParameterSetName = 'ByProperty', ValueFromPipelineByPropertyName = $true)] [Bool]$Api = $true, [Parameter(Mandatory = $false)] [Switch]$Passthru, [Parameter(Mandatory = $true, ParameterSetName = 'ByProfileObject')] [TMProfile]$TMProfile, [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'ByProfile')] [ArgumentCompleter( { Get-TMProfile -List })] [Alias('Profile')] [String]$ProfileName ) begin { ## Create the TMServers Array that will be reachable at $global:TMSessions if (-not $global:TMSessions) { New-Variable -Name TMSessions -Scope Global -Value @{} } } process { if ($ProfileName) { $TMProfile = Get-TMProfile -Name $ProfileName if (!$TMProfile) { Write-Error "Could not load a TransitionManager profile named '$ProfileName'" return } } if ($TMProfile) { $SessionName = (($global:TMSessions.Keys -contains 'Default') -and ($SessionName -eq 'Default')) ? $TMProfile.Name : 'Default' $Server = $TMProfile.Server $Credential = $TMProfile.Credential $AllowInsecureSSL = $TMProfile.AllowInsecureSSL if ([String]::IsNullOrWhiteSpace($Project)) { $Project = $TMProfile.Project } $Api = $TMProfile.Api } else { if ($SessionName -eq 'Default'){ $SessionName = (($global:TMSessions.Keys -contains 'Default') -and ($SessionName -eq 'Default')) ? $Server : 'Default' } } ## Check for Existing Session to this server if ($global:TMSessions.Keys -contains $SessionName) { $NewTMSession = $global:TMSessions[$SessionName] } else { ## Create a session object for this new connection $NewTMSession = [TMSession]::new($SessionName, $Server, $AllowInsecureSSL) } ## Honor SSL Settings from the user $TMCommandSwitches = @{ AllowInsecureSSL = $AllowInsecureSSL } $TMCertSettings = @{ SkipCertificateCheck = $AllowInsecureSSL } ## Trim the server name $Instance = $Server.Replace('/tdstm', '').Replace('https://', '').Replace('http://', '') ## Add this shortened Instance name (Just the hostname) into the Session Object $NewTMSession.TMServer = $Instance # Get the TM Version (Allow a different api usage of an alternate version) $NewTMSession.TMVersion = [String]::IsNullOrWhiteSpace($TMVersion) ? (Get-TMVersion -Server $Server @TMCommandSwitches) : $TMVersion if ( $NewTMSession.TMVersion -notlike '4.7*' -and $NewTMSession.TMVersion -notlike '5.*' -and $NewTMSession.TMVersion -notlike '6.*' ) { Write-Error "Unable to Log into $Instance. Version $($NewTMSession.TMVersion) not supported" return } ## Prepare Request Headers for use in the Session Header Cache $ContentType = 'application/json;charset=UTF-8' $RequestHeaders = @{ "Accept-Version" = "1.0"; "Content-Type" = $ContentType; "Accept" = "application/json"; "Cache-Control" = "no-cache"; } # Format the uri $uri = "https://$Instance/tdstm/auth/signIn" $PostBody = @{ username = $Credential.UserName password = $Credential.GetNetworkCredential().Password } | ConvertTo-Json $WebRequestSplat = @{ Method = 'POST' Uri = $uri Headers = $RequestHeaders Body = $PostBody SessionVariable = 'TMWebSession' PreserveAuthorizationOnRedirect = $true ContentType = $ContentType } ## Attempt Login if ($VerbosePreference -eq 'Continue') { Write-Host "Logging into TransitionManager instance [ " -NoNewline Write-Host $Instance -ForegroundColor Cyan -NoNewline Write-Host " ]" } # Make the request try { Write-Verbose "Web Request Parameters:" Write-Verbose ($WebRequestSplat | ConvertTo-Json -Depth 10) Write-Verbose "Invoking web request" $Response = Invoke-WebRequest @WebRequestSplat @TMCertSettings Write-Verbose "Response status code: $($Response.StatusCode)" Write-Verbose "Response Content: $($Response.Content)" } catch { throw $_ } ## Check the Response code for 200 if ($Response.StatusCode -eq 200) { $ResponseContent = ($Response.Content | ConvertFrom-Json -Depth 100 ) ## Ensure the login succeeded. if ($ResponseContent.PSObject.Properties.name -eq 'error' ) { ## Report Error Condition Write-Error "Login Failed: $($ResponseContent[0].'error')" return } ## Login Succeeded if ($ResponseContent.PSObject.Properties.name -notcontains 'userContext') { Write-Error "Login Failure! Unable to Log into $Server. Check the URL and Credentials and try again" return } } else { Write-Error "Login Failure! Unable to Log into $Server. Check the URL and Credentials and try again" return } if ($Api) { if (($NewTMSession.TMVersion -like '5.*') -or ($NewTMSession.TMVersion -like '6.*')) { $WebRequestSplat.Uri = "https://$Server/tdstm/api/login" $WebRequestSplat.SessionVariable = 'TMRestSession' # Make the request try { Write-Verbose "Web Request Parameters:" Write-Verbose ($WebRequestSplat | ConvertTo-Json -Depth 10) Write-Verbose "Invoking web request" $Response = Invoke-RestMethod @WebRequestSplat Write-Verbose "Response status code: $($Response.StatusCode)" Write-Verbose "Response Content: $($Response.Content)" } catch { throw $_ } if ($Response.access_token) { $TMRestSession.Headers.Add('Authorization', "Bearer $($Response.access_token)") } else { Write-Error "Could not login to the TransitionManager API" } } else { Write-Verbose "TransitionManager version '$($NewTMSession.TMVersion)' does not support the REST API" } } $NewTMSession.PublicKey = Get-TMPublicKey -Server $Instance ## Create the TMSessionObject that will be checked/used by the rest of the TransitionManager Modules $NewTMSession.TMWebSession = $TMWebSession $NewTMSession.TMRestSession = $TMRestSession $NewTMSession.UserContext = $ResponseContent.userContext ## Version 5 adds a CSRF token to the login. This must be added to the WebSession Headers for future requests if ($ResponseContent.csrf) { $NewTMSession.TMWebSession.Headers.($ResponseContent.csrf.tokenHeaderName) = $ResponseContent.csrf.token } ## Add this Session to the TMSessions list $global:TMSessions[$SessionName] = $NewTMSession ## Check that the correct projet is selected if ([String]::IsNullOrWhiteSpace($ProjectName)) { if ($VerbosePreference -eq 'Continue') { Write-Host "Login Successful! Entering Last Project used: [" -NoNewline Write-Host $NewTMSession.UserContext.project.name -ForegroundColor Cyan -NoNewline Write-Host "]" } } elseif ($NewTMSession.UserContext.project.name -ne $ProjectName) { Enter-TMProject -TMSession $SessionName -ProjectName $ProjectName } ## Return the session if requested if ($Passthru) { $NewTMSession } } } function Get-TMVersion { <# .SYNOPSIS Gets the version of a given TransitionManager instance .DESCRIPTION This function queries the given TransitionManager server for it's version number and returns it in the specified format .PARAMETER Server The server to be checked .PARAMETER Format The format of the version string that is returned .PARAMETER AllowInsecureSSL Boolean indicating whether or not an insecure SSL connection is allowed .EXAMPLE Get-TMVersion -Server 'tmddev.transitionmanager.net' .OUTPUTS A string containing the server's version in the specified format #> [CmdletBinding()] [OutputType([String])] param( [Parameter(Mandatory = $true)] [String]$Server, [Parameter(Mandatory = $false)] [ValidateSet('VersionSemVer', 'SemVer', 'Minor')] [String]$Format = "SemVer", [Parameter(Mandatory = $false)] [Bool]$AllowInsecureSSL = $false ) ## Select the Version formatter $regex = switch ($Format) { "VersionSemVer" { "/Version\ [0-9].[0-9].[0-9]/" } "SemVer" { "[0-9].[0-9].[0-9]" } "Minor" { "[0-9].[0-9]" } Default { "((.|\n)*)" } } #Honor SSL Settings $TMCertSettings = @{SkipCertificateCheck = $AllowInsecureSSL } $Instance = $Server.Replace('/tdstm', '').Replace('https://', '').Replace('http://', '') # Check for a version 4.7, 5.0, 5.1 $uri = "https://$Instance/tdstm/auth/loginInfo" $Response = Invoke-WebRequest -Method Get -Uri $uri @TMCertSettings if ($Response.StatusCode -eq 200) { #Removes 'Beta', 'Dev' and commit details when getting the server version on a beta server. $BuildVersion = (((($Response.Content | ConvertFrom-Json).data.buildVersion) -replace 'Version', '') -replace "\([^\)]+\)","") -replace '-beta', '' $TMVersion = ($BuildVersion -replace 'Version ', '' -split '-')[0].Trim() $Version = [System.version]::new($TMVersion) if($Version){ $Version.ToString() } else { throw 'Unable to determine TM Server Version' } } else { # Check for 4.6 $uri = "https://$Instance/tdstm/auth/login" try { $Response = Invoke-WebRequest -Method Get -Uri $uri @TMCertSettings if ($Response.StatusCode -eq 200) { $BuildNumbers = $Response.Content | Select-String -Pattern "Version\ [0-9]\.[0-9]\.[0-9]" if ($BuildNumbers.Matches.Count -gt 0) { $BuildNumbers.Matches | Select-String -Pattern $regex | ForEach-Object { $_.Matches } | Select-Object -First 1 } } } catch { throw "Could not get version" } } } function Get-TMPublicKey { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String]$Server ) $Server = $Server.Replace('/tdstm', '').Replace('https://', '').Replace('http://', '') # Check for a version 4.7, 5.0, 5.1 $Uri = "https://$Server/tdstm/auth/loginInfo?secure=true" try { $Response = Invoke-WebRequest -Method Get -Uri $Uri if ($Response.StatusCode -in 200, 204) { ($Response.Content | ConvertFrom-Json).data.publicKey } else { throw "The response status code does not indicate success" } } catch { throw "Could not get public key information: $($_.Exception.Message)" } } function Get-TMSession { <# .SYNOPSIS Gets a TMSession by Name, Server or Version .PARAMETER Name One or more TMSession names to get .PARAMETER Server One or more TM servers for which a TMSession has been created .PARAMETER Version One or more TM server versions for which a TMSession has been created .EXAMPLE Get-TMSession -Name 'Default', 'TMDDEV' .EXAMPLE Get-TMSession -Version '5.0.*' .EXAMPLE Get-TMSession -Server '*.transitionmanager.net' .OUTPUTS A hashtable with the TMSession details #> [CmdletBinding(DefaultParameterSetName = 'ByName')] param( [Parameter(Mandatory = $false, Position = 0, ParameterSetName = 'ByName', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('SessionName')] [String[]]$Name, [Parameter(Mandatory = $false, ParameterSetName = 'ByServer', ValueFromPipelineByPropertyName = $true)] [Alias('TMServer')] [String[]]$Server, [Parameter(Mandatory = $false, ParameterSetName = 'ByVersion', ValueFromPipelineByPropertyName = $true)] [Alias('TMVersion')] [String[]]$Version ) begin { $Keys = @($Global:TMSessions.Keys) } process { switch ($PSCmdlet.ParameterSetName) { 'ByName' { if ($Name) { $Name | ForEach-Object { foreach ($Key in $Keys) { if ($Key -match $_) { $Global:TMSessions[$Key] } } } } else { foreach ($Key in $Keys) { $Global:TMSessions[$Key] } } } 'ByServer' { $Server | ForEach-Object { foreach ($Key in $Keys) { if ($Global:TMSessions[$Key].TMServer -match $_) { $Global:TMSessions[$Key] } } } } 'ByVersion' { $Version | ForEach-Object { foreach ($Key in $Keys) { if ($Global:TMSessions[$Key].TMVersion.Value -match $_) { $Global:TMSessions[$Key] } } } } default { $Global:TMSessions } } } } function Remove-TMSession { <# .SYNOPSIS Disconnects one or more sessions with a TransitionManager instance .DESCRIPTION This function signs out of one or more TransitionManager instances and removes the session from the $Global:TMSessions variable .PARAMETER Name The name of one or more TMSessions to disconnect and remove .PARAMETER InputObject One or more TMSessions to disconnect and remove .EXAMPLE Remove-TMSession -Name 'Default', 'TMDDEV' .EXAMPLE Get-TMSession | Remove-TMSession .OUTPUTS None #> [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'ByName')] [Alias('SessionName')] [String[]]$Name, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByObject')] [Object[]]$InputObject ) process { Write-Verbose "Parameter Set: $($PSCmdlet.ParameterSetName)" # Compile a list of the sessions that need to be disconnected $SessionsToRemove = @() switch ($PSCmdlet.ParameterSetName) { 'ByName' { $Name | ForEach-Object { $SessionsToRemove += $Global:TMSessions[$_] } } 'ByObject' { $SessionsToRemove = $InputObject } } # Iterate over each session that was passed and sign out of TM foreach ($Session in $SessionsToRemove) { Write-Host "Logging out of TransitionManager instance [ " -NoNewline Write-Host $Session.TMServer -ForegroundColor Cyan -NoNewline Write-Host " ]" # Format the parameters for the sign out request $WebRequestSplat = @{ Method = 'POST' Uri = "https://$($Session.TMServer)/tdstm/auth/signOut" WebSession = $Session.TMWebSession ContentType = 'application/json;charset=UTF-8' PreserveAuthorizationOnRedirect = $true SkipCertificateCheck = $Session.AllowInsecureSSL } try { # Make the request to sign out $Response = Invoke-WebRequest @WebRequestSplat if ($Response.StatusCode -eq 200) { # Ensure the sign out was successful $ResponseContent = $Response.Content | ConvertFrom-Json if ($ResponseContent.status -ne 'success') { Write-Error ($ResponseContent.errors -join '; ') } Write-Host "Log out successful!" # Remove the session from the global sessions variable $Global:TMSessions.Remove($Session.Name) } else { Write-Error "Could not sign out of TransitionManager" } } catch { Write-Error $_ } } } } function New-TMProfile { <# .SYNOPSIS Creates a TransitionManager connection profile on the local hard drive .DESCRIPTION This function creates a TransitionManager connection profile on the localk hard drive that can be used with the New-TMSession function to more easily create connections to TransitionManager instances .PARAMETER Name The name name of the profile that will be saved. This name will be used to reference the TMProfile in other functions. .PARAMETER Server The URI of the TransitionManager instance .PARAMETER Project The name of the project on the TransitionManager instance .PARAMETER Credential The credentials used to connect to TransitionManager .PARAMETER AllowInsecureSSL Boolean indicating whether or not an insecure SSL connection is allowed .PARAMETER Passthru Switch indicating that the newly created TMProfile should be output .EXAMPLE New-TMProfile -Name 'TMDDEV-RVTools' -Server 'tmddev.transitionmanager.net' -Project 'RD - RVTools' -Credential (Get-Credential) .EXAMPLE $Profile = @{ Name = 'TMDDEV2' Server = 'tmddev2.transitionmanager.net' Credential = (Get-StoredCredential -Name 'ME') } New-TMProfile @Profile -Passthru | New-TMSession .OUTPUTS If Passthru switch is used, a TMProfile object. Otherwise, none #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String]$Name, [Parameter(Mandatory = $true)] [String]$Server, [Parameter(Mandatory = $false)] [String]$Project, [Parameter(Mandatory = $true)] [PSCredential]$Credential, [Parameter(Mandatory = $false)] [Bool]$AllowInsecureSSL = $false, [Parameter(Mandatory = $false)] [Bool]$Api = $true, [Parameter(Mandatory = $false)] [Switch]$Passthru = $false ) process { $ProfileDirectory = Join-Path $HOME 'TMD_Files' 'Profiles' Test-FolderPath -FolderPath $ProfileDirectory $TMProfile = [TMProfile]::new($Name, $Server, $Project, $Credential, $AllowInsecureSSL, $Api) $TMProfile | Export-Clixml -Path (Join-Path $ProfileDirectory "$Name.tmprofile") if ($Passthru) { $TMProfile } } } function Get-TMProfile { <# .SYNOPSIS Gets a TMProfile that is stored on the local hard drive .DESCRIPTION Gets one or more of the TransitionManager profiles that are saved on the local hard drive .PARAMETER Name The name of the TMProfile to get .PARAMETER List Switch indicating that a list of all TMProfile names should be returned .EXAMPLE $AllProfiles = Get-TMProfile .EXAMPLE Get-TMProfile -Name TMDDEV2 .EXAMPLE Get-TMProfile -List .OUTPUTS One or more objects representing a saved TMProfile #> [CmdletBinding(DefaultParameterSetName = "Single")] [OutputType([TMProfile])] param( [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "Single")] [ArgumentCompleter( { Get-TMProfile -List })] [String]$Name, [Parameter(Mandatory = $true, ParameterSetName = "List")] [Switch]$List ) process { try { if (-not $Name -or $List) { $ProfileDirectory = Join-Path $HOME 'TMD_Files' 'Profiles' $ProfileFiles = Get-ChildItem -Path $ProfileDirectory -Filter '*.tmprofile' -File $ProfileFiles | ForEach-Object { if ($List) { $_.Name -replace $_.Extension, '' } else { $TMProfile = Import-Clixml -Path $_.FullName [TMProfile]::new( $TMProfile.Name, $TMProfile.Server, $TMProfile.Project, $TMProfile.Credential, $TMProfile.AllowInsecureSSL, $TMProfile.Api ) } } } else { $ProfileFilePath = Join-Path $HOME 'TMD_Files' 'Profiles' ($Name + '.tmprofile') if (Test-Path -Path $ProfileFilePath) { $TMProfile = Import-Clixml -Path $ProfileFilePath [TMProfile]::new( $TMProfile.Name, $TMProfile.Server, $TMProfile.Project, $TMProfile.Credential, $TMProfile.AllowInsecureSSL, $TMProfile.Api ) } } } catch { $PSCmdlet.WriteError($_) } } } function Remove-TMProfile { <# .SYNOPSIS Removes a previously stored TMProfile from the local hard drive .DESCRIPTION Overwrites a stored .tmprofile file multiple times with a random bytestream before deleting it from the local hard drive .PARAMETER Name The name of the TMProfile to be removed .PARAMETER OverwriteCount The number of times to overwrite the file before removing it .EXAMPLE Remove-TMProfile -Name 'TMDDEV2' .EXAMPLE Get-TMProfile | Remove-TMProfile .NOTES General notes #> [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ArgumentCompleter( { Get-TMProfile -List })] [String]$Name, [Parameter(Mandatory = $false)] [Int]$OverwriteCount = 500 ) process { $ProfileFilePath = Join-Path $HOME 'TMD_Files' 'Profiles' ($Name + '.tmprofile') Write-Verbose "Processing file '$ProfileFilePath'" if (Test-Path -PathType Leaf -Path $ProfileFilePath) { $BufferSize = $Name.Length $RandomDataBuffer = [System.Byte[]]::new($BufferSize) Write-Verbose "Overwriting the data within the file $OverwriteCount time(s)" for ($i = 0; $i -lt $OverwriteCount; $i++) { $Random = [System.Random]::new() $Random.NextBytes($RandomDataBuffer) | Set-Content -Path $ProfileFilePath -AsByteStream } Write-Verbose "Removing the file" Remove-Item -Path $ProfileFilePath -Force } else { $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( [Exception]::new("A TransitionManager profile with the name '$Name' does not exist."), "TMD.AUTH02", [System.Management.Automation.ErrorCategory]::InvalidArgument, $ProfileFilePath ) $PSCmdlet.WriteError($ErrorRecord) } } } |