Windows Management and Scripting

A wealth of tutorials Windows Operating Systems SQL Server and Azure

Archive for the ‘Scripting’ Category

List of new features in Powershell V3

Posted by Alin D on April 11, 2012

Windows PowerShell v3 is right around the corner. It’s likely to ship with Windows 8, and it’s likely that releases for “downlevel” versions of Windows (Windows Vista, Windows 7, Windows Server 2008, and Windows Server 2008 R2 — but not Windows Server 2003 or Windows XP) will be released within a month or two, if not on the same day.

PowerShell’s becoming pretty hard to ignore. Windows Server 8 is building the vast majority of its administration on PowerShell, providing the option for GUI administration as well as command-line automation. Version 3 of the shell introduces some pretty important new features; here are the top five.

Better Remoting

PowerShell’s Remoting is becoming more important, as it gradually transitions into being the primary channel for administrative communications on the network. More and more GUI admin consoles will rely on Remoting, so it was important to Microsoft to make it more robust. Remoting sessions can now be disconnected, enabling you to later re-connect to the same session from the same or from a different computer. Currently, the Community Technology Preview (CTP) of v3 doesn’t disconnect a session if your client computer crashes; instead, that session is permanently closed — so it’s not quite like Remote Desktop, which could be configured to hold the session open if that happened.

Workflow

This is a big, big, big deal of a feature. Essentially, PowerShell’s new workflow construct enables you to write something very similar to a function, with PowerShell translating your commands and script code into Windows Workflow Foundation (WWF) processes. WWF then manages your entire task — which can include surviving things like network outages, computer reboots and more. It’s a way of orchestrating long-running, complex, multi-step tasks more efficiently and reliably. And don’t be surprised if the feature gets deeply integrated into the next release of System Center Orchestrator, as well as becoming a foundation feature that’s leveraged by any number of other server products.

Updatable Help

PowerShell has always struggled with errors in the help files. Not that there were a ton of them, but it was tough for Microsoft to fix them, since doing so would basically entail issuing an OS patch. Nobody wants patches for help files! The presence of online help, hosted on the TechNet website, mitigated the problem — but only a little. In v3, help files can be updated on-demand, with new XML files downloaded from Microsoft servers whenever you like. So Microsoft can fix errors as they find them, and get you the fixes without requiring an OS service pack or patch.

Scheduled Jobs

PowerShell v2 introduced jobs, with the idea that the concept of “job” could be extended over time. In v3, a new type of job — a scheduled job — can be created to run on a schedule, or in response to an event. This isn’t all that different from Windows’ Task Scheduler, but you’ll finally have a way of accessing the capability from within PowerShell. Unlike the v2 jobs, which still exist, the scheduled jobs exist outside PowerShell, meaning they’ll continue to work even if PowerShell isn’t running.

Better Discovery

One tough part about any command-line shell is figuring out how to use it. PowerShell’s help system tackles that problem quite well, provided you know the name of the command you want, and provided you know which add-in the command lives in, and that you remembered to load the add-in into memory. Yeah, that’s a lot of caveats. PowerShell v3 addresses the problem by automatically including all commands from all installed modules when it searches for commands; try to run a command that isn’t loaded, and the shell implicitly loads it in the background for you. Ahhh… much easier. This only works with modules that are stored in one of the folder paths listed in the PSModulePath environment variable, but that variable can be modified anytime you like to include additional paths.

BONUS: CIM

PowerShell has always been great for working with Windows Management Instrumentation (WMI), a Microsoft technology that builds on the more-or-less industry-standard Common Information Model (CIM). In PowerShell v3, the WMI cmdlets continue to do a great job, but they’re joined by a new set of CIM cmdlets. At first it might seem like pointless overlap in functionality, but there’s a reason: The CIM cmdlets use WS-MAN, the protocol behind PowerShell’s Remoting feature, and Microsoft’s new standard for administrative communications. WMI, which uses harder-to-pass-through-firewalls DCOM, is officially deprecated, meaning it won’t be developed further — although it’ll continue to be available. CIM will be the “way forward,” with not only additional development to what we’ve always known as “WMI,” but with the possibility for cross-platform management in the future.

Overall, PowerShell v3 is shaping up to be pretty incredible. Get your hands on CTP No. 2 here and start trying it out today.

About these ads

Posted in Powershell | Tagged: , , , , , , | Leave a Comment »

Script to list all global and local groups on a given server

Posted by Alin D on August 2, 2011

Used to list all global and local groups on a given server.

Usage: $script /[s]erver /[g]lobal /[l]ocal /[v]erbose

/server Name of server for which to list all groups.
Server can be a domain controller. If no server
is specified, this defaults to localhost.
/global List only global groups.
/local List only local groups.
/verbose Show group comments.
/help Displays this help message.
use Getopt::Long;
use diagnostics;
use strict;
use Win32::Console;
use Win32::Lanman;

##################
# main procedure #
##################
my (%config);

p_parsecmdline(%config, @ARGV);
p_checkargs();

# set console codepage
Win32::Console::OutputCP(1252);

if ($config{global}) {
p_listglobalgroups($config{server});
} elsif ($config{local}) {
p_listlocalgroups($config{server});
} else {
p_listglobalgroups($config{server});
p_listlocalgroups($config{server});
}

exit 0;

##################
# sub-procedures #
##################

# procedure p_help
# displays a help message
sub p_help {
my ($script)=($0=~/([^\/]*?)$/);
my ($header)=$script." v1.1 - Author: alin@keptprivate.com";
my ($line)="-" x length($header);
print < <EOT;

$header
$line
Used to list all global and local groups on a given server.

Usage: $script /[s]erver /[g]lobal /[l]ocal /[v]erbose

/server Name of server for which to list all groups.
Server can be a domain controller. If no server
is specified, this defaults to localhost.
/global List only global groups.
/local List only local groups.
/verbose Show group comments.
/help Displays this help message.
EOT

exit 1;
}
# procedure p_parsecmdline
# parses the command line and retrieves arguments values
sub p_parsecmdline {
my ($config) = @_;
Getopt::Long::Configure("prefix_pattern=(-|/)");
GetOptions($config, qw(
server|s=s
global|g
local|l
verbose|v
help|?|h));
}
# procedure p_checkargs
# checks the arguments which have been used are a valid combination
sub p_checkargs {
p_help() if defined($config{help});
if (!$config{server}) {
$config{server} = Win32::NodeName();
}
}
# procedure p_listglobalgroups
# lists all global groups on a given server
sub p_listglobalgroups {
my $server = shift;
$server =~ s/\//g;
my (@groups,$group);
my ($header)="Global groups on '\\$server':";
my ($line)="-" x length($header);

if (!$config{verbose}) {
print "n$headern$linen";
}
if (Win32::Lanman::NetGroupEnum("\\$server",@groups)) {
foreach $group (sort (@groups)) {
next if (${$group}{name} eq "None");
if ($config{verbose}) {
$~ = 'GLOBAL';
write;
} else {
print "${$group}{name}n";
}
}
} else {
print "ERROR: ".Win32::FormatMessage(Win32::Lanman::GetLastError());
}

format GLOBAL_TOP =
Group Name Comment Type
--------------------------------- ---------------------------------- -------
.
format GLOBAL =
@< <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< global
${$group}{name},${$group}{comment}
~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
${$group}{comment}
.

}
# procedure p_listlocalgroups
# lists all local groups on a given server
sub p_listlocalgroups {
my $server = shift;
$server =~ s/\//g;
my (@groups,$group);
my ($header)="Local groups on '\\$server':";
my ($line)="-" x length($header);

if (!$config{verbose}) {
print "n$headern$linen";
}
if (Win32::Lanman::NetLocalGroupEnum("\\$server",@groups)) {
foreach $group (sort (@groups)) {
if ($config{verbose}) {
$~ = 'LOCAL';
write;
} else {
print "${$group}{name}n";
}
}
} else {
print "ERROR: ".Win32::FormatMessage(Win32::Lanman::GetLastError());
}

format LOCAL_TOP =
Group Name Comment Type
--------------------------------- ---------------------------------- -------
.
format LOCAL =
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< local
${$group}{name},${$group}{comment}
~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
${$group}{comment}
.

}

