I have a script that I think will work to get my printers to output their permissions in username format, I'm getting an error "A positional parameter cannot be found that accepts argument '.PermissionSDDL'
Below is my script
$computerfile = get-content "\\test\d$\test\Ltest\test\MW
scripts\test.txt"
ForEach ($computer in $computerfile) {
Get-WmiObject Win32_Printer -ComputerName $computer |
Select-Object Name,Systemname, Local |
Format-Table -AutoSize
}
$printers = Get-Printer Name -Full.PermissionSDDL
ForEach ($Printer in $Printers) {
$objSID = New-Object System.Security.Principal.SecurityIdentifier
$objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
$objUser.Value
}
This happens, as object property is referred in improper a way.
This
$printers = Get-Printer Name -Full.PermissionSDDL
attempts to pass a parameter -Full.PermissionSDDL that Get-Printer doesn't support. What's more, the -Name switch is missing the switch identifier and, well, actual printer name.
The proper syntax is
$printers = (Get-Printer -Name $myPrinter -Full).PermissionSDDL
Related
I did a search and I understand the issue that some of these are arrays. What I am not sure I understand is why I am getting this when I am specifically trying to return just one value.
I am using a ForEach loop and pulling from a text file the names of the computers I want to query.
I want to verify that the IP address I am pinging is the same computer I want to connect to. We have many users on VPN and the DNS doesn't update fast enough. This means we get some incorrect information.
$NIC2 = Get-WmiObject win32_NetworkAdapterConfiguration -ComputerName $computer | Where-Object {$_.DNSDomain -eq "usms.contoso.com"} | Select-Object IPAddress
This should give me only one IP address and not an array, but unfortunately I get the System.String[] in the CSV output. If I just type this command out in the PowerShell command line it works beautifully, but not when I put it into a CSV.
any ideas?
Thanks in advance.
Entire code below - I get the results for everything except the $NIC2 and doing $NIC2.IPAddress[0] doesn't seem to work.
Putting $NIC2[0] just give me this in that column
dapterConfiguration -ComputerName $computer | Where-Object {$_.DNSDomain -eq "contoso.com"} | Select-Object IPAddress
#Definitions of variables
#************************
#Defines the location and name of the text file from where computers will be pulled
$Computers = Get-Content "c:\Temp\computers.txt"
#Defines the variable and format for Date
$Date = Get-date -UFormat %h_%d_%y_%H%M
#Define the name of the files to output
$OutputFile = "C:\Temp\Verify-Computer-$Date.csv" #All Computers On-Line Status will be provided in this file
#Defines the Array where to put the data and the $Ping variable to store the IP address
$DataArray = #()
$Resolution = New-Object System.Object.NetworkInformation.Ping
#Define A Function that will test which computers are on-line and create files to use in the other function.
ForEach ($computer in $Computers)
{
#Defining the variables
$TESTConnection = $null
$Object = $null
$Value = $null
$IPAddress = $null
$NameResolution = $null
$PingAddress = $null
$NIC = $null
$NIC2 = $null
$NICIP = $null
# Ping computer
$TESTConnection = Test-NetConnection -ComputerName $computer | Select-Object PingSucceeded
#If connection is live Get IP address
IF ($TESTConnection.PingSucceeded -eq "True") {
$IPAddress = Test-NetConnection -ComputerName $computer -InformationLevel "Detailed" | Select-Object RemoteAddress
$NIC = Get-WmiObject win32_NetworkAdapterConfiguration -ComputerName $computer | Where-Object {$_.DNSDomain -eq "contoso.com"} | Select-Object MACAddress, DNSHostName
$NIC2 = {Get-WmiObject win32_NetworkAdapterConfiguration -ComputerName $computer | Where-Object {$_.DNSDomain -eq "contoso.com"} | Select-Object IPAddress}
$Value = $TESTConnection.PingSucceeded
$PingAddress = $IPAddress.RemoteAddress
}
Else {
$Value = $TESTConnection.PingSucceeded
$PingAddress = $IPAddress.RemoteAddress
$NICIP = "NOT REACHABLE"
}
# Create object
$Object = New-Object PSObject -Property ([ordered]#{
Computer_Target = $computer
ONLINE = $Value
IPAddress_PING = $PingAddress
IPResolved = $NIC2.IPAddress
NameResolved = $NIC.DNSHostName
MACAddress = $NIC.MACAddress
})
# Add object to array
$DataArray += $Object
#Display object
}
If($DataArray)
{
#Output Array Data into a CSV File
$DataArray | Export-Csv -Path $OutputFile -NoTypeInformation
}
#end of file
OK - I finally figured it out because I kept going around in circles and getting nowhere.
Thank you to Zett42 who led me in the right direction but I was still failing.
My first problem was that I hadn't identified $NIC2 as an Array which was part of my issue when trying $NIC2[0]. However, even after being Defined as an Array it still didn't work.
I then created another array and did this $NICIP = $NIC2.IPAddress and then output $NICIP[0] which works. Not exactly sure why that works.
Here is the entire code
#Definitions of variables
#************************
#Defines the location and name of the text file from where computers will be pulled
$Computers = Get-Content "c:\Temp\computers.txt"
#Defines the variable and format for Date
$Date = Get-date -UFormat %h_%d_%y_%H%M
#Define the name of the files to output
$OutputFile = "C:\Temp\Verify-Computer-$Date.csv" #All Computers On-Line Status will be provided in this file
#Defines the Array where to put the data and the $Ping variable to store the IP address
$DataArray = #()
$Resolution = New-Object System.Object.NetworkInformation.Ping
#Define A Function that will test which computers are on-line and create files to use in the other function.
ForEach ($computer in $Computers)
{
#Defining the variables
$TESTConnection = $null
$Object = $null
$Value = $null
$IPAddress = $null
$NameResolution = $null
$PingAddress = $null
$NIC = $null
$NIC2 = #()
$NICIP = #()
# Ping computer
$TESTConnection = Test-NetConnection -ComputerName $computer | Select-Object PingSucceeded
#If connection is live Get IP address
IF ($TESTConnection.PingSucceeded -eq "True") {
$IPAddress = Test-NetConnection -ComputerName $computer -InformationLevel "Detailed" | Select-Object RemoteAddress
$NIC = Get-WmiObject win32_NetworkAdapterConfiguration -ComputerName $computer | Where-Object {$_.DNSDomain -eq "contoso.com"} | Select-Object MACAddress, DNSHostName
$NIC2 = Get-WmiObject win32_NetworkAdapterConfiguration -ComputerName $computer | Where-Object {$_.DNSDomain -eq "contoso.com"} | Select-Object IPAddress
$Value = $TESTConnection.PingSucceeded
$PingAddress = $IPAddress.RemoteAddress
$NICIP = $NIC2.IPAddress
}
Else {
$Value = $TESTConnection.PingSucceeded
$PingAddress = "NOT REACHABLE"
}
# Create object
$Object = New-Object PSObject -Property ([ordered]#{
Computer_Target = $computer
ONLINE = $Value
IPAddress_PING = $PingAddress
IPResolved1 = $NICIP[0]
NameResolved = $NIC.DNSHostName
MACAddress = $NIC.MACAddress
})
# Add object to array
$DataArray += $Object
#Display object
}
If($DataArray)
{
#Output Array Data into a CSV File
$DataArray | Export-Csv -Path $OutputFile -NoTypeInformation
}
#end of file
I was trying to take advantage of CIM's built-in parallel processing to get information about all the installed printers against a given subnet of computers. The script works faster than my WMI variation, but doesn't return the same information and doesn't always return as much as the Get-WmiObject call.
EDIT: The information the script drops is information about entire computers.
Here's the CIM version:
$Computer = Get-Content -Path c:\Scripts\input.txt
$Objects = foreach ($ComputerName in $Computer) {
# New CIM Instance
Write-Host Collecting information on $ComputerName
$Cim = New-CimSession -ComputerName $ComputerName
# Collect Printer Info
Get-CimInstance -CimSession $Cim -Class Win32_printer -Property deviceid, drivername, portname, systemName
# Define Hashtable properties
$ObjectProperties = #{
SystemName = $Cim.systemName
DeviceID = $Cim.deviceid
DriverName = $Cim.drivername
PortName = $Cim.portname
}
# Create new object
New-Object PSObject -Property $ObjectProperties
}
# Export Results
$Objects | Select DeviceID, DriverName, PortName, SystemName |
Export-Csv - NoTypeInformation -Path c:\Scripts\output.csv
Here's the WMI version:
$results = #()
$Computer = Get-Content -Path c:\Scripts\input.txt
# Check each computer in the list
foreach ($ComputerName in $Computer) {
$results += Get-WmiObject -Class Win32_printer -cn $ComputerName |
Select deviceid, drivername, portname, systemName
Start-Sleep -Milliseconds 500
}
# Export to CSV file
$Results | Select DeviceID, DriverName, PortName, SystemName |
Export-Csv -NoTypeInformation -Path c:\Scripts\output.csv
We sometimes need to run this script against multiple subnets. I moved to the CIM sessions because it reduced the total run of the script to consistently under 5 minutes, but if it's not going to return all of the information, it might be better to wait.
Does anyone have any idea on how to prevent CIM from dropping information?
It should be noted that WinRM is not enabled by default on these machines and the script has to force enable CIMs with the following command.
& "c:\Scripts\SnIPT\psexec.exe" \\$ComputerName -s -d -n 5 winrm.cmd quickconfig -q -force
The same WMI-class should return the same data (however CIM-cmdlets convert dates ++). Since you haven't explained what's different I'd guess it's missing output for certain computers. Usually this is because the target computer is missing Windows Management Framework 3.0 or later (think PS 3.0+) which is required for CIM. If that's the case, it should generate an error that you can catch and use to use DCOM (same as WMI) as a fallback. Ex:
$Computer = Get-Content -Path c:\Scripts\input.txt
$DCOM = New-CimSessionOption -Protocol Dcom
$Objects = ForEach($ComputerName in $Computer)
{
#New Cim Instance with fallback to DCOM
Write-Host Collecting information on $ComputerName
$Cim = $null
try {
$Cim = New-CimSession -ComputerName $ComputerName -ErrorAction Stop
} catch [Microsoft.Management.Infrastructure.CimException] {
#CIM not available on target (requires WMF 3.0+). Using DCOM (used by WMI)
try { $Cim = New-CimSession -ComputerName $ComputerName -SessionOption $DCOM -ErrorAction Stop }
catch { Write-Host $_.Exception.Message }
}
#Collect Printer Info
Get-CimInstance -CimSession $Cim -Class Win32_printer -Property DeviceID, DriverName, PortName, SystemName
#Best practice to store the original object.
#No need to create a new one with a few properties when you do it during export anyways.
#If you really need it, add "| Select-Object -Property DeviceID, DriverName, PortName, SystemName" to the previous line
}
#Export Results
$Objects | Select-Object -Property DeviceID, DriverName, PortName, SystemName | Export-Csv - NoTypeInformation -Path c:\Scripts\output.csv
I'm trying to pull default gateway info from get-wmiobject and then add a new property to an object, using switch to fill in the info, by using a Foreach-object within a foreach. I'm knocking my head on the wall. Can you tell me what I'm doing wrong?
I can export the CN and the DefaultIpgateway fine, but the location property and the $location variable isn't updating. I want to get that $location variable to switch and then put the data into Location Property of $output.
I'm very new to Powershell. Any help is appreciated.
$hostname = Get-Content -path C:\workspace\location_project\Workspace_818\pcs.txt
$output = foreach ($h in $hostname) {
#get-wmiobject will get the comptuer info for each system.
get-wmiobject Win32_NetworkAdapterConfiguration -filter "IPEnabled=TRUE" -computername $H | foreach-object {
new-object PSObject -property #{
"CN" = $h
"DefaultIPGateway" = $_.DefaultIPGateway[0]
"Location" = $location ## = $output."DefaultIPGateway"[0])
}
switch ($output."DefaultIPGateway") {
10.10.1.2{$location = "Location1"}
10.10.1.20{$location = "Locaiton2"}
}
select-object DefaultIPGateway, CN, Location
}
}
$output | Export-csv C:\workspace\CN_Gateway_Loc_Get2.csv -NoTypeInformation -force
In your sample code, you add $location the object, when its still empty. You have to set $location before you add it to the object.
Further considerations:
use Get-CimInstance over Get-WmiObject because it uses WinRM to connect to remote computers by default and acceppt CimSession as input, which is has the lowest footprint. And maybe most important: It queries the computers in parallel which makes the complete script in scale suuuuper fast!
Get-CimInstance (as well as Get-WmiObject) accepts multiple CimSessions/ComputerNames as input, hence you don't need the foreach.
[PSCustomObject] is faster then New-Object
After applying the improvements, your code would look somewhat like this:
$hostname = Get-Content -path C:\workspace\location_project\Workspace_818\pcs.txt
$csvPath = 'C:\workspace\CN_Gateway_Loc_Get2.csv'
$CimSessions = New-CimSession -ComputerName $hostname
$NetworkAdapterConfiguration = Get-CimInstance -CimSession $CimSessions -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled=TRUE"
$output = foreach ($adapter in $NetworkAdapterConfiguration) {
switch ($adapter.DefaultIPGateway[0]) {
'10.10.1.2' {$location = 'Location1'}
'10.10.1.20' {$location = 'Location2'}
default {$location = 'Unknown'}
}
[PSCustomObject]#{
ComputerName = $adapter.PSComputername
DefaultIPGateway = $adapter.DefaultIPGateway[0]
Location = $location
}
}
$output | Export-Csv -Path $csvPath -NoTypeInformation -Force
If that works for you, I highly recommend to convert your script into a tool (Cmdlet). Make a function that allows ComputerName and CimSession as input. Output would be your already defined object. This would allow anyone to use the tool as he needs to use it (instead of, how you want them to use it). If your new Cmdlet is called Get-DefaultGateway you can do things like this:
Get-DefaultGateway -ComputerName (Get-ADComputer).Name | Out-GridView
or
$CimSessions = Get-Clipboard | New-CimSession
Get-DefaultGateway -CimSession $CimSessions | Export-Csv -Path $env:TEMP\export.csv
I would do something like this. You need to get the location first. So I set $location to the result of switch. Then I create the pscustomobject with the parameters you wanted. You don't need a Select-Object because the three attributes are the only attributes in the object.
$hostname = Get-Content -Path C:\workspace\location_project\Workspace_818\pcs.txt
$output = foreach ($h in $hostname) {
#get-wmiobject will get the comptuer info for each system.
Get-WmiObject Win32_NetworkAdapterConfiguration -Filter "IPEnabled=TRUE" -ComputerName $H `
| ForEach-Object -Process {
$location = switch ($_.DefaultIPGateway) {
10.10.1.2 { 'Location1'; break }
10.10.1.20 { 'Locaiton2'; break }
default { 'unknown' }
}
[pscustomobject] #{
"CN" = $h
"DefaultIPGateway" = $_.DefaultIPGateway[0]
"Location" = $location
}
}
}
$output | Export-csv C:\workspace\CN_Gateway_Loc_Get2.csv -NoTypeInformation -force
I am learning to create new objects and combine properties from other objects. In this script I want to find out what the PS version is and also add some other properties like OS, IP etc... but I am running into 2 problems. We have 6 domains and I can't seem to iterate over each domain. I tried (Get-ADForest).Domains and can see the list of domains. It still only returns objects in the domain my workstation belongs to. The second issue is the Invoke-Command. The version always returns 5. I know many of the servers being returned do not have PSVersion 5.
function Get-PSVersion {
(Invoke-Command -Scriptblock {$PSVersionTable.PSVersion}) | Select Major
}
$servers = Get-ADComputer -Filter {(enabled -eq $true) -and (OperatingSystem -like "Windows Server* *")} -Properties * |
ForEach-Object {
$ps = Get-PSVersion
$server = $_
New-Object -TypeName PSObject -Property #{
Name = $server.Name
OS = $server.OperatingSystem
IPAddress = $server.IPv4Address
Location = $server.CanonicalName
PSVersion = $ps.Major
}
}
$servers | Select Name,Location,OS,IPAddress,PSVersion | FT -AutoSize
Ok so starting with the Invoke-Command, You need to tell that cmdlet which server to target, just calling it as you loop over server names will keep calling it on your local computer, so you'll need to use the -computername parameter, and provide your function an argument to pass to invoke-command. Which would look something like this:
function Get-PSVersion($name) {
(Invoke-Command -ComputerName $name -Scriptblock {$PSVersionTable.psversion | Select Major})
}
You'll notice I also moved your select, this isn't strictly necessary but imo it looks cleaner and means slightly less data gets sent over the network. note that this will create an object with a single property called Major, if you want just the version number returned as an integer you'd want to do it like this
function Get-PSVersion($name) {
(Invoke-Command -ComputerName $name -Scriptblock {$PSVersionTable.psversion.Major})
}
You'll need to add an extra loop into the script if you want to target more than one domain, basically you want an array of the domains you wish to target and then loop over that array calling get-adcomputer for each and specifying the domain name for the -server parameter. I've put a simplified example below you can incorporate into your own code.
$arr = #("test.domain","othertest.domain")
foreach($domain in $arr){
Get-ADComputer -Filter * -Server $domain
}
Hope that helps!
Got it to work. Thanks for the assistance.
clear-host
$arr = #("x.local","x.local")
foreach($domain in $arr){
$servers = (Get-ADComputer -Filter {(enabled -eq $true) -and (OperatingSystem -like "Windows Server* *")}-Server $domain -Properties *|Select -First 10)
}
$MasterList = #()
foreach ($server in $servers) {
$MyObj = New-Object PSObject -Property #{
Name = $server.Name
Os = $server.OperatingSystem
Ip = $server.IPv4Address
PSV = Invoke-Command -ComputerName $server.Name -ScriptBlock {$PSVersionTable.psversion}
}
$MasterList += $MyObj
}
$MasterList|Select Name,PSV,OS,IP
I am a complete noob with regards to programming logic and some help would be greatly appreciated. My question concerns the Get-WmiObject win32_bios and Get-WmiObject win32_computersystem commandlets in the Try Block. The both work as expected if they are in there without the other, but not together. It produces an email report with all computers in domain that looks something like this:
ServerName BIOS version Serial Number
exserver DELL - 1 Phoenix ROM BIOS PLUS Version 1.10 2.7.0 3ZTVDC1
I want to add the model number, but that property is not in win32_bios (at least that I know of). So, I was going to grab it from win32_computersystem. Like I said, them both work, just not together. It always just runs whichever commandlet is first and then goes to the next computer in the list.
Import-Module ActiveDirectory
function getthebios {
$badcomp = #()
$CompList = Get-ADComputer -Filter 'name -like "*"' | select -ExpandProperty Name
foreach ($c in $CompList) {
Try {
Get-WmiObject win32_bios -ComputerName $c |
Select-Object #{l='ServerName';e= {$_.__SERVER} },
#{l='BIOS version';e = {$_.BIOSVersion} },
#{l='Serial Number';e = {$_.SerialNumber} }
Get-WmiObject win32_computersystem -ComputerName $c |
Select-Object #{l='Model Number';e = {$_.Model} }
}
Catch {
$badcomp += $c
}
}
"the following servers could not be reached:"
$badcomp
}
Send-MailMessage -To person#example.com -From "person#example.com" -SmtpServer
server.domain.net -Subject "BIOS Version Report" -body (getthebios | Sort-Object 'BIOS version'
| convertTo-Html | out-string ) -BodyAsHtml
I remember reading about functions returning all output. However the output you are using the second time would not match the object of the previous one. I imagine it is getting discarded due to a mismatch. What you need to do is create a single object with data from both queries. If you have PowerShell 3.0 this should work in place of your Try block
$bios = Get-WmiObject win32_bios -ComputerName $c | Select-Object __SERVER, BIOSVersion, SerialNumber
$computerSystem = Get-WmiObject win32_computersystem -ComputerName $c | Select-Object Model
[psCustomObject]#{
"ServerName" = $bios.__SERVER
"BIOS version" = $bios.BIOSVersion
"Serial Number" = $bios.SerialNumber
"Model" = $computerSystem.Model
}
Capture the output of both WMI calls into variables. Output the results from both in a single object. The Select-Object statements on both lines are not required but since we only need that data it made sense. Now the outputed data will match. The following would work in 2.0 PowerShell if you have that
New-Object psobject -Property #{
"ServerName" = $bios.__SERVER
"BIOS version" = $bios.BIOSVersion
"Serial Number" = $bios.SerialNumber
"Model" = $computerSystem.Model
}
Update from comments
I noticed this as well. What I did to get past that was to cast it as string
"BIOS version" = [string]$bios.BIOSVersion