Unable to pass variable to get wmi object query - powershell

Hope you could help me with my issue. I tried to import a list of packages to $list variable and a list of DP server to variable named $DP. I used foreach and write-host and have confirmed that all items in the variable are showing ok, but when I use the variable in get-wmi object as parameter to filter the $i.packageid it is empty/null. Please advise how to resolve this.
sample code
$list=import-csv -Path 'C:\Users\Administrator\desktop\SCCM\packagelist.csv'
$DP="SCCMDP1","SCCMDP2"
foreach ($i in $list)
{
write-host "Item is $($i)" # showing ok
write-host "package is $($i.package)" # showing ok
write-host "packageid is $($i.packageid)" # showing ok
get-wmiobject -namespace root\sms\site_v01 -class sms_distributiondpstatus -Filter {packageid like "$i.packageid"}|select name

You're calling your variables differently in the parameters than from your Write-Host calls. You can't access object properties without a subexpression in the string $().
$list = import-csv -Path 'C:\Users\Administrator\desktop\SCCM\packagelist.csv'
$DP = 'SCCMDP1','SCCMDP2'
foreach ($i in $list)
{
write-host "Item is $i" # showing ok
write-host "package is $($i.package)" # showing ok
write-host "packageid is $($i.packageid)" # showing ok
Params = #{
Namespace = 'root\sms\site_v01'
Class = 'sms_DistributionDPStatus'
Filter = "PackageId LIKE '%$($i.PackageId)%'"
}
Get-WmiObject #Params | Select-Object -Property Name
}
If you're not going to use wildcards, using the LIKE WQL comparison is unnecessary.

Related

Powershell problem with values comparison in ARS - false positive

I am updating mass info about users. The script is getting data from a file, comparing with the current data in ARS and changing if necessary.
Unfortunately for two parameters - "st" and "postOfficeBox" - it is updating data all the time altho the data is the same in the file and in AD.
first one is empty, the second one is not
I have checked directly -
PS> $user.$parameters.postofficebox -eq $userQuery.$parameters.postofficebox
True
How can I handle this? It is not an error, but it is annoying and not efficient updating the same data all the time.
#Internal Accounts
$Parameters = #("SamAccountName", "co", "company", "department", "departmentNumber","physicalDeliveryOfficeName","streetAddress","l","st","postalCode","employeeType","manager", "division", "title", "edsvaEmployedByCountry", "extensionAttribute4", "EmployeeID", "postOfficeBox")
#import of users
$users = Import-csv -Path C:\ps\krbatch.csv -Delimiter "," -Encoding UTF8
Connect-QADService -Proxy
#Headers compliance
$fileHeaders = $users[0].psobject.Properties | foreach { $_.Name }
$c = Compare-Object -ReferenceObject $fileHeaders -DifferenceObject $Parameters -PassThru
if ($c -ne $null) {Write-Host "headers do not fit"
break}
#Check if account is enabled
foreach ($user in $users) {
$checkEnable = Get-ADUser $user.SamAccountName | select enabled
if (-not $checkEnable.enabled) {
Write-Host $user.SamAccountName -ForegroundColor Red
}
}
#Main loop
$result = #()
foreach ($user in $users) {
$userQuery = Get-QADUser $user.sAMaccountName -IncludedProperties $Parameters | select $Parameters
Write-Host "...updating $($user.samaccountname)..." -ForegroundColor white
foreach ($param in $Parameters) {
if ($user.$param -eq $userQuery.$param) {
Write-Host "$($user.samaccountname) has correct $param" -ForegroundColor Yellow
}
else {
try {
Write-Host "Updating $param for $($user.samaccountname)" -ForegroundColor Green
Set-QADUser -Identity $user.SamAccountName -ObjectAttributes #{$param=$user.$param} -ErrorVariable ProcessError -ErrorAction SilentlyContinue | Out-Null
If ($ProcessError) {
Write-Host "cannot update $param for $($user.samaccountname) $($error[0])" -ForegroundColor Red
$problem = #{}
$problem.samaccountname = $($user.samaccountname)
$problem.param = $param
$problem.value = $($user.$param)
$problem.error = $($error[0])
$result +=[pscustomobject]$problem
}
}
catch { Write-Host "fail, check if the user account is enabled?" -ForegroundColor Red}
}
}
}
$result | Select samaccountname, param, value, error | Export-Csv -Path c:\ps\krfail.csv -NoTypeInformation -Encoding UTF8 -Append
And also any suggestions to my code, where I can make it better will be appreciated.
Similar to what Mathias R. Jessen was suggesting, the way you are testing the comparison doesn't look right. As debugging approaches either add the suggested Write-Host command or a break point such that you can test at run time.
Withstanding the comparison aspect of the question there's a loosely defined advisory request that I'll try to address.
Why are you you using QAD instead of the native AD module. QAD is awesome and still outshines the native tools in a few areas. But, (without a deep investigation) it looks like you can get by with the native tools here.
I'd point out there's an instance capability in AD cmdlets that allows for incremental updates even without comparison... ie you can run the Set-ADUser cmdlet and it will only write the attributes if they different.
Check out the help file for Set-ADUser
It would be inappropriate and time consuming for me to rewrite this. I'd suggest you check out those concepts for a rev 2.0 ... However, I can offer some advice bounded by the current approach.
The way the code is structured it'll run Set-QADUser for each attribute that needs updating rather than setting all the attributes at once on a per/user basis. Instead you could collect all the changes and apply in a single run of Set-QADUser per each user. That would be faster and likely have more compact logging etc...
When you're checking if the account is enabled you aren't doing anything other than Write-Host. If you wanted to skip that user, maybe move that logic into the main loop and add a Continue statement. That would also save you from looping twice.
Avoid using +=, you can use an [ArrayList] instead. Performance & scalability issues with += are well documented, so you can Google for more info. [ArrayList] might look something like:
$result = [Collections.ArrayList]#()
# ...
[Void]$result.Add( [PSCustomObject]$problem )
I'm also not sure how the catch block is supposed to fire if you've set -ErrorAction SilentlyContinue. You can probably remove If($ProcessError)... and and move population of $Result to the Catch{} block.

