I'm trying to create a working PowerShell script. However, it doesn't quite work correctly in the function, and we think it's because of the type of format.
Could you help me pass this function to a cmd/batch command?
$datos = quser
foreach($id in $datos)
{
$nom = ""
$userid = $id -split '\s+'
$ad = $userid[0].Replace(">","")
if ($ad -eq $usuario)
{
$nom = $ad
$userid[2]
}
}
It is a function to get only the ID from the quser command utility.
This PowerShell will output the active console session ID from quser.exe. Save to file Get-Qusers.ps1
$pattern = '(?<username>.*?) {2,}(?<sessionname>.*?) {2,}(?<id>.*?) {2,}(?<state>.*?) {2,}(?<idletime>.*?) {2,}(?<logontime>.*?)$'
$quser = quser.exe
($quser | Select-String -Pattern $pattern ).Matches | Select-Object -Skip 1 | ForEach-Object {
[PSCustomObject]#{
Username = $_.Groups['username'].Value.Trim() -replace '>'
Sessionname = $_.Groups['sessionname'].Value
Id = $_.Groups['id'].Value
State = $_.Groups['state'].Value
IdleTime = $_.Groups['idletime'].Value
LogonTime = $_.Groups['logontime'].Value
}
} |
Where-Object { $_.Sessionname -eq 'console' } |
Select-Object -ExpandProperty Id
To run the ps1 script in a batch file and save the Id to a variable
echo off
FOR /F "tokens=*" %%g IN ('powershell.exe -f get-qusers.ps1') do (SET QuserId=%%g)
echo %QuserId%
Related
I have a Powershell script that queries for the pwdLastSet attribute for every user in
the Active Directory domain. Essentially, the script determines when each user in the domain last changed their password. However, when I try and output the result using scriptname.ps1 | Export-Csv "filename.csv" it creates the file, however, I'm not getting the results I see in the console. I'm getting the following:
When I run the script without Export-Csv the results I desire display correctly.
This is the Powershell script:
Trap {"Error: $_"; Break;}
$D = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$Domain = [ADSI]"LDAP://$D"
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.PageSize = 200
$Searcher.SearchScope = "subtree"
$Searcher.Filter = "(&(objectCategory=person)(objectClass=user))"
$Searcher.PropertiesToLoad.Add("distinguishedName") > $Null
$Searcher.PropertiesToLoad.Add("pwdLastSet") > $Null
$Searcher.PropertiesToLoad.Add("userAccountControl") > $Null
$Searcher.SearchRoot = "LDAP://" + $Domain.distinguishedName
$Results = $Searcher.FindAll()
ForEach ($Result In $Results)
{
$DN = $Result.Properties.Item("distinguishedName")
$PLS = $Result.Properties.Item("pwdLastSet")
$UAC = $Result.Properties.Item("userAccountControl")
# Retrieve user password settings to check if password can expire.
$blnPwdExpires = -not (($UAC.Item(0) -band 64) -or ($UAC.Item(0) -band 65536))
If ($PLS.Count -eq 0)
{
$Date = [DateTime]0
}
Else
{
# Interpret 64-bit integer as a date.
$Date = [DateTime]$PLS.Item(0)
}
If ($Date -eq 0)
{
# 0 really means never.
$PwdLastSet = "<Never>"
}
Else
{
# Convert from .NET ticks to Active Directory Integer8 ticks.
# Also, convert from UTC to local time.
$PwdLastSet = $Date.AddYears(1600).ToLocalTime()
}
"$DN;$blnPwdExpires;$PwdLastSet"
}
There are two possible issues on your code, the first one, Export-Csv is expecting an object or object[] as input and will convert it to CSV format, you're already passing a formatted semi-colon delimited string[].
In this case you should use | Out-File path\to\csv.csv instead of Export-Csv.
Do not format objects before sending them to the Export-CSV cmdlet. If Export-CSV receives formatted objects the CSV file contains the format properties rather than the object properties.
An example of what you're passing to the cmdlet and what it actually expects:
PS \> 0..5 | ForEach-Object{ 'asd;asd;asd' } | ConvertTo-Csv
#TYPE System.String
"Length"
"12"
"12"
"12"
"12"
"12"
"12"
PS \> 0..5 | ForEach-Object{ [pscustomobject]#{col1='asd';col2='asd';col3='asd'} } | ConvertTo-Csv -Delimiter ';'
#TYPE System.Management.Automation.PSCustomObject
"col1";"col2";"col3"
"asd";"asd";"asd"
"asd";"asd";"asd"
"asd";"asd";"asd"
"asd";"asd";"asd"
"asd";"asd";"asd"
"asd";"asd";"asd"
The alternative to this, and cleaner approach in my opinion, would be to cast a [pscustomobject]on each iteration of your loop and then pass the resulting array to Export-Csv (code below).
The other possible issue, assuming you're choosing the path of using [pscustomobject], could be that $Result.Properties.Item(...) will yield an object of the type System.DirectoryServices.ResultPropertyValueCollection and you would need to convert it to [string] before passing the results to Export-Csv (also code below).
# Save the resulting pscustomobject array to the $output variable
$output = ForEach ($Result In $Results)
{
...
...
...
# All code should be as is up until:
# "$DN;$blnPwdExpires;$PwdLastSet" => Remove this line
[pscustomobject]#{
DistinguishedName = [string]$DN
blnPwdExpires = [string]$blnPwdExpires
pwdLastSet = [string]$PwdLastSet
}
}
# Then pipe the result to Export-Csv
$output | Export-Csv path\to\csv.csv -NoTypeInformation -Delimiter ';'
I found this script Get-ProcessPlus by PMental here on Stack Overflow. With the help of zett42
and several others I manged to get it to run (very new to PS). Thanks guys, It had everything I was really looking for.
I opted too see if I could have the script add one more feature. I wanted it too return the commandline value of the process. I got it to partially work. With my modifications it still runs as default, and by Id, but no longer by name. I have done quite a bit of reading but still can not get it to work properly. Here is the code and my mods. Any help would be appreciated.
*$Command = Get-WmiObject Win32_Process | select name, CommandLine*
function Get-ProcessPlus {
[CmdletBinding(DefaultParameterSetName = 'Default')]
param (
[Parameter(ParameterSetName='ProcessName',Position = 0)]
[string[]]
$Name,
*[Parameter(ParameterSetName='CommandLine',Position = 0)]
[string[]]
$Command,*
[Parameter(ParameterSetName='PID',Position = 0)]
[int[]]
$Id
)
# Check which parameter set is in use and get our processes
switch ($PSCmdlet.ParameterSetName) {
'ProcessName' {
$AllProcesses = Get-Process -Name $Name
break
}
*'CommandLine' {
$AllProcesses = Get-Process -Name $Command
break
}*
'PID' {
$AllProcesses = Get-Process -Id $Id
break
}
default { $AllProcesses = Get-Process }
}
foreach ($Process in $AllProcesses) {
# Retrieve TCP and UDP Connection information for the current process (if any)
$UDPConnections = Get-NetUDPEndpoint -OwningProcess $Process.Id -ErrorAction Ignore |
Select-Object LocalAddress,LocalPort
$TCPConnections = Get-NetTCPConnection -OwningProcess $Process.Id -State Listen -ErrorAction Ignore |
Select-Object LocalAddress,LocalPort
$TCPPorts = $TCPConnections.LocalPort | Where-Object { $null -ne $_} | Select-Object -Unique
$UDPPorts = $UDPConnections.LocalPort | Where-Object { $null -ne $_} | Select-Object -Unique
$TCPAddresses = $TCPConnections.LocalAddress | Select-Object -Unique
$UDPAddresses = $UDPConnections.LocalAddress | Select-Object -Unique
# Collect and output all information about the current process
[PSCustomObject] #{
'ProcessName' = $Process.ProcessName
'Id' = $Process.Id
'Description' = $Process.Description
'Path' = $Process.Path
*'CommandLine' = $Process.Command*
'CPU usage (s)' = $Process.CPU
'TCP Addresses' = $TCPAddresses
'TCP Ports' = $TCPPorts
'UDP Addresses' = $UDPAddresses
'UDP Ports' = $UDPPorts
}
}
}
You need to match the Get-WMIObject output to the Get-Process Output. In your case, just replace this line:
'CommandLine' = $Process.Command
With this:
# Query WMI for process command line
'CommandLine' = Get-WmiObject -Query "
SELECT CommandLine from Win32_Process WHERE ProcessID = $($Process.ID)" |
# Select only the commandline property so we can display it
Select -ExpandProperty CommandLine
My output looks like so:
Get-ProcessPlus -Name notepad
ProcessName : notepad
Id : 10568
Description : Notepad
Path : C:\WINDOWS\system32\notepad.exe
CommandLine : "C:\WINDOWS\system32\notepad.exe" C:\temp\test.txt
CPU usage (s) : 0.390625
Sorry if the description is unclear, but I couldn't think of how else to word it.
I have two CSV files:
LocalAdmins.csv -- ColumnA = PC name; ColumnB = username in local admin group
Exempt.csv -- ColumnA = PC name; ColumnB = username allowed to be a local admin
What I'm trying to do is loop through LocalAdmins.csv, and for each one check to see if the PC name shows up in Exempt.csv (or matches any defined naming patterns in that file), and if a match is found, check to see if the local admin username for that PC in LocalAdmins.csv shows up in the list of AllowedUsers for that PC in Exempt.csv.
If the username is NOT in the AllowedUsers list, or if the PC name is not in Exempt.csv, then output the entry from LocalAdmins.csv. Here is what I have so far:
$admins = Import-Csv .\LocalAdmins.csv
$exempt = Import-Csv .\Exempt.csv
$violations = ".\Violations.csv"
foreach ($admin in $admins) {
foreach ($item in $exempt) {
if ($admin.PC -like $item.PC) {
if ($admin.Name -notin ($item.AllowedUsers -split ",")) {
$admin | Export-Csv $violations -Append -NoTypeInformation
}
}
else {
$admin | Export-Csv $violations -Append -NoTypeInformation
}
}
}
The problem is the nested foreach loop generates duplicates, meaning if there are 3 lines in Exempt.csv then a single entry in LocalAdmins.csv will have 3 duplicate outputs (one for each line in Exempt.csv). So the output looks like this:
When it should look like this:
I'm guessing the problem is somewhere in the structure of the loops, but I just need some help figuring out what to tweak. Any input is greatly appreciated!
Not optimized (unique sort by any property should work):
$admins = Import-Csv .\LocalAdmins.csv
$exempt = Import-Csv .\Exempt.csv
$violations = ".\Violations.csv"
$(
foreach ($admin in $admins) {
foreach ($item in $exempt) {
if ($admin.PC -like $item.PC) {
if ($admin.Name -notin ($item.AllowedUsers -split ",")) {
$admin
}
}
else {
$admin
}
}
}
) | Sort-Object -Property PC, Name -Unique |
Export-Csv $violations -Append -NoTypeInformation
With better restrictions of the forEach, there shouldn't be duplicates
and no need to Sort -unique.
Getting input from here-strings
## Q:\Test\2019\02\05\SO_54523868.ps1
$admins = #'
PC,NAME
XYZlaptop,user6
workstationXYZ,user7
computerABC,user8
ABClaptop,user1
'# | ConvertFrom-Csv # .\LocalAdmins.csv
$exempt = #'
PC,AllowedUsers
*laptop,"user1,user2"
computerXYZ,"user3,user4"
workstation*,"user5"
'# | ConvertFrom-Csv # .\Exempt.csv
$violationsFile = ".\Violations.csv"
$violations = foreach ($admin in $admins) {
$violation = $True
foreach ($item in ($exempt|Where-Object {$admin.PC -like $_.PC})){
if ($admin.NAME -in ($item.AllowedUsers -split ',')){
$violation = $False
}
}
if ($violation){$admin}
}
$violations
$violations | Export-Csv $violationsFile -NotypeInformation
## with Doug Finke's ImportExcel module installed, you can directly get the excel file:
#$violations | Export-Excel .\Violatons.xlsx -AutoSize -Show
I need to check the warranty of many servers, but the output returned by the module I found in https://www.powershellgallery.com/packages/HPWarranty/2.6.2 seems to be a hashtable and the first column contains what I want to be my rows.
the script below will return this where every 5 rows the fields repeat - output1.csv:
TYPE System.Management.Automation.PSCustomObject
"Component","Codecount"
"SerialNumber","CZ36092P5H"
"ProductNumber","727021-B21"
"OverallEntitlementStartDate","2016-03-04"
"OverallEntitlementEndDate","2019-04-02"
"ActiveEntitlement","true"
"SerialNumber","CZ36092P5K"
"ProductNumber","727021-B21"
"OverallEntitlementStartDate","2016-03-04"
"OverallEntitlementEndDate","2019-04-02"
"ActiveEntitlement","true"
How can I transpose the output so that SerialNumber, ProductNumber, OverallEntitlementStartDate, OverallEntitlementEndDate and ActiveEntitlement are the columns?
# variables
$dest_path = "C:\Scripts\HPE\HPWarranty"
$export_date = Get-Date -Format o | ForEach-Object {$_ -replace ":", "-"}
$myScriptName = $MyInvocation.MyCommand.Name
$transcriptPath = $dest_path + "\" + $myScriptName + "_transcript_" + $export_date + ".txt"
$csvPath = $dest_path + "\" + "hpe_list1.csv"
#Start transcript of script activities and set transcript location
start-transcript -append -path $transcriptPath | Out-Null
# import serials & part numbers to be processed
$csv_info = Import-Csv $csvPath
foreach ($line in $csv_info) {
$hash = (Get-HPEntWarrantyEntitlement -ProductNumber $line.ProductNumber -SerialNumber $line.SerialNumber)
&{$hash.getenumerator() |
ForEach-Object {new-object psobject -Property #{Component = $_.name;Codecount=$_.value}}
} | Export-Csv "C:\Scripts\HPE\HPWarranty\output1.csv" -Append
}
# Stop Transcript
Stop-Transcript | Out-Null
hpe_list1.csv that the script processes contains the details for two servers:
ProductNumber,SerialNumber
727021-B21,CZ36092P5H
727021-B21,CZ36092P5K
Cast the output hashtable to a [pscustomobject]:
$WarrantyInfo = foreach ($line in $csv_info) {
[pscustomobject](Get-HPEntWarrantyEntitlement -ProductNumber $line.ProductNumber -SerialNumber $line.SerialNumber)
}
$WarrantyInfo | Export-Csv "C:\Scripts\HPE\HPWarranty\output1.csv"
I got this Powershell script that queries users that have not changed their password for 24 hours. The query redirects the output to csv file. Below are the Powershell script and batch script:
Powershell script:
$root = [ADSI]''
$searcher = new-object System.DirectoryServices.DirectorySearcher($root)
$searcher.filter = "(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
$searcher.sizelimit = 5000
[Void]$searcher.PropertiesToLoad.Add("cn")
[Void]$searcher.PropertiesToLoad.Add("samAccountName")
[Void]$searcher.PropertiesToLoad.Add("pwdLastSet")
$users = $searcher.findall()
$UserOU = "OU=Mountain,DC=Atlanta,DC=ga"
$PWDays = (Get-Date).AddDays(-1)
$UserCount = 0
$UserPW = 0
foreach($user in $users)
{
if ($user.path -like "*$UserOU")
{
$usercount = $UserCount
if ([datetime]::FromFileTime(($user.properties.pwdlastset)[0]) -le $PWDays)
{
$UserPW = $UserPW + 1
Write-Host $user.Properties.cn
}
}
}
Batch script:
powershell.exe d:\temp\query.ps1 > D:\temp\query.csv
My question is: How do I put change the script to put header for username in the the csv output file?
The header may simple be 'Username' not necessarily Firstname and Lastname.
Any reason why you aren't using Export-Csv? You can just pipe your objects into it and it will include headers. Something along the lines of
$users |
? { $_.Path -like "*$UserOU" } |
? { [datetime]::FromFileTime(($user.properties.pwdlastset)[0]) -le $PWDays } |
% { $_ | Add-Member -PassThru NoteProperty Username $_.Properties.cn } |
select Username |
Export-Csv D:\temp\query.csv
might work. (Hint: The pipeline is more fun than the loop :))
Not sure (never have user PS) but I guess that sticking
Write-Host "Username"
before the foreach, might do the trick