Get computers last logon with powershell - powershell

I have found the following script, but not able to get the expected result: computer, lastlogon.
Using Windows 10.
For some reason I am getting this error:
Get-ADComputer : Variable: 'NTName' found in expression: $NTName is not defined.
At line:10 char:19
+ ... astLogon = (Get-ADComputer -Filter {Name -eq $NTName} -Properties Las ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-ADComputer], ArgumentException
+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.ArgumentException,Microsoft.ActiveDirectory.Management.Commands.GetADComputer
And a random number of dates and times:
04/11/2017 07:16:44 04/26/2017 01:08:31 04/26/2017 22:33:51 05/01/2017 07:05:13 04/24/2017 09:01:12 04/25/2017 06:36:43 08/21/2020 21:27:23 01/11/2018 11:47:14 12/27/20
20 14:37:44 01/02/2021 11:24:41 01/01/2021 13:48:16 12/31/2020 14:12:24 01/01/2021 06:57:39 01/05/2021 14:14:14 01/05/2021 21:04:12 01/06/2021 04:37:42 10/13/2015 22:53:5
2 01/01/2015 17:58:55 11/04/2015 04:02:00 06/02/2015 18:46:48 03/23/2011 05:21:05 01/11/2013 23:59:16 01/10/2012 09:46:40 10/16/2015 22:11:38 10/16/2015 22:05:24 04/24/20
18 12:12:35 07/26/2017 13:50:14 04/24/2012 14:22:04 11/17/2014 17:44:11 05/18/2016 13:52:34 03/15/2015 08:52:39 07/21/2016 08:46:35 11/19/2015 06:37:05 02/26/2015 11:50:5
0 02/23/2015 20:03:49 01/22/2015 10:49:49 12/08/2015 14:52:41 02/17/2017 11:05:13 09/08/2015 13:11:49 05/24/2015 13:17:40 05/12/2015 00:51:28 11/05/2015 13:44:19 10/28/20
15 23:28:23 07/23/2015 18:28:34 11/17/2015 17:29:39 10/24/2018 23:43:50 02/15/2016 10:15:05 04/07/2015 10:08:14 02/07/2019 06:07:01 07/26/2016 16:19:09 08/25/2015 12:08:1
9 10/25/2018 05:26:48 03/07/2015 03:30:19 06/10/2015 12:00:27 02/20/2015 10:15:37 08/04/2015 08:48:43 04/14/2015 05:58:17 08/26/2015 16:10:23 02/20/2017 21:02:25 03/14/20
18 14:15:23 08/25/2015 16:45:42 07/09/2015 05:12:25 03/02/2015 13:18:07 04/14/2015 05:37:20 04/22/2015 03:42:14 09/14/2015 13:51:48
# Specify CSV file of computer names.
$File = ".\Computers.csv"
$Computers = Import-Csv -Path $File
ForEach ($Computer In $Computers)
{
$NTName = $Computer.Name
# Retrieve last logon date of the computer (accurate with 14 days).
$LastLogon = (Get-ADComputer -Filter {Name -eq $NTName} -Properties LastLogonDate).LastLogonDate
"$NTName, $LastLogon"

Give this a shot and see if it works.
Please note: ensure that there is a column in the CSV with the first block actually being "Name". That's where $Computers1.Name references for the computer names in that column.
#I would recommend putting the whole file path to your csv here and not just '.\'
#This is the location of your CSV
$File = ".\Computers.csv"
#Here you're importing the CSV into your console and storing it into the $Computers1 variable.
$Computers1 = Import-Csv -Path $File
#Here youre using the $Computers variable to store the computer names being referenced in the "Name" column in your CSV, hence the $Computers1.Name.
#$Computers1.Name like mentioned above, this is just referencing the column beginning with "Name" (assuming this is where the computer names are)
$Computers = $Computers1.Name
#Here youre assigning the variable $Computer to each Computer name stored in $Computers and telling it to do the following FOR EACH Computer in Computers.
ForEach ($Computer In $Computers)
{
#Here you're just querying AD against the list of computers with it referencing $Computer as each computer(like mentioned above), and storying the result in $LastLogon.
$LastLogon = Get-ADComputer -Identity:$Computer -Property LastLogonDate | Select-Object -ExpandProperty LastLogonDate
# Retrieve last logon date of the computer (accurate with 14 days).
#This is just outputting to your console screen your computer name($Computer) and, the result for your Last Logon query ($LastLogon).
"$Computer - $LastLogon"
}

Related

Check and Update multiple attributes of AD users

I am trying to do an update to Active Directory from a CSV.
I want to check each value to see if the AD and CSV values match.
If the AD value and CSV values don't match, then I want to update the AD value.
finally I want to create a log of the values changed, which would eventually be exported to a CSV report.
Now there is about 30 values I want to check.
I could do an if statement for each value, but that seems like the hard way to do it.
I am try to use a function, but I cant seem to get it working.
I am getting errors like:
set-ADUser : replace
At line:94 char:9
+ set-ADUser -identity $ADUser -replace #{$ADValue = $DIAccount ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (JDoe:ADUser) [Set-ADUser], ADInvalidOperationException
+ FullyQualifiedErrorId : ActiveDirectoryServer:0,Microsoft.ActiveDirectory.Management.Commands.SetADUser
set-ADUser : The specified directory service attribute or value does not exist
Parameter name: Surname
At line:94 char:9
+ set-ADUser -identity $ADUser -replace #{$ADValue = $DIAccount ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (JDoe:ADUser) [Set-ADUser], ArgumentException
+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.ArgumentException,Microsoft.ActiveDirectory.Management.Commands.SetADUser
Any suggestions would be welcome
Code I am using:
Function AD-Check ($ADValue, $ADUser, $ADAccount, $UpdateAccount)
{
If ($ADAccount -ne $UpdateAccount)
{
set-ADUser -identity $ADUser -replace #{$ADValue = $UpdateAccount}
$Change = "Updated"
}
Else
{
$Change = "No Change"
}
Return $Change
}
$Import = get-content C:\temp\ADUpdates.csv
Foreach ($user in $Import)
{
$Account = get-aduser $User.Samaccountname -Properties *
#First Name Check
$Test = AD-Check "GivenName" $Account.samaccountname $Account.givenname $user.givenname
$ChangeGivenName = $Test
#Initials Check
$Test = AD-Check "Initials" $Account.samaccountname $Account.Initials $user.Initials
$ChangeInitials = $Test
#Last Name Check
$Test = AD-Check "Surname" $Account.samaccountname $Account.SurnameSurname $user.Surname
$ChangeSurname = $Test
}
Reply to Theo, cant seem to add this any other way...
Thanks Theo, it seems to make sense, but getting an error.
Select-Object : Cannot convert System.Collections.Specialized.OrderedDictionary+OrderedDictionaryKeyValueCollection to one of the following types {System.String,
System.Management.Automation.ScriptBlock}.
changed the following to get all properties for testing and it works.
$Account = Get-ADUser -Filter "SamAccountName -eq '$sam'" -ErrorAction SilentlyContinue -Properties $propsToCheck
Left the following and it kicks the error
$oldProperties = $Account | Select-Object $propsToCheck
Using the following just for testing:
$propertiesMap = [ordered]#{
SamAccountName = 'sAMAccountName'
mail = 'mail'
GivenName = 'givenName'
Initials = 'initials'
Surname = 'sn'
Office = 'physicalDeliveryOfficeName'
MobilePhone = 'mobile'
DistinguishedName = 'DistinguishedName'
}
Starting of with a WARNING:
Replacing user attributes is not something to be taken lightly and you
need to check any code that does that on a set of testusers first.
Keep the -WhatIf switch to the Set-ADUser cmdlet so you
can first run this without causing any problems to the AD.
Only once you are satisfied all goes according to plan, remove the -WhatIf switch.
Please carefully read all inline comments in the code.
In your code you use an input CSV file, apparently with properties and values to be checked/updated, but instead of using Import-Csv, you do a Get-Content on it, so you'll end up with just lines of text, not an array of parsed properties and values..
Next, as Mathias already commented, you need to use the LDAP attribute names when using either the -Add, -Remove, -Replace, or -Clear parameters of the Set-ADUser cmdlet.
To do what you intend to do, I would first create a hashtable to map the PowerShell attribute names to their LDAP equivalents.
To see which property name maps to what LDAP name, you can use the table here
# create a Hashtable to map the properties you want checked/updated
# the Keys are the PowerShell property names as they should appear in the CSV
# the Values are the LDAP AD attribute names in correct casing.
$propertiesMap = [ordered]#{
SamAccountName = 'sAMAccountName'
GivenName = 'givenName'
Initials = 'initials'
Surname = 'sn'
Office = 'physicalDeliveryOfficeName'
Organization = 'o'
MobilePhone = 'mobile'
# etcetera
}
# for convenience, store the properties in a string array
$propsToCheck = $propertiesMap.Keys | ForEach-Object { $_.ToString() }
# import your CSV file that has all the properties you need checked/updated
$Import = Import-Csv -Path 'C:\temp\ADUpdates.csv'
# loop through all items in the CSV and collect the outputted old and new values in variable $result
$result = foreach ($user in $Import) {
$sam = $user.SamAccountName
# try and find the user by its SamAccountName and retrieve the properties you really want (not ALL)
$Account = Get-ADUser -Filter "SamAccountName -eq '$sam'" -ErrorAction SilentlyContinue -Properties $propsToCheck
if (!$Account) {
Write-Warning "A user with SamAccountName '$sam' does not exist"
continue # skip this one and proceed with the next user from the CSV
}
# keep an object with the current account properties for later logging
$oldProperties = $Account | Select-Object $propsToCheck
# test all the properties and create a Hashtable for the ones that need changing
$replaceHash = #{}
foreach ($prop in $propsToCheck) {
if ($Account.$prop -ne $user.$prop) {
$ldapAttribute = $propertiesMap[$prop] # get the LDAP name from the $propertiesMap Hash
# If any of the properties have a null or empty value Set-ADUser will return an error.
if (![string]::IsNullOrWhiteSpace($($user.$prop))) {
$replaceHash[$ldapAttribute] = $user.$prop
}
else {
Write-Warning "Cannot use '-Replace' with empty value for property '$prop'"
}
}
}
if ($replaceHash.Count -eq 0) {
Write-Host "User '$sam' does not need updating"
continue # skip this one and proceed with the next user from the CSV
}
# try and do the replacements
try {
##########################################################################################################
# for safety, I have added a `-WhatIf` switch, so this wll only show what would happen if the cmdlet runs.
# No real action is performed when using '-WhatIf'
# Obviously, there won't be any difference between the 'OLD_' and 'NEW_' values then
##########################################################################################################
$Account | Set-ADUser -Replace $replaceHash -WhatIf
# refresh the account data
$Account = Get-ADUser -Identity $Account.DistinguishedName -Properties $propsToCheck
$newProperties = $Account | Select-Object $propsToCheck
# create a Hashtable with the old and new values for log output
$changes = [ordered]#{}
foreach ($prop in $propsToCheck) {
$changes["OLD_$property"] = $oldProperties.$prop
$changes["NEW_$property"] = $newProperties.$prop
}
# output this as object to be collected in variable $result
[PsCustomObject]$changes
}
catch {
Write-Warning "Error changing properties on user '$sam':`r`n$($_.Exception.Message)"
}
}
# save the result as CSV file so you can open with Excel
$result | Export-Csv -Path 'C:\temp\ADUpdates_Result.csv' -UseCulture -NoTypeInformation

