How to get Database Name from Connectionstring in PowerShell - powershell

I'm trying to get the Database name from a connection string in PowerShell.
"Server=server\instance;uid=User;pwd=Hello;Database=SomeName;"
I can think of two ways to do that, either to search for the string Database, up until the first ; after that split the string on = and select the Databasename - but I don't really know how to do that.
The second way could be with the DBConnectionStringBuilder like this:
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.set_ConnectionString($cstring)
[string]$Database = ($sb | ? {$_.Keys -eq 'Database'}).value
but with this way, no matter how hard i try to filter the Databasename, it won't give me the databasename returned.
Question: What's the best way to get my Databasename from the connection string?

Use the second method, but simplify it:
$cstring = "Server=server\instance;uid=User;pwd=Hello;Database=SomeName;"
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.set_ConnectionString($cstring)
$Database = $sb.database
This works perfectly fine.
If you want to avoid an error in the case where the key doesn't exist, there are a lot of ways to do that, the more idiomatic method of looking for the key first:
if ($sb.HasKey('Database')) {
$Database = $sb.Database
}
Or the object's own TryGetValue method:
if ($sb.TryGetValue('Database', [ref] $Database)) {
# It was successful
# $Database already contains the value, you can use it.
} else {
# The key didn't exist.
}
String Parsing
I don't recommend these in this case because there is some flexibility in the database connection string format, and why make your code aware of all the possibilities and try to correctly handle them all when that code was already written (the object you're using above)?
But for completeness, I'd do it with splitting and regular expression matching and capturing:
$cstring -split '\s*;\s*' |
ForEach-Object -Process {
if ($_ -imatch '^Database=(?<dbname>.+)$') {
$Database = $Matches.dbname
}
}
So here I'm first splitting on a semi-colon ; surrounded by any amount of whitespace. Each element (which should be just key-value pairs) is then checked against another regex, looking specifically for Database= and then capturing what comes after that until the end of the string, in a named capture group called dbname. If the match is successful, then the result of the capture group is assigned to the variable.
I still prefer a proper parser when one exists.

