Create Function Parameter Accept Wild Cards - powershell

I have a function that I am writing and I would like to create a wild card process on the WMI call. Wondering what my options might be. My first thought was to take the parameter value sent and replace any asterisk with percent signs and have a case statement that will use a query string depending on if I need to use a LIKE statement or not. Am I over complicating it and is there a much simpler way I have not found?
Here is the top portion of the function as it so
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$True)][string]$Name = "",
[Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$True)][string]$ComputerName = $env:ComputerName,
[Parameter()][string]$Port = "",
[Parameter()][switch]$Full
)
Process {
if($ComputerName.Trim()) {
try {
$printers = (Get-WmiObject -Query "select * from Win32_printer where portname like '%$port%' and name like '%$Name%' " -ComputerName $ComputerName -EnableAllPrivileges -ErrorAction Stop)
This is kind of what I was trying to no avail.

We clear the clutter for this function to show the bare minimum needed to prove our point. Like's strong point is using wildcards however the comparison will still function without them. Only caveat is that the could be a performance issue and that = would be better. Point being is that, for simplicity sake at least, we are going to optionally add the wildcards to the query string.
Function Get-Printers{
[CmdletBinding()]
Param(
[Parameter()][string]$Name = "",
[Parameter()][string]$Port = "",
[Parameter()][switch]$Wild = $True
)
$wildcard = if($wild){"%"}Else{$null}
Get-WmiObject -Query ("select * from Win32_printer where portname like '{0}$port{0}' and name like '{0}$Name{0}'" -f $wildcard)
}
So by default the function will use wildcards. In the query string we use the format operator to add in whatever the $wildcard was determined to be. So if the -Wild was true then the query would look like this:
select * from Win32_printer where portname like '%Fax%' and name like '%Fax%'
Else, if it was false then they same query would look like this:
select * from Win32_printer where portname like 'Fax' and name like 'Fax'
To reiterate, with the latter query in mind, the use of like without wildcards will produce the same results as if we just had =.

With Matts reference I found that this seems to work as well. I was trying to get all crazy before with a case statement and checking the value passed via $name[0] and $name[-1] to see what position the wild card value was to define the particular case statement to run not thinking that the like statement doesn't really care.
Function Get-Printers{
[CmdletBinding()]
Param(
[Parameter()][string]$Name = "*",
[Parameter()][string]$Port = "*"
)
$Name = $Name.Replace('*','%')
$Port = $Port.Replace('*','%')
Get-WmiObject -Query ("select * from Win32_printer where portname like '$port' and name like '$Name'")
}
Thanks Matt!

Related

how to use both parameters and hard-code the servers list through powershell

I am trying to create a script which should run using parameter and without parameter should result for for multiple list:
Ex: I have a function under which I gave param as servername which is working fine for single server ad fetching the results.
(RestoreFileList -servername XXXXXXXXXXX1)
If I do not want to give a parameter as servername and output should result full set of servers list data
Ex : Restorefilecount
Am i missing something between parameter and serverlist condition which should fetch the results, Any help on this?
Script *************
function Restorefilecount()
{
[cmdletbinding()]
param(
[Parameter(position = 0,mandatory = $false)]
[string] $Servername
)
$servername = #()
$servername = ('XXXXXXXX1', 'XXXXXXXXX2', 'XXXXXXXXX3', 'XXXXXXXXXX4')
$result = Invoke-Client -ComputerName $Servername -ScriptBlock {
$Server = HOSTNAME.EXE
$Query = #'
Select ##ServerName AS ServerName , name,(SUBSTRING(NAME ,15,3) * 100 ) / 100 AS
file,state_desc,user_access_desc FROM master.sys.databases where name like 'TKT' ORDER BY 2
'#
Invoke-Sqlcmd -ServerInstance $Server -Database master -Query $Query
}
Building on Abraham Zinala's helpful comments:
It looks like you're simply looking to define a parameter's default value:
function Restore-FileCount {
[CmdletBinding()]
param(
[string[]] $Servername = #('XXXXXXXX1', 'XXXXXXXXX2', 'XXXXXXXXX3', 'XXXXXXXXXX4')
)
# ...
$Servername # Output for demo purposes
}
Note how the parameter type had to be changed from [string] to [string[]] in order to support an array of values.
Incidental changes:
Since you're using a param(...) block to define your parameters (which is generally preferable), there is no need to place () after the function name (Restorefilecount()) - while doing so doesn't cause a syntax error as long as there's nothing or only whitespace between ( and ), note that you declare parameters either via a param(...) block or via function foo(...); also, in parameter-less functions () is never needed - see the conceptual about_Functions help topic.
I've inserted a hyphen (-) in your function name, to make it conform to PowerShell's naming convention.
I've omitted [Parameter(position = 0, mandatory = $false)], because what this attribute instance specifies amounts to the default behavior (all parameters are non-mandatory by default, and Position=0 is implied by $ServerName being the first (and only) parameter).

Is it possible to use variable nested command splatting

I am just wanting to check if it is possible to use If statements to set the value of a nested command splat in PowerShell. Below is what I have currently:
$SADUParams = #{
Identity = $SAM
Company = $Company
Server = $ADserver
Replace = #{"extensionattribute11"="$Department";"extensionattribute12"="RESOURCE"}
}
if($PhoneNo){
$SADUParams["OfficePhone"] = $PhoneNo
}
Set-ADUser #SADUParams
What I would like to also do is update ExtensionAttribute12 using an if statement like with the phone number in the example above.
Is this possible, and if so, how do I code it?
Sure, just simple access the desired property from the $SADUParams hashtable and update it:
if ($true <#yourcondition#>)
{
$SADUParams.Replace.extensionattribute12 = "your_new_value"
}

How to get Database Name from Connectionstring in 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

Concat invoke-SqlCmd query string does not work

How can I concat a list of parameter OR a string of parameter (better) to my sql query? The below does not work.
$parameters = #("-ServerInstance `"MyMachine\SQLEXPRESS`"", "-Database %TargetDbName%", "-Username %SQLUserName%", "-Password %SQLPassword%")
$row = Invoke-Sqlcmd -Query "SELECT field FROM Table;" $parameters
I want to execute later multiple queries all with the same connection parameters and it is usefull to reuse them in a string which I can just add to the query string.
You were on the right track. Sounds like you are looking for splatting.
Splatting is a method of passing a collection of parameter
values to a command as unit.
I don't use Invoke-SQLcmd but it should work just like this:
$parameters = #{
ServerInstance = "MyMachine\SQLEXPRESS"
Database = "TargetDbName"
Username = "SQLUserName"
Password = "SQLPassword"
Query = "SELECT field FROM Table;"
}
$row = Invoke-Sqlcmd #parameters
Collect all the parameters as a hashtable and splat the cmdlet. If you wanted to use this parameter set again later, but make small changes, that would be easy now by referencing the name/value pair of the hashtable.
$parameters.Query = "SELECT field FROM DifferentTable;"
$anotherRow = Invoke-Sqlcmd #parameters
Have a look at parameter splatting
This means that you can put the arguments into a hashtable and pass the hashtable as parameters. Given your code, you could change it to. Notice that even though i assign the parameters to a hashtable $parameters you have to send it to the cmdlet using the #parameter syntax.
$parameters = #{ServerInstance="MyMachine\SQLEXPRESS";Database="$env:TargetDbName";Username="$env:SQLUserName";Password="$env:SQLPassword"}
$row = Invoke-Sqlcmd -Query "SELECT field FROM Table;" #parameters
I assumed that the TargetDBName, Username and password were to be found in environment variables so i changed the code a little to get those as well.
Give it a go.

Powershell: pass in multiline txt file values as an array in param statement

I have a text file containing a list of servers, one per line, like:
SERVER1
SERVER2
SERVER3
When I do a Get-Content on the file, I do see the output as:
SERVER1
SERVER2
SERVER3
So now I am writing a function that I want to take in the multiple servers as an array, and iterate over the array in the function. The function is currently like this:
function Get-LocalAdministrators
{
param(
[Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string[]]$computers
)
foreach ($computername in $computers)
{
$ADMINS = get-wmiobject -computername $computername -query "select * from win32_groupuser where GroupComponent=""Win32_Group.Domain='$computername',Name='administrators'""" | % {$_.partcomponent}
foreach ($ADMIN in $ADMINS)
{
$admin = $admin.replace("\\$computername\root\cimv2:Win32_UserAccount.Domain=","") # trims the results for a user
$admin = $admin.replace("\\$computername\root\cimv2:Win32_Group.Domain=","") # trims the results for a group
$admin = $admin.replace('",Name="',"\")
$admin = $admin.REPLACE("""","") # strips the last "
$objOutput = New-Object PSObject -Property #{
Machinename = $($computername)
Fullname = ($admin)
#DomainName =$admin.split("\")[0]
#UserName = $admin.split("\")[1]
}
$objReport+=#($objOutput)
}
return $objReport
}
}
Then I plan to call the function as:
Get-Content “C:\temp\computers.txt” | Get-LocalAdministrators
However, when I run the function, I can see that the value of $computers is {SERVER3} (i.e. only the last line in the file.) I have tried to find the answer to this via teh Google, and although there are a lot of array references/examples, I cannot find one where it combines reading the values from a file, into an array in a param statement. Please forgive my PS newb ignorance, and provide the clue that I need... Thanks.
UPDATE: Link to screenshot of script running in PowerGUI Script Editor showing the value of $computers during the run: debug run screenshot
When you pass objects via the pipeline into a function, only one is passed into your function at a time - not the whole array. So you don't need the foreach loop, nor do you need to make $computers an array in the function.
Also, when you have a pipeline function, you should be making use of the begin,process and end keywords. Each denotes a script block - begin is a scriptblock that is executed once (when the pipeline is "set up"), process is the scriptblock to be executed for each object passed via the pipeline, and end is just like begin only it runs after the last item has passed through.
So minimally, your function should be this:
function Get-LocalAdministrators
{
param(
[Parameter(Mandatory=$True,Position=0,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$computer
)
process{
$ADMINS = get-wmiobject -computername $computername -query "select * from win32_groupuser where GroupComponent=""Win32_Group.Domain='$computername',Name='administrators'""" | % {$_.partcomponent}
# Do other stuff here
}
}
The MSDN documentation says (this is in get-help about_functions - there's more to it than this):
Piping Objects to Functions
Any function can take input from the pipeline. You can control how a
function processes input from the pipeline using Begin, Process, and End
keywords. The following sample syntax shows the three keywords:
function <name> {
begin {<statement list>}
process {<statement list>}
end {<statement list>}
}
The Begin statement list runs one time only, at the beginning of
the function.
The Process statement list runs one time for each object in the pipeline.
While the Process block is running, each pipeline object is assigned to
the $_ automatic variable, one pipeline object at a time.
After the function receives all the objects in the pipeline, the End
statement list runs one time. If no Begin, Process, or End keywords are
used, all the statements are treated like an End statement list.
Edit: After seeing the full code added by the OP, this works. Note that I have made the changes to your code as I describe above.
function Get-LocalAdministrators
{
param(
[Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$computer
)
process{
$ADMINS = get-wmiobject -computername $computer -query "select * from win32_groupuser where GroupComponent=""Win32_Group.Domain='$computer',Name='administrators'""" | % {$_.partcomponent}
foreach ($ADMIN in $ADMINS)
{
$admin = $admin.replace("\\$computername\root\cimv2:Win32_UserAccount.Domain=","") # trims the results for a user
$admin = $admin.replace("\\$computername\root\cimv2:Win32_Group.Domain=","") # trims the results for a group
$admin = $admin.replace('",Name="',"\")
$admin = $admin.REPLACE("""","") # strips the last "
$objOutput = New-Object PSObject -Property #{
Machinename = $($computer)
Fullname = ($admin)
#DomainName =$admin.split("\")[0]
#UserName = $admin.split("\")[1]
}
$objReport+=#($objOutput)
}
$objReport
}
}