Create a String array from an object array in powershell - arrays

Im getting the names of these computers and putting them into an array. Now what i want to do is to convert them into a string array to be able to check which policy they are on using a Get-ADComputer for loop or using a foreach loop (Can you recommend which one to use)
$global:arrComputers = #()
$computerStrings = Get-ADComputer -Filter 'SamAccountName -like "*Name*"' | Select -Expand Name
foreach ($line in $computerStrings)
{
$a = $line.ToString()
$b = $a.split()
$temp = #{}
$temp = New-Object object
$temp | Add-Member -MemberType "noteproperty" -Name Name -Value $b[0]
$global:arrComputers += $temp
}
$global:arrComputers
This is the command i want to run to check the policy they are under
Get-ADComputer "Name" -Properties MemberOf | %{if ($_.MemberOf -like "*POLICY_NAME*") {Write-Host "ON"} else {Write-Host "NOT ON"}}
I have tested both blocks of code and they are working the only problem im having is turning that array into a string array. I also tried the ToString() To be able to loop through it with the Get-ADComputer "Name"

"memberOf" property in objects returned by "Get-ADComputer" returns a list of strings containing Distinguished Name of each group this computer is a member of.
Therefore, I assume when you say "This is the command i want to run to check the policy they are under", you are referring to a group membership that a group policy is targeting right?
Below code then will do it:
$computers = #();
Get-ADComputer -Filter * -Properties Name,MemberOf | %{if ($_.MemberOf -like "*computer_group_name*") { $computers += $_.Name } }
Explanation:
First line, define an array $computers
Second line, query AD for computer object properties Name,MemberOf
then, $_.MemberOf contains group name in string, add Name property(string) to array of strings you defined on line 1

Related

How do i compare every PC name from a multiobject array to a single object array

