Bringing OneGet to the end user using ZervicePoint & PowerShell 5.0 Preview

This is a cross-blog post from

In this demo you’ll see how you can bring the latest technology from Microsoft to your end-users.

Click here to learn more about Windows Management Framework 5.0 Preview and OneGet

The End User Experience
Let’s start from the end users perspective. The end user, Mr User in this scenario, wants to install Winrar on his computer, but he doesn’t know how to install it and he lacks permissions to install applications on his computer.

1. Mr User starts by logging in to the ZervicePoint portal


2. He opens the OneGet category (the category name can of course be changed to Applications)


3. Next, he chooses the “Install Package” service (Only one service? Yeah, OneGet & Windows Management Framework 5.0 is still in Preview, but we will add more OneGet Services as soon as it goes live)


4. The service is pretty simple to use for Mr User.
He can simply type the name of the application that he wants to install in the “Select a Package” field. The “Select a computer” field is pre-populated with Mr User’s computer (this is based on an Active Directory lookup that returns all Mr User’s computers, the one’s where the ManagedBy attribute is set to Mr User and defaults to the computer that he logged onto most recently).


5. Mr User types “Winrar” in the “Select a package” field


And voila, more information appears. The information describes additional information about the selected package.


6. Mr User then clicks on “Add to Cart” and place the orders for the Winrar package.
It is a good idea to control what end users can install. To handle this we’ve added an approval activity. When adding an approval activity to ZervicePoint you basically tell the workflow (the tasks that is running in the background) to wait until a specific person has approved the order. If the order is approved, the package will be installed. If the request is denied Mr User will not get the application installed. in this case we’ve set the approval to the user’s manager (based on the Active Directory attribute).

7. Now, let’s take a look what happens when Mr Manager logs into the portal.
Mr Manager just logged in to ZervicePoint since he got notified via e-mail that he has something to approve (yep, adding e-mail notifications is really simple).


8. Mr Manager clicks on “My Tasks” and sees that Mr User wants to install a package and that he need to either approve or deny the request.


When clicking on the order more information is displayed.


 9. Mr Manager chooses to approve the Winrar package for Mr User. He therefore adds a comment and clicks approve.


Once Mr Manager approves the order and the workflow continues. When the workflow is completed Winrar has been installed on Mr user’s computer and is now ready to use.

10. Mr User can simply find his new package in the Windows 8.1 Search as shown below.



The Technical aspects
Now let’s take a look at what actually happens in the background. The service is built using ZervicePoint’s Service Designer interface.

First we set some basic catalog information:


And here’s a description of the fields:

  • Current Language: Yeah, we support multiple languages!
  • Display Name: The name of the service.
  • Short Description: A simple one-liner describing the service.
  • Description: A detailed description of the service.
  • Allow user to change orders quantity: You can design your service to support quantity, useful if you are ordering hardware or guitar picks.. somehow I always loose my guitar picks.
  • Owner: The service owner. (lookup field from Active Directory or other source).
  • Cost: the initial cost for the service.
  • Monthly Cost: useful for subscriptions and such.
  • Categories: The categories where the service will be visible
  • Keywords: Hashtags, useful when searching for services.
  • Visible for Roles: ZervicePoint has a pretty cool Role system based on ADFS claims. This allows us to control, in detail, what kind of services an end-user should see.

Next we have the Form (this is what the user sees as a service):


The form designer supports full drag ‘n drop functionality. Simply pick the element you want and drag it into your form.
ZervicePoint supports a couple of different elements:

  • Text Field: Free text.
  • Text Area: A larger chunk of text.
  • Text block: Supports movies, images, and other interactive coolness.
  • Drop down list: A drop down that displays single or multiple choices that the user can select. I’ll get back to this one in a while since it’s pretty awesome.
  • Checkbox: Yes or No (boolean True/False)
  • Radiobutton: Well, its a radiobutton..
  • Date & Time picker: Supports Date & Time (depending on your regional settings you’ll see date & time differently).
  • File upload: Useful if you want to upload documents and files. Awesome if you add some approval activities to it :)
  • Hidden field: Used to store additional data, not displayed to the end user.
  • Form section: Helps you divide the service into different sections for a cleaner look and feel.

So what about Winrar and the ComputerName? where did that come from?
Glad you asked! Winrar, and all the other packages that the end user can search for comes from a Dynamic Drop Down PowerShell Module. The module is connected to the “Select a package” drop down list. If we click on the drop down list we can see the details about it.