try this
"Server=server\instance;uid=User;pwd=Hello;Database=SomeName;".split(";") |
%{[pscustomobject]#{Property=$_.Split("=")[0];Value=$_.Split("=")[1]}} |
where Property -eq "Database" | select Value

other solution
$template=#"
{Property*:Abc123}={Value:Test123}
{Property*:Def}={Value:XX}
"#
"Server=server\instance;uid=User;pwd=Hello;Database=SomeName;".replace(";", "`r`n") | ConvertFrom-String -TemplateContent $template |
where Property -eq "Database" | select Value

Related

Read a CSV in powershell with a variable number of columns

I have a CSV that contains a username, and then one or more values for the rest of the record. There are no headers in the file.
joe.user,Accounting-SG,CustomerService-SG,MidwestRegion-SG
frank.user,Accounting-SG,EastRegion-SG
I would like to read the file into a powershell object where the Username property is set to the first column, and the Membership property is set to either the remainder of the row (including the commas) or ideally, an array of strings with each element containing a single membership value.
Unfortunately, the following line only grabs the first membership and ignores the rest of the line.
$memberships = Import-Csv -Path C:\temp\values.csv -Header "username", "membership"
#{username=joe.user; membership=Accounting-SG}
#{username=frank.user; membership=Accounting-SG}
I'm looking for either of these outputs:
#{username=joe.user; membership=Accounting-SG,CustomerService-SG,MidwestRegion-SG}
#{username=frank.user; membership=Accounting-SG,EastRegion-SG}
or
#{username=joe.user; membership=string[]}
#{username=frank.user; membership=string[]}
I've been able to get the first result by enclosing the "rest" of the data in the csv file in quotes, but that doesn't really feel like the best answer:
joe.user,"Accounting-SG,CustomerService-SG,MidwestRegion-SG"
Well, the issue is that what you have isn't really a (proper) CSV. The CSV format doesn't support that notation.
You can "roll your own" and just process the file yourself, something like this:
$memberships = Get-Content -LiteralPath C:\temp\values.csv |
ForEach-Object -Process {
$user,$membership = $_.Split(',')
New-Object -TypeName PSObject -Property #{
username = $user
membership = $membership
}
}
You could do a half and half sort of thing. Using your modification, where the groups are all a single field in quotes, do this:
$memberships = Import-Csv -Path C:\temp\values.csv -Header "username", "membership" |
ForEach-Object -Process {
$_.membership = $_.membership.Split(',')
$_
}
The first example just reads the file line by line, splits on commas, then creates a new object with the properties you want.
The second example uses Import-Csv to create the object initially, then just resets the .membership property (it starts as a string, and we split the string so it's now an array).
The second way only makes sense if whatever is creating the "CSV" can create it that way in the first place. If you have to modify it yourself every time, just skip this and process it as it is.

Powershell - Iterate through variables dynamically

I am importing a CSV file with two records per line, "Name" and "Path".
$softwareList = Import-Csv C:\Scripts\NEW_INSTALLER\softwareList.csv
$count = 0..($softwareList.count -1)
foreach($i in $count){
Write-Host $softwareList[$i].Name,$softwareList[$i].Path
}
What I am trying to do is dynamically assign the Name and Path of each record to a WPFCheckbox variable based on the $i variable. The names for these checkboxes are named something such as WPFCheckbox0, WPFCheckbox1, WPFCheckbox2 and so on. These objects have two properties I planned on using, "Command" to store the $SoftwareList[$i].path and "Content" to store the $SoftwareList[$i].Name
I cannot think of a way to properly loop through these variables and assign the properties from the CSV to the properties on their respective WPFCheckboxes.
Any suggestions would be very appreciated.
Invoke-Expression is one way, though note Mathias' commented concerns on the overall approach.
Within your foreach loop, you can do something like:
invoke-expression "`$WPFCheckbox$i`.Command = $($SoftwareList[$i].Path)"
invoke-expression "`$WPFCheckbox$i`.Content= $($SoftwareList[$i].Name)"
The back-tick ` just before the $WPFCheckBox prevents what would be an undefined variable from being immediately evaluated (before the expression is invoked), but the $I is. This gives you a string with your $WPFCheckbox1, to which you then append the property names and values. The $SoftwareList values are immediately processed into the raw string.
The Invoke-Expression then evaluates and executes the entire string as if it were a regular statement.
Here's a stand-alone code snippet to play with:
1..3 |% {
invoke-expression "`$MyVariable$_` = New-Object PSObject"
invoke-expression "`$MyVariable$_` | add-member -NotePropertyName Command -NotePropertyValue [String]::Empty"
invoke-expression "`$MyVariable$_`.Command = 'Path #$_'"
}
$MyVariable1 | Out-String
$MyVariable2 | Out-String
$MyVariable3 | Out-String
As a side note (since I can't comment yet on your original question,) creating an array just to act as iterator through the lines of the file is really inefficient. There are definitely better ways to do that.

Powershell to read some strings from each line

I have a requirement like:
Have a text file containing the following in the following pattern
172.26.xxy.zxy:Administrator:Password
172.26.xxy.yyx:Administrator:Password
172.26.xxy.yyy:Administrator:Password
172.26.xxy.yxy:Administrator:Password
I need my powershell script to read each word and use that word whereever required. For example,
foreach(something)
{
I want the IP's(172.26.---.---) to read and store the value as a variable.
I want to store the two words after **:** in seperate variables.
}
How can this be done? I know to read an entire file or get some specific string. But I need the same to be done on each line.Any help would be really appreciated.
Something like this? You can just split on the : and then store your variables based on the index
$contents = Get-Content C:\your\file.txt
foreach($line in $contents) {
$s = $line -split ':'
$ip = $s[0]
$user = $s[1]
$pass = $s[2]
write-host $ip $user $pass
}
minor edit: "t" missing in content.
You can write a regular expression to replace to remove the parts you do not need
$ip_address= '172.26.xxy.zxy:Administrator:Password' -replace '^(.+):(.+):(.+)$','$1'
$user= '172.26.xxy.zxy:Administrator:Password' -replace '^(.+):(.+):(.+)$','$2'
$pwd= '172.26.xxy.zxy:Administrator:Password' -replace '^(.+):(.+):(.+)$','$3'
I think the more generic and pure Powershell way would be something like this:
Select-String "(.*):(.*):(.*)" c:\file.txt |
Select #{Name="IP"; Expression = {$_.Matches.Groups[1]}},
#{Name="User"; Expression = {$_.Matches.Groups[2]}},
#{Name="Password"; Expression = {$_.Matches.Groups[3]}}
The Output would be then an array of objects each having three properties IP, User and Password. So you can now use them for your purposes, or just add more commands at the end of the pipe.

create var from .exe output in powershell

I need to make a variable from the ID number of a username
qwinsta.exe /server:vm041 derpy.herp
This returns the following output
SESSIONNAME USERNAME ID STATE TYPE DEVICE
derpy.herp 3 Disc
I need to create a variable in powershell with a value of 3 as per output above.
This code will be used to look through a number of servers to see if an individual user is logged in and disconnect them with rwinsta.exe
Here's quite a crude regular expression - I don't know qwinsta.exe so I'm not sure of the permutations of data it can return - this works with your above example though.
This will assign 3 to the variable $ID.
$output = qwinsta.exe /server:vm041 derpy.herp
$output -match ".*derpy.herp.*(\d).*"
$ID = $Matches[1]
you may be able to parameterise the username like so:
$username = "derpy.herp"
$output = qwinsta.exe /server:vm041 $username
$output -match ".*$username.*(\d).*"
$ID = $Matches[1]
HTH,
Matt
You can parse the output you are obtaining, get your ID, and then use New-Variable cmdlet to instance your variable.

Get last element of pipeline in powershell

This might be weird, but stay with me.
I want to get only the last element of a piped result to be assigned to a varaiable.
I know how I would do this in "regular" code of course, but since this must be a one-liner.
More specifically, I'm interested in getting the file extension when getting the result from an FTP request ListDirectoryDetails.
Since this is done within a string expansion, I can't figure out the proper code.
Currently I'm getting the last 3 hars, but that is real nasty.
New-Object PSObject -Property #{
LastWriteTime = [DateTime]::ParseExact($tempDate, "MMM dd HH:mm",[System.Globalization.CultureInfo]::InvariantCulture)
Type = $(if([int]$tempSize -eq 0) { "Directory" } else { $tempName.SubString($tempName.length-3,3) })
Name = $tempName
Size = [int]$tempSize
}
My idea was doing something similar to
$tempName.Split(".") | ? {$_ -eq $input[$input.Length-1]}
that is, iterate over all, but only take out where the element I'm looking at is the last one of the input-array.
What am I missing ?
A few ways to do this:
$name = 'c:\temp\aaa.bbb.ccc'
# way 1
$name.Split('.') | Select-Object -Last 1
# way 2
[System.IO.Path]::GetExtension($name)
# or if the dot is not needed
[System.IO.Path]::GetExtension($name).TrimStart('.')
In general, getting the last element in the pipeline would be done using Select -Last 1 as Roman suggests above. However, an alternate and easier way to do this if the input is a simple array is to use array slicing e.g.:
PS> $name = "c:\temp\aaa.bbb.txt"
PS> $name.Split('.')[-1]
txt
Was your intent to get the file's extension or basename? Because it seems that the Type property already contains the extension for the filename.