Posted in Perl | Tagged: , , , , | Leave a Comment »

Script to display local and global groups only in the ACL of a specified share

Posted by Alin D on August 2, 2011

Used to display local and global groups only in the ACL of a specified share, this script has been  designed to facilitate the UAR process. Only groups are shown so that the admin may get an idea
 of what group a user should belong to in order to have specific rights on a share.

 Usage: $script /[s]hare <UNC path> /[v]erbose /[h]elp

        /share      UNC path to the share (exp: \\server1\share1)
        /verbose    Show all groups in NTFS permissions
        /help       Displays this help message

use Getopt::Long;
#use diagnostics;
#use strict;
use Win32::Console;

use Win32;
use Win32::Lanman;
use Array::Compare;
use Win32::NetAdmin;
use Win32::Perms;

##################
# main procedure #
##################
my (%config);

sub_parseCmdLine(%config, @ARGV);
sub_checkArgs();

# set console codepage
Win32::Console::OutputCP(1252);

########################
### INSERT CODE HERE ###

my @array = split(/\+/,$config{share});
my $server = $array[1];
my $share = $array[2];
my $localhost = Win32::NodeName();

# step 1: retrieve share permissions
my %sharePerm = sub_listACL($config{share});
if ($sharePerm{Everyone} ne “Full Control”) {
print “n”;
print “##############################################################n”;
print “WARNING: Everyone group does not have Full Control permission!n”;
print “##############################################################n”;
}

# step 2: retrieve share full path
my $sharePath = sub_getShareInfo($server,$share);
my $savedSharePath = $sharePath;
$sharePath =~ s/:/$/;
$sharePath = “\\”.$server.”\”.$sharePath;

# step 3: retrieve NTFS permissions
my %ntfsPerm = sub_listACL($sharePath);

# print header
print “n”;
print “Hostname: $servern”;
print “Share Name: $sharen”;
print “Share Path: $savedSharePathn”;

my $account;

# print share permissions
print “n”;
print “Share permissions:n”;
print “——————n”;

foreach $account (sort(keys %sharePerm)) {
$permission = $sharePerm{$account};
$~ = ‘Permissions’;
write;
}

# delete common user groups from NTFS permission list
# if verbose option has not been specified
my $count = 0;
unless ($config{verbose}) {
foreach my $key (keys %ntfsPerm) {
if ($key =~ /administrators|domain admins|creator owner|system|server operators|backup operators|power users/i) {
delete $ntfsPerm{$key};
++$count;
}
}
}

# print NTFS permissions
print “n”;
print “NTFS permissions:n”;
print “—————–n”;

foreach $account (sort(keys %ntfsPerm)) {

# find out who can determine the account type for us
my $authority = “”;
my @array = split(/\/,$account);
if (@array > 1) {
unless ($array[0] =~ /builtin|$server|nt authority/i) {
# determine who is the PDC of the account’s domain
Win32::NetAdmin::GetDomainController(“\\$localhost”,$array[0],$authority);
$authority =~ s/\//g;
} else {
$authority = $server;
}
} else {
$authority = $server;
}

# determine if $account is a user, a local group, or a global group
my $accountType = “”;
my @members;
if (@array < 2) { $array[1] = $array[0]; } if (Win32::NetAdmin::GroupGetMembers($authority,$array[1],@members)) { $accountType = “global group”; } elsif (Win32::NetAdmin::LocalGroupGetMembersWithDomain($authority,$array[1],@members)) { $accountType = “local group”; } else { $accountType = “user”; ++$count; } # display permission for $account $permission = $ntfsPerm{$account}; if (($config{verbose}) or ($accountType ne “user”)) { $~ = ‘Permissions’; write; } # if $account is a local group, then display members if ($accountType eq “local group”) { foreach my $entry (@members) { # determine account type $authority = “”; @array = split(/\/,$entry); if (@array > 1) {
unless ($array[0] =~ /builtin|$server|nt authority/i) {
# determine who is the PDC of the account’s domain
Win32::NetAdmin::GetDomainController(“\\$localhost”,$array[0],$authority);
$authority =~ s/\//g;
} else {
$authority = $server;
}
} else {
$authority = $server;
}
$accountType = “”;
my @phonyMembers;
if (@array < 2) { $array[1] = $array[0]; } if (Win32::NetAdmin::GroupGetMembers($authority,$array[1],@phonyMembers)) { $accountType = “global group”; } else { $accountType = “user”; } # exclude users if (($config{verbose}) or ($accountType ne “user”)) { print “t$entryn”; } } } } # show if any groups have been excluded if (($count > 0) and (!$config{verbose})) {
print “nNumber of entries excluded from NTFS permissions: $countn”;
print “Use /verbose option to view all users and groups.n”;
}

# FORMATS
format Permissions =
^<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<
$account,$permission
~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<
$account
.

### CODE ENDS HERE ###
########################

exit 0;

##################
# sub-procedures #
##################