Select option from Array

I am working on a side project and to make it easier for managment since almost all of out server names are 15 charactors long I started to look for an RDP managment option but none that I liked; so I started to write one and I am down to only one issue, what do I do to manage if the user types not enough for a search so two servers will match the Query. I think I will have to put it in an array and then let them select the server they meant. Here is what I have so far
function Connect-RDP
{
param (
[Parameter(Mandatory = $true)]
$ComputerName,
[System.Management.Automation.Credential()]
$Credential
)
# take each computername and process it individually
$ComputerName | ForEach-Object{
Try
{
$Computer = $_
$ConnectionDNS = Get-ADComputer -server "DomainController:1234" -ldapfilter "(name=$computer)" -ErrorAction Stop | Select-Object -ExpandProperty DNSHostName
$ConnectionSearchDNS = Get-ADComputer -server "DomainController:1234" -ldapfilter "(name=*$computer*)" | Select -Exp DNSHostName
Write-host $ConnectionDNS
Write-host $ConnectionSearchDNS
if ($ConnectionDNS){
#mstsc.exe /v ($ConnectionDNS) /f
}Else{
#mstsc.exe /v ($ConnectionSearchDNS) /f
}
}
catch
{
Write-Host "Could not locate computer '$Computer' in AD." -ForegroundColor Red
}
}
}
Basically I am looking for a way to manage if a user types server1
that it will ask does he want to connect to Server10 or Server11 since both of them match the filter.
Another option for presenting choices to the user is Out-GridView, with the -OutPutMode switch.
Borrowing from Matt's example:
$selection = Get-ChildItem C:\temp -Directory
If($selection.Count -gt 1){
$IDX = 0
$(foreach ($item in $selection){
$item | select #{l='IDX';e={$IDX}},Name
$IDX++}) |
Out-GridView -Title 'Select one or more folders to use' -OutputMode Multiple |
foreach { $selection[$_.IDX] }
}
else {$Selection}
This example allows for selection of multiple folders, but can you can limit them to a single folder by simply switching -OutPutMode to Single
I'm sure what mjolinor has it great. I just wanted to show another approach using PromptForChoice. In the following example we take the results from Get-ChildItem and if there is more than one we build a collection of choices. The user would select one and then that object would be passed to the next step.
$selection = Get-ChildItem C:\temp -Directory
If($selection.Count -gt 1){
$title = "Folder Selection"
$message = "Which folder would you like to use?"
# Build the choices menu
$choices = #()
For($index = 0; $index -lt $selection.Count; $index++){
$choices += New-Object System.Management.Automation.Host.ChoiceDescription ($selection[$index]).Name, ($selection[$index]).FullName
}
$options = [System.Management.Automation.Host.ChoiceDescription[]]$choices
$result = $host.ui.PromptForChoice($title, $message, $options, 0)
$selection = $selection[$result]
}
$selection
-Directory requires PowerShell v3 but you are using 4 so you would be good.
In ISE it would look like this:
In standard console you would see something like this
As of now you would have to type the whole folder name to select the choice in the prompt. It is hard to get a unique value across multiple choices for the shortcut also called the accelerator key. Think of it as a way to be sure they make the correct choice!