Notice that Searchable is checked. This allows the end user to search for “something”. Dynamic bind is also checked and connected to Package.

Package is a variable that stores the value of whatever the end user selected. in this scenario it would contain “Winrar”.

And now for the cool part: Data Source!
Notice how data source is connected to ZervicePoint.DropDown.OneGet. This is actually a PowerShell module running on the server side.

Drop down modules in PowerShell are specific to ZervicePoint and follow a set of rules. They need to include a Search function that returns a PowerShell Object containing an ID and a Name. The Name is displayed to the End User and the ID is passed to the variable connected to the drop down list. Lets take a look at the actual code behind:


Note that the Search function includes a single PowerShell Cmdlet: Find-Package. it binds $search to the Name parameter and $search contains whatever the end user types!
So when the end user typed Winrar, the Find-Package Cmdlet executed and returned information about the Winrar package. Since we are working with a list we need to return an ID and a Name. Again, the ID is passed to the variable and the Name is displayed to the end user.

The Computer drop down list also uses PowerShell in the background but instead of using the OneGet Module it queries Active Directory. The ZervicePoint.Dropdown.ADComputer module is configured to only display computers where the ManagedBy attribute is equal to the end users DistinguishedName (an end-user can only see his own computers). It’s also possible to add additional rules and queries if needed. This is based on roles in ZervicePoint.

Another fun thing that happens is that ZervicePoint auto-populates a couple of fields. In this scenario the Provider Name, Source, Version, Status and Summary fields are automatically populated with information based on the users selection of package.

This is also performed by PowerShell. When populating additional fields we use a webservice to call a PowerShell “Get” function or CmdLet and return the object to ZervicePoint. In this example we’ve connected the Get-ZPOneGetPackage function to the service.


The function is placed in a PowerShell module and here’s what it looks like:


Again, this is a really simple PowerShell function that contains a oneliner! Notice how we select ProviderName, Source, Name, Version, Status, and Summary.
What’s even cooler is that if we name an element ProviderName it will automatically be populated with the ProviderName value from the PowerShell function. Pretty neat!

Here’s how the Provider Name element is configured:


As you can see we’ve simply set the Name so that it matches the PowerShell objects property.

Now, let’s take a look at the Process Activity.


The process activity only contains two steps. The first step is an Approval activity and the second step is a Drumroll PowerShell function! ZervicePoint includes a couple of built-in activities such as:

  • Approval: Allows you to add an approval to any service.
  • Task: Allows you to add a manual task. A good example would be: Type in the MAC Address for the newly delivered computer.
  • State: Allows you to display information about the current state. This is really useful if you build a longer running activity.
  • Send E-mail: Sends an email.
  • Sequence: Allows you to group activities.
  • Conditional sequence: Allows you to add logic to you activities. An example is if you have different approaches to an order in different offices or countries.
  • Delay: Allows you to delay an activity for a number of hours, days, week, years.
  • End process: Allows you to terminate a process if some criteria is not met.

All other activities are PowerShell Modules. Each function in a module is shown as an activity and can be used in ZervicePoint!

Let’s take a closer look on the activities used in our service.


The Approval activity starts of with a list of users that need to approve the order. In this example its connected to the Users manager. The users manager is grabbed from the users Manager attribute in Active Directory so its pretty dynamic. We can also add specific users, such as a helpdesk user account or we could add approvers based on a custom PowerShell function. We can add one or multiple approvers to an Approval activity.

The Approval Activity includes additional settings:

  • Form: The form that is displayed to the Approver.
  • Type: Parallel or Sequence. Allows us to send the approval request to the approvers in a sequence or all at once.
  • Required: All approvals or One approver.
  • On Timeout: Reject or Approve.
  • Timeout: The time to wait for an approver to either approve or reject.
  • Approval result: Allows us to store the choice made by the approver in a variable.
  • Notify approver(s): Allows us to automatically send an e-mail to the approvers whenever they have a new task in ZervicePoint.

Next we have the Install-ZPOneGetPackage. This is an activity written in PowerShell and placed in the ZervicePoint.OneGet module. Here’s the actual code behind:


The module contains the function Install-ZPOneGetPackage. The function has two mandatory parameters: ComputerName and Package.

ComputerName is used when connecting to the remote computer using New-PSSession and Package is used as an input to the Install-Package Cmdlet on the remote computer. Again, this is a fairly simple, straightforward PowerShell function that relies on PowerShell’s Remoting and Cmdlets.

