{"id":2623,"date":"2013-11-04T00:01:00","date_gmt":"2013-11-04T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/11\/04\/the-admins-first-steps-capacity-planning-part-1\/"},"modified":"2013-11-04T00:01:00","modified_gmt":"2013-11-04T00:01:00","slug":"the-admins-first-steps-capacity-planning-part-1","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/the-admins-first-steps-capacity-planning-part-1\/","title":{"rendered":"The Admin&#8217;s First Steps: Capacity Planning Part 1"},"content":{"rendered":"<p><strong>Summary<\/strong>: Richard Siddaway talks about using Windows PowerShell to collect data for capacity planning.\n<img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" alt=\"Hey, Scripting Guy! Question\">&nbsp;Hey, Scripting Guy! I&rsquo;ve just starting learning Windows PowerShell and I understand how to use it as a scripting language and shell. I&rsquo;ve been told to start performing capacity planning for my servers and was wondering how I can use Windows PowerShell to help with this task.\n&mdash;PH\n<img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" alt=\"Hey, Scripting Guy! Answer\">&nbsp;Hello PH,\nHonorary Scripting Guy, Richard Siddaway, here today&mdash;filling in for my good friend, The Scripting Guy. As part of <em>The Admin&rsquo;s First Steps<\/em> series, I&rsquo;m going to devote the next three posts to capacity planning. Hopefully, this will answer your question.\nTo view the previous posts in this series, see <a href=\"http:\/\/social.technet.microsoft.com\/Search\/en-US?query=%22The%20Admin's%20first%20steps%22&amp;beta=0&amp;rn=Hey%2c+Scripting+Guy!+Blog&amp;rq=site:blogs.technet.com\/b\/heyscriptingguy\/&amp;ac=5\" target=\"_blank\">The Admin&rsquo;s First Steps Series<\/a>.\nCapacity planning is all about answering questions such as, &ldquo;When will file servers run out of disk space?&rdquo; and &ldquo;How many more users will this application support before the server becomes too slow?&rdquo;\nYou can&rsquo;t answer these questions exactly. It would be great to be able to know that file server File01 will run out of disk space on 14 June 2014 at 10:37 AM. You could then plan to add extra capacity before that time so that your users don&rsquo;t experience issues. Unfortunately in the real world, things don&rsquo;t work like that, and you can&rsquo;t make such exact predictions.\nHowever, what you can do is measure the trend of usage. If the disk space that is used by your file server is growing at 2% per month, you can predict when you need to add disk space&mdash;assuming that the rate of usage remains constant. That last part is important. You have to assume that the trends will continue.\nCapacity planning is a three stage process:<\/p>\n<ul>\n<li>Data gathering<\/li>\n<li>Data storage<\/li>\n<li>Reporting on that data<\/li>\n<\/ul>\n<p>In this post, we&rsquo;ll look at how to collect data. In the following blog posts, I will discuss how you can store the data and how to create the reports.\nBefore you can start collecting data, you need to know what you want to show. The two main groups you need to worry about are:<\/p>\n<ul>\n<li>Space issues: When will my disks be full? When will I have used all of the memory if each additional user requires another 2&nbsp;MB of RAM for the application?<\/li>\n<li>Performance issues: Can my processors cope with adding 50 more users to the server? Will the network cards cope with the extra traffic from those users?<\/li>\n<\/ul>\n<p>Collecting space-type data is relatively straightforward because it tends to be a one-way process with usage growing (unless you do some cleaning up). So recording data every couple of weeks or every month may be sufficient.\nCollecting disk space data can be accomplished by using WMI. The following function provides one way to perform this task:<\/p>\n<p style=\"padding-left: 30px\">function get-diskcapacity {<\/p>\n<p style=\"padding-left: 30px\">[CmdletBinding()]<\/p>\n<p style=\"padding-left: 30px\">param (<\/p>\n<p style=\"padding-left: 30px\">&nbsp;[string[]]$computername = $env:COMPUTERNAME<\/p>\n<p style=\"padding-left: 30px\">)<\/p>\n<p style=\"padding-left: 30px\">PROCESS {<\/p>\n<p style=\"padding-left: 30px\">foreach ($computer in $computername) {<\/p>\n<p style=\"padding-left: 30px\">Get-CimInstance -ClassName Win32_LogicalDisk -Filter &#8220;DriveType = 3&#8221; -ComputerName $computer |<\/p>\n<p style=\"padding-left: 30px\">select PSComputerName, Caption,<\/p>\n<p style=\"padding-left: 30px\">@{N=&#8217;Capacity_GB&#8217;; E={[math]::Round(($_.Size \/ 1GB), 2)}},<\/p>\n<p style=\"padding-left: 30px\">@{N=&#8217;FreeSpace_GB&#8217;; E={[math]::Round(($_.FreeSpace \/ 1GB), 2)}},<\/p>\n<p style=\"padding-left: 30px\">@{N=&#8217;PercentUsed&#8217;; E={[math]::Round(((($_.Size &#8211; $_.FreeSpace) \/ $_.Size) * 100), 2) }},<\/p>\n<p style=\"padding-left: 30px\">@{N=&#8217;PercentFree&#8217;; E={[math]::Round((($_.FreeSpace \/ $_.Size) * 100), 2) }}<\/p>\n<p style=\"padding-left: 30px\">} # end foreach<\/p>\n<p style=\"padding-left: 30px\">} # end PROCESS<\/p>\n<p style=\"padding-left: 30px\">}\nThe function takes an array of computer names as input. It loops through those computers and uses <strong>Get-CimInstance<\/strong> to retrieve the Win32_LogicalDisk class information from the machine. A filter limits the data to local hard disks. Other potential filters can be found in Windows PowerShell and WMI or online at <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa394173%28v=vs.85%29.aspx\" target=\"_blank\">Win32_LogicalDisk class<\/a>.\nWMI returns the disk size and free space as bytes. This isn&rsquo;t the easiest of values to relate to, so the function converts those values to GB. Notice the use of the Windows PowerShell size constant for 1&nbsp;GB rather than typing in the full value. The percentages of the disk that are used and free are also calculated. All calculations are rounded to two decimal places.\nUsing <strong>Get-CIMinstance<\/strong> in this manner assumes that you have Windows PowerShell&nbsp;3.0 on all of the remote machines.&nbsp; If you don&rsquo;t (especially if you have a mixture of Windows PowerShell&nbsp;3.0 &nbsp;and Windows PowerShell&nbsp;2.0), you should use <strong>Get-WmiObject<\/strong> instead.<\/p>\n<p style=\"padding-left: 30px\">function get-diskcapacity {<\/p>\n<p style=\"padding-left: 30px\">[CmdletBinding()]<\/p>\n<p style=\"padding-left: 30px\">param (<\/p>\n<p style=\"padding-left: 30px\">&nbsp;[string[]]$computername = $env:COMPUTERNAME<\/p>\n<p style=\"padding-left: 30px\">)<\/p>\n<p style=\"padding-left: 30px\">PROCESS {<\/p>\n<p style=\"padding-left: 30px\">foreach ($computer in $computername) {<\/p>\n<p style=\"padding-left: 30px\">Get-WmiObject -Class Win32_LogicalDisk -Filter &#8220;DriveType = 3&#8221; -ComputerName $computer |<\/p>\n<p style=\"padding-left: 30px\">select PSComputerName, Caption,<\/p>\n<p style=\"padding-left: 30px\">@{N=&#8217;Capacity_GB&#8217;; E={[math]::Round(($_.Size \/ 1GB), 2)}},<\/p>\n<p style=\"padding-left: 30px\">@{N=&#8217;FreeSpace_GB&#8217;; E={[math]::Round(($_.FreeSpace \/ 1GB), 2)}},<\/p>\n<p style=\"padding-left: 30px\">@{N=&#8217;PercentUsed&#8217;; E={[math]::Round(((($_.Size &#8211; $_.FreeSpace) \/ $_.Size) * 100), 2) }},<\/p>\n<p style=\"padding-left: 30px\">@{N=&#8217;PercentFree&#8217;; E={[math]::Round((($_.FreeSpace \/ $_.Size) * 100), 2) }}<\/p>\n<p style=\"padding-left: 30px\">} # end foreach<\/p>\n<p style=\"padding-left: 30px\">} # end PROCESS<\/p>\n<p style=\"padding-left: 30px\">}\nThe reason <strong>Get-CimInstance<\/strong> won&rsquo;t work against the machines running Windows PowerShell&nbsp;2.0 is the WSMAN version. The CIM cmdlets use WSMAN to connect to remote machines, and they require WSMAN&nbsp;3.0, or above, which means at least Windows PowerShell 3.0 must be installed.\nWindows Server&nbsp;2012 and Windows 8 have a <strong>NetAdapter<\/strong> module. (It&rsquo;s not available in earlier versions of Windows because it uses the ROOT\/StandardCimv2:MSFT_NetAdapterStatisticsSettingData WMI class, which isn&rsquo;t ported to the older versions.)<\/p>\n<p style=\"padding-left: 30px\"><strong>Note<\/strong>&nbsp;&nbsp;&nbsp;Details for the cmdlet can be found in the Help file. Or if you don&rsquo;t have access to a computer running Windows Server&nbsp;2012 or Windows 8, it&rsquo;s available online at <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/jj130889.aspx\" target=\"_blank\">Get-NetAdapterStatistics<\/a>. The WMI class documentation can be found on MSDN at <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/hh872390(v=vs.85).aspx\" target=\"_blank\">MSFT_NetAdapterStatisticsSettingData class<\/a>. I&rsquo;d recommend examining the output of this class because there is a lot of useful information. The WMI class documentation at <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa394554(v=vs.85).aspx\" target=\"_blank\">MI Classes<\/a> has been expanded to include a lot of the WMI classes that were introduced in Windows 8. Book mark the page if you use WMI!\nThe cmdlet is used like this:<\/p>\n<p style=\"padding-left: 30px\">Get-NetAdapterStatistics -Name WiFi |<\/p>\n<p style=\"padding-left: 30px\">select ReceivedBytes, SentBytes,<\/p>\n<p style=\"padding-left: 30px\">ReceivedDiscardedPackets, ReceivedPacketErrors,<\/p>\n<p style=\"padding-left: 30px\">OutboundDiscardedPackets, OutboundPacketErrors\nThe name of the adapter is used to limit the data returned. If you have multiple adapters in the system, you may want to return data for all of them. The selected properties show the number of bytes sent and received, together with error data for inbound and outbound connections.\nThe output looks like this:<\/p>\n<p style=\"padding-left: 30px\">ReceivedBytes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 1290212919<\/p>\n<p style=\"padding-left: 30px\">SentBytes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 163510700<\/p>\n<p style=\"padding-left: 30px\">ReceivedDiscardedPackets : 0<\/p>\n<p style=\"padding-left: 30px\">ReceivedPacketErrors&nbsp;&nbsp;&nbsp;&nbsp; : 0<\/p>\n<p style=\"padding-left: 30px\">OutboundDiscardedPackets : 0<\/p>\n<p style=\"padding-left: 30px\">OutboundPacketErrors&nbsp;&nbsp;&nbsp;&nbsp; : 0\nThe statistics are cumulative from when the machine started. However, the data collected is only for a point in time. You need to be able to track usage across a time period. I normally use the busiest parts of the working day (mid-morning and mid-afternoon) to collect information when the systems are under the greatest load. You can achieve that by taking two readings separated by a known time interval and calculating the difference between the various values. Because you want this to run for a significant period, this job is an ideal candidate for Windows PowerShell.<\/p>\n<p style=\"padding-left: 30px\">$sb = {<\/p>\n<p style=\"padding-left: 30px\">param ([string]$computername)<\/p>\n<p style=\"padding-left: 30px\">Import-Module NetAdapter<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$sess = New-CimSession -ComputerName $computername<\/p>\n<p style=\"padding-left: 30px\">$start = Get-NetAdapterStatistics -Name WiFi -CimSession $sess<\/p>\n<p style=\"padding-left: 30px\">Start-Sleep -Seconds 60&nbsp; # 1800 = 30 minutes<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$end = Get-NetAdapterStatistics -Name WiFi -CimSession $sess<\/p>\n<p style=\"padding-left: 30px\">Remove-CimSession $sess<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$props = [ordered]@{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;ComputerName = $computername<\/p>\n<p style=\"padding-left: 30px\">&nbsp;ReceivedBytes = $end.ReceivedBytes &#8211; $start.ReceivedBytes<\/p>\n<p style=\"padding-left: 30px\">&nbsp;SentBytes = $end.SentBytes &#8211; $start.SentBytes<\/p>\n<p style=\"padding-left: 30px\">&nbsp;ReceivedDiscardedPackets = $end.ReceivedDiscardedPackets &#8211; $start.ReceivedDiscardedPackets<\/p>\n<p style=\"padding-left: 30px\">&nbsp;ReceivedPacketErrors = $end.ReceivedPacketErrors &#8211; $start.ReceivedPacketErrors<\/p>\n<p style=\"padding-left: 30px\">&nbsp;OutboundDiscardedPackets = $end.OutboundDiscardedPackets -$start.OutboundDiscardedPackets<\/p>\n<p style=\"padding-left: 30px\">&nbsp;OutboundPacketErrors = $end.OutboundPacketErrors -$start.OutboundPacketErrors<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">New-Object -TypeName PSObject -Property $props<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">}\n&nbsp;<\/p>\n<p style=\"padding-left: 30px\">Start-Job -ScriptBlock $sb&nbsp; -ArgumentList $env:COMPUTERNAME&nbsp; -Name &#8220;netstats-$($env:COMPUTERNAME)&#8221;\nA script block is used to define the processing. It starts by importing the <strong>Netadapter<\/strong> module. Windows PowerShell jobs run a separate context, and although Windows PowerShell&nbsp;3.0 will auto-load modules, I always prefer to categorically import the module. The <strong>Get-NetAdapterStatistics<\/strong> cmdlet is used to populate the <strong>$Start<\/strong> variable, and it uses a CIM session to the remote machine. <strong>Start-Sleep<\/strong> pauses the processing. In this case, I&rsquo;ve used 60 seconds; but in reality, you&rsquo;d probably want to use 30 minutes or more.\nA second call to <strong>Get-NetAdapterStatistics<\/strong> over the CIM session, is used to get the end values. An ordered hash table is created with the values being calculated by subtracting the start values from the end values. The computer name is added as another property. Ordered hash tables keep the properties in the order you define rather than &ldquo;randomizing&rdquo; them as with a normal hash table. The hash table is used as the properties for a new object, which forms the output.\n<strong>Start-Job<\/strong> is used to execute the script block. The job is given a name based on the computer name, with the computer name being passed to the script block as an argument.\nWays you could extend this include:<\/p>\n<ul>\n<li>Add extra items to the collection process<\/li>\n<li>Create function to build jobs for retrieving network statistics from remote machines<\/li>\n<\/ul>\n<p>PH, that is how you go about gathering data for your capacity planning. Obviously, there are other parameters that you can measure, but they are dependent on your environment. I would recommend starting with a small number of measurements and expand that over time. Next time, we&rsquo;ll look at storing the data you are collecting.\nBye for now.\n~Richard\nThanks, Richard. I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"http:\/\/blogs.technet.commailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><strong>Ed Wilson, Microsoft Scripting Guy<\/strong>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Richard Siddaway talks about using Windows PowerShell to collect data for capacity planning. &nbsp;Hey, Scripting Guy! I&rsquo;ve just starting learning Windows PowerShell and I understand how to use it as a scripting language and shell. I&rsquo;ve been told to start performing capacity planning for my servers and was wondering how I can use Windows [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[453,51,56,41,31,189,3,4,130,39,45,6],"class_list":["post-2623","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-admin-first-steps","tag-getting-started","tag-guest-blogger","tag-monitoring","tag-operating-system","tag-richard-siddaway","tag-scripting-guy","tag-scripting-techniques","tag-servers","tag-services","tag-windows-powershell","tag-wmi"],"acf":[],"blog_post_summary":"<p>Summary: Richard Siddaway talks about using Windows PowerShell to collect data for capacity planning. &nbsp;Hey, Scripting Guy! I&rsquo;ve just starting learning Windows PowerShell and I understand how to use it as a scripting language and shell. I&rsquo;ve been told to start performing capacity planning for my servers and was wondering how I can use Windows [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2623","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=2623"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2623\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=2623"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=2623"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=2623"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}