Local Groups and Members

I have a requirement to report the local groups and members from a specific list of servers. I have the following script that I have pieced together from other scripts. When run the script it writes the name of the server it is querying and the server's local group names and the members of those groups. I would like to output the text to a file, but where ever I add the | Out-File command I get an error "An empty pipe element is not allowed". My secondary concern with this script is, will the method I've chosen the report the server being queried work when outputting to a file. Will you please help correct this newbies script errors please?
$server=Get-Content "C:\Powershell\Local Groups\Test.txt"
Foreach ($server in $server)
{
$computer = [ADSI]"WinNT://$server,computer"
"
"
write-host "==========================="
write-host "Server: $server"
write-host "==========================="
"
"
$computer.psbase.children | where { $_.psbase.schemaClassName -eq 'group' } | foreach {
write-host $_.name
write-host "------"
$group =[ADSI]$_.psbase.Path
$group.psbase.Invoke("Members") | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
write-host **
write-host
}
}
Thanks,
Kevin
You say that you are using Out-File and getting that error. You don't show_where_ in your code that is being called from.
Given the code you have my best guess is that you were trying something like this
Foreach ($server in $server){
# All the code in this block
} | Out-File c:\pathto.txt
I wish I had a technical reference for this interpretation but alas I have not found one (Think it has to do with older PowerShell versions). In my experience there is not standard output passed from that construct. As an aside ($server in $server) is misleading even if it works. Might I suggest this small change an let me know if that works.
$servers=Get-Content "C:\Powershell\Local Groups\Test.txt"
$servers | ForEach-Object{
$server = $_
# Rest of code inside block stays the same
} | Out-File c:\pathto.txt
If that is not your speed then I would also consider building an empty array outside the block and populate is for each loop pass.
# Declare empty array to hold results
$results = #()
Foreach ($server in $server){
# Code before this line
$results += $group.psbase.Invoke("Members") | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
# Code after this line
}
$results | Set-Content c:\pathto.txt
Worthy Note
You are mixing Console output with standard output. depending on what you want to do with the script you will not get the same output you expect. If you want the lines like write-host "Server: $server" to be in the output file then you need to use Write-Output

Remote Powershell to retrieve specific registry value from lots of servers