In ZervicePoint we simply add the users choice of package to the Package parameter and the users Computer to the ComputerName parameter.


And there we have it. Bringing OneGet to the end-user in a controlled manner :)

//Niklas Goude – ZervicePoint Development Team


Rating 4.00 out of 5

PowerShell User Group Sweden

First of all I would like to thank everyone who attended the Swedish PowerShell Community Day 2013.
The Community day was a success with lots of great discussions and sessions. Thank You!
And a special thanks to TrueSec & Labcenter for letting us use your facilities!


PowerShell 3.0 Technical Drilldown (remote & workflows)


Part 1: Running PowerShell V 2.0 Specific CmdLets from PowerShell V 3.0


Running SharePoint CmdLets in PowerShell V 3.0 is a little problematic since it’s not supported with version 4.0.30319.296 of the Microsoft .Net Runtime.
Here’s what happens if you try to run a SharePoint CmdLet in PowerShell V 3.0


Add-PSSNapin Microsoft.SharePoint.PowerShell

The local farm is not accessible. Cmdlets with FeatureDependencyId are not registered.


Get-SPSite : Microsoft SharePoint is not supported with version 4.0.30319.296 of the Microsoft .Net Runtime.
At line:1 char:1
+ Get-SPSite
+ ~~~~~~~~~~
    + CategoryInfo          : InvalidData: (Microsoft.Share...SPCmdletGetSite:SPCmdletGetSite) [Get-SPSite], PlatformNotSupportedException
    + FullyQualifiedErrorId : Microsoft.SharePoint.PowerShell.SPCmdletGetSite


One way of solving this is by running powershell.exe with the -Version parameter and start PowerShell in version 2.0.


powershell.exe -Version 2.0


Another way is to set up a dedicated SessionConfiguration that runs powershell in version 2.0.


First, create a configuration file and include the -PowerShellVersion parameter


New-PSSessionConfigurationFile -Path C:\ps\SP.pssc -PowerShellVersion 2.0


Next, Register the Session Configuration. In the example below I use the -Name parameter to specify a name for my Session Configuration.
I also add a StartupScript that loads the SharePoint Snapin, and I include -Path,pointing it to the Configuration File that I just created.


# SharePoint.ps1 Script contains one line
# Script Start
Add-PSSNapin Microsoft.SharePoint.PowerShell
# Script End

Register-PSSessionConfiguration -Name SP -StartupScript C:\PS\SharePoint.ps1 -Path C:\PS\SP.pssc


Now i can simply connect to my Session Configuration and execute any SharePoint CmdLet. Here’s an example:


PS > Enter-PSSession -ComputerName localhost -ConfigurationName SP

[localhost]: PS > Get-SPSite




[localhost]: PS > Exit-PSSession



Part 2: Setting up Restricted Endpoints


Next demo showed how to set up a restricted endpoint. A restricted endpoint allows you to specify which CmdLets a user is permitted to run through the endpoint.
In the example below we restrict the user to the following cmdlets: Get-ADUser,Set-ADUser,Get-ADComputer,Set-ADComputer,Get-ADGroup,Set-ADGroup.
We also use the ModulesToImport parameter and set it to ActiveDirectory when creating the Configuration File.


New-PSSessionConfigurationFile -Path C:\PS\AD.pssc -SessionType RestrictedRemoteServer -ModulesToImport ActiveDirectory -VisibleCmdlets Get-ADUser,Set-ADUser,Get-ADComputer,Set-ADComputer,Get-ADGroup,Set-ADGroup


The CmdLet generates a Configuration File that we can use when we register the endpoint.


When setting up an endpoint we have the possibility to run commands with the permissions of a different user account, such as a service account.
First store the users credentials in a PSCredential object.


$runAsCred = Get-Credential powershell\UserManagement


Next, use PSCredential object when registering a Session Configuration using the RunAsCredential parameter.
We also add the Path to the Configuration File generated earlier.


Register-PSSessionConfiguration -Name AD -Path C:\PS\AD.pssc -Force -RunAsCredential $runAsCred


In a default setup you have to be member of the builtin\administrators group or the builtin\remote management users group.
It’s also possible to add a custom group and allow the custom group to access the remote endpoint. You can do this
by using the Set-PSSessionConfiguration CmdLet with the ShowSecurityDescriptorUI switch.


