Internet Explorer version?

No updates for over a year, terrible! In my defence I have been very busy. My roles have moved away from being purely technical and now are primarily managerial / IT strategy / project management. That being said I do still like to get stuck in to some technical tasks every now and again.

A recent Intranet implementation project caused a mild panic at the last minute when it was discovered that one of the users was still using IE8…a contraption which unfortunately did not cope too well with our all-singing all-dancing responsive collaboration tool.

It turns out that despite IE11 being cleared for install in the WSUS server, some machines were not receiving the software due to its reliance on some non-critical updates. Super.

We then needed to quickly identify which machines were still stuck on a 6 year old browser.

So, how to check if you have IE11 installed? It’s not listed in Programs and Features, making the script I wrote to centrally dump this data for all machines (which I may share at another time) pretty useless.

I discovered a reg key that stores the version number. The reg key concerned is HKLM\SOFTWARE\Microsoft\Internet Explorer\Version (don’t bother to cross-check this with the versions listed at https://support.microsoft.com/en-us/kb/969393?wa=wsignin1.0 – my IE 11 install shows version 9.11.9600.17728 in the reg key, as long your version doesn’t start with an 8 you should be good).

I quickly whipped up this code to let us know what state we were in.

The code below relies on PSRemoteRegistry, which you can download from https://psremoteregistry.codeplex.com/

You will also need a text file with a list of all the machines you want to query.

Import-Module PSRemoteRegistry
$Date = get-date -format yyyyMMdd
$Output = New-Item -type file "C:\Scripts\IEversion-$date.log" -Force

Function LogWrite
{param([string]$logstring)
     Add-Content $Output -Value $logstring
}

$Computers = Get-Content -Path C:\Scripts\Machines\List.txt

FOREACH ($Computer in $Computers){
     IF (Test-Connection $Computer -quiet) {
          $RegValue = Get-RegValue -ComputerName $Computer -Key "SOFTWARE\Microsoft\Internet Explorer" -Value Version
          $IEVersion = $RegValue.Data
          LogWrite "$Computer has version $IEVersion"
     }
     ELSE
     {
          LogWrite "$Computer is down"
     }
}
Posted in Guides Tagged with: , ,

Database white space in Exchange 2007

Database white space in Exchange has been an evolving topic. Great improvements were made in Exchange 2010 and 2013 and you can retrieve the white space for each database by running the simple and short command

Get-MailboxDatabase -Status | Select Server, Name, AvailableNewMailboxSpace

However there is no such option in Exchange 2007. As reported here the previous method of retrieving whitespace is to look for event log entry 1221.

Changes were made to online maintenance in Exchange 2010 which meant it’s now very rare that you would need to dismount and run ESEUTIL to regain the database white space (see http://blogs.technet.com/b/exchange/archive/2011/12/14/database-maintenance-in-exchange-2010.aspx), the event ID 1221 was also removed in favour of the PowerShell command above.

There are a few scripts online that do some funky stuff around interrogating the event log to retrieve 1221 events within a certain time range. Examples would be

$ComputerName = Get-ExchangeServer |where {$_.IsMailboxServer -eq 'True'}
$dmtf = [System.Management.ManagementDateTimeconverter] 
$Start = $dmtf::ToDmtfDateTime( (Get-Date).AddDays(-1).Date ) 
$db = @{Name="DB";Expression={$_.InsertionStrings[1]}} 
$free = @{Name="WhiteSpace(MB)";Expression={[int]$_.InsertionStrings[0]}} 
$filter = "LogFile='Application' AND EventCode=1221 AND TimeWritten>='$Start'" 
Get-WMIObject Win32_NTLogEvent -ComputerName $ComputerName -Filter $filter | select ComputerName,$db,$free

and

$ComputerName = Get-ExchangeServer |where {$_.IsMailboxServer -eq 'True'}
$tc = [System.Management.ManagementDateTimeconverter] 
$Start =$tc::ToDmtfDateTime( (Get-Date).AddDays(-10).Date ) 
$End =$tc::ToDmtfDateTime( (Get-Date).Date) 
$DB = @{Name="DB";Expression={$_.InsertionStrings[1]}} 
$FreeMB = @{Name="FreeMB";Expression={[int]$_.InsertionStrings[0]}} 
Get-WMIObject Win32_NTLogEvent -ComputerName $ComputerName -Filter "LogFile='Application' AND EventCode=1221 AND TimeWritten>='$Start' AND TimeWritten<='$End'" | Select-Object ComputerName,$DB,$FreeMB | Sort-Object FreeMB –Unique –Descending

These are great but have some limitations. For example if you scan a 3 day period and have 1 database that had online maintenance run on it for 3 days in a row you will see 3 entries for the database. Conversely if you only retrieve 1 day’s worth of logs you might find (depending how long your online maintenance window is) that there won’t be 1221 entries for all of your databases.

I’ve managed to put together a script that searches through the event log entries and returns the most recent entry for each database.

The 1221 log pulls back info including:

RecordNumber : 2037976
SourceName : MSExchangeIS Mailbox Store
TimeGenerated : 20140310053541.000000+780
TimeWritten : 20140310053541.000000+780
Type : Information
Category : 6
CategoryString : General
EventCode : 1221
EventIdentifier : 1074136261
InsertionStrings : {nnnn, storage_group>\

I started by borrowing some code from the second script above, to ensure that today’s date and the date to start from are in the same format that the event log uses. I have set the script to search the last 7 days of logs, but you can customise it as you wish.

$tc = [System.Management.ManagementDateTimeconverter] 
$Start = $tc::ToDmtfDateTime( (Get-Date).AddDays(-7).Date ) 
$End = $tc::ToDmtfDateTime( (Get-Date).Date) 

I then create some arrays and variables to work with. First is a variable for the cluster server that has the events in the log, then an array which contains all the events we want to iterate through, another array with all databases we want information for and finally an empty array which will be used as a record to ensure once we have the most recent data for a database that the information is not overwritten.

The RecordNumber sort on the events is important, as when we work through the array later we want to ensure we’re working from newest entry to oldest.

$ComputerName = Get-ExchangeServer | where {$_.IsMailboxServer -eq 'True' -and $_.IsMemberOfCluster -eq 'Yes'}
$AppEvents = Get-WMIObject Win32_NTLogEvent -ComputerName $ComputerName -Filter "LogFile='Application' AND EventCode=1221 AND TimeWritten>='$Start' AND TimeWritten<='$End'" | Sort RecordNumber -desc
$Databases = Get-MailboxDatabase
$LatestEvent = @()

As we’re looking for each database in each event entry we need to do a nested foreach loop. The $SG variable is set specifically to match the format in the event entry, it needs to be a string else the IF comparison fails.

The InsertionStrings[0] and InsertionStrings[1] refer to the areas in the event that correspond to the storage group name and amount of database white space.

Finally the IF -not statement combined with the $LatestEvent array ensure if the database has already been processed then the entry will be ignored (so we always have the most recent entry). The $LatestEvent array is then appended to in order to ensure we don’t retrieve older data for that database.

FOREACH ($Database in $Databases) {
    $SGName = $Database.StorageGroupName
    $DBName = $Database.Name
    [string]$SG = "$SGName\$DBname"
    FOREACH ($Event in $AppEvents) {
        $EventSG = $Event.InsertionStrings[1]
        $DBSize = $Event.InsertionStrings[0]
        IF ($SG -eq $EventSG) {
                IF (-not ($LatestEvent -contains $SG)) {
                Write-Host "For database" $DBName "DBSize is:" $DBSize "Mb" -foregroundcolor Green  
                $LatestEvent += $SG
                }
        }
    }
} 

So to put it all together:

Add-PSSnapin Microsoft.Exchange.Management.Powershell.Admin
$tc = [System.Management.ManagementDateTimeconverter] 
$Start = $tc::ToDmtfDateTime( (Get-Date).AddDays(-7).Date ) 
$End = $tc::ToDmtfDateTime( (Get-Date).Date) 
$ComputerName = Get-ExchangeServer | where {$_.IsMailboxServer -eq 'True' -and $_.IsMemberOfCluster -eq 'Yes'}
$AppEvents = Get-WMIObject Win32_NTLogEvent -ComputerName $ComputerName -Filter "LogFile='Application' AND EventCode=1221 AND TimeWritten>='$Start' AND TimeWritten<='$End'" | Sort RecordNumber -desc
$Databases = Get-MailboxDatabase
$LatestEvent = @()
FOREACH ($Database in $Databases) {
    $SGName = $Database.StorageGroupName
    $DBName = $Database.Name
    [string]$SG = "$SGName\$DBname"
    FOREACH ($Event in $AppEvents) {
        $EventSG = $Event.InsertionStrings[1]
        $DBSize = $Event.InsertionStrings[0]
        IF ($SG -eq $EventSG) {
                IF (-not ($LatestEvent -contains $SG)) {
                Write-Host "For database" $DBName "DBSize is:" $DBSize "Mb" -foregroundcolor Green  
                $LatestEvent += $SG
                }
        }
    }
} 
Posted in Guides Tagged with: , ,

NTFS folder permissions with PowerShell

***Before we start, I should say that these steps were made on a Windows Server 2008 R2 machine running PowerShell 2.0. I understand there are different switches available to other OSs and PowerShell versions. Changing permissions on the home folders of multiple users is a potential CIM (Career Influencing Move!) and you should only undertake it if you understand fully the steps involved and implications of the change. Testing might be a good idea too.***

You may find yourself in a situation, after a migration of Operating System or filestore, where only the users themselves have ownership and access to their home drives. This makes backups impossible (without taking ownership) and makes the role of support personell difficult if a user wants you to look at some files in their home drive for problem resolution.

The steps included in this article are:

1. Take ownership of the folder.
2. Re-permission the folder so Domain Admins have access.
3. Return permissions to the user.

To save an avalanche of clicks I thought it would be best to script the required steps.

First off you need to make an array of the user folders affected. Get-ADUser or Get-QADUser could do the trick, but would return service accounts and admin accounts that don’t have a Home Drive set. In addition to this, my testing on a reasonably typical home drive (just over 3000 files and 1.2Gb in size) took about 5 minutes to re-permission. If you have 5000 users you might want to set a weekend aside to go through it all! So in this example I will iterate through the folders in the root share. For granularity it might be better to choose a departmental group (you could do $Group = Get-QADGroup “Finance Department” and then iterate through $Group.members).

So to set the array and start to iterate through:

$Folders = \\server\share\
$Folder = Get-ChildItem $Folders
FOREACH ($User in $Folder) {

Next we need to take ownership of the folder. It’s best to do this within the foreach loop and not at the root, as taking ownership will remove the user’s access to the folder (at least it did in my situation, where the user’s ownership of the folder was the only privilege, when I changed the ownership to the admin user the user was stripped of all rights). If you change ownership folder by folder you can have 1 annoyed user at a time, instead of hundreds all at once! Fortunately taking ownership is very simple:

TAKEOWN /F $Folders$User /R /D Y

Next we need to re-permission the folder, you can change permissions in PowerShell with Get-ACL and some logic, but ICACLS (which is built into Windows) will do the job in fewer lines, I always prefer a more direct route to a solution. The ICACLS TechNet page at http://technet.microsoft.com/en-us/library/cc753525.aspx is very similar to the output provided by ICACLS /? and is heavy on information on the available switches, but light on the examples. I found with extensive testing that running

ICACLS '\\server\share\user' /grant '"$Username":(OI)(CI)F' /T

Doesn’t translate well if you try to script it and substitute a variable into the path or user fields. The frustrating error that was repeatedly returned is

No mapping between account names and security IDs was done.

The way to get around this issue is to encase the username you are passing in quotations, then use a “+” to attach to the rest of the string, then wrap the whole lot in brackets, as below:

$FullPath = "$Folder" + "$Username"
ICACLS ("$FullPath") /grant ("$Username" + ':(OI)(CI)F') /T

Then remember to set the owner to Domain Admins:

ICACLS ("$FullPath") /setowner "Domain Admins" /c /t

If you are running Server 2003 SP2 you may need to install a hotfix in order to run setowner as you can get an Access Denied error. See http://support.microsoft.com/kb/947870

Also, depending on your directory structure the user’s folder may be named a variety of ways (ideally consistently!). I have structured the script so that you can replace tha SAMAccountName below with DisplayName or NTAccountName as necessary.

Finally, I have used the Quest AD Management SnapIn for the Get-QADUser to work.

Add-PSSnapin Quest.ActiveRoles.ADManagement
$Folders = "\\server\share\"
$Folder = Get-ChildItem $Folders
FOREACH ($User in $Folder) {
    TAKEOWN /F $Folders$User /R /D Y
    $Username = Get-QADuser $User
    $Username = $Username.SamAccountName
    $FullPath = "$Folders" + "$Username"
    ICACLS ("$FullPath") /grant '"Domain Admins":(OI)(CI)F' /T
    ICACLS ("$FullPath") /grant ("$Username" + ':(OI)(CI)F') /T
    ICACLS ("$FullPath") /setowner "Domain Admins" /c /t
}

nb. setowner may fail if you are running Windows Server 2003. See hotfix here.

Posted in Guides Tagged with: , ,

Obtaining Databases sizes in Exchange 2007

I’m taking a trip back in time for a client at the moment. In Exchange 2010 I’m used to running:

Get-MailboxDatabase -Status | select ServerName,Name,DatabaseSize

To get an overview of the size of my databases.

However, in the days of yore with Exchange 2007 there is no DatabaseSize property. There are several articles which point to a variant of

Get-MailboxDatabase | foreach-object {add-member -inputobject $_ -membertype noteproperty -name mailboxdbsizeinGB -value ([math]::Round(([int64](get-wmiobject cim_datafile -computername $_.server -filter (‘name=”’ + $_.edbfilepath.pathname.replace(“\”,”\\”) + ””)).filesize / 1GB),2)) -passthru} | Sort-Object mailboxdbsizeinGB -Descending | format-table identity,mailboxdbsizeinGB

Which uses the Computername and database path to return the flat file size of the database on the disk.

I have a problem with this script in my current environment where the identity and name of the databases are not the same, so the above script fails. I get around this by assigning the databases to a variable and then iterating through each database, like so:

$Databases = Get-MailboxDatabase
FOREACH ($Database in $Databases) {
    Get-MailboxDatabase -identity $Database.identity | FOREACH {
        Add-Member -inputobject $_ -membertype noteproperty -name DatabaseSizeInGB -value ([math]::Round(([int64](get-wmiobject cim_datafile -computername $_.server -filter ('name=''' + $_.edbfilepath.pathname.replace("\","\\") + '''')).filesize / 1GB),2)) -passthru
        } | Sort-Object Name | Format-Table Name,DatabaseSizeInGB
}

This is great and returns the required information, but the output is pretty shonky and not great for quick manual parsing:

DBSizes

I have created a script that relies on an array in order to display the information in a much simpler format:

$a = @{Expression={$_.Name};Label="Database Name";width=27}, `
@{Expression={([math]::Round(([int64](Get-WMIObject cim_datafile -computername $_.server -filter ('name=''' + $_.edbfilepath.pathname.replace("\","\\") + '''')).filesize / 1GB),2))};Label="Size in GB";width=12}
Get-MailboxDatabase | Format-Table $a

With a much improved output:

DBSizesSmall

Posted in Guides Tagged with: , ,

Calculating how long a PowerShell script takes to run

I put this together to get a handle on some of the longer AD pull / spreadsheet populating scripts that I run. It has some logic in it to return a meaningful display of time (so you don’t get “This script took 0 hours 0 minutes and 7 seconds to run” returned).

First you set a variable at the start of the script to log the exact time it started processing.

#### Start the clock
$s=Get-Date

Then after the bulk of the script (but perhaps before any mails are sent) you can paste the below. It will produce a popup box, so isn’t suitable for script run on a scheduled task.

#### Calculate how long script took to run
$WShell = New-Object -ComObject Wscript.Shell -ErrorAction Stop
$tt = new-timespan -seconds $(($e - $s).TotalSeconds)
IF ( ($tt.TotalSeconds) -lt 60) {
$rt = $tt.seconds
$WShell.Popup("Time taken to run: $rt seconds",0,"Script finished",48+0)
}
ELSEIF  ( ($tt.TotalSeconds) -gt 3599) {
$rt = '{0:00} hours {1:0} minutes {2:00} seconds' -f $tt.Hours,$tt.Minutes,$tt.Seconds 
$WShell.Popup("Time taken to run: $rt",0,"Script finished",48+0)
}
ELSE {
$rt = '{1:0} minutes {2:00} seconds' -f $tt.Hours,$tt.Minutes,$tt.Seconds 
$WShell.Popup("Time taken to run: $rt",0,"Script finished",48+0)
}

The $WShell.Popup entries can be removed and $rt can then be used to add the duration to a text file or e-mail.

Posted in Guides Tagged with:

Obtain the size of your Public Folder database using PowerShell

If you’re looking to find out the size of your Public Folder database you’ll be out of luck in the Exchange Management Console. Looking at the Properties (Under Server Config > Mailbox) will tell you the path, last backup and modified dates, but nothing about size. Of course using the path you can look at the disk directly and obtain information on the size the database consumes from there.

Another GUI option is from Outlook. Right-click on the public folder and select “Data File Properties”, in the window that pops up hit “Folder Size”, after a bit (or a lot!) of thinking the total folder size will be returned.

The article at http://technet.microsoft.com/en-us/library/bb123996(v=exchg.80).aspx states “A larger maximum database size is possible when continuous replication is used. We recommend the following maximum database sizes for Exchange 2007: Databases hosted on a Mailbox server with continuous replication and Gigabit Ethernet: 200 GB”. So I think it would be useful to tracking Public Folder growth over time (we all know how management loves a good graph) in order to avoid going over the 200Gb recommended maximum.

So what if you want to script this check? I couldn’t find any steps online on how to get a script to return the results, which is why I’ve included it here.

The command to work with is Get-PublicFolderStatistics. This is a powerful command which returns a huge amount of data. The columns returned by default are: Name, ItemCount and LastAccessTime, and each folder within your entire public folder infrastructure will be enumerated.

The main problem I found with the data returned is that the sizes are not numeric values, smaller folder sizes will be appended with a “B” for Bytes, whereas larger (+1Mb) folders will be appended with “KB” for KiloBytes. The script below makes allowances for these and strips the letters from the numbers (by converting to a string), and in the case of data in kilobytes multiplying the output by 1024 so all values are in bytes.

You could expand the IF statement if any folders are over 1Gb in size, and warrant an “Mb” appended, however I would hope this situation doesn’t occur!

Finally it rounds up to Gb with 2 decimal points for ease of display.

$Server = 'servername'
$Folders = Get-PublicFolderStatistics -server $Server | Where {$_.TotalItemSize -ne "0B"}  
$TotalBytes = 0
ForEach ($Item in $Folders) {
     $TotalItemSize = $Item.TotalItemSize
     $TotalItemSize = [string]$TotalItemSize
     IF ( ($TotalItemSize.contains('KB')) ) {
          $TotalItemSize = $TotalItemSize -Replace ('KB','')
          $TotalItemSize = [int]$TotalItemSize * 1024
     }
     $TotalItemSize = $TotalItemSize -Replace ('B','')
     $TotalBytes = [long]$TotalItemSize + [long]$TotalBytes
}
[math]::round($TotalBytes/1Gb, 2)
Posted in Guides Tagged with: , , ,

Script to check remote machine info

As a follow-up to my last post I have now completed a script that enables you to search your Active Directory to get a list of applicable servers, then pull information from machines that you can run PowerShell scripts on remotely.

This script has expanded on the previous script and (I hope!) is a good reflection of my developing PowerShell skills. There are now IF statements to load modules (if required), a ping to test that the server is online, an AD lookup of the machine’s OS (better to check AD than attempt to query the server) as if you have non-PowerShell machines or cluster instances then the meaty part of the script would fail, a record of the Last Boot Time, processor family information and finally a list of installed server roles.

Two text files with a list of your servers is created, one that remains after the script finishes, and has a header with the number of servers queried, and another that is used by the script to iterate through the servers obtained from the AD search.

You can easily expand this script, so if you have multiple domains or an inconsistent machine naming policy it’s easy to include all required boxes.

If you want to automate this script you would need to run Get-credentials manually, so that a stored copy of your credentials be kept on the remoting server.

This script returns a text file for each machine queried. Next up is an Excel spreadsheet!

#################################################################### 
# Script  : ServerInfo.ps1 
# Author  : Tom Anderson  
# Date    : 17-10-2013    
# Purpose : Provide quick overview of servers in your domain. 
# History : 
#################################################################### 

#### Import required modules
IF (-not (Get-Module ServerManager)){            
  Import-Module ServerManager            
}  
IF (-not (Get-Module ActiveDirectory)){            
  Import-Module ActiveDirectory            
}  

#### Set parameters for output filename  
$date = Get-Date -format yyyyMMdd 
$machine = $env:computername
$folder = "\\$machine\E$\Folder\ServerInfo\$date"
$WServers = New-Item -type file "\\$machine\E$\Folder\ServerInfo\$date-ServerList.txt" -Force
$Servers = New-Item -type file "$folder\ServerList.txt" -Force

#### Set domain connectivity parameters
$LocalDomain = Get-ADComputer -SearchBase "DC=local,DC=yourdomain,DC=net" -Filter {(Name -like "UKServer*")} | Sort-Object Name | Select-Object -ExpandProperty Name
$ExtDomain = Get-ADComputer -SearchBase "OU=Users,DC=otherdomain,DC=net" -Filter {(Name -like "USServer*")} -Server "otherdomain.net" | Sort-Object Name | Select-Object -ExpandProperty Name

#### Pull credentials from stored file (saves you having to enter password multiple times)
#### Or prompts you to create a stored (and encrypted) file.
$Username = $env:username
$Folder = "\\server\folder"
$CredsFile = "$Folder\$Username-PowershellCreds.txt"
$FileExists = Test-Path $CredsFile
if  ($FileExists -eq $false) {
    Write-Host 'Credential file not found. Please enter your password:' -ForegroundColor Red
    Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File $CredsFile}
else
    {Write-Host 'You have a stored credential file which will be used to authenticate this session' -ForegroundColor Green}      
$Password = Get-Content $CredsFile | ConvertTo-SecureString
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$Password

#### Count the number of servers
$a = $LocalDomain | measure 
$b = $ExtDomain | measure
$Number = $a.count + $b.count
'There are ' + $Number + ' servers being queried.' | out-file $WServers -append
"`n" | out-file $WServers -append
Write-Host 'There are' $Number 'servers being queried.' -ForegroundColor Green

#### Output server names to file
$LocalDomain | out-file $Servers –append
$LocalDomain | out-file $WServers –append
$ExtDomain | out-file $Servers –append 
$ExtDomain | out-file $WServers –append  
$ServerList = Get-Content $Servers 
ForEach ($Server in $ServerList) {

#### Test connection to server. if no ping reply recived a files is created to advise.
#### If a reply is received then script continues.
IF(!(Test-Connection -Cn $Server -BufferSize 16 -Count 1 -ea 0 -quiet))
      {$file = New-Item -type file "\\machine\E$\Folder\ServerInfo\$date\$Server-$date.txt" -Force
      'Unable to ping server' | out-file $file -append}
       ELSE {

#### Set search root for AD OS search, depending on server name
IF ($Server -like "*UKServer*")
    {$root = [ADSI]"LDAP://local.yourdomain.net"
    $Account = Get-ADComputer $server -Server "local.yourdomain.net"}
        ELSE {$root = [ADSI]"LDAP://otherdomain.net"
        $Account = Get-ADComputer $server -Server "otherdomain.net"}

#### Look up server in AD, and only continue if OS is 2008 or 2008 R2
$SAMName = $Account.SamAccountName
$searcher = new-object System.DirectoryServices.DirectorySearcher($root)
$searcher.filter = "(&(objectClass=computer)(SAMAccountName= $SAMName))"
$computer = $searcher.findall()
$computerprops = $computer[0].Properties
$targetaddress = $computerprops.operatingsystem
IF($targetaddress -notlike "*Windows Server 2008*")
    {$file = New-Item -type file "\\machine\E$\Folder\ServerInfo\$date\$Server-$date.txt" -Force
      'Server is 2003 or lower' | out-file $file -append }
       ELSE {

Invoke-Command -ComputerName $Server -ScriptBlock {

#### Create file
$date = Get-Date -format yyyyMMdd 
$ComputerName = hostname
$file = New-Item -type file "\\machine\E$\Folder\ServerInfo\$date\$ComputerName-$date.txt" -Force

#### Add header information
'System report for ' + $ComputerName | out-file $file -append
$date = get-date -Format D
$time = get-date -Format T
"`n" | out-file $file -append
'Run at ' + $time + " on " + $date | out-file $file -append
"`n" | out-file $file -append
"`n" | out-file $file -append

#### Get Server Manager on board
Import-Module ServerManager

#### Get Operating System information
'-=Operating System Information=-' | out-file $file -append
"`n" | out-file $file -append
$OS = Get-WMIObject Win32_OperatingSystem
$OSName = $OS.Caption
$System = Get-WMIObject Win32_ComputerSystem
$installdate = ([WMI]'').ConvertToDateTime((Get-WmiObject Win32_OperatingSystem).InstallDate) 
$installdateuk = $installdate.ToString("dd/MM/yyyy hh:mm:ss")
$boot = $OS.ConvertToDateTime($OS.LastBootUpTime)
$bootuk = $boot.ToString("dd/MM/yyyy hh:mm:ss")
'Operating system: ' + "`t"+ $OSName | out-file $file -append
'Service pack: ' + "`t" + "`t"+ $OS.CSDVersion | out-file $file -append
'Install date: ' + "`t" + "`t"+ $installdateuk | out-file $file -append
'Last Boot Time: ' + "`t" + $bootuk | out-file $file -append
"`n" | out-file $file -append
"`n" | out-file $file -append

#### Get Hardware Model information
'-=Hardware Model Information=-' | out-file $file -append
"`n" | out-file $file -append
'Architecture: ' + "`t" + "`t"+ $OS.OSArchitecture | out-file $file -append
'Manufacturer: ' + "`t" + "`t"+ $System.Manufacturer | out-file $file -append
'Model: ' + "`t" + "`t" + "`t"+ $System.Model | out-file $file -append
"`n" | out-file $file -append
"`n" | out-file $file -append

#### Get Disk information
'-=Disk Information=-' | out-file $file -append
"`n" | out-file $file -append
$Disks = Get-WmiObject Win32_LogicalDisk -Filter "DriveType='3'"
foreach ($Disk in $Disks) {
$DiskGB = [math]::round($Disk.Size/1073741824, 0)
'Drive: ' + $Disk.DeviceID + "`t" + "`t" + 'Size: ' + $DiskGB + 'Gb' | out-file $file -append
}
"`n" | out-file $file -append
"`n" | out-file $file -append

#### Get Processor information
'-=Processor Information=-' | out-file $file -append
"`n" | out-file $file -append
$Processors = Get-WmiObject win32_processor
[int]$cores = 0
[int]$sockets = 0
[string]$test = $null
foreach ($processor in $processors)
{
      if ($processor.numberofcores -eq $null)
      {
            If (-not $Test.contains($processor.SocketDesignation))
            {
                  $Test = $Test + $processor.SocketDesignation
                  $sockets++
            }
                  $cores++
      }
      else
      {
            $sockets++
            $cores = $cores + $processor.numberofcores
      }
}
'Family: ' + "`t" + "`t" + $processor.name | out-file $file –append 
'Processor cores: ' + "`t" + $cores | out-file $file –append 
'Logical processors: ' + "`t" + $sockets | out-file $file –append 
"`n" | out-file $file -append
"`n" | out-file $file -append

#### Get Memory information
'-=Memory Information=-' | out-file $file -append
"`n" | out-file $file -append
$RAMGB = [math]::round($System.TotalPhysicalMemory/1024/1024/1024, 0)
'Memory size: ' + "`t" + "`t" + $RAMGB + 'Gb'  | out-file $file –append 
"`n" | out-file $file -append
"`n" | out-file $file -append

#### Get Pagefile information
'-=Pagefile Information=-' | out-file $file -append
"`n" | out-file $file -append
$pagefiles = Get-WmiObject Win32_PageFileSetting
foreach ($pagefile in $pagefiles) {
$PagefileGB = [math]::round($pagefile.MaximumSize/1024, 0)
'Location: ' + "`t" + "`t" + $pagefile.Name | out-file $file –append 
'Size: ' + "`t" + "`t" + "`t" + $PagefileGB + 'Gb' | out-file $file –append 
}
"`n" | out-file $file -append
"`n" | out-file $file -append

#### Get Network information information
'-=NIC Information=-' | out-file $file -append
"`n" | out-file $file -append
$NICS = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName . | Select-Object -Property [a-z]* -ExcludeProperty IPX*,WINS* 
foreach ($NIC in $NICS) {
'Adapter name: ' + "`t" + "`t" + $NIC.Description  | out-file $file –append 
'IP Address: ' + "`t" + "`t" + $NIC.IPAddress | out-file $file –append 
'Search suffixes: ' + "`t" + $NIC.DNSDomainSuffixSearchOrder | out-file $file –append 
"DNS Suffix: " +  "`t" +  "`t" + $NIC.DNSDomain | out-file $file –append 
'DNS Servers: ' + "`t" + "`t" + $NIC.DNSServerSearchOrder | out-file $file –append 
"`n" | out-file $file –append 
"`n" | out-file $file -append
}

#### Lookup DNS entry
'-=NSLookup of host entry=-' | out-file $file -append
"`n" | out-file $file -append
nslookup $ComputerName | out-file $file –append 
"`n" | out-file $file –append 

#### Get Server Roles
IF ($OSName -like "Microsoft Windows Server 2008 R2*") {
'-=Installed Roles=-' | out-file $file -append
Get-WindowsFeature | Where {$_.Installed -and $_.FeatureType -eq "Role"} | Sort Name | Format-Table DisplayName -hidetableheaders | out-file $file -append}
ELSE {
'Unable to retrieve installed roles as server in 2008 standard' | out-file $file -append}
} -authentication credssp -credential $Cred #Close from invoke command
} #Close from attempted ping
} #Close from OS check
} #Close from FOR EACH
Remove-Item $Servers
Posted in Uncategorized

Get-credentials

You can use the code below to save your password in an encrypted text file. This text file can be called to authenticate you in other sessions. It negates the need for you to manually authenticate or store your password in clear text.

$Username = $env:username
$Folder = "\\server\folder"
$CredsFile = "$Folder\$Username-PowershellCreds.txt"
Write-Host 'Credential file not found. Please enter your password:' -ForegroundColor Red
Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File $CredsFile
$Password = Get-Content $CredsFile | ConvertTo-SecureString
Posted in Guides Tagged with:

Script to check local machine info

Throughout my career I have noticed issues with build consistency across servers. As this is a recurring and seemingly never-ending problem I tried to find a script that could be run on a bunch of servers to check their configuration.

What I hoped to find was something that you could pipe a text file of servernames (or specify an OU in AD) and get the information pushed out to an Excel spreadsheet where you could easily manipulate the data. Alas I haven’t been able to find anything even remotely like what I’m looking for, so decided to start work on one myself.

While the final result might be a bit of a way off I have completed a preliminary script that you can use immediately. It will provide information on the local machine you run the script on. A text file is outputted to a directory that you can set.

It brings back information on disks, processors, memory, pagefile, IP address, search suffixes, DNS server and also performs an NS lookup of the server so you can be sure there is an entry.

I hope to expand this when I get time, but thought I would put it up now so others can use it. Let me know if you can think of ways to expand it.

Be sure to add your own DNS Server and file share information.

##################################################
# Script  : PCInfo.ps1
# Author  : Tom Anderson
# Date    : 15-08-2013
# Purpose : Provide quick overview of a server. To be used
#         : after building a server or in lieu of an audit.
# History :
################################################## 

#### Set parameters for output filename
$date = Get-Date -UFormat "%d-%m-%Y"
$ComputerName = hostname
$DNSServer = DNSServerName
$file = New-Item -type file "\\servername\share\$ComputerName-$date.txt" -Force

#### Get Disk information
'-=Disk Information=-' | out-file $file -append
$Disks = Get-WmiObject Win32_LogicalDisk -Filter "DriveType='3'"
foreach ($Disk in $Disks) {
$DiskGB = [math]::round($Disk.Size/1073741824, 0)
'Drive: ' + $Disk.DeviceID + "`t" + 'Disk size: ' + $DiskGB + 'Gb' | out-file $file -append
}
"`n" | out-file $file -append

#### Get Processor information
'-=Processor Information=-' | out-file $file -append
$Processors = Get-WmiObject win32_processor
foreach ($Processor in $Processors) {
'Processor cores: ' + "`t" + $Processor.numberOfCores | out-file $file –append
'Logical processors: ' + "`t" + $Processor.NumberOfLogicalProcessors | out-file $file –append
}
"`n" | out-file $file -append

#### Get Memory information
'-=Memory Information=-' | out-file $file -append
$RAM = Get-WmiObject Win32_ComputerSystem
$RAMGB = [math]::round($RAM.TotalPhysicalMemory/1024/1024/1024, 0)
'Memory size: ' + "`t" + "`t" + $RAMGB + 'Gb'  | out-file $file –append
"`n" | out-file $file -append

#### Get Pagefile information
'-=Pagefile Information=-' | out-file $file -append
$pagefiles = Get-WmiObject Win32_PageFileSetting
foreach ($pagefile in $pagefiles) {
$PagefileGB = [math]::round($pagefile.MaximumSize/1024, 0)
'Location: ' + "`t" + "`t" + $pagefile.Name | out-file $file –append
'Size: ' + "`t" + "`t" + "`t" + $PagefileGB + 'Gb' | out-file $file –append
}
"`n" | out-file $file -append

#### Get Network information information
'-=NIC Information=-' | out-file $file -append
$NICS = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName . | Select-Object -Property [a-z]* -ExcludeProperty IPX*,WINS*
foreach ($NIC in $NICS) {
'Adapter name: ' + "`t" + "`t" + $NIC.Description  | out-file $file –append
'IP Address: ' + "`t" + "`t" + $NIC.IPAddress | out-file $file –append
'Search suffixes: ' + "`t" + $NIC.DNSDomainSuffixSearchOrder | out-file $file –append
'DNS Servers: ' + "`t" + "`t" + $NIC.DNSServerSearchOrder | out-file $file –append
"`n" | out-file $file –append
}

#### Lookup DNS entry
'-=NSLookup of host entry=-' | out-file $file -append
nslookup $ComputerName $DNSServer | out-file $file –append
Posted in Guides Tagged with: , ,

Quirks of VBScript

A couple of things I’ve learnt recently:

Variables of a single character don’t get picked up with Option Explicit. This is definitely worth knowing!

Running either

 
Set Shell = wscript.createObject("wscript.shell")

or

Set WshNetwork = WScript.CreateObject("WScript.Network")

Will generally work, however they don’t seem to work consistently.

 
Set Shell = CreateObject("wscript.shell")

or

Set WshNetwork = CreateObject("WScript.Network")

Will work much better. I’m not sure why this is!

Posted in Guides Tagged with: