List users whose accounts isn't locked - linux

I have created a script which lists list of users from the server and emails them.
I can get the user list from multiple host, and also it filters a file called ignore.txt (which has unwanted users) before sending the list.
The challenge is now, that /etc/passwd has users whose account are locked (not active). Is it possible to edit the script below and send a list of users who are active, and aslo a list of users whose account is locked?
The new report should reflect a list of active users and also a list of disabled/locked users.
My current script is the following:
#!/bin/ksh
#title :user.list.script.ksh
#description :This script collects list of users from the server
#note :Multiple hosts can be added
#HOSTS format:
HOSTS="00.00.00.00-DEV" #example "hostname-DEV"
FILE=/home/zaira/report.txt #list will be saved in report.txt
date > $FILE
echo >> $FILE
for host in $HOSTS; do
ip=$(echo $host|cut -d '-' -f 1)
title=$(echo $host|cut -d '-' -f 2)
ssh $ip -q -l zaira cat /etc/passwd | awk -F: '{printf("%-30s\t%-30s\t%-40s\n ", $1, $3, $5)}' >/tmp/temp 2>/dev/null
echo "$title instance" >> $FILE
perl -e 'print "=" x 65,"\n"' >>$FILE
#we filter through ignore.list to remove unwanted users
cat /tmp/temp|grep -wvf /home/zaira/ignore.list|sort >> $FILE
echo >> $FILE
done
rm -f /tmp/temp
mailx -r From-DEV-no-reply#abc.com -s " User list" zairabanu#abc.com < "$FILE"

Going from memory, as it's been a loo,ooo,ooo,ong time since I've used AIX, you can use lsuser to get locked accounts:
lsuser -a account_locked ALL | grep '=true$' | awk '{ print $1 }'
If you don't want to manually maintain an ignore list, you can get a list of those users who can log in:
lsuser -a login ALL | grep '=true$' | awk '{ print $1 }'
With respect to the OP original script, you can probably now do something like:
lsuser -a login ALL | grep '=true$' | awk '{ print $1 }' > /tmp/temp
Actually modifying the original script is left up to the OP.

Related

2 Linux scripts nearly identical. Variables getting confused between to different scripts

I have two scripts. The only difference between the two scripts is the log file name and the device ip address that it fetches the data from. The problem is that the log file that concats continuously mixes up and starts writing the contents of one device onto the log of the other. So, 1 particular log file randomly switches from showing the data from one device to the other device..
Here is a sample of what it gets from the curl call.
{"method":"uploadsn","mac":"04786364933C","version":"1.35","server":"HT","SN":"267074DE","Data":[7.2]}
I'm 99% the issue is with the log variable, as one script runs every 30 minutes and one script runs every 15 minutes, so i can tell by the date stamps that the issue is not from fetching from the wrong device, but the concatenating of the files. It appears to concat the wrong file to the new file....
Here is the code of both.
#!/bin/bash
log="/scripts/cellar.log"
if [ ! -f "$log" ]
then
touch "$log"
fi
now=`date +%a,%m/%d/%Y#%I:%M%p`
json=$(curl -m 3 --user *****:***** "http://192.168.1.146/monitorjson" --silent --stderr -)
celsius=$(echo $json | cut -d "[" -f2 | cut -d "]" -f1)
temp=$(echo "scale=4; $celsius*1.8 + 32" | bc)
line=$(echo $now : $temp)
echo $line
echo $line | cat - $log > temp && mv temp $log | sed -n '1,192p' $log
and here is the second
#!/bin/bash
log="/scripts/gh.log"
if [ ! -f "$log" ]
then
touch "$log"
fi
now=`date +%a,%m/%d/%Y#%I:%M%p`
json=$(curl -m 3 --user *****:***** "http://192.168.1.145/monitorjson" --silent --stderr -)
celsius=$(echo $json | cut -d "[" -f2 | cut -d "]" -f1)
temp=$(echo "scale=4; $celsius*1.8 + 32" | bc)
line=$(echo $now : $temp)
#echo $line
echo $line | cat - $log > temp && mv temp $log | sed -n '1,192p' $log
Example of bad log file (shows contents of both devices when should only contain 1):
Mon,11/28/2022#03:30AM : 44.96
Mon,11/28/2022#03:00AM : 44.96
Mon,11/28/2022#02:30AM : 44.96
Tue,11/29/2022#02:15AM : 60.62
Tue,11/29/2022#02:00AM : 60.98
Tue,11/29/2022#01:45AM : 60.98
The problem is that you use "temp" as the filename for a temporary file in both scripts.
I'm not good in understanding sed, but as I read it, you print only the first 192 lines of the logfile with your command. You don't need a temporary file for that.
First: logfiles are usually written from oldest to newest entry (top to bottom), so probably you want to view the 192 newest lines? Then you can make use of the >> output redirection to append your output to the file. Then use tail to get only the bottom of the file. And if necessary, you could reverse that final output.
That last line of your script would then be replaced by:
sed -i '1i '"$line"'
192,$d' $log
Further possible improvements:
Use a single script that gets URL and log filename as parameters
Use the usual log file order (newest entries appended at the end)
Don't truncate log files inside the script, but use logrotate to not exceed a certain filesize

