PowerShell Disk Space report permissions

After getting the following error:

The error information returned by PowerShell is: ‘Access is denied.

  • The account you are running as needs to be added to:
    • Distributed COM Users
    • Performance Monitor Users
    • WMI Control Properties\Security\
      • Click Security button
        • Add MYSRVNAM\Performance Monitor Users
          • Enable Account;
          • Remote Enable

           

This allows me to run the following as the user.

Get-WmiObject Win32_logicaldisk -ComputerName MYSRVNAM |? {$_.DeviceID -eq "E:" -or $_.DeviceID -eq "F:" -or $_.DeviceID -eq "G:" -or $_.DeviceID -eq "H:" }  `
| Format-Table SystemName, DeviceID, MediaType, `
@{Name="Size(GB)";Expression={[decimal]("{0:N0}" -f($_.size/1gb))}}, `
@{Name="Free Space(GB)";Expression={[decimal]("{0:N0}" -f($_.freespace/1gb))}}, `
@{Name="Free (%)";Expression={"{0,6:P0}" -f(($_.freespace/1gb) / ($_.size/1gb))}} `
-AutoSize | Out-String

Thanks to the original source!

Remoting needs to be set up on [ServerName]

PowerShell error:

Remoting needs to be set up on [ServerName]
Try running winrm quickconfig on remote server.

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.

DOS;winrm quickconfig
WinRM already is set up to receive requests on this machine.
WinRM is not set up to allow remote access to this machine for management.
The following changes must be made:

Create a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this
machine.
Enable the WinRM firewall exception.

Make these changes [y/n]? y

WinRM has been updated for remote management.

Created a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this
machine.
WinRM firewall exception enabled.

POWERSHELL: A wrapper for Get-ADGroupMember

I just wanted a wrapper for Get-ADGroup and Get-ADGroupMember to return all members of a group.

function Get-ADGroupMembers {
	<#
		.SYNOPSIS
			For any Get-ADGroup Get-ADGroupMember's Get-ADUser properties and output our PSObject

		.DESCRIPTION
			For any AD group return a list of members including members of subgroups.

		.PARAMETER  Group
			The AD Group name to find members for (without the DOMAIN).
 
		.EXAMPLE
			Get-ADGroupMembers -Group "SQL DBA"

		.EXAMPLE
			$Group = "SQL DBA"
			$Output = Get-ADGroupMembers -Group $Group
			$Output |Sort-Object LastName |Select-Object ObjectClass, GroupName, LastName, GivenName, SamAccountName, Name |ft -AutoSize
			Write-Host ("{0} Members found in AD group: {1}" -f $Output.Count, $Group)
			
		.INPUTS
			System.String 

		.OUTPUTS
			PSObject

		.NOTES
			20170113 Created

 

	#>
	[CmdletBinding()] 
	param(
		[Parameter(Position=0, Mandatory=$true)]
		[ValidateNotNullOrEmpty()]
		[System.String]
		$Group 
	)
	try {
				
		#
		## Get all 
		#  
		
		$Groups = Get-ADGroup -Identity $Group 	 
		$output = ForEach ($g in $groups)  {
			$results = Get-ADGroupMember -Identity $g.name -Recursive | Get-ADUser -Properties displayname, objectclass, name 

			ForEach ($r in $results){
				New-Object PSObject -Property @{
				    GroupName = $g.Name
				    Username = $r.Name
				    ObjectClass = $r.ObjectClass
				    Name = $r.Name
					GivenName = $r.GivenName
					LastName = $r.Surname
					Enabled = $r.Enabled
					SamAccountName = $r.SamAccountName 
				 }
			}
		} 
	  Write-Output $output
	}
	catch {
		throw
	}
}

POWERSHELL: Get all sysadmins on a SQL Server

I use this to retrieve all SQL sysadmins on a server, and drill down, if any are Active Directory groups, to the individuals.


$SQLInstance = "SQLDEV";
$DomainFilter = "MYCOMPANYDOMAIN"

$Server = new-object Microsoft.SqlServer.Management.Smo.Server $SQLInstance;
$SQLLogins = $Server.Logins;

$sysadmins = $null;
$sysadmins = foreach($sysadmin in $SQLLogins)  {
	foreach($role in $sysadmin.ListMembers()) {
		if($role -match 'sysadmin') {
			Write-Verbose "sysadmin found: $($sysadmin.Name)" 
			$sysadmin | Select-Object `
				@{label = "SQLServer"; Expression = {$SQLInstance}}, `
				@{label = "CurrentDate"; Expression = {(Get-Date).ToString("yyyy-MM-dd")}}, `
				Name, LoginType, CreateDate, DateLastModified;
			$sysadmin |?{$_.LoginType -eq 'WindowsGroup' -and $_.Name -match '$DomainFilter'} |%{ 
				Get-ADGroupMembers -Group $_.Name.Replace("$DomainFilter\",'') |Sort-Object LastName | Select-Object `
						@{label = "SQLServer"; Expression = {$SQLInstance}}, `
						@{label = "CurrentDate"; Expression = {(Get-Date).ToString("yyyy-MM-dd")}}, `
						Name,@{label = "LoginType"; Expression = {'GroupUser'}}
	 
	}
		};
	};
};

$sysadmins | ft -AutoSize

PowerShell: Check all servers for failed jobs.

This is a two part process.

First we use our [master_admin].[dbo].[rp_JobFailures] stored procedure to report any failures on the server. Here’s the guts of our procedure.