Set-PSSessionConfiguration -Name AD -ShowSecurityDescriptorUI



Now any user who is member of the domain group RemoteUsers can access the restricted endpoint and run the cmdlets exposed by the endpoint under the service accounts credentials.


$cred = Get-Credential powershell\testuser

Enter-PSSession -ComputerName SRV01 -ConfigurationName AD -Credential $cred

[SRV01]: PS> Get-Command

CommandType     Name             ModuleName                                                 
-----------     ----             ----------                                                 
Function        Exit-PSSession   
Function        Get-Command                                                                                                   
Function        Get-FormatData   
Function        Get-Help         
Function        Measure-Object   
Function        Out-Default      
Function        Select-Object    
Cmdlet          Get-ADComputer   ActiveDirectory                                            
Cmdlet          Get-ADGroup      ActiveDirectory                                            
Cmdlet          Get-ADUser       ActiveDirectory                                            
Cmdlet          Set-ADComputer   ActiveDirectory                                            
Cmdlet          Set-ADGroup      ActiveDirectory                                            
Cmdlet          Set-ADUser       ActiveDirectory


Part 3: Disconnected Sessions


In PowerShell V 3.0 it’s possible to disconnect from a remote session and connect to it at a later time.
This is useful if you start a job friday afternoon and don’t want to leave your computer or spend the night at your office.
You can simply disconnect from the powershell session, go home, and connect to the session again.


First, create a new session against a machine.


$session = New-PSSession -ComputerName DC01 -Name Demo


Next, run some PowerShell code and add the AsJob switch.