Bash Script To Remove Users From Wordlist

I wrote the following script to remove users from a wordlist. The wordlist has 3 or 4 fields which are First Name, Middle Name, Last Name, and User ID. I am using awk to create a username that comprises of the user's firstname initial, lastname and last two digits of their ID. Then using the command userdel with flag r to remove users with their home directories as well.
However when I run the script it gives me an error saying the following:
Usage: userdel [options] LOGIN
Options:
-f, --force force some actions that would fail otherwise
e.g. removal of user still logged in
or files, even if not owned by the user
-h, --help display this help message and exit
-r, --remove remove home directory and mail spool
-R, --root CHROOT_DIR directory to chroot into
-Z, --selinux-user remove any SELinux user mapping for the user
The script:
#! /bin/bash
# Removing users using positional parameters
getusername(){
line=${1}
len=`echo ${line}|awk '{ FS = " " } ; { print NF}'`
if [[ ${len} -eq 3 ]]
then
initial=`echo ${line}| awk {'print $1'} |cut -c1`
lastname=`echo ${line} | awk {'print $2'}`
id=`echo ${line}| awk {'print $3'}|grep -o '..$'`
username=`echo ${initial}${lastname}${id} |tr '[:upper:]' '[:lower:]'`
elif [[ ${len} -eq 4 ]]
then
initial=`echo ${line} | awk {'print $1'} |cut -c1`
lastname=`echo ${line} | awk {'print $3'}`
id=`echo ${line}| awk {'print $4'}|grep -o '..$'`
username=`echo ${initial}${lastname}${id} |tr '[:upper:]' '[:lower:]'`
else
echo "Line ${line} is not expected as it should be considered for creating Username and Password"
fi
}
sudo userdel -r $getusername
How to invoke userdel?
userdel -r username
This deletes the account of user username, and removes that user's home directory and associated mail files.
For that, you need to use a variable instead of to invoke a function.
Otherwise userdel will complain, like it is already doing or like just typing userdel.

echo text to multiple files in bash script

I am working on a bash script that uses pssh to run external commands, then join the output of the commands with the IP of each server. pssh has an option -o that writes a file for each server into a specified directory, but if the commands do not run, you just have an empty file. What I am having issues with is updating these empty files with something like "Server Unreachable" so that I know there was a connection issue reaching the server and to not cause problems with the rest of the script.
Here is what I have so far:
#!/bin/bash
file="/home/user/tools/test-host"
now=$(date +"%F")
folder="./cnxhwinfo-$now/"
empty="$(find ./cnxhwinfo-$now/ -maxdepth 1 -type f -name '*' -size 0 -printf '%f%2d')"
command="echo \$(uptime | awk -F'( |,|:)+' '{d=h=m=0; if (\$7==\"min\") m=\$6; else {if (\$7~/^day/) {d=\$6;h=\$8;m=\$9} else {h=\$6;m=\$7}}} {print d+0,\"days\",h+0,\"hours\",m+0,\"minutes\"}'), \$(hostname | awk '{print \$1}'), \$(sudo awk -F '=' 'FNR == 2 {print \$2}' /etc/connex-release/version.txt), \$(lscpu | awk -F: 'BEGIN{ORS=\", \";} NR==4 || NR==6 || NR==15 {print \$2}' | sed 's/ *//g') \$(free -k | awk '/Mem:/{print \$2}'), \$(df -Ph | awk '/var_lib/||/root/ {print \$2,\",\"\$5,\",\"}')"
pssh -h $file -l user -t 10 -i -o /home/user/tools/cnxhwinfo-$now -x -tt $command
echo "Server Unreachable" | tee "./cnxhwinfo-$now/$empty"
ls ./cnxhwinfo-$now >> ./cnx-data-$now
cat ./cnxhwinfo-$now/* >> ./cnx-list-$now
paste -d, ./cnx-data-$now ./cnx-list-$now >>./cnx-data-"$(date +"%F").csv"
I was trying to use find to locate the empty files and write "Server" unavailable using tee with this:
echo "Server Unreachable" | tee "./cnxhwinfo-$now/$empty"
if the folder specified doesn't already exist i get this error:
tee: ./cnxhwinfo-2019-09-03/: Is a directory
And if it does exist (ie, i run the script again), it instead creates a file named after the IP addresses returned by the find command, like this:
192.168.1.2 192.168.1.3 192.168.1.4 1
I've also tried:
echo "Server Unreachable" | tee <(./cnxhwinfo-$now/$empty)
The find command outputs the IP addresses on a single line with a space in between each one, so I thought that would be fine for tee to use, but I feel like I am either running into syntax issues, or am going about this the wrong way. I have another version of this same script that uses regular ssh and works great, just much slower than using pssh.
empty should be an array, assuming none of the file names will contain any whitespace in their names.
readarray -t empty < <(find ...)
echo "Server unreachable" | (cd ./cnxhwinfo-$now/; tee "${empty[#]}" > /dev/null)
Otherwise, you are building a single file name by concatenating the empty file names.

Bash script to find filesystem usage

EDIT: Working script below
I have used this site MANY times to get answers, but I am a little stumped with this.
I am tasked with writing a script, in bash, to log into roughly 2000 Unix servers (Solaris, AIX, Linux) and check the size of OS filesystems, most notable /var /usr /opt.
I have set some variables, which may be where I am going wrong right off the bat.
1.) First I am connecting to another server that has a list of all hosts in the infrastructure. Then I parse this data with some sed commands to get a list I can use properly
1.) Then I do a ping test, to see if the server is alive. If the server is decom. The idea behind this, is if the server is not pingable, I don't want it being reported on, or any attempt to be made to connect to it, as it is just wasting time. I feel I am doing this wrong, but don't know how to do it corectly (a re-occurring theme you will here in this post lol)
If any FS is over 80% mark, then it should output to a text file with the servername, filesystem, size on one line <== very important for me
If the FS is under 80% full, then I don't want it in my output, it can me omitted completely.
I have created something that I will post below, and am hoping to get some help in figuring out where I am going wrong. I am very new to bash scripting, but have experience as a Unix admin (i have never been good at scripting).
Can anyone provide some direction and teach me where I am going wrong?
I will upload my script that i can confirm is working hopefully tomorrow. thanks everyone for your input in this!
Here is my "disk usage" linux script, i hope that help you.
#!/bin/sh
df -H | awk '{ print $5 " " $6 }' | while read output;
do
echo $output
usep=$(echo $output | awk '{ print $1}' | cut -d'%' -f1 )
partition=$(echo $output | awk '{ print $2 }' )
if [ $usep -ge 90 ]; then
echo "Running out of space \"$partition ($usep%)\" on $(hostname) as on $(date)" |
mail -s "Warning! There is no space on the disk: $usep%" root#domain.com
fi
done
Some trouble is here:
ping -c 1 -W 3 $i > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "$i is offline" >> $LOG
fi
You need a continue statement inside that if. Your program isn't really treating non-pingable hosts differently, just logging they're not pingable.
Okay, now I'm looking a little deeper, and there's more naive stuff in here. These shouldn't work:
SOLVARFS=$(df -h /var |cut -f5 |grep -v capacity |awk '{print $5}')
SOLUSRFS=$(df -h /usr |cut -f5 |grep -v capacity |awk '{print $5}')
SOLOPTFS=$(df -h /opt |cut -f5 |grep -v capacity |awk '{print $5}')
etc...
The problem with these lines is, the command substitution gets assigned to the variables before the ssh session happens. So the content of each variable is the command's result on your local system, not the command itself. Since you're doing command substitution around your ssh calls, it might well work just to rewrite these lines as (note the backslash escapes on $5):
SOLVARFS="df -h /var |cut -f5 |grep -v capacity |awk '{print \$5}'"
SOLUSRFS="df -h /usr |cut -f5 |grep -v capacity |awk '{print \$5}'"
SOLOPTFS="df -h /opt |cut -f5 |grep -v capacity |awk '{print \$5}'"
etc...
The part where you're contacting another server has some more stuff to correct. You don't need three if statements per server, and there's no reason to echo anything to /dev/null. Here's a rewrite for the SunOS section. For each directory you're checking, it outputs the host name, the command name (so you can see which dir was being checked), and the result:
if [[ $UNAME = "SunOS" ]]; then
for SSH_COMMAND in SOLVARFS SOLUSRFS SOLOPTFS ; do
RESULT=`ssh -o PasswordAuthentication=no -o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=2 GSSAPIAuthentication=no -q $i ${!SSH_COMMAND}`
if ["$RESULT" -gt 80] ; do
echo "$i, $SSH_COMMAND, $RESULT" >> $LOG
fi
done
fi
Note that the ${!BLAH} construction is variable indirection. "Give me the contents of the variable named by BLAH".
Your original script does a bunch of things less-than-optimally. Rather than running an almost-identical block of code for each filesystem and each operating system, the thing to do would be to record the differences in a way that a SINGLE piece of code can iterate over all your objects, adapting as required.
Here's my take on this. Commands should appear ONCE, but
they get run multiple times by loops, and
they get run multiple ways using arrays.
The following script passes lint checks, but obviously this is untested, as I don't have your environment to test in.
You might still want to think about how your logging and notifications work.
#!/bin/bash
# Assign temp file, remove it automatically upon successful exit.
tmpfile=$(mktemp /tmp/${0##*/}.XXXX)
trap "rm '$tmpfile'" 0
#NOW=$(date +"%Y-%m-%d-%T")
NOW=$(date +"%F")
LOG=/usr/scripts/disk_usage/Unix_df_issues-$NOW.txt
printf '' > "$LOG"
# Use variables to refer to commonly accessed files. If you change a name, just do it once.
rawhostlist=all_vms.txt
host_os=${rawhostlist}_OS
# Commonly-used options need only be declared once. Use an array for easier management.
declare -a ssh_opts=()
ssh_opts+=(-o PasswordAuthentication=no)
ssh_opts+=(-o BatchMode=yes)
ssh_opts+=(-o StrictHostKeyChecking=no) # Eliminate prompts on new hosts
ssh_opts+=(-o ConnectTimeout=2) # This should make your `ping` unnecessary.
ssh_opts+=(-o GSSAPIAuthentication=no) # This is default. Do we really need it?
# Note: Associative arrays require Bash 4.x.
declare -A df_opts=(
[SunOS]="-h"
[Linux]="-hP"
[AIX]=""
)
declare -A df_column=(
[SunOS]=5
[Linux]=5
[AIX]=4
)
# Fetch host list from configserver, stripping /^adm/ on the remote end.
ssh "${ssh_opts[#]}" -q configserver "sed 's/^adm//' /reports/*/HOSTNAME" > "$rawhostlist"
# Confirm that our host_os cache is up to date and process any missing hosts.
awk '
NR==FNR { h[$1]; next } # Add everything in rawhostlist to an array...
{ delete h[$1] } # Then remove any entries that exist in host_os.
END {
for (i in h) print i # And print whatever remains.
}' "$rawhostlist" "$host_os" |
while read h; do
printf '%s\t%s\n' "$h" $(ssh "$h" "${ssh_opts[#]}" -q uname -s)
done >> "$host_os"
# Next, step through the host list and collect data.
while read host os; do
ssh "${ssh_opts[#]}" "$host" df "${df_opts[$os]}" /var /usr /opt |
awk -v column="${df_column[$os]}" -v host="$host" 'NR>1 { print host,$1,$column }'
)
done < "$host_os" > "$tmpfile"
# Now that we have all our data, check for warning/critical levels.
while read host filesystem usage; do
if [ "$usage" -gt 80 ]; then
status="CRITICAL"
elif [ "$usage" -gt 70 ]; then
status="WARNING"
else
continue
fi
# Log our results to our log file, AND send them to stderr.
printf "[%s] %s: %s:%s at %d%%\n" "$(date +"%F %T")" "$status" "$host" "$filesystem" "$usage" | tee -a "$LOG" >&2
done < "$tmpfile"
# Email and record our results.
if [ -s "$LOG" ]; then
mail -s "Daily Unix /var Report - $NOW" unixsystems#examplle.com < "$LOG"
mv "$LOG" /var/log/vm_reports/
fi
Consider this example code. If you like the way it looks, your next task is to debug it, or open new questions for parts that you're having trouble debugging. :-)