DECLARE @nDaysAgo INT
SELECT  @nDaysAgo = 1
SELECT  CAST([j].[name] AS VARCHAR(128)) [FAILED_job_name],
        LEFT(DATENAME(dw, CAST([run_date] AS CHAR(8))) + SPACE(1)
             + CAST([master_admin].[dbo].[fn_INTtoDateTime]([h].[run_date], [h].[run_time]) AS VARCHAR), 30) [RunDate],
        CASE [run_status]
          WHEN 0 THEN 'Failed'
          WHEN 1 THEN 'Succeeded'
          WHEN 2 THEN 'Retry'
          WHEN 3 THEN 'Canceled'
          WHEN 4 THEN 'In progress'
        END [job_status],
        (ISNULL(MAX([s].[step_id]), '')) [StepID],
        (ISNULL(MAX([s].[step_name]), '')) [StepName],
        (ISNULL(MAX([command]), MAX([h].[message]))) [StepCommand],
        MAX([operator_id_emailed]) [NotifiedID]
FROM    [msdb]..[sysjobhistory] [h]
JOIN    [msdb]..[sysjobs] [j]
ON      [j].[job_id] = [h].[job_id]
JOIN    [msdb]..[syscategories] [c]
ON      [j].[category_id] = [c].[category_id]
LEFT OUTER JOIN (
                 -- All jobsteps that have a final history job outcome of
	SELECT  [s].[step_name],
            [s].[command],
            [s].[step_id],
            [s].[job_id],
            [last_run_outcome]
    FROM    [msdb]..[sysjobsteps] [s] WITH (NOLOCK)
    LEFT OUTER JOIN (
                     SELECT [job_id]
                     FROM   [msdb]..[sysjobhistory] [h]
                     WHERE  [run_status] = 0
                            AND [h].[run_date] >= REPLACE(CONVERT(VARCHAR(10), DATEADD(DAY, -@nDaysAgo, GETDATE()), 121),
                                                          '-', '')
                    ) [okay]
    ON      [okay].[job_id] = [s].[job_id]
    WHERE   [okay].[job_id] IS NOT NULL
            AND [last_run_date] >= REPLACE(CONVERT(VARCHAR(10), DATEADD(DAY, -@nDaysAgo, GETDATE()), 121), '-', '')
                ) [s]
ON      [h].[job_id] = [s].[job_id]
        AND [h].[step_id] = [s].[step_id]
WHERE   [h].[run_date] >= REPLACE(CONVERT(VARCHAR(10), DATEADD(DAY, -@nDaysAgo, GETDATE()), 121), '-', '')
        AND [run_status] NOT IN (1, 4)
GROUP BY [j].[name],
        [run_date],
        [run_time],
        [run_status]
ORDER BY [h].[run_date],
        [h].[run_time],
        [j].[name] 

Then we use PowerShell to run that procedure on each of our SQL Servers, stored in $AllServersList.

# ------------------------------------------------------------------------------------------------------------------------
# What jobs failed since yesterday?

$TSQL = 'EXEC [master_admin].[dbo].[rp_JobFailures] @nDaysAgo=1, @vcSkipJob=NULL'

$VerbosePreference="Continue"
$bDEBUG = $true
#	$bDEBUG = $false ; $VerbosePreference="SilentlyContinue" ; $dt=@()
$AllServersList | %{
	$CurrentServer = $_
	Write-Host -ForegroundColor Blue "$(Get-Date -Format g)	[$CurrentServer]"
	Write-Verbose "	Invoke-sqlcmd2 -ConnectionTimeout 10 -ServerInstance $_ -Query ""$TSQL"""; 

	if (!($ExclusionList -contains $CurrentServer)) {
		if ($bDEBUG -eq $false) {
			$dr = Invoke-sqlcmd2 -ConnectionTimeout 10 -ServerInstance $CurrentServer -Query "$TSQL" -As DataRow
			if ($dr -ne $null) {
				$dr | add-member -name ServerName -type noteproperty -value "$CurrentServer"
				$dt += $dr
			}
		}
	} else {
		Write-Host -ForegroundColor Red "Skipping $CurrentServer $_"
	}
}

$dt |Select-Object ServerName, RunDate, Failed_Job_Name, StepID, StepName, StepCommand |ft -AutoSize

You can see I like to run Foreach without doing anything initially to make sure I got it right. Then highlighting from the commented $bDEBUG line to the end gets us what we want. I also use an exclusion list which is simply an array of server names to bypass.

The SQL purists will suggest we just run it within SSMS. Just create server groups for all your servers and right-click and run the query.

SQL Lessons: PowerShell basics

Part of my series pointing to valuable instruction on the web.

This one demonstrates the following functions by Chad Miller.

  • invoke-sqlcmd2
  • Write-DataTable
  • Out-DataTable
  • Add-SqlTable

Use PowerShell to Collect Server Data and Write to SQL

Learn about Windows PowerShell

Source: blogs.technet.microsoft.com/heyscriptingguy/2010/11/01/use-powershell-to-collect-server-data-and-write-to-sql/

Load the latest .NET assembly installed in your system, e.g. for SQL SMO

Load the latest .NET assembly for that PowerShell version using the following command:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")

 

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | out-null
 $SMOserver = New-Object ('Microsoft.SqlServer.Management.Smo.Server') -argumentlist $SrcServer
 $WhichVersion = $SMOserver.Version.Major

This command will grab the latest SMO installed in your system. You can also use this method with PowerShell 2.0 and later. More here.

Operation is not valid due to the current state of the object

This error occurs for me when I loop through a list of $CurrentServers. The solution was to add Out-Host to the end of the output line.

Operation is not valid due to the current state of the object
At line: 0 char: 0


$dsOut = Invoke-sqlcmd2  -ConnectionTimeout 10 -ServerInstance $CurrentServer -Query $SQL -Verbose -QueryTimeout 10 -Database master_admin -As DataTable

# Use Out-Host to prevent format-table errors
$dsOut |Select-Object * |ft -AutoSize |Out-Host