Invoke-Command -Session $session -ScriptBlock {
  1..100 | ForEach-Object {
    "Run: $_"
    Start-Sleep -Seconds 2
} -AsJob


Next, use the Disconnect-PSSession CmdLet to disconnect the session.


Disconnect-PSSession -Session $session


When you want to connect to the session again you can simply use the Receive-PSSession CmdLet.


Get-PSSession -ComputerName DC01
Receive-PSSession -Name Demo -ComputerName DC01


Rating 3.00 out of 5

NASA’s Alien Planet Archive using PowerShell

Nasa has just opened up their Exoplanet Archive for the world. The archive contains exoplanet sightings by the Kepler observatory.

Click here for more information

The Exoplanet Archive data are accessed through the Exoplanet Archive’s application programming interface (API).

Click here for more information

Now for the fun stuff! Windows PowerShell Version 3.0 includes a CmdLet called Invoke-RestMethod. We can use the CmdLet to get data from Nasa’s API.

Before we download the data we need a query. All queries against the API must begin with the following base URL.

PS > $url = ""

Next we specify which data table to query. In the example below query the exoplanets table.

PS > $url = $url + "table=exoplanets"

We also add the names of the columns we want to be returned. In the example below we select the pl_hostname, and ra columns.

PS > $url = $url + "&select=pl_hostname,ra"

Here’s what the complete URL looks like.

PS > $url,ra

Now we can use Invoke-Restmethod to download the data from Nasa.

PS > Invoke-RestMethod -Uri $url

HD 173416,280.900452
HD 175167,285.003479
HD 175541,283.920349
HD 176051,284.256714
HD 177830,286.336548
HD 177830,286.336548

The example above displays the csv data as lines of text. Converting the text to objects is a simple task in PowerShell v 3.0 thanks to the ConvertFrom-Csv CmdLet.

PS > Invoke-RestMethod -Uri $url | ConvertFrom-Csv

pl_hostname       ra
-----------       --
HD 173416         280.900452
HD 175167         285.003479
HD 175541         283.920349
HD 176051         284.256714
HD 177830         286.336548
HD 177830         286.336548

Let’s wrap up the code in a reusable function instead.

Click here to download the Get-ExoPlanet function.

function Get-ExoPlanet {
    Gets data from the Exoplanet Archive.
    Uses the Exoplanet Archive Application Programming Interface (API) to retrieve information regarding Exoplanets.
  [CmdletBinding(HelpUri = '')]
  process {
    # Check PowerShell Version.
    if($Host.Version.Major -ne 3) {
      Write-Warning -Message "This function requires PowerShell Version 3.0"
    # Data table to access (exoplanets or koi or tce)
    $table = "Exoplanets"

    # Only retrieve Common Columns
    [string[]]$Column = @("pl_hostname", "pl_letter", "pl_discmethod", "pl_orbper", "pl_orbsmax", "pl_orbeccen", "pl_massj", "pl_msinij", "pl_radj", "pl_dens", "pl_orbincl", "pl_ttvflag", "ra", "dec", "st_dist", "st_vj", "st_teff", "st_mass", "st_rad", "hd_name", "hip_name")

    # Uri.
    $uri = "" + $table + "&select=" + [string]::Join(",",$Column)

    # Get Data
    $result = Invoke-RestMethod -Uri $uri
    # Convert from Csv to PowerShell Object.
    $result | ConvertFrom-Csv | ForEach-Object {
      # Return as a Custom PowerShell Object
      New-Object PSObject -Property @{
        PlanetHostStarName = [string]$($_.pl_hostname);
        PlanetLetter = [string]$($_.pl_letter);
        DiscoveryMethod = [string]$($_.pl_discmethod);
        OrbitalPeriod = [double]$($_.pl_orbper);
        SemiMajorAxis = [double]$($_.pl_orbsmax);
        Eccentricity = [double]$($_.pl_orbeccen);
        PlanetMass = [double]$($_.pl_massj);
        PlanetMsin = [double]$($_.pl_msinij);
        PlanetRadius = [double]$($_.pl_radj);
        PlanetDensity = [double]$($_.pl_dens);
        Inclination = [double]$($_.pl_orbincl);
        TTVFlag = [double]$($_.pl_ttvflag);
        RightAscension = [double]$($_.ra);
        Declination = [double]$($_.dec);
        Distance = [double]$($_.st_dist);
        VJohnsonSystemMagnitude = [double]$($_.st_vj);
        EffectiveStellarTemperature = [double]$($_.st_teff);
        StellarMass = [double]$($_.st_mass);
        StellarRadius = [double]$($_.st_rad);
        HDName = [string]$($_.hd_name);
        HIPName = [string]$($_.hip_name)

Now we can simply type Get-ExoPlanet to display the data from NASA’s Alien Planet Archive.

PS > $exoPlanets = Get-ExoPlanet
PS > $exoPlanets

PlanetLetter                : b
PlanetDensity               : 0
StellarMass                 : 0
VJohnsonSystemMagnitude     : 6.037
HDName                      : HD 173416
OrbitalPeriod               : 323.6
PlanetRadius                : 0
Declination                 : 36.556606
SemiMajorAxis               : 1.16
StellarRadius               : 0
Inclination                 : 0
Eccentricity                : 0.21
RightAscension              : 280.900452
Distance                    : 134.95
TTVFlag                     : 0
PlanetMsin                  : 2.7
DiscoveryMethod             : Radial Velocity
PlanetMass                  : 0
EffectiveStellarTemperature : 0
HIPName                     : HIP 91852
PlanetHostStarName          : HD 173416

The function displays the “default” columns from the Exoplanet Archive.

Working with the data as powershell objects allows us to combine the objects with other PowerShell CmdLets.

If we want to group the Exoplanets by Planet Letter we simply type:

PS > $exoPlanets | Group-Object PlanetLetter

Count Name                      Group
----- ----                      -----
  638 b                         {@{PlanetLetter=b;
  124 c                         {@{PlanetLetter=c;
   33 d                         {@{PlanetLetter=d;
   12 e                         {@{PlanetLetter=e;
    6 f                         {@{PlanetLetter=f;
    3 g                         {@{PlanetLetter=g;
    1 h                         {@{PlanetLetter=h;

Sorting the objects is also a simple task. Let’s say we want to display the top 10 planets with the largest Planet Mass (Jupiter).

PS > $exoPlanets | Sort-Object PlanetMass -Descending | 
Select-Object PlanetHostStarName , PlanetMass -First 10

PlanetHostStarName       PlanetMass
------------------       ----------
2MASS J22062280-2047058A      31.42
2MASS J07464256+2000321A      31.42
Kepler-47                        28
KELT-1                        27.23
nu Oph                           27
nu Oph                           24
HIP 78530                     23.04
HD 180314                        22
HN Peg                      21.9987
CoRoT-3                       21.66

If we want to display the Data in a Gridview we can pipe the objects to the Out-GridView CmdLet.

PS > $exoPlanets | Out-GridView

And if we want to Export the data to a Csv file we type:

PS > $exoPlanets | Export-Csv C:\ExoPlanets.csv

Click here to download the Get-ExoPlanet function.

Rating 4.50 out of 5

Checking Battery Status using PowerShell and PInvoke

You can use PowerShell when checking the battery status on your laptop.

One way of doing this is by using wmi as Scripting Guy demonstrates here.

Another way is by using P/Invoke to call unmanaged Win32 APIs from managed code.
In this example we will use GetSystemPowerStatus.

First we store the C# signature in a here-string, The signature is described here. Note the we add the public declaration.
We also add the SYSTEM_POWER_STATUS struct. The structure is described here.

$signature = @"
 [DllImport("kernel32.dll", SetLastError = true)]
 public static extern Boolean GetSystemPowerStatus(out SystemPowerStatus sps);

 public struct SystemPowerStatus
  public Byte ACLineStatus;
  public Byte BatteryFlag;
  public Byte BatteryLifePercent;
  public Byte Reserved1;
  public Int32 BatteryLifeTime;
  public Int32 BatteryFullLifeTime;

Next, we add the call to Add-Type and place it in a custom namespace (to prevent it from colliding with other classes).

Add-Type -MemberDefinition $signature -Name PowerStatus -Namespace PowerStatus

Now, we use New-Object to create an instance of PowerStatus.PowerStatus+SystemPowerStatus.

$systemPowerStatus = New-Object PowerStatus.PowerStatus+SystemPowerStatus

Next we call the static method GetSystemPowerStatus() and reference $systemPowerStatus.


Now we can call the variable to display the current Power Status.


ACLineStatus        : 1
BatteryFlag         : 1
BatteryLifePercent  : 99
Reserved1           : 0
BatteryLifeTime     : -1
BatteryFullLifeTime : -1

Next step is to convert the numeric values to values we understand. Each memeber is described in the following MSDN Article.

The ACLineStatus can be one of the following values:

  • 0 = Offline
  • 1 = Online
  • 255 = Unknown status

Let’s change the numeric value using a switch.

$acLineStatus = 
 switch( [byte]$systemPowerStatus.ACLineStatus) {
  0 { "Offline" }
  1 { "Online" }
  255 { "Unknown status" }
  default { "Unknown status" }

The BatteryFlag can be one of the following values:

  • 0 = Not Charging, between 66% and 33%
  • 1 = High. The battery capacity is at more than 66 percent
  • 2 = Low. The battery capacity is at less than 33 percent
  • 4 = Critical. The battery capacity is at less than five percent
  • 8 = Charging
  • 128 = No system battery
  • 255 = Unknown status—unable to read the battery flag information

We’ll use a switch again to determine the BatteryFlag.

$batteryStatus =
switch( [byte]$systemPowerStatus.BatteryFlag) {
 0 { "Not Charging, between 66% and 33%" }
 1 { "High. The battery capacity is at more than 66 percent" }
 2 { "Low. The battery capacity is at less than 33 percent" }
 4 { "Critical. The battery capacity is at less than five percent" }
 8 { "Charging" }
 128 { "No system battery" }
 255 { "Unknown status—unable to read the battery flag information" }
 default { "Unknown"}

The BatteryLifePercent displays the percentage of full battery charge remaining. This can be a value in the range 0 to 100, or 255 if status is unknown. Here we use an if/else statement.

# Check Battery Life Percent
if( [byte]$systemPowerStatus.BatteryLifePercent -eq [byte]255 ) {
 $batteryLifePercent = "Unknown"
} else {
 $batteryLifePercent = [byte]$systemPowerStatus.BatteryLifePercent

The BatteryLifeTime displays the number of seconds of battery life remaining, or –1 if remaining seconds are unknown. Again we’ll use an if/else statement to determine the batteries lifetime.

# Check Battery Lifetime
if( $systemPowerStatus.BatteryLifeTime -eq -1 ) {
 $batteryLifeTime = "Unknown"
} else {
 $batteryLifeTime = (Get-Date).AddSeconds($systemPowerStatus.BatteryLifeTime)

Now we can simply use New-Object and create a custom object containing the information gathered.

New-Object PSObject -Property @{
 ACPowerStatus = $acLineStatus;
 BatteryStatus = $batteryStatus;
 BatteryLifePercent = $batteryLifePercent
 BatteryLifeTime = $batteryLifeTime
 ComputerName = $env:COMPUTERNAME
} | Select-Object ACPowerStatus, BatteryStatus, BatteryLifePercent, BatteryLifeTime, ComputerName

And finally, here’s a reusable function that simplifies the steps

Rating 4.00 out of 5