Linux - List users which have specific directory

I am trying to create a script which will list the name of the user, only if that specific user has a specific and available directory in his folder.
#!/bin/bash
for i in `cat /etc/passwd | cut -d : -f5\,6 | cut -d : -f2`
do
cd $i
if [ -d public_html ]
then
echo `cat /etc/passwd | cut -d : -f5,6 | cut -d : -f1`
fi
done
First, I get a list of all the user names that have home folders.
Then, for each user, I enter in his directory
If in his directory, public_html directory is found, echo the user.
When I run this in the terminal, all the users are listed:
cat /etc/passwd | cut -d : -f5,6 | cut -d : -f1
However, I need to somehow get the user i from that whole list.
Can anybody be so kind to explain what I´m doing wrong, and what to look out for?
You're getting the wrong result because you're checking for a file (with -f). Try using -d.
Using cat and cut is not bash either and can easily be replaced with script in most cases.
I'd do something like this:
while IFS=: read -r user pass uid gid desc homedir shell; do [ -d "$homedir"/public_html ] && echo "$user"; done < /etc/passwd
We set the field separator (IFS) to : (since that's what /etc/passwd uses), we read the file in and set the variables we want to use.
Then for each line we do the test and (&&) if the test is successful we echo the result.
The quotes are not really necessary since we know the formatting of the file but good practice.
If users' home directories are set up in the standard way:
cd /home
for pubdir in */public_html/ ; do
echo "${pubdir%%/*}"
done
I did it ghetto style...
#!/bin/bash
counter=0
for i in `cat /etc/passwd | cut -d : -f6`
do
if [ -r $i ]
then
cd $i
if [ -d public_html ]
then
counter2=2
for j in `cat /etc/passwd | cut -d : -f5`
do
counter2=$(($counter2 + 1))
if [ $counter -eq $counter2 ]
then
echo $j
fi
done
fi
counter=$(($counter + 1))
fi
done
#!/bin/bash
getent passwd | awk -F: '{printf "%s\t%s\n",$1, $6}'| while read -r user home
do
if [ -d ${home}/public_html ] ; then
echo ${user}
fi
done
This should work irrespective of use of /etc/passwd or ldap ...

Resources