I have the following..
$output = #()
$servers =Get-Content "C:\Windows\System32\List3.txt"
foreach ($server in $servers)
{
trap [Exception] {continue}
Import-Module PSRemoteRegistry
$key="SOFTWARE\Microsoft\'Microsoft Antimalware'\'Signature Updates'"
$regkey=Get-RegBinary -ComputerName $server -Key $Key -Value SignatuesLastUpdated
#$regkey=(Get-Item HKLM:\SOFTWARE\Microsoft\'Microsoft Antimalware'\'Signature Updates').getValue('SignaturesLastUpdated')
#$regkey=[datetime]::ParseExact("01/02/03", "dd/MM/yy", $null) | Export-csv -path c:\temp\avinfo.csv -append
#$regkey
}
$output | Select $server , $Regkey | Export-Csv c:\temp\avinfo.csv -NoTypeInformation
I think it's pretty close but doesn't work as needed - can anyone tell me what I am doing wrong here - been reading a lot and managed to get this far, just need the help to finalise.
Thanks
Ok... so there is alot that needed to be changed to get this to work. I will update the answer frequently after this is posted.
$servers = Get-Content "C:\Windows\System32\List3.txt"
$key="SOFTWARE\Microsoft\Microsoft Antimalware\Signature Updates"
$servers | ForEach-Object{
$server = $_
Try{
Get-RegBinary -ComputerName $server -Key $Key -Value SignatuesLastUpdated -ErrorAction Stop
} Catch [exception]{
[pscustomobject]#{
ComputerName = $server
Data = "Unable to retrieve data"
}
}
} | Select ComputerName,#{Label=$value;Expression={If(!($_.Data -is [string])){[System.Text.Encoding]::Ascii.GetBytes($_.data)}Else{$_.Data}}} | Export-Csv c:\temp\avinfo.csv -NoTypeInformation
What the above code will do is more in line with your intentions. Take the list and for each item get the key data from that server. If there is an issue getting that data then we output a custom object stating that so we can tell in the output if there was an issue. The part that is up in the air is how you want to export the binary data to file. As it stands it should create a space delimited string of the bytes.
The issues that you did have that should be highlighted are
No need to import the module for every server. Moved that call out of the loop
You have declared the variable $output but do not populate it during your loop process. This is important for the foreach construct. You were, in the end, sending and empty array to you csv. My answer does not need it as it just uses standard output.
As #Meatspace pointed out you had a typo here: SignatuesLastUpdated
Get-RegBinary does not by default create terminating errors which are needed by try/catch blocks. Added -ErrorAction Stop. Don't think your code trap [Exception] {continue} would have caught anything.
The single quotes you have in your $key might have prevented the path from being parsed. You were trying to escape spaces and just need to enclose the whole string in a set of quotes to achieve that.
While Select can use variables they are there, in a basic form, to select property names. In short what you had was wrong.

Multiple variables in Foreach loop [PowerShell]

Is it possible to pull two variables into a Foreach loop?
The following is coded for the PowerShell ASP. The syntax is incorrect on my Foreach loop, but you should be able to decipher the logic I'm attempting to make.
$list = Get-QADUser $userid -includeAllProperties | Select-Object -expandproperty name
$userList = Get-QADUser $userid -includeAllProperties | Select-Object -expandproperty LogonName
if ($list.Count -ge 2)
{
Write-host "Please select the appropriate user.<br>"
Foreach ($a in $list & $b in $userList)
{
Write-host "<a href=default.ps1x?UserID=$b&domain=$domain>$b - $a</a><br>"}
}
}
Christian's answer is what you should do in your situation. There is no need to get the two lists. Remember one thing in PowerShell - operate with the objects till the last step. Don't try to get their properties, etc. until the point where you actually use them.
But, for the general case, when you do have two lists and want to have a Foreach over the two:
You can either do what the Foreach does yourself:
$a = 1, 2, 3
$b = "one", "two", "three"
$ae = $a.getenumerator()
$be = $b.getenumerator()
while ($ae.MoveNext() -and $be.MoveNext()) {
Write-Host $ae.current $be.current
}
Or use a normal for loop with $a.length, etc.
Try like the following. You don't need two variables at all:
$list = Get-QADUser $userid -includeAllProperties
if ($list.Count -ge 2)
{
Write-Host "Please select the appropriate user.<br>"
Foreach ($a in $list)
{
Write-Host "<a href=default.ps1x?UserID=$a.LogonName&domain=$domain>$a.logonname - $a.name</a><br>"
}
}