Powershell Script not recognising ForEach-Object as a valid cmdlet

I have writen a powershell script to make ammendments to Active Directory.
I am Getting a funny error.
Here is the script.
#imports the module active directory if it isn't there.
function add-ADmodule()
{
$modules = Get-Module | Where-Object{$_.Name -like "*ActiveDirectory*"}
if($modules -eq $null)
{
Import-Module ActiveDirectory
}
}
#import the data file
$user_csv = import-csv C:\temp\users.csv
#makes the ammendments to the AD object
function ammend-ADUsers($user_csv)
{#this is the loop to make ammendments to each object
$users_csv|ForEach-Object`
{
#assigns each user's AD object to a variable
$user_object = get-aduser -filter * `
-Properties mail |`
Where-Object{$_.mail -like $_."Email Address"}
#ammends the ad object in the above variable
set-aduser -Identity $user_object `
-OfficePhone $_."Office Number" `
-MobilePhone $_."Mobile Number" `
-StreetAddress $_."Street" `
-City $_."City" `
-PostalCode $_."PostCode"
}
}
#this is the main part of the code where it gets executed
add-ADmodule
Write-Verbose "Active Directory Module Added"
ammend-ADUsers($user_csv)
This is the error I am getting.
PS C:\Users\admin> C:\Scripts\ammend-aduser.ps1
ForEach-Object : The term 'ForEach-Object' is not recognized as the name of a
cmdlet, function, script file, or operable program. Check the spelling of the
name, or if a path was included, verify that the path is correct and try again.
At C:\Scripts\ammend-aduser.ps1:18 char:20
+ $users_csv|ForEach-Object`
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (ForEach-Object:String) [], Com
mandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
I am not not sure what could be causing this error or why it is happening.
Your issue is because you have not put a space between the cmdlet and the backtick character, but it would be better to not use a backtick and instead just keep the opening curly brace { on the same line:
$users_csv|ForEach-Object {
You also don't need a backtick after a pipe character. You might want to also consider using splatting instead of backticks to improve your formatting (backticks are generally discouraged as they can be hard to see and easy to use improperly). I suggest the following revision:
$users_csv | ForEach-Object {
#assigns each user's AD object to a variable
$user_object = Get-ADUser -filter * -Properties mail |
Where-Object{$_.mail -like $_."Email Address"}
$Props = #{
Identity = $user_object
OfficePhone = $_."Office Number"
MobilePhone = $_."Mobile Number"
StreetAddress = $_."Street"
City = $_."City"
PostalCode = $_."PostCode"
}
#ammends the ad object in the above variable
Set-ADUser #Props
}

calling a variable in select -expandproperty

I'm trying to make a report that lists a server name, vm name, and notes section from the vm but I cannot seem to get this code to run, it always gives me this error:
Select-Object : Cannot convert 'System.Object[]' to the type
'System.String' required by parameter 'ExpandProperty'. not supported.
At C:\Cooper\Jobs\Get-VmNotes - Copy.ps1:32 char:48 + get-vm -server
FA0150 | Select -expandproperty $server, Name, Notes +
~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidArgument:
(:) [Select-Object], ParameterBindingException +
FullyQualifiedErrorId :
CannotConvertArgument,Microsoft.PowerShell.Commands.SelectObjectCommand
I can get the vmname and notes to output together but I just want to have a column that lists the vcenter server it is associated with.
Expand property is like the same as saying (Get-VM -Server FA0150).Name. It expands the property name you are selecting. You were trying to expand 3 properties (System.Object[]), but it is looking for just a string name of the property you want to expand. Use -Property instead.
get-vm -server FA0150 | Select-Object -Property Name,Notes
get-vm -server FA0150 | Select-Object -Property Name,Notes | Format-Table -Autosize
To also include the server name I made it into a script form since it can no longer be a one-liner:
[CmdletBinding()]
Param ( $Server )
$VMs = Get-VM -Server $Server
$VMs | Select-Object -Properties #(
#{ Label = 'Server';Expression = { $Server } }
'Name'
'Notes'
)
I found a solution to my problem using New-ViProperty. The only problem is now it creates four separate csv files and I want to combine them all into one based on the filename, keeping the same format, and delete the four others based on their filename. Is there an easy way to do this?

How to list AD group membership for AD users using input list?

I'm fairly new PS user... Looking for some assistance with a powershell script to obtain list of security groups user is member of.
To describe what I need:
I have input list (txt file) with many users (samaccountnames). Every name is on a new line.
I need the script to search these names in AD - whole forest, not just one single domain
output should look like "samaccountname" and list of groups this account is member of in one line, so I can sort it in excel
This is the script I have:
$users = Get-Content C:\users.txt
ForEach ($User in $users) {
$getmembership = Get-ADUser $User.Users -Properties MemberOf | Select -ExpandProperty memberof
$getmembership | Out-File -Append c:\membership.txt
}
but it throws me an error:
Get-ADUser : Cannot validate argument on parameter 'Identity'. The argument is null. Supply a non-null argument and try the command again.
At line:4 char:28
+ $getmembership = Get-ADUser <<<< $User.Users -Properties MemberOf | Select -ExpandProperty memberof
+ CategoryInfo : InvalidData: (:) [Get-ADUser], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.GetADUser
Anyway, this script wouldn't search the whole forest.
Sample input list:
username1
username2
username3
username4... etc
Sample output list
username1;group1;group2;group3
username2;group1;group2;group3;group4... etc or something similar
Any help would be greatly appreciated.
First: As it currently stands, the $User variable does not have a .Users property. In your code, $User simply represents one line (the "current" line in the foreach loop) from the text file.
$getmembership = Get-ADUser $User -Properties MemberOf | Select -ExpandProperty memberof
Secondly, I do not believe you can query an entire forest with one command. You will have to break it down into smaller chunks:
Query forest for list of domains
Call Get-ADUser for each domain (you may have to specify alternate credentials via the -Credential parameter
Thirdly, to get a list of groups that a user is a member of:
$User = Get-ADUser -Identity trevor -Properties *;
$GroupMembership = ($user.memberof | % { (Get-ADGroup $_).Name; }) -join ';';
# Result:
Orchestrator Users Group;ConfigMgr Administrators;Service Manager Admins;Domain Admins;Schema Admins
Fourthly: To get the final, desired string format, simply add the $User.Name, a semicolon, and the $GroupMembership string together:
$User.SamAccountName + ';' + $GroupMembership;
Get-ADPrincipalGroupMembership username | select name
Got it from another answer but the script works magic. :)
Or add "sort name" to list alphabetically
Get-ADPrincipalGroupMembership username | select name | sort name
Everything in one line:
get-aduser -filter * -Properties memberof | select name, #{ l="GroupMembership"; e={$_.memberof -join ";" } } | export-csv membership.csv
The below code will return username group membership using the samaccountname. You can modify it to get input from a file or change the query to get accounts with non expiring passwords etc
$location = "c:\temp\Peace2.txt"
$users = (get-aduser -filter *).samaccountname
$le = $users.length
for($i = 0; $i -lt $le; $i++){
$output = (get-aduser $users[$i] | Get-ADPrincipalGroupMembership).name
$users[$i] + " " + $output
$z = $users[$i] + " " + $output
add-content $location $z
}
Sample Output:
Administrator Domain Users Administrators Schema Admins Enterprise Admins Domain Admins Group Policy Creator Owners
Guest Domain Guests Guests
krbtgt Domain Users Denied RODC Password Replication Group
Redacted Domain Users CompanyUsers Production
Redacted Domain Users CompanyUsers Production
Redacted Domain Users CompanyUsers Production

Powershell: How to obtain the complete list of distribution groups

I am completely new to powershell, I have never touched this scripting language before. However, I have some backgrounds in perl and bash scripting. I am trying to implement a small script that will obtain the list of DG in Exchange server, filters the results to get only the groups that have a certain string, corresponding to the current year.
Example: check the year, in this case 2011.
Filter Name Contains 'P11'
Return only the last DG name and parse the first 7 characters.
How could I do this using powershell from an exchange server? Here is what I got:
add-pssnapin Microsoft.Exchange.Management.PowerShell.E2010
# Retrieve all DGs
$temp = Get-DistributionGroup -ResultSize Unlimited |
foreach($group in $temp)
{
write-output "GroupName:$group "
Write-output "GroupMembers:"
Get-DistributionGroupMember $group |ft displayname,alias,primarysmtpaddress
write-output ‘ ‘
}
this results in the following error:
Unexpected token 'in' in expression or statement. At
C:\Users\jfb\Desktop\NewGroupProject.ps1:7 char:18
+ foreach($group in <<<< $temp)
+ CategoryInfo : ParserError: (in:String) [],
ParseException
+ FullyQualifiedErrorId : UnexpectedToken
Remove the trailing | in the line $temp = Get-DistributionGroup -ResultSize Unlimited | and it should work fine.
What is happening is that since you had the | it is treating the foreach as a foreach-object
Try this (not tested). Create a date object,using Get-Date, and format the date to include the last two digits of the year enclosed in asterisks. This would be the wildcard for the Get-DistributionGroup cmdlet. Select the last DG object and expand its name.
$name = Get-Date -Format *Pyy*
$group = Get-DistributionGroup $name | Select-Object -Last 1 -ExpandProperty Name
if($group)
{
$group.Substring(0,7)
}