Getting Local user objects and comparing them to a known good string? - windows

Hi im currently working on a script to monitor back to an RMM tool, seem to be having issues converting my objects to match a "known string" inside my script.
ideally i'd like to poll the local computers local admin group then inline compare that with a string i've predefined, i was hoping to get the value, then just write a multi-lined string to match, then do some if statements to compare the 2.
$test3 = Get-LocalGroupMember -SID "S-1-5-32-544" | select -ExpandProperty Name | out-string
$test =#"
PC\Administrator
PC\test
"#
this is a little snippet, so the first one pulls the local ad group then saves it to a varible, and $test is my defined variable.
Both appear identical when outputted to console.
thanks so much in advance.

Instead of a predefined multiline string, Use either a string array or a hashtable to compare against.
The way you try to do it can fail the comparison simply because the items returned can be in a different order as in your predefined string.
Option 1: use an array
$testUsers = 'PC\Administrator', 'PC\test'
# this gets the users that are mentioned in the $testUsers array.
# if you want the opposite (users in the group, but NOT in the $testUsers array),
# change '-contains' into '-notcontains'
(Get-LocalGroupMember -SID "S-1-5-32-544").Name | Where-Object { $testUsers -contains $_ }
Option 2: use a Hashtable (a bit more work to set up, but extremely fast)
$testusers = #{
'PC\Administrator' = $true # the Values will not be used, so anything can go in here
'PC\test' = $true
}
# this gets the users that are mentioned in the $testUsers Hashtable.
# if you want the opposite (users in the group, but NOT in the $testUsers Hashtable),
# change '$testUsers.ContainsKey($_)' into '!$testUsers.ContainsKey($_)'
(Get-LocalGroupMember -SID "S-1-5-32-544").Name | Where-Object { $testUsers.ContainsKey($_) }

It's a bug in Windows where orphaned SIDs are left in the group. Try this instead:
$adminGroup = [ADSI]::new("WinNT://$env:COMPUTERNAME/$((Get-LocalGroup -SID S-1-5-32-544).Name)")
$adminGroupMembers = $adminGroup.Invoke('Members') |% {([ADSI]$_).Path.Replace('WinNT://', '')}
$adminGroupMembers | Out-String
You'll need to manipulate the output as required.

Related

Insert string into multiple filenames