# procedure sub_help
# displays a help message
sub sub_help {
my ($script)=($0=~/([^\/]*?)$/);
my ($header)=$script.” v1.1.2 – Author: alin.dumenica@siemens.com – October 2007″;
my ($line)=”-” x length($header);
print <

$header
$line
Used to display local and global groups only in the ACL of a specified share, this script has been
designed to facilitate the UAR process. Only groups are shown so that the admin may get an idea
of what group a user should belong to in order to have specific rights on a share.

Usage: $script /[s]hare/[v]erbose /[h]elp

/share UNC path to the share (exp: \\server1\share1)
/verbose Show all groups in NTFS permissions
/help Displays this help message
EOT

exit 1;
}
# procedure sub_parseCmdLine
# parses the command line and retrieves arguments values
sub sub_parseCmdLine {
my ($config) = @_;
Getopt::Long::Configure(“prefix_pattern=(-|/)”);
GetOptions($config, qw(
share|s=s
verbose|v
help|?|h));
}
# procedure sub_checkArgs
# checks the arguments which have been used are a valid combination
sub sub_checkArgs {
sub_help() if defined($config{help});
sub_help() if !defined($config{share});
unless ($config{share} =~ /z\/) {
$config{share} .= “\”;
}
}
# procedure sub_listACL
# lists an ACL
sub sub_listACL {
no strict ‘refs’;

my $targetobject = shift;
my $targetSD;
unless ($targetSD = new Win32::Perms($targetobject)) {
print “ERROR: $^En”;
exit 2;
}
my (@perms,@ACL,%ACLhash);
my $comp = Array::Compare->new;

# define reference arrays for friendly NTFS permissions
my @read = qw(
READ_CONTROL
SYNCHRONIZE
FILE_READ_EA
FILE_EXECUTE
FILE_READ_ATTRIBUTES);
my @readNT4 = qw(
GENERIC_EXECUTE
GENERIC_READ);
my @change = qw (
DELETE
READ_CONTROL
SYNCHRONIZE
FILE_READ_EA
FILE_WRITE_EA
FILE_EXECUTE
FILE_READ_ATTRIBUTES
FILE_WRITE_ATTRIBUTES);
my @changeNT4 = qw (
DELETE
GENERIC_EXECUTE
GENERIC_WRITE
GENERIC_READ);
my @full = qw (
STANDARD_RIGHTS_ALL
FILE_READ_EA
FILE_WRITE_EA
FILE_EXECUTE
FILE_DELETE_CHILD
FILE_READ_ATTRIBUTES
FILE_WRITE_ATTRIBUTES);
my @fullNT4 = qw (
GENERIC_ALL);
my @write = qw (
READ_CONTROL
SYNCHRONIZE
FILE_READ_EA
FILE_WRITE_EA
FILE_EXECUTE
FILE_READ_ATTRIBUTES
FILE_WRITE_ATTRIBUTES);

# define reference arrays for user friendly share permissions
my @shareRead = qw (
FILE_SHARE_READ
READ_CONTROL
SYNCHRONIZE
FILE_READ_EA
FILE_EXECUTE
FILE_READ_ATTRIBUTES);
my @shareChange = qw (
FILE_SHARE_READ
FILE_SHARE_WRITE
FILE_SHARE_DELETE
DELETE
READ_CONTROL
SYNCHRONIZE
FILE_READ_EA
FILE_WRITE_EA
FILE_EXECUTE
FILE_READ_ATTRIBUTES
FILE_WRITE_ATTRIBUTES);
my @shareFullControl = qw (
FILE_SHARE_READ
FILE_SHARE_WRITE
FILE_SHARE_DELETE
STANDARD_RIGHTS_ALL
FILE_READ_EA
FILE_WRITE_EA
FILE_EXECUTE
FILE_DELETE_CHILD
FILE_READ_ATTRIBUTES
FILE_WRITE_ATTRIBUTES);

# retrieve all ACLs (DACL and SACL) for target object
unless ($targetSD->Get(@ACL)) {
print “ERROR: Could not retrieve ACL on $targetobject: $^En”;
exit 2;
}
# process each entry in all ACLs of target object
foreach my $ACE (@ACL) {
# ignore entry if it is not a DACL
next unless (“DACL” eq $ACE->{Entry});
my $account=””;
# format user name in ACE with domain name or if it can’t be resolved, show SID
if (“” eq $ACE->{Account}) {
my $machine;
my $domain;
my @targetobject;
my $SID;
my $binSID;
my $sidtype;
if ($targetobject =~ /\\/) {
#extract machine name
@targetobject = split (/\/,$targetobject);
$machine = $targetobject[2];
} else {
$machine = “”;
}
$SID = $ACE->{SID};
$binSID = Win32::Lanman::StringToSid($SID) or die “ERROR: $^En”;
unless (Win32::LookupAccountSID($machine,$binSID,$account,$domain,$sidtype)) {
$account = $SID;
} else {
my $useraccount = (“” ne $domain)? “$domain\”:””;
$account = $useraccount.$account;
}
} else {
my $useraccount = (“” ne $ACE->{Domain})? “$ACE->{Domain}\”:””;
$account .= $useraccount.$ACE->{Account};
}
Win32::Perms::DecodeMask($ACE,@perms);

# create user name reference if it does not exist yet
if (!defined $ACLhash{$account}) {
# creating unique array based on user name
# this is because @perms will be overwritten next time we go into the loop
@$account = @perms;
$ACLhash{$account} = @$account;
}
# compare current ACE list to reference friendly permissions ACLs
if (($comp->compare(@perms,@read)) or ($comp->compare(@perms,@readNT4)) or ($comp->compare(@perms,@shareRead))) {
$ACLhash{$account} = “Read”;
} elsif (($comp->compare(@perms,@change)) or ($comp->compare(@perms,@changeNT4)) or ($comp->compare(@perms,@shareChange))) {
$ACLhash{$account} = “Change”;
} elsif (($comp->compare(@perms,@full)) or ($comp->compare(@perms,@fullNT4)) or ($comp->compare(@perms,@shareFullControl))) {
$ACLhash{$account} = “Full Control”;
} elsif ($comp->compare(@perms,@write)) {
$ACLhash{$account} = “Write”;
} else {
$ACLhash{$account} = “Special”;
}
}
return %ACLhash;
}
# procedure sub_getShareInfo
# retrieves full path information for a given share
sub sub_getShareInfo {
my ($server,$share) = @_;
my %shareInfo;

unless (Win32::Lanman::NetShareGetInfo(“\\$server”,$share,%shareInfo)) {
my $error = Win32::FormatMessage(Win32::Lanman::GetLastError());
print “ERROR:NetShareGetInfo:$errorn”;
}
return $shareInfo{‘path’};
}

Posted in Perl | Tagged: , , , | Leave a Comment »

Perl Script to move members of a group to a specified organizational unit – ADSI.MoveMembers.pl

Posted by Alin D on August 2, 2011

Used to move members of a group to a specified organizational unit.  Only users are moved.
 RDN names of group and OU are automatically retrieved using an LDAP query.

 Usage: $script /[d]omain <fqdn> /[g]roup <group name> /[o]u <ou name> /help

        /domain     FQDN DNS name of the Active Directory domain where the group
                    resides. Default domain is "ww300.siemens.net"
        /group      Name of group whose members must be moved.
        /ou         Target organizational unit where group members must be moved to.
        /help       Displays this help message.

    Example: $script /d mydomain.local /g "Marketing Group" /o "Marketing OU"

use Getopt::Long;
#use diagnostics;
#use strict;
use Win32::Console;
use Win32::OLE 'in';

##################
# main procedure #
##################
my (%config);

sub_parseCmdLine(%config, @ARGV);
sub_checkArgs();

# set console codepage
Win32::Console::OutputCP(1252);

########################
### INSERT CODE HERE ###

#connect to Active Directory
my $adsPath=”LDAP://$config{domain}”;
my $adsObject = Win32::OLE->GetObject($adsPath);
die “ERROR: binding to Active Directory:n “,Win32::OLE->LastError() if Win32::OLE->LastError( );

unless (eval {my $enobj = Win32::OLE::Enum->new($adsObject)}) {
print “ERROR: $adsPath is not a container objectn”;
exit 1;
}

#search for the group LDAP/ads path
my $groupAdsPath = sub_searchAD(“Group”,$config{group},$adsPath);

#search for the organizational unit LDAP/ads path
my $ouAdsPath = sub_searchAD(“OrganizationalUnit”,$config{ou},$adsPath);

#retrieve group members, move only users to the target OU
my $groupObject = Win32::OLE->GetObject($groupAdsPath);
die “ERROR: binding to group:n “,Win32::OLE->LastError() if Win32::OLE->LastError( );

my $ouObject = Win32::OLE->GetObject($ouAdsPath);
die “ERROR: binding to organizational unit:n “,Win32::OLE->LastError() if Win32::OLE->LastError( );

my $members = $groupObject->Members();
foreach my $member (in $members) {
if ($member->{Class} eq “user”) {
if ($ouObject->MoveHere(“$member->{AdsPath}”,”$member->{Name}”)) {
print ” Moved $member->{Name} to $config{ou}n”;
} else {
print “ERROR: moving $member->{Name}:n “,Win32::OLE->LastError();
}
}
}

### CODE ENDS HERE ###
########################

exit 0;

##################
# sub-procedures #
##################