Hi Powershell newbie alert,
I am trying to compare the computer name of my $getADComp function (CN) with the output of $WSUSArr.
I want to know which PC's are in the WSUS but not and the AD and vice versa. I want the PC names to go in 2 diffrent result arrays so i can use these again later
Eventually i want something like this
$separator = "."
$GetWSUSComp = Get-wsuscomputer -UpdateServer $wsus
$GetADComp = Get-ADComputer -Filter * -Property CN, CanonicalName, Description | Select-Object CN, CanonicalName, Description
$WSUSArr = #()
for ($i = 0; $i -lt $GetWSUSComp.Count; $i++){
$WSUSArr += $GetWSUSComp[$i].FullDomainName.split($separator)[0].ToUpper()
}
Compare-Object -ReferenceObject $WSUSArr -DifferenceObject $GetADComp
If there is a more efficient way to do this feel free to make use of another method (hash table, etc.)
I have another array which is called $WSUSArr which contains the names of all the computer that are connected to the WSUS server i will have to compare those two lists with eachother.
Why not go for an array of objects ?
$GetADComp = Get-ADComputer -Filter * -Property CN, DistinguishedName, Description |
Select-Object CN, DistinguishedName, Description
This way you avoid gathering all properties with -Property * where you only want three.
Every property of items in the array can be accessed by using
$GetADComp[$index].CN
$GetADComp[$index].DistinguishedName
$GetADComp[$index].Description
and compared with an array of CN's like
$GetADComp | Where-Object { $_.CN -eq $WSUSArr[$index] }
In order to compare the computer CN's with the objects returned from the Get-ADComputer cmdlet:
1.
Get the computers that are both in AD and in the WSUS array.
If you want this to be a simple string array of just the CN's, do this:
$ADcomputersInWsus = $GetADComp | Where-Object { $WSUSArr -contains $_.CN } | Select-Object -ExpandProperty CN
Without the Select-Object, this will get you an array of objects with three
properties: CN, DistinguishedName, Description.
$ADcomputersInWsus = $GetADComp | Where-Object { $WSUSArr -contains $_.CN }
# An object array like this is perfect for saving as CSV:
# $ADcomputersInWsus | Export-Csv -Path 'X:\ADcomputersInWsus.csv' -NoTypeInformation
2.
Get a list of computers that are in WSUS, but not in AD:
# ($GetADComp).CN returns an string array with just the CN's, just like the $WSUSArr
$WsusComputersNotInAD = $WSUSArr | Where-Object { ($GetADComp).CN -notcontains $_ }
3.
Get a list of AD computers that are not in WSUS:
$ADcomputersNotInWsus = $GetADComp | Where-Object { $WSUSArr -notcontains $_.CN } | Select-Object -ExpandProperty CN
Note, the $WsusComputersNotInAD is derived from the $WSUSArr string array and is therefore also an array of strings, not objects.
To save that to file, either use:
$WsusComputersNotInAD | Out-File -FilePath "C:\XXX\XXX\WSUSCompNotInAD.txt" -Force
Or convert to an object array and use Export-Csv like the other results:
$WsusComputersNotInAD | ForEach-Object { [PsCustomObject]#{'ComputerName' = $_}} |
Export-Csv -Path "C:\XXX\XXX\WSUSCompNotInAD.csv" -NoTypeInformation

How to dynamically reference a powershell variable

I have an array that contains different rows where one column identifies the "record" "type." I want to iterate through this array and sort each item based on that value into a new array so that I have one array per type.
Here's what I have so far:
$data = Get-ADObject -SearchBase $sb -filter * -properties * | select samaccountname,canonicalname,objectclass,distinguishedname | sort objectclass,samaccountname
$oct = $data | select objectclass -Unique
foreach ($o in $oct)
{
$oc = $o.objectclass
Remove-Variable -name "$oc"
New-Variable -name "$oc" -value #()
}
$d = #()
$user = #()
foreach ($d in $data)
{
$oc = $d.objectclass
foreach ($o in $oct)
{
$1 = $o.objectclass
if ($1 -eq $oc)
{
('$' + $oc) += $d
}
}
}
(the lines: Remove-Variable -name "$oc", $d = #(), and $user = #() are for testing purposes so ignore those)
This works great up to the line where I try to dynamically reference my new arrays. What am I doing wrong and how can I fix it?
The error text is:
('$' + $oc) += $d
~~~~~~~~~ The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept
assignments, such as a variable or a property.
CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
FullyQualifiedErrorId : InvalidLeftHandSide
I have tried using $($oc), but that didn't work either. If I change it to the name of one of my dynamically created arrays like $user, the code works fine except that it loads everything into the $user array (obviously).
The reason I tried ('$' + $oc) is because this is the only way I could get ISE to output $user.
I also tried ('$' + $oc).add($d) but it appears to be seeing it as a string rather than the array.
Any pointers are appreciated.
Use the Get-Variable and Set-Variable cmdlets:
$curVal = Get-Variable -Name $oc -ValueOnly
Set-Variable -Name $oc -Value ($curVal+$d)
But note that you would be better off building this array in a local variable first, and then assigning it to your "runtime-named" variable once, as these get and set operations are going to be way slower.
Rather than fiddling around with dynamically named variables, I'd use dictionary-type, like for example a hashtable:
# initialize an empty hashtable
$objectsByClass = #{}
# Define list of properties
$properties = 'samaccountname','canonicalname','objectclass','distinguishedname'
# Retrieve AD objects
$Data = Get-ADObject -SearchBase $sb -filter * -properties $properties | select $properties | sort objectclass,samaccountname
#Populate hashtable
$Data |ForEach-Object {
if(-not $objectsByClass.ContainsKey($_.objectClass)){
# Create entry in hashtable
$objectsByClass[$_.objectClass] = #()
}
# Add entry to dictionary
$objectsByClass[$_.objectClass] += $_
}
Now you can access the items by class name:
$users = $objectsByClass['user']
And you can easily discover all class names:
$classNames = $objectsByClass.Keys
As briantist points out, you can also have Group-Object build the hashtable for you if the above gets too verbose:
$objectsByClass = $Data |Group-Object objectClass -AsHashTable

Iterate through Rows in SQL to Output to Text File

I have a SQL table that contains several hundred rows of data. One of the columns in this table contains text reports that were stored as plain text within the column.
Essentially, I need to iterate through each row of data in SQL and output the contents of each row's report column to its own individual text file with a unique name pulled from another column.
I am trying to accomplish this via PowerShell and I seem to be hung up. Below is what I have thus far.
foreach ($i=0; $i -le $Reports.Count; $i++)
{
$SDIR = "C:\harassmentreports"
$FILENAME = $Reports | Select-Object FILENAME
$FILETEXT = $Reports | Select-Object TEXT
$NAME = "$SDIR\$FILENAME.txt"
if (!([System.IO.File]::Exists($NAME))) {
Out-File $NAME | Set-Content -Path $FULLFILE -Value $FILETEXT
}
}
Assuming that $Reports is a list of the records from your SQL query, you'll want to fix the following issues:
In an indexed loop use indexed access to the elements of your array:
$FILENAME = $Reports[$i] | Select-Object FILENAME
$FILETEXT = $Reports[$i] | Select-Object TEXT
Define variables outside the loop if their value doesn't change inside the loop:
$SDIR = "C:\harassmentreports"
foreach ($i=0; $i -le $Reports.Count; $i++) {
...
}
Expand properties if you want to use their value:
$FILENAME = $Reports[$i] | Select-Object -Expand FILENAME
$FILETEXT = $Reports[$i] | Select-Object -Expand TEXT
Use Join-Path for constructing paths:
$NAME = Join-Path $SDIR "$FILENAME.txt"
Use Test-Path for checking the existence of a file or folder:
if (-not (Test-Path -LiteralPath $NAME)) {
...
}
Use either Out-File
Out-File -FilePath $NAME -InputObject $TEXT
or Set-Content
Out-File -Path $NAME -Value $TEXT
not both of them. The basic difference between the two cmdlets is their default encoding. The former uses Unicode, the latter ASCII encoding. Both allow you to change the encoding via the parameter -Encoding.
You may also want to reconsider using a for loop in the first place. A pipeline with a ForEach-Object loop might be a better approach:
$SDIR = "C:\harassmentreports"
$Reports | ForEach-Object {
$file = Join-Path $SDIR ($_.FILENAME + '.txt')
if (-not (Test-Path $file)) { Set-Content -Path $file -Value $_.TEXT }
}

Comparing AD users with File Names

I'm trying to find files that are named after members of a specific AD group with a foreach loop using the code below. The script seems to have a problem which causes the loop to stop after the first exception. I think I need to throw the exception because there seems to be no default return value or error if no file for one of the group members is found.
$ErrorActionPreference = "Stop"
$BVAU = Get-ADGroupMember ADGroupName | Select-Object -Property Name
foreach($entry in $BVAU) {
trap [System.IO.DirectoryNotFoundException]{
Write-Host $_.Exception.Message
continue
}
}
if (-not (Get-ChildItem "\\samplepath" -Recurse | Where-Object FullName -like "*$entry*")) {
throw [System.IO.DirectoryNotFoundException] "$entry not found"
}
}
I only want to display the group members that don't have an equally named file. (A PDF form that legally qualifies the AD group membership)
The if statement belongs inside the loop, and the trap should be defined before the loop. However, you don't need the trap in the first place, because it's pointless to throw exceptions just for echoing a username. Simply echo the name right away and continue. Also, avoid running code multiple times if it doesn't need to.
$files = Get-ChildItem "\\samplepath" -Recurse |
Select-Object -Expand Basename -Unique
Get-ADGroupMember ADGroupName |
Select-Object -Expand Name |
Where-Object { $files -notcontains $_ } |
ForEach-Object { "$_ not found" }
The above is assuming that the usernames aren't just partial matches of the file basenames. Otherwise the Where-Object filter becomes slightly more complicated:
...
Where-Object { $n = $_; -not ($files | Where-Object {$_ -like '*$n*'}) } |
...

writing values to a registry key from an array

Trying to write a script that will take a prepolulated array and add those values to a registry key. The below works, but does not place a comma between the values.
$apps = #("whatApp.exe","thatApp.exe","thisAapp.exe")
set-location HKLM:\
foreach($app in $apps){
set-itemproperty -path Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Citrix\wfshell\TWI -Name logoffCheckSysModulestest -Value $apps
}
Also, how would i check if these apps were already in that key and to continue to the next value instead of adding it a second time?
Other ways you could do this but this would be a simple easy to read one.
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Citrix\wfshell\TWI"
$regName = "logoffCheckSysModulestest"
$apps = #("whatApp.exe","thatApp.exe","thisAapp.exe")
$values = (Get-ItemProperty -Path $regPath | Select -ExpandProperty $regName).Split(",")
$apps | ForEach-Object{
If($values -notcontains $_){
$values += $_
}
}
Set-ItemProperty -Path $regPath -Name $regName -Value ($values -join ",")
First we take the current value of that keyname in the registry and split it into an array. Then we compare that array with the list of elements in $apps. If one of the elements is missing append it to the $values arrray.
The $values array then should be the updated with all new entries that did not already exist. -Join then back together and write the change back to the registry.
In place of the ForEach-Object loop you could also use Compare-Object to do the work
$apps = #("whatApp.exe","thatApp.exe","thisAapp.exe")
$values = (Get-ItemProperty -Path $regPath | Select -ExpandProperty $regName).Split(",")
Set-ItemProperty -Path $regPath -Name $regName -Value ((Compare-Object $apps $values -IncludeEqual -PassThru) -join ",")

Resources