I have multiple files named in this format:
Fat1920OVXPlacebo_S20_R1_001.fastq
Kidney1235SHAM_S65_R1_001.fastq
Kidney1911OVXPlacebo_S94_R2_001.fastq
Liver1289OVXEstrogen_S24_R2_001.fastq
I need to insert the string "L1000_" into their names so that they read
Fat1920OVXPlacebo_S20_L1000_R1_001.fastq
Kidney1235SHAM_S65_L1000_R1_001.fastq
Kidney1911OVXPlacebo_S94_L1000_R2_001.fastq
Liver1289OVXEstrogen_S24_L1000_R2_001.fastq
I apologize but I have absolutely no experience in coding in powershell. The closest thing I could find to do this was a script that renames the entire file:
Set objFso = CreateObject(“Scripting.FileSystemObject”)
Set Folder = objFSO.GetFolder(“ENTER\PATH\HERE”)
For Each File In Folder.Files
sNewFile = File.Name
sNewFile = Replace(sNewFile,”ORIGINAL”,”REPLACEMENT”)
if (sNewFile<>File.Name) then
File.Move(File.ParentFolder+”\”+sNewFile)
end if
Next
however, I just need to insert a string at a specific place in the file's title. I have 257 files and do not want to go 1 by 1. Does anyone have an idea on how to run this in windows?
Use Get-ChildItem to enumerate the files of interest, pipe them to Rename-Item, and use a delay-bind script block ({ ... }) to dynamically determine the new name, via a regex-based -replace operation.
(Get-ChildItem $yourFolder -Filter *.fastq) |
Rename-Item -NewName { $_.Name -replace '(?<=_S\d+_)', 'L1000_' } -WhatIf
Note:
• The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.
• Even though not strictly necessary in this case, enclosing the Get-ChildItem command in (...), the grouping operator ensures that already renamed files don't accidentally re-enter the enumeration of files to be renamed - see this answer.
(?<=_S\d+_) uses a positive look-behind assertion ((?<=...)) to match verbatim string _S, followed by one or more (+) digits (\d), followed by verbatim _.
Since the look-behind assertion merely matches a position in the string rather than a substring, the replacement operand, verbatim L1000_ in this case, is inserted at that position in (a copy of) the input string.
For a more detailed explanation of the delay-bind script-block technique, see this answer.
here's one way to do that with PoSh. note that the demo does not handle either the rename or directory related stuff. it ONLY handles generating the new file names.
what it does ...
fakes reading in a list of fileinfo objects
when ready to do this for real, replace the entire #region/#endregion block with a call to Get-ChildItem and save it to $FileList.
sets the text to be inserted
iterates thru the file list
splits the file .Name property on the underscores
saves that to a $Var
adds the 1st two splits, the insertion text, and the last two splits to a new array
joins that array with an underscore as the delimiter
sends the new file name to the $Result collection
displays the list of new names
the code ...
#region - fake reading in a list of files
# in real life, use Get-ChildItem
$FileList = #(
[system.io.fileinfo]'Fat1920OVXPlacebo_S20_R1_001.fastq'
[system.io.fileinfo]'Kidney1235SHAM_S65_R1_001.fastq'
[system.io.fileinfo]'Kidney1911OVXPlacebo_S94_R2_001.fastq'
[system.io.fileinfo]'Liver1289OVXEstrogen_S24_R2_001.fastq'
)
#endregion - fake reading in a list of files
$InsertionText = 'L1000'
$Result = foreach ($FL_Item in $FileList)
{
$FLI_Parts = $FL_Item.Name.Split('_')
($FLI_Parts[0,1] + $InsertionText + $FLI_Parts[2,3]) -join '_'
}
$Result
output ...
Fat1920OVXPlacebo_S20_L1000_R1_001.fastq
Kidney1235SHAM_S65_L1000_R1_001.fastq
Kidney1911OVXPlacebo_S94_L1000_R2_001.fastq
Liver1289OVXEstrogen_S24_L1000_R2_001.fastq
Using PowerShell, you could use a regular expression to rename the files. Example:
Get-ChildItem "C:\foldername\here\*.fastq" | ForEach-Object {
$oldName = $_.Name
$newName = [Regex]::Replace($oldName,'(S\d+)_(R\d+)','$1_L1000_$2')
Rename-Item $_ $newName -WhatIf
}
[Regex] is a PowerShell type accelerator for the .NET Regex class, and Replace is the method for the Regex class that performs text substitutions. The first parameter to the Replace method is the input string (the old filename), the second parameter is the regular expression pattern (run help about_Regular_Rxpressions for more information), and the third parameter is the replacement string pattern, where $1 is the first capture pattern in ( ), and $2 is the second capture pattern in ( )). Finally, the Rename-Item cmdlet renames the files. Remove the -WhatIf parameter if the output looks correct to actually perform the renames.

Find array elements which values are not part of another array PowerShell

I have two arrays
$adg - (A list of AD groups)
$dbs - (A list of database names)
Lets say I use this command
$adg -match $dbs.i
The output will be all the AD groups which have the string $dbs in its name.
However, I am aiming to find the DBs in which are not part of the AD groups array.
Eg:
$adg = #("Dev22_Owner","Test49_Owner","Rocket_Owner")
$dbs = #("Dev22", "Confidential", "InternDB", "Rocket", "Test49")
What approach should I take to get the output:
Confidential
InternDB
I tried $dbs | Where $adg -notmatch $dbs.i but there is no output.
I would first remove the unecessary user part from the ad group:
$groups = $adg | ForEach-Object {
$_ -replace '_.*?$'
}
Then you can use the Where-Object cmdlet with the -notin operator to filter them:
$dbs | Where-Object { $_ -notin $groups }
Output:
Confidential
InternDB
To offer a more concise PSv3+ alternative to Martin Brandl's helpful answer:
PS> (Compare-Object ($adg -replace '_Owner$') $dbs).InputObject
Confidential
InternDB
($adg -replace '_Owner$') returns a new array with copies of the input string that have _Owner suffix stripped from them.
Compare-Object compares the stripped array with the other array and returns objects that represent the differences between the two arrays.
Accessing the .InputObject on the difference objects returns the values that differ.
Note: If $dbs contained items that aren't in the stripped $adg array, the above would return a union of all differing items, irrespective of which side they're unique to; to distinguish the sides / limit to a side, you'd have to use the .SideIndicator property (value => indicates values exclusive to the right side, <= those exclusive to the left side).
As for what you tried:
$adg -match $dbs.i doesn't work as intended and is effectively the same as $adg -match '', which invariably returns all $adg items.
The reason is that array $dbs has no .i property, so the expression evaluates to $null, which on the RHS of match is coerced to a string and therefore is converted to the empty string.
Generally, the RHS of -match does not support arrays - only a single regular expression.
If you do supply an array, it is implicitly converted to a string by joining the elements to form a space-separated list; e.g. array 1, 2 is coerced to '1 2' and '1 2' -match (1, 2) therefore evaluates to $True.

Sorting movie titles into CSV

I've got a media server at home, and I've created a script that pulls all the file names and sorts them before putting them into a CSV. My only problem is that it sorts alphanumeric, but from a movie titles perspective, I'd like to ignore "A", "An", and "The". Is there a way to ignore those strings and have the sort work correctly without actually altering the file name in the CSV?
Yes, you can sort multiple objects into order by any property, and if none of the properties are quite what you want then you can provide a scriptblock to Sort-Object with some code "do xyz to each object" and it will sort them based on the output of the scriptblock - and that will only be used for sorting, it won't change anything.
So calculate the name without the leading words A, An, The using any code you want to. Here, I'm cooking with regex because it's quick, tasty and does case-insensitive matching by default:
Get-ChildItem | Sort-Object -Property { $_.Name -replace '^(A|An|The).' }
But you can do something just as effective with the plain ingredients around your kitchen:
Function Mangle-FilmName
{
param($file)
$name = $file.Name.ToLower()
if ($name.startswith('an'))
{
$name.Substring(4)
}
elseif ($name.startswith('the')
{
$name.Substring(5)
}
...
else
{
$name
}
}
Get-ChildItem | Sort-Object -Property Mangle-FilmName
Or with switch statements or loops over arrays of words, and/or/etc.
You could use something like the following.
Due to missing sample data I don't know the structure of your file names (word separators etc.) but you can customize the following code to your needs. What the code essentially does is splitting the base file name by separators '_', ' ' and '.', filters out your ignored words ('The', 'A', 'An' etc.) and joins back the parts to a single string.
Please note that at the end of this, the file names are compared without their initial word separators (i.e. 'The_Blue_House.mpg' and 'The.Blue.House.mpg' would be considered the same) which IMHO is a good thing but your needs may be different.
Hope that helps
$wordSeparator = '_| |\.'
$ignoredWords = #(
'The'
'A'
'An'
# add more
)
filter sortableFileName {
($_ -split $wordSeparator | ? { $_ -notin $ignoredWords }) -join ''
}
Get-ChildItem | Sort-Object { $_.BaseName | sortableFileName } # | Export-CSV

Are there any other uses of parenthesis in powershell?