# procedure sub_help
# displays a help message
sub sub_help {
my ($script)=($0=~/([^\/]*?)$/);
my ($header)=$script.” v1.0 – Author: alin.dumenica@siemens.com – November 2007″;
my ($line)=”-” x length($header);
print <

$header
$line
Used to move members of a group to a specified organizational unit. Only users are moved.
RDN names of group and OU are automatically retrieved using an LDAP query.

Usage: $script /[d]omain/[g]roup/[o]u/help

/domain FQDN DNS name of the Active Directory domain where the group
resides. Default domain is “ww300.siemens.net”
/group Name of group whose members must be moved.
/ou Target organizational unit where group members must be moved to.
/help Displays this help message.

Example: $script /d mydomain.local /g “Marketing Group” /o “Marketing OU”
EOT

exit 1;
}
# procedure sub_parseCmdLine
# parses the command line and retrieves arguments values
sub sub_parseCmdLine {
my ($config) = @_;
Getopt::Long::Configure(“prefix_pattern=(-|/)”);
GetOptions($config, qw(
domain|d=s
group|g=s
ou|o=s
help|?|h));
}
# procedure sub_checkArgs
# checks the arguments which have been used are a valid combination
sub sub_checkArgs {
sub_help() if defined($config{help});
$config{domain} = ‘europe1.ds.honeywell.com’ if !defined($config{domain});
sub_help() if !defined($config{group});
sub_help() if !defined($config{ou});
}
# procedure sub_searchAD
# searches for an AD object and returns the ads path for that object
sub sub_searchAD {
my ($objectClass,$objectName,$adsPathParam) = @_;
my $objectAdsPath;

# get ADO object, set the provider, open the connection
my $adoObject = Win32::OLE->new(“ADODB.Connection”);
$adoObject->{Provider} = “ADsDSOObject”;
$adoObject->Open(“ADSI Provider”);
die Win32::OLE->LastError() if Win32::OLE->LastError( );

# prepare command for query (necessary to set properties)
my $adoCommand = Win32::OLE->new(“ADODB.Command”);
die Win32::OLE->LastError() if Win32::OLE->LastError( );
$adoCommand->{ActiveConnection} = $adoObject;
$adoCommand->{Properties}->{“Page Size”} = 1000;
$adoCommand->{CommandText} = “;(objectClass=$objectClass);Name,AdsPath;SubTree”;

#prepare and then execute the query
my $query = $adoCommand->Execute(“;(objectClass=$objectClass);Name,AdsPath;SubTree”);
die Win32::OLE->LastError() if Win32::OLE->LastError( );

#retrieve the ads path
until ($query->EOF){
if ($query->Fields(0)->{Value} eq $objectName) {
$objectAdsPath = $query->Fields(1)->{Value};
$query->Close;
$adoObject->Close;
return $objectAdsPath;
}
$query->MoveNext;
}

$query->Close;
$adoObject->Close;

return $objectAdsPath;
}

Posted in Perl | Tagged: , , , , , , | Leave a Comment »

Script to search Folder size and owner

Posted by Alin D on August 2, 2011

'******************************************************************
'Returns subdirectories path, size and owner in the given directory which
'are bigger than your specified size and
'******************************************************************
'
'Author: Alin Dumenica,
'Version: 1.01
'Web: http://www.windows-scripting.info
'Revision History:
'Date Author Remark
'--------------+-------+-------------------------------------------------
'Mar 02 2006 PR Initial version
'--------------+--------+-------------------------------------------------
'Usage: cscript search_folder_sizes_and_owner.vbs path_where_to_search where_to_put_result size > logname

‘*********** Declarations and Arguments Handling ***********
Dim ArgObj, path, fname,folsize

Set ArgObj = WScript.Arguments
path = ArgObj(0) ‘ this is the path you want to scan
fname = ArgObj(1) ‘ this is the resulting excel file
folsize = ArgObj(2)
folsize = CInt(folsize)
temp_file1 = path & “~temp1.fol” ‘ temp file that will be deleted at the end
temp_file2 = path & “~temp2.fol” ‘ temp file that will be deleted at the end

searchFolders path, temp_file1, temp_file2, fname, folsize

‘*********** User Functions ***********

Function ShowSubFolders(Folder,fisier,folsize)
For Each Subfolder in Folder.SubFolders
If SubFolder.Attributes AND 2 then
WScript.Echo “This is a system folder: ” & Subfolder.path & “. SKIPPED!”
Else
If Len(SubFolder.Path) > 255 then
Wscript.echo “path is too long”
Else
‘WScript.echo SubFolder.path, SubFolder.Attributes
On Error Resume Next
Folder_Size = SubFolder.Size / (1024*1024)
If Err.number <>0 then
wscript.echo “FOlder: ” & SubFolder.Path & ” was skipped!”
End If
err.clear
Folder_Size = CInt(Folder_Size)
If Folder_Size > folsize Then
fisier.writeline (Subfolder.Path) & “;” & Folder_Size
ShowSubFolders SubFolder, fisier, folsize
End If
End If
End If
Next
End Function

Function searchFolders(Arg1,Arg2, Arg3,Arg4, Arg5)
Dim Folder_Size, res_file
Set objFSO = CreateObject(“Scripting.FileSystemObject”)
Set filesys = CreateObject(“Scripting.FileSystemObject”)
Set res_file = filesys.CreateTextFile(Arg2, True)
wscript.echo “New file was created: ” & Arg2

ShowSubfolders objFSO.GetFolder(Arg1), res_file, Arg5

wscript.echo “Script has successfully created: ” & Arg2
getowner Arg2, Arg3, Arg4
End Function

Function getowner(temp1,temp2,temp3)
Const ForReading = 1
Set filesys_r = CreateObject(“Scripting.FileSystemObject”)
Set res_file = filesys_r.OpenTextFile(temp1, ForReading)
Set filesys_w = CreateObject(“Scripting.FileSystemObject”)
Set res_file2 = filesys_w.CreateTextFile(temp2, True)

Do While res_file.AtEndOfStream <> True
strCurrentLine = res_file.ReadLine
cut_size_pos = InstrRev(strCurrentLine, “;”,-1,1)
cut_size = left(strCurrentLine,cut_size_pos -1)
ParentFolderPos = InstrRev(cut_size, “”,-1,1)
ParentFolder = left(cut_size, ParentFolderPos)
Set objShell = CreateObject (“Shell.Application”)
Set objFolder = objShell.Namespace (ParentFolder)
Set objFSO = CreateObject(“Scripting.FileSystemObject”)

For Each strFileName in objFolder.Items
if strFileName.Path = cut_size Then
res_file2.writeline objFolder.GetDetailsOf (strFileName, 8)
End If
Next
Loop
wscript.echo “Successfully wroted OWNER!”
res_file.close
res_file2.Close
writetoExcel temp1, temp2, temp3

End Function

Function writetoExcel(fisier1,fisier2, result)

Const ForReading = 1
Set filesys_r1 = CreateObject(“Scripting.FileSystemObject”)
Set res_file = filesys_r1.OpenTextFile(fisier1, ForReading)
Set filesys_r2 = CreateObject(“Scripting.FileSystemObject”)
Set res_file2 = filesys_r2.OpenTextFile(fisier2, ForReading)
Set filesys_w = CreateObject(“Scripting.FileSystemObject”)
Set res_file3 = filesys_w.CreateTextFile(result, True)

Do while res_file.AtEndOfStream <> true
res_file3.writeline res_file.readline & “;” & res_file2.readline
Loop
wscript.echo “Successfully wroted csv file!”
res_file.close
res_file2.Close

End Function

Posted in VBscript | Tagged: , | Leave a Comment »

How to manage Hyper-V with powershell

Posted by Alin D on June 10, 2011

Many admins use PowerShell to automate components like user creation and folder permissions, but virtualization technologies can also be managed from the command line, including Microsoft Hyper-V.

While there are several ways to manage Hyper-V with PowerShell, this article will focus on the free approaches using Windows Management Instrumentation (WMI) scripting and an open source tool from CodePlex.

Before using WMI scripting to manage Hyper-V, it’s important to understand what classes are available. Microsoft’s list includes a significant number of classes and while is fairly complete, they are not necessarily easy to use and are certainly not intuitive. Therefore, using WMI to manage Hyper-V is not for the faint of heart.

One of the more popular methods for managing Hyper-V with PowerShell is with PowerShell Management Library for Hyper-V (PSHyperV) a free, open source CodePlex project written by James O’Neil. This is by far the best free option out there and gives administrators a very thorough collection of cmdlets that do everything from virtual machine inventory to virtual network management. Let’s touch on a few of them:

Get-VM – returns all the virtual machines on a given Hyper-V server

The following code demonstrates the Get-VM command:
Function Get-VM
{# .ExternalHelp MAML-VM.XML
param(
[parameter(ValueFromPipeLine = $true)]
[ValidateNotNullOrEmpty()][Alias("VMName")]
$Name = "%",

[parameter()][ValidateNotNullOrEmpty()]
$Server = ".", #May need to look for VM(s) on Multiple servers
[Switch]$Suspended,
[switch]$Running,
[switch]$Stopped
)
Process {
# In case people are used to the * as a wildcard...
if ($Name.count -gt 1 ) {[Void]$PSBoundParameters.Remove("Name")
; $Name | ForEach-object {Get-VM -Name $_ @PSBoundParameters}}
if ($name -is [String]) {
$Name = $Name.Replace("*","%")
# Note in V1 the test was for caption like "Virtual%" which
did not work in languages other than English.
# Thanks to Ronald Beekelaar - we now test for a processID ,
the host has a null process ID, stopped VMs have an ID of 0.
$WQL = "SELECT * FROM MSVM_ComputerSystem WHERE ElementName
LIKE '$Name' AND ProcessID >= 0"
if ($Running -or $Stopped -or $Suspended) {
$state = ""
if ($Running) {$State += " or enabledState = " +
[int][VMState]::Running }
if ($Stopped) {$State += " or enabledState = " +
[int][VMState]::Stopped }
if ($Suspended) {$State += " or enabledState = " +
[int][VMState]::Suspended }
$state = $state.substring(4)
$WQL += " AND ($state)"
}
Get-WmiObject -computername $Server -NameSpace $HyperVNamespace -Query $WQL | Add-Member -MemberType ALIASPROPERTY -Name "VMElementName" -Value "ElementName" -PassThru
}
elseif ($name.__class) {
Switch ($name.__class) {
"Msvm_ComputerSystem" {$Name}
"Msvm_VirtualSystemSettingData" {get-wmiobject -
computername $Name.__SERVER -namespace $HyperVNamespace -Query
"associators of {$($name.__path)} where
resultclass=Msvm_ComputerSystem"}
Default get-wmiobject -
computername $Name.__SERVER -namespace $HyperVNamespace -Query
"associators of {$($Name.__path)} where
resultclass=Msvm_VirtualSystemSettingData" |
ForEach-Object
{$_.getRelated("Msvm_ComputerSystem")} | Select-object -unique }
}
}
}
}

As you can see, the code basically wraps the WMI class with some helper logic and reports the results.

Get-VMSwitch – Returns all the virtual switches on the Hyper-V server

Function Get-VMSwitch
{# .ExternalHelp MAML-VMNetwork.XML
param(
[parameter(ValueFromPipeline = $true)][Alias("Name")]
[String]$VirtualSwitchName="%",

[parameter()][ValidateNotNullOrEmpty()]
$Server = "." #Can query multiple servers for switches
)
process {
$VirtualSwitchName=$VirtualSwitchName.replace("*","%")
Get-WmiObject -computerName $server -NameSpace $HyperVNamespace
-query "Select * From MsVM_VirtualSwitch Where elementname like '$VirtualSwitchname' "
}
}

Get-VMSnapShot – Provides all the snapshots on the Hyper-V server

The following command demonstrates the Get-VMSnapShot command:

Function Get-VMSnapshot
{# .ExternalHelp MAML-VMSnapshot.XML
Param(
[parameter(Position=0 , ValueFromPipeline = $true)]
$VM = "%",

[String]$Name="%",

[parameter()][ValidateNotNullOrEmpty()]
$Server="." ,
[Switch]$Current,
[Switch]$Newest,
[Switch]$Root
)
process{
if ($VM -is [String]) {$VM=(Get-VM -Name $VM -Server $server) }
if ($VM.count -gt 1 ) {[Void]$PSBoundParameters.Remove("VM") ; $VM |
ForEach-objectGet-VMSnapshot -VM $_ @PSBoundParameters}}
if ($vm.__CLASS -eq 'Msvm_ComputerSystem') {
if ($current) {Get-wmiobject -computerNam $vm.__server -
Namespace $HyperVNamespace -q "associators of {$($vm.path)} where assocClass=MSvm_PreviousSettingData"}
else {$Snaps=Get-WmiObject -computerName $vm.__server -NameSpace $HyperVNameSpace -Query "Select * From MsVM_VirtualSystemSettingData Where systemName='$($VM.name)' and
instanceID <> 'Microsoft:$($VM.name)' and elementName like '$name' "
if ($newest) {$Snapssort-object -property
creationTimeselect-object -last 1 }
elseif ($root) {$snapswhere-object {$_.parent -eq
$null} }
else {$snaps}
}
}
}
}

PSHyperV includes several additional functions to help admins perform related tasks, including finding, manipulating and configuring different components of the hypervisor and can be found on the CodePlex website.

Writing WMI wrappers and using PSHyperV are just a few of the ways admins can manage Hyper-V using PowerShell. Note that the latest release of PSHyperV isn’t a complete version, and thus, isn’t as stable as other options may be.

 

 

Posted in Powershell | Tagged: , , , , , , , , , , , | Leave a Comment »

Script to solve client machines not appearing in WSUS Console

Posted by Alin D on June 9, 2011

Depending on your imaging method some clients may not show up on your WSUS console, and refuse to check for updates. Since starting my new job I’ve seen this occur on machines which have been imaged with a non-sysprepped image. I quickly whipped up a script to reset some settings and forcing the machine to contact your WSUS server and retrieve a new Client Id and thus show up in your console.
Dim objShell, strKeyPath, strValueName,strComputer

set objShell = wscript.createObject("wscript.shell")

const HKEY_LOCAL_MACHINE = &H80000002

strComputer = "."