As new to Powershell world, sometime I'm stuck in the tricky syntax. That's why I'm trying to figure out all the possible uses of the parenthesis inside the language.
Do you know some more? Can you add here?
Here mine (left out basic use of curly in pipeline and round in method calls):
# empty array
$myarray = #()
# empty hash
$myhash = #{}
# empty script block
$myscript = {}
# variables with special characters
${very strange variable # stack !! overflow ??}="just an example"
# Single statement expressions
(ls -filter $home\bin\*.ps1).length
# Multi-statement expressions inside strings
"Processes: $($p = “a*”; get-process $p )"
# Multi statement array expression
#( ls c:\; ls d:\)
Cause a statement to yield a result in an expression:
($x=3) + 5 # yields 8
When using generics, you need to wrap the type in [..]:
New-Object Collections.Generic.LinkedList[string]
For some people this might look confusing, because it is similar to indexing in arrays.
The Param( ) statement (in a function, script, or scriptblock)
Around the condition in an If (or Elseif statement)
Around the expression in a switch statement.
Edit: Forgot the condition in the while statement.
Edit2: Also, $() for subexpressions (e.g. in strings).
Regular expressions are arguably a first-class construct in Powershell.
If we're compiling a complete list, we can include the role that square and round brackets play in regular expressions.
An example:
$obj.connectionString = $obj.connectionString -replace '(Data Source)=[^;]+', '$1=serverB\SQL2008_R2'
Because of the support for XML, you can go so far as to include the square brackets used in XPath. (That's really drawing a long bow though :-)
select-xml $config -xpath "./configuration/connectionStrings/add[#name='LocalSqlServer']"
It's even written, but not enough clearly in the first short list after "Multi-statement expressions inside strings I'will add
# Var property inside a string
$a = get-process a*
write-host "Number of process : $a.length" # Get a list of process and then ".length
Number of process : System.Diagnostics.Process (accelerometerST) System.Diagnostics.Process (AEADISRV) System.Diagnostics.Process (agr64svc).length
write-host "Number of process : $($a.length)" # To get correct number of process
Number of process : 3
The parenthesis is most powerfully.
Suppose that you want collect all output, including errors, of some scriptblock and redirect to a variable or another functions for handle this... With the parenthesis, this is easy task:
$customScript = { "This i as test"; This will be procedure error! }
(. $customScript 2>&1 ) | %{"CAPTURING SCRIPT OUTPUT: "+$_}

What does the "#" symbol do in PowerShell?

I've seen the # symbol used in PowerShell to initialise arrays.
What exactly does the # symbol denote and where can I read more about it?
In PowerShell V2, # is also the Splat operator.
PS> # First use it to create a hashtable of parameters:
PS> $params = #{path = "c:\temp"; Recurse= $true}
PS> # Then use it to SPLAT the parameters - which is to say to expand a hash table
PS> # into a set of command line parameters.
PS> dir #params
PS> # That was the equivalent of:
PS> dir -Path c:\temp -Recurse:$true
PowerShell will actually treat any comma-separated list as an array:
"server1","server2"
So the # is optional in those cases. However, for associative arrays, the # is required:
#{"Key"="Value";"Key2"="Value2"}
Officially, # is the "array operator." You can read more about it in the documentation that installed along with PowerShell, or in a book like "Windows PowerShell: TFM," which I co-authored.
While the above responses provide most of the answer it is useful--even this late to the question--to provide the full answer, to wit:
Array sub-expression (see about_arrays)
Forces the value to be an array, even if a singleton or a null, e.g. $a = #(ps | where name -like 'foo')
Hash initializer (see about_hash_tables)
Initializes a hash table with key-value pairs, e.g.
$HashArguments = #{ Path = "test.txt"; Destination = "test2.txt"; WhatIf = $true }
Splatting (see about_splatting)
Let's you invoke a cmdlet with parameters from an array or a hash-table rather than the more customary individually enumerated parameters, e.g. using the hash table just above, Copy-Item #HashArguments
Here strings (see about_quoting_rules)
Let's you create strings with easily embedded quotes, typically used for multi-line strings, e.g.:
$data = #"
line one
line two
something "quoted" here
"#
Because this type of question (what does 'x' notation mean in PowerShell?) is so common here on StackOverflow as well as in many reader comments, I put together a lexicon of PowerShell punctuation, just published on Simple-Talk.com. Read all about # as well as % and # and $_ and ? and more at The Complete Guide to PowerShell Punctuation. Attached to the article is this wallchart that gives you everything on a single sheet:
You can also wrap the output of a cmdlet (or pipeline) in #() to ensure that what you get back is an array rather than a single item.
For instance, dir usually returns a list, but depending on the options, it might return a single object. If you are planning on iterating through the results with a foreach-object, you need to make sure you get a list back. Here's a contrived example:
$results = #( dir c:\autoexec.bat)
One more thing... an empty array (like to initialize a variable) is denoted #().
The Splatting Operator
To create an array, we create a variable and assign the array. Arrays are noted by the "#" symbol. Let's take the discussion above and use an array to connect to multiple remote computers:
$strComputers = #("Server1", "Server2", "Server3")<enter>
They are used for arrays and hashes.
PowerShell Tutorial 7: Accumulate, Recall, and Modify Data
Array Literals In PowerShell
I hope this helps to understand it a bit better.
You can store "values" within a key and return that value to do something.
In this case I have just provided #{a="";b="";c="";} and if not in the options i.e "keys" (a, b or c) then don't return a value
$array = #{
a = "test1";
b = "test2";
c = "test3"
}
foreach($elem in $array.GetEnumerator()){
if ($elem.key -eq "a"){
$key = $elem.key
$value = $elem.value
}
elseif ($elem.key -eq "b"){
$key = $elem.key
$value = $elem.value
}
elseif ($elem.key -eq "c"){
$key = $elem.key
$value = $elem.value
}
else{
Write-Host "No other value"
}
Write-Host "Key: " $key "Value: " $value
}

Resources