Set objWMIService = GetObject("winmgmts:\" & strComputer & "rootcimv2")
Set objRegistry = GetObject("winmgmts:\" & strComputer & "rootdefault:StdRegProv")

strKeyPath = "SOFTWAREMicrosoftWindowsCurrentVersionWindowsUpdate"
strValueName = "SUSClientIdReset"

objRegistry.GetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, dwValue
IF (dwValue = "1") Then
'do nothing
Else
objRegistry.DeleteValue HKEY_LOCAL_MACHINE,strKeyPath,"SusClientId"
objRegistry.DeleteValue HKEY_LOCAL_MACHINE,strKeyPath,"SusClientIdValidation"

Set colServiceList = objWMIService.ExecQuery ("Select * from Win32_Service where Name = 'wuauserv'")

For Each objService in colServiceList
If objService.State = "Running" Then
objService.StopService()
Wscript.Sleep 10000
objService.StartService()
End If
Next
objShell.Run("wuauclt /resetauthorization /detectnow ")
Wscript.Sleep 10000
objShell.Run("wuauclt /r /reportnow")

'Set reg value for SUSClientIdReset for checking against later.
dwValue = "1"
objRegistry.SetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, dwValue
End If

Run that on the effected machine with Admin Rights and the client will eventually appear in your WSUS Console.

As for the cause, the master image was joined to the domain and tested. During that time received group policy settings which included WSUS and contacted the server settings it’s clientId.

Posted in VBscript | Tagged: , , | Leave a Comment »

New mobile functionality for Windows PowerShell explained

Posted by Alin D on May 31, 2011

Those familiar with Windows PowerShell might also recognize PowerGUI Pro from Quest Software, a graphical front-end for PowerShell that automates common tasks for the command-line system. What you might not know is that there is new functionality that expands on this concept: PowerGUI Pro – MobileShell.

MobileShell runs the PowerGUI Pro command engine on a remote server through a Web browser. Internet Explorer 8 and Mozilla Firefox are both supported out of the box, and the programmers are working on adding support for many other browsers, including Google Chrome and Opera.
MobileShell installs on a Windows Server running Internet Information Services (IIS). It will install by default in a subdirectory named /MobileShell within the default website. All connections to MobileShell are SSL-encrypted by default, so snooping the traffic on the connection is no easier than it would be for any other SSL-protected transaction. Note that you can run MobileShell without HTTPS, but it is not recommended since (among other things) you’ll have to pass credentials in plain sight. Also, if you are disconnected in the middle of a session by a browser crash or network disruption, you can reconnect to the session spawned before in much the same manner as with a Remote Desktop session.

When you connect to MobileShell, you’ll see a three-pane display: an output window at the top, a command window at the bottom, and a pair of panels labeled Recent Commands and Favorites on the right. When you begin typing in a command in the bottom window, MobileShell will provide an auto-completion prompt for the command—a big timesaver since PowerShell commands can be a bit wordy.

The Recent Commands and Favorites panels are more or less what they sound like. The former maintains a history of the commands submitted through MobileShell. Click an item in the list and you can repopulate the command window with the same text. The Favorites panel is a list of commonly-used commands which you can customize by adjusting the settings. Among other things that can be controlled in the settings window is the output buffer size, which is set to 1,000 lines by default.

Finally, when using PowerGUI Pro – MobileShell it is important to avoid clicking the back button in your browser, as you risk closing the current session and losing your work; a minor tradeoff for another strong innovation.

Mobile Shell Pro

Posted in Powershell | Tagged: , , , , , , | Leave a Comment »

view membership of a group – ListGoupMembers.pl

Posted by Alin D on May 12, 2011

Used to view membership of a group.

Usage: $script /s[server] /g[roup]

/server Remote server name. By default, this is the
localhost.
/group Name of group to view.
use Getopt::Long;
#use diagnostics;
#use strict;
use Win32::Lanman;
use Win32::NetAdmin;
use Win32;
use Win32::AdminMisc;
use Win32::Console;

##################
# main procedure #
##################
my (%config);

p_parsecmdline(%config, @ARGV);
p_checkargs();

# set console codepage
Win32::Console::OutputCP(1252);

p_viewgroup($config{server},$config{group});

##################
# sub-procedures #
##################

# procedure p_help
# displays a help message
sub p_help {
my ($script)=($0=~/([^\/]*?)$/);
my ($header)=$script.” v2.1 – Author: alin.dumenica@siemens.com – June 2007″;
my ($line)=”-” x length($header);
print < <EOT;

$header
$line
Used to view membership of a group.

Usage: $script /s[server] /g[roup]

/server Remote server name. By default, this is the
localhost.
/group Name of group to view.
EOT

exit 1;
}
# procedure p_parsecmdline
# parses the command line and retrieves arguments values
sub p_parsecmdline {
my ($config) = @_;
Getopt::Long::Configure(“prefix_pattern=(-|/)”);
GetOptions($config, qw(
server|s=s
group|g=s
global
help|?|h));
}
# procedure p_checkargs
# checks the arguments which have been used are a valid combination
sub p_checkargs {
if ($config{help}) {
p_help();
}
unless ($config{group}) {
p_help();
}
if (!$config{server}) {
$config{server} = Win32::NodeName();
}
}
# procedure p_viewgroup
# adds a local group on target server
sub p_viewgroup {
my ($server,$group) = @_;
my (@members,$user,%info,%attribs);
my $scope = “unknown”;

if (Win32::NetAdmin::GroupGetMembers($server,$group,@members)) {
Win32::Lanman::NetServerGetInfo(“\\$server”, %info, 1);
print “nGroup membership for global group ‘$info{domain}\$group’:nn”;
$scope = “global”;
} elsif (Win32::NetAdmin::LocalGroupGetMembersWithDomain($server,$group,@members)) {
print “nGroup membership for local group ‘$server\$group’:nn”;
$scope = “local”;
} else {
print “ERROR: “.Win32::FormatMessage(Win32::NetAdmin::GetError());
exit 1;
}

if ($scope eq “global”) {
foreach $user (@members) {
# retrieve user’s full name
Win32::AdminMisc::UserGetMiscAttributes(“\\$server”,$user,%attribs);
$~ = ‘REPORT’;
write;
}
} elsif ($scope eq “local”) {
foreach $user (@members) {
# get the user’s domain
my @user = split(/\/,$user);
# if user is not local to the server, then get the domain’s PDC, then retrieve user’s full name
unless ((lc ($user[0]) eq lc ($server)) or ($user[0] eq “BUILTIN”)) {
my $pdc;
if (Win32::NetAdmin::GetDomainController(“\\$server”,$user[0],$pdc)) {
$pdc =~ s/\//g;
Win32::AdminMisc::UserGetMiscAttributes(“\\$pdc”,$user[1],%attribs);
}
$~ = ‘REPORT’;
write;
} else {
Win32::AdminMisc::UserGetMiscAttributes(“\\$server”,$user[1],%attribs);
$~ = ‘REPORT’;
write;
}
}
}
format REPORT_TOP =
User Name Full Name
——————————— ———————————-
.
format REPORT =
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$user,$attribs{USER_FULL_NAME}
.

}

Posted in Perl | Tagged: , , , | Leave a Comment »

Clean Drive Script -CleanDrive.pl

Posted by Alin D on May 12, 2011

use Getopt::Long;
#use diagnostics;
#use strict;
use Win32::Console;
use File::Find;
use Cwd;
use Win32::API::Prototype;

##################
# main procedure #
##################
my (%config);
my ($files,$dirs);
my ($totalsize) = 0;

p_parsecmdline(%config, @ARGV);
p_checkargs();

# set console codepage
Win32::Console::OutputCP(1252);

# check drive is valid
my $drive = $config{drive};
$drive =~ s/W+//;
$drive .= “:\”;
if (! -d $drive) {
die “ERROR: $drive is not a valid drive”;
}
# compute log file name if it hasn’t been specified
unless (defined ($config{log})) {
my $time = time();
$time = localtime($time);
my @time = split(/s+/,$time);
$config{log} = “cleandrive-$time[4]$time[1]$time[2].log”;
}
unless ($config{log} =~ /\/) {
my $cwd = getcwd;
unless ($cwd =~ //$/) {
$cwd .= “/”;
}
$config{log} = “$cwd$config{log}”;
}
# delete temp dirs content and dump files
p_cleantemp($config{drive});
p_cleandumps($config{drive});
# apply ntfs compression
if (defined ($config{compress})) {
p_compressfolders($config{drive},$config{compress});
}
# process files to delete
if (defined ($config{filelist})) {
p_delfiles($config{drive},$config{filelist});
}
# print summary of what was done
if (defined ($files) and defined ($dirs)) {
if (defined ($config{test})) {
print “n$files files and $dirs directories would have been deleted.n”;
} else {
print “n$files files and $dirs directories were deleted.n”;
}

my $TotalSizeUnit = “bytes”;
my $count = 0;
while ($totalsize > 1024) {
$totalsize = $totalsize / 1024;
++$count;
}
if ($count == 1) {
$TotalSizeUnit = “KB”;
} elsif ($count == 2) {
$TotalSizeUnit = “MB”;
} elsif ($count == 3) {
$TotalSizeUnit = “GB”;
} elsif ($count == 4) {
$TotalSizeUnit = “TB”;
} elsif ($count == 5) {
$TotalSizeUnit = “PB”;
}

print “Total space saved: “;
printf (“%.2f”,$totalsize);
print ” $TotalSizeUnit.n”;
my $logpath = $config{log};
$logpath =~ s///\/g;
print “Log file is $logpathn”;
} else {
print “nThere were no files or directories to process.n”;
}

my $LocalHost = Win32::NodeName();
p_getfreespace($LocalHost,$config{drive});

exit (0);

##################
# sub-procedures #
##################

# procedure p_help
# displays a help message
sub p_help {
my ($script)=($0=~/([^\/]*?)$/);
my ($header)=$script.” v1.3.1 – Author: alin.dumenica@gmail.com”;
my ($line)=”-” x length($header);
print < <EOT;

$header
$line
Used to clean a logical drive. This script deletes temporary files,
kernel and user memory dumps as well as internet temporary files.
Optionally, it can also compress log directories and/or delete a
list of specified files and/or directories.

Usage: $script /[d]rive /[l]og
/[c]ompress /[f]ilelist
/[t]est /[v]erbose /[h]elp

/drive Logical drive letter to clean.
/log Name of logfile (default is cleandrive-YYYYmonthDD.log).
/compress Use NTFS compression on specified path (e.g. c:\temp).
A file containing a list of paths can also be specified.
File contains one entry per line.
/filelist Name of a file containing a list of files or
directories to delete (example of entry in file is
c:\temp; file contains one entry per line; wildcard
* can be used to specify that line applies to all drives).
/test Do not delete any files, but only log what would
be done.
/verbose Shows what is being done as it is being done.
/help Shows this help message.
EOT

exit 1;
}
# procedure p_parsecmdline
# parses the command line and retrieves arguments values
sub p_parsecmdline {
my ($config) = @_;
Getopt::Long::Configure(“prefix_pattern=(-|/)”);
GetOptions($config, qw(
drive|d=s
log|l=s
compress|c=s
filelist|f=s
test|t
verbose|v
help|?|h));
}
# procedure p_checkargs
# checks the arguments which have been used are a valid combination
sub p_checkargs {
if ($config{help}) {
p_help();
}
unless (defined ($config{drive})) {
p_help();
}
}
# procedure p_cleantemp
# deletes content of temporary directories
sub p_cleantemp {
my $drive = shift;
# strip drive letter of all non alpha-numeric characters
$drive =~ s/W+//;
my @temp;

# populate array @temp of temporary directories on $drive
if ((-d “$drive:/temp”) and (“$drive:\temp” ne lc($ENV{TEMP})) and (“$drive:\temp” ne lc($ENV{TMP}))) {
push (@temp,”$drive:/temp”);
}
if ((-d “$drive:/tmp”) and (“$drive:\tmp” ne lc($ENV{TEMP})) and (“$drive:\tmp” ne lc($ENV{TMP}))) {
push (@temp,”$drive:/tmp”);
}
# add environment variables that define temporary directories,
# only if they are located on $drive
if ($ENV{TEMP} =~ /^$drive/i) {
my $temp = $ENV{TEMP};
$temp =~ s/\///g;
push (@temp,$temp);
}
if (($ENV{TMP} =~ /^$drive/i) and (lc($ENV{TMP}) ne lc($ENV{TEMP}))) {
my $tmp = $ENV{TMP};
$tmp =~ s/\///g;
push (@temp,$tmp);
}

foreach my $dir (@temp) {
my $FormattedDir = $dir;
$FormattedDir =~ s///\/g;
print “n Processing ‘$FormattedDir’ directory” if defined ($config{verbose});
finddepth (&p_del, “$dir”);
}

# determine if $drive is the system drive, in which case process user profiles temp directories
if (lc($ENV{SYSTEMDRIVE}) eq lc(“$drive:”)) {
my @profiles = (“$ENV{SYSTEMROOT}\Profiles”,”$drive:\Documents and Settings”);
foreach my $dir (@profiles) {
if (-d $dir) {
opendir (PROFILES, $dir) or next;
while (my $folder = readdir(PROFILES)) {
if (($folder eq “.”) or ($folder eq “..”)) {
next;
}
my $path = “$dir\$folder”;
if (-d “$path\Local Settings\temp”) {
print “n Processing ‘$path\Local Settings\temp’ directory” if defined ($config{verbose});
finddepth (&p_del, “$path\Local Settings\temp”);
}
if (-d “$path\Local Settings\Temporary Internet Files”) {
print “n Processing ‘$path\Local Settings\Temporary Internet Files’ directory” if defined ($config{verbose});
finddepth (&p_del, “$path\Local Settings\Temporary Internet Files”);
}
}
closedir (PROFILES);
}
}
# process SYSTEMROOTtemp if it exists
if (-d “$ENV{SYSTEMROOT}\temp”) {
my $path = “$ENV{SYSTEMROOT}\temp”;
print “n Processing ‘$path’ directory” if defined ($config{verbose});
finddepth (&p_del, “$path”);
}
}
}
# procedure p_cleandumps
# deletes kernel and user memory dumps
sub p_cleandumps {
my $drive = shift;
$drive =~ s/W+//;
# search logical drive for memory.dmp and user.dmp files and delete them
if (lc($ENV{SYSTEMDRIVE}) eq lc(“$drive:”)) {
# test for memory.dmp and delete it if it is 5 days or older only
if (-f “$ENV{SYSTEMROOT}\memory.dmp”) {
open (FILE, “$ENV{SYSTEMROOT}\memory.dmp”);
my @filestat = stat(FILE);
my $time = time();
my $seconds = $time – $filestat[9];
if ($seconds > 432000) {
print “n Processing $ENV{SYSTEMROOT}\memory.dmp” if defined ($config{verbose});
if (defined ($config{test})) {
p_log($config{log},”File $ENV{SYSTEMROOT}\memory.dmp would have been deleted.n”);
++$files;
$totalsize += $filestat[7];
} elsif (unlink (“$ENV{SYSTEMROOT}\memory.dmp”)) {
p_log($config{log},”File $ENV{SYSTEMROOT}\memory.dmp was deleted.n”);
++$files;
$totalsize += $filestat[7];
}
}
close (FILE);
}
if (-d “$ENV{SYSTEMROOT}\Minidump”) {
print “n Processing the ‘$ENV{SYSTEMROOT}\Minidump’ directory” if defined ($config{verbose});
opendir (MINIDUMP, “$ENV{SYSTEMROOT}\Minidump”);
while (my $file = readdir(MINIDUMP)) {
# delete mini dumps that are older than 5 days
next if (($file eq “.”) or ($file eq “..”));
my $path = “$ENV{SYSTEMROOT}\Minidump”;
if (-f “$path\$file”) {
my @filestat = stat(FILE);
my $time = time();
my $seconds = $time – $filestat[9];
if ($seconds > 432000) {
if (defined ($config{test})) {
p_log($config{log},”File $path\$file would have been deleted.n”);
++$files;
$totalsize += $filestat[7];
} elsif (unlink (“$path\$file”)) {
p_log($config{log},”File $path\$file was deleted.n”);
++$files;
$totalsize += $filestat[7];
}
}
}
}
closedir (MINIDUMP);
}
}
}
# procedure p_compressfolders
# applies NTFS compression to path(s)
sub p_compressfolders {
my ($drive,$folders) = @_;
my (@folderlist);
$drive =~ s/W+//;
# determine if a filename has been specified, if so, then call sub p_readfile
if (-f $folders) {
@folderlist = p_readfile($folders);
} else {
@folderlist = $folders;
}
print “n Applying NTFS compression” if defined ($config{verbose});
# for each entry in the array, apply NTFS compression after making sure the path is valid
# and that the path is on the drive being cleaned
foreach my $file (@folderlist) {
$file =~ s/^*:/$drive:/i;
if ((-d “$file”) and ($file =~ /^$drive:/i)) {
print “.” if defined ($config{verbose});
if (defined ($config{test})) {
p_log($config{log},”Would have attempted to NTFS compress $filen”);
} else {
`compact /C /S /I $file`;
p_log($config{log},”Attempted to NTFS compress $filen”);
}
}
}
}
# procedure p_readfile
# reads the content of a file into an array
sub p_readfile {
my $file = shift;
my (@list);
# open handle to the file
open (FILE,$file);
my $i = 0;
while (defined (my $entry = )) {
chomp ($entry);
$list[$i] = $entry;
++$i;
}
return (@list);
}
# procedure p_delfiles
# delete specified files
sub p_delfiles {
my ($drive,$files) = @_;
my (@filelist);
$drive =~ s/W+//;
# call sub p_readfile
if (-f $files) {
@filelist = p_readfile($files);
print “n Processing entries in ‘$files’” if defined ($config{verbose});
# for each element in the array, delete the file or directory
foreach my $file (@filelist) {
$file =~ s/^*:/$drive:/i;
if ((-d $file) and ($file =~ /^$drive:/i)) {
finddepth (&p_del, “$file”);
} elsif ((-f $file) and ($file =~ /^$drive:/i)) {
print “.” if defined ($config{verbose});
open (FILE,”$file”);
my @filestat = stat(FILE);
close (FILE);
if (defined ($config{test})) {
p_log($config{log},”File $file would have been deleted.n”);
++$files;
$totalsize += $filestat[7];
} elsif (unlink($file)) {
p_log($config{log},”File $file was deleted.n”);
++$files;
$totalsize += $filestat[7];
}
}
}
}
}
# procedure p_del
# used by File::Find call to delete files or directories
sub p_del {
if (-d $File::Find::name) {
if (defined ($config{test})) {
p_log($config{log},”Directory $File::Find::name would have been removed.n”);
++$dirs;
} elsif (rmdir(“$File::Find::name”)) {
p_log($config{log},”Directory $File::Find::name was removed.n”);
++$dirs;
}
print “.” if defined ($config{verbose});
} elsif (-f $File::Find::name) {
open (FILE,”$File::Find::name”);
my @filestat = stat(FILE);
close (FILE);
if (defined ($config{test})) {
p_log($config{log},”File $File::Find::name would have been deleted.n”);
++$files;
$totalsize += $filestat[7];
} elsif (unlink(“$File::Find::name”)) {
p_log($config{log},”File $File::Find::name was deleted.n”);
++$files;
$totalsize += $filestat[7];
}
print “.” if defined ($config{verbose});
}
}
# procedure p_log
# manages creating log entries
sub p_log {
my ($logfile,$message) = @_;
my $time = time();
$time = localtime($time);
open (LOG, “>>$logfile”) or die “nERROR: could not open $logfile: $^En”;
$message =~ s///\/g;
print LOG “$time: $message”;
close (LOG);
}
# procedure p_getfreespace
# returns the number of free bytes on a remote drive
sub p_getfreespace {
my ($servername,$drive) = @_;
my $Win32Error = 0;
my $pFree = pack(“L2″,0,0);
my $pTotal = pack(“L2″,0,0);
my $pTotalFree = pack(“L2″,0,0);
my $path = “\\”.$servername.”\”.$drive.”$\”;

# import Win32API function
ApiLink(‘kernel32.dll’,’BOOL GetDiskFreeSpaceEx(
LPCTSTR lpDirectoryName,
PVOID lpFreeBytesAvailable,
PVOID lpTotalNumberOfBytes,
PVOID lpTotalNumberOfFreeBytes)’)
or die “nERROR: cannot link to GetDiskFreeSpaceExn”;

# make the function call
if (GetDiskFreeSpaceEx($path,$pFree,$pTotal,$pTotalFree)) {
# compute the number of free bytes
my $freespace = p_MakeLargeInt(unpack(“L2″,$pTotalFree));
my $TotalSpace = p_MakeLargeInt(unpack(“L2″,$pTotal));
my $SpaceUsed = $TotalSpace – $freespace;
my $PercentageUsed = ($SpaceUsed * 100) / $TotalSpace;

my $FreeSpaceUnit = “bytes”;
my $i = 0;
while ($freespace > 1024) {
$freespace = $freespace / 1024;
++$i;
}
if ($i == 1) {
$FreeSpaceUnit = “KB”;
} elsif ($i == 2) {
$FreeSpaceUnit = “MB”;
} elsif ($i == 3) {
$FreeSpaceUnit = “GB”;
} elsif ($i == 4) {
$FreeSpaceUnit = “TB”;
} elsif ($i == 5) {
$FreeSpaceUnit = “PB”;
}

my $TotalSpaceUnit = “bytes”;
$i = 0;
while ($TotalSpace > 1024) {
$TotalSpace = $TotalSpace / 1024;
++$i;
}
if ($i == 1) {
$TotalSpaceUnit = “KB”;
} elsif ($i == 2) {
$TotalSpaceUnit = “MB”;
} elsif ($i == 3) {
$TotalSpaceUnit = “GB”;
} elsif ($i == 4) {
$TotalSpaceUnit = “TB”;
} elsif ($i == 5) {
$TotalSpaceUnit = “PB”;
}

$freespace = p_FormatNumber($freespace);
$TotalSpace = p_FormatNumber($TotalSpace);
print “There now is “;
printf “%.2f”,$freespace;
print ” $FreeSpaceUnit available out of “;
printf “%.2f”,$TotalSpace;
print ” $TotalSpaceUnit (“;
printf “%.2f”,$PercentageUsed;
print “% used) on the $drive: drive.n”;
} else {
$Win32Error = Win32::GetLastError();
my $ErrorMessage = Win32::FormatMessage($Win32Error);
print “\\$servername\$drive$ ERROR $Win32Error: $ErrorMessage”;
}

exit $Win32Error;
}
# procedure p_MakeLargeInt
# convert number into a decimal number
sub p_MakeLargeInt {
my($Low,$High) = @_;
return($High*(1+0xFFFFFFFF)+$Low);
}
# procedure p_FormatNumber
# add comas in number to make it more readable
sub p_FormatNumber {
my($Num) = @_;
{} while ($Num =~ s/^(-?d+)(d{3})/$1,$2/);
return($Num);
}

Posted in Perl | Tagged: , , , , , | Leave a Comment »

 
Follow

Get every new post delivered to your Inbox.

Join 167 other followers

%d bloggers like this: