Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to become a SHiPS wright - Building with SHiPS

Glenn
April 27, 2019

How to become a SHiPS wright - Building with SHiPS

A Shipwright an artisan skilled in one or more of the tasks required to build vessels. A SHiPSwright is an artisan skilled in one or more of the tasks required to build PowerShell Providers. The SHiPS toolkit has been around for a while but it can be a little difficult to get started.

Links
https://github.com/PowerShell/SHiPS

Glenn

April 27, 2019
Tweet

More Decks by Glenn

Other Decks in Technology

Transcript

  1. Becoming a
    SHiPS wright
    Building with SHiPS
    @glennsarti glenn-sarti

    View Slide

  2. @glennsarti

    View Slide

  3. @glennsarti
    Becoming a
    SHiPS wright

    View Slide

  4. @glennsarti
    What is SHiPS?

    View Slide

  5. @glennsarti
    SHiPS is a PowerShell provider.
    More accurately it is a provider
    platform that simplifies developing
    PowerShell providers.
    - https://www.powershellgallery.com/packages/SHiPS

    View Slide

  6. @glennsarti
    PowerShell providers provide access to data
    and components that would not otherwise
    be easily accessible at the command line. The
    data is presented in a consistent format that
    resembles a file system drive..
    - about_providers

    View Slide

  7. @glennsarti
    PS C:\Users\glenn.sarti> Get-PSProvider
    Name Capabilities Drives
    ---- ------------ ------
    Registry ShouldProcess {HKLM, HKCU}
    Alias ShouldProcess {Alias}
    Environment ShouldProcess {Env}
    FileSystem Filter, ShouldProcess, Credentials {C}
    Function ShouldProcess {Function}
    Variable ShouldProcess {Variable}

    View Slide

  8. @glennsarti
    PS C:\Users\glenn.sarti> Get-ChildItem HKLM:\System
    Hive: HKEY_LOCAL_MACHINE\System
    Name Property
    ---- --------
    ActivationBroker
    ControlSet001
    DriverDatabase Version : 167772160
    SchemaVersion : 65536
    Architecture : 9
    UpdateDate : {64, 148, 108, 19...}
    OemInfMap : {255, 255, 255, 255...}
    HardwareConfig LastConfig : {a4c71b4c-22d3-11b2-a85c-f593fd660ebe}
    LastId : 0

    View Slide

  9. @glennsarti
    Why do I need SHiPS?

    View Slide

  10. @glennsarti
    Providers are great

    View Slide

  11. @glennsarti
    Providers are hard

    View Slide

  12. @glennsarti
    Providers are hard

    View Slide

  13. @glennsarti
    Providers are hard

    View Slide

  14. @glennsarti
    File System 9,965
    Registry 4,373
    Certificates 3,454
    Functions 388
    Aliases 358
    Environment 246
    Variables 239

    View Slide

  15. @glennsarti
    File System 13,113
    Registry 7,521
    Certificates 6,602
    Functions 3,537
    Aliases 3,507
    Environment 3,388
    Variables 3,395

    View Slide

  16. @glennsarti
    SHiPS is a PowerShell provider.
    More accurately it is a provider
    platform that simplifies developing
    PowerShell providers.
    - https://www.powershellgallery.com/packages/SHiPS

    View Slide

  17. @glennsarti
    When did SHiPS
    become a thing?

    View Slide

  18. @glennsarti
    2018 2019
    2014 2015 2016 2017
    Simplex
    P2F
    Azure Cloud Shell Public Preview
    SHiPS on PS Gallery
    Releases (0.8.1)
    SHiPS

    View Slide

  19. @glennsarti
    Module
    You are here
    SHiPS
    P2F
    PowerShell

    View Slide

  20. @glennsarti
    using namespace Microsoft.PowerShell.SHiPS
    # Root
    class Parent : SHiPSDirectory
    {
    Parent([string]$name): base($name) { }
    [object[]] GetChildItem()
    {
    return [Child]::new("Child");
    }
    }
    class Child : SHiPSLeaf
    {
    Child($name): base($name) { }
    }

    View Slide

  21. @glennsarti
    What things can I SHiPS-ify?

    View Slide

  22. @glennsarti
    Deepak - https://github.com/DexterPOSH
    Glenn - https://github.com/glennsarti
    Ravi - https://github.com/rchaganti
    Patrick - https://github.com/SeeminglyScience
    PS - https://github.com/PowerShell

    View Slide

  23. @glennsarti

    View Slide

  24. @glennsarti
    Directed
    Acyclical
    Graph

    View Slide

  25. @glennsarti
    Directed
    Acyclical
    Graph

    View Slide

  26. @glennsarti
    Directed
    Acyclical
    Graph

    View Slide

  27. @glennsarti
    Directed
    Acyclical
    Graph
    A
    B
    C
    D
    A/B/C

    View Slide

  28. @glennsarti
    Directed
    Acyclical
    Graph
    A
    B
    C
    D

    View Slide

  29. @glennsarti
    Directed
    Acyclical
    Graph

    A
    B
    C
    D
    A/B/C/A/B/C/A …

    View Slide

  30. @glennsarti
    Directed
    Acyclical
    Graph
    A
    B
    C
    D
    A/B/C A/B/D/C

    View Slide

  31. @glennsarti
    DAGs in the wild …

    View Slide

  32. @glennsarti
    Filesystem Registry

    View Slide

  33. @glennsarti
    Filesystem Registry

    View Slide

  34. @glennsarti
    AD Org. Units Certificates

    View Slide

  35. @glennsarti
    AD Org. Units Certificates

    View Slide

  36. @glennsarti
    Azure Resource Groups

    View Slide

  37. @glennsarti
    Azure Resource Groups

    View Slide

  38. @glennsarti
    Azure DevOps Pipelines

    View Slide

  39. @glennsarti
    Azure DevOps Pipelines

    View Slide

  40. @glennsarti

    View Slide

  41. @glennsarti
    DAGs are everywhere!

    View Slide

  42. @glennsarti
    And?

    View Slide

  43. @glennsarti
    Austin
    Cathy
    Ben
    Bill Chris

    View Slide

  44. @glennsarti
    Austin
    Bill
    SHiPSDirectory
    SHiPSLeaf
    GetChildItem

    View Slide

  45. @glennsarti
    Become a
    SHiPS wright

    View Slide

  46. @glennsarti
    Step 0
    Put the computer away

    View Slide

  47. @glennsarti

    View Slide

  48. @glennsarti

    View Slide

  49. @glennsarti
    Step 0.5
    Install SHiPS
    https://github.com/PowerShell/SHiPS#installing-ships

    View Slide

  50. @glennsarti
    Step 1
    Create root object

    View Slide

  51. @glennsarti
    Root

    View Slide

  52. @glennsarti
    @{
    RootModule = 'PSSummitNA2019.psm1'
    ModuleVersion = '1.0'
    GUID = '2e946999-7d90-4bfb-88f1-9336e3a01587'
    Author = 'glenn.sarti'
    CompanyName = 'Glenn Sarti'
    Description = 'A PowerShell Provider for the Pow…'
    # Modules that must be imported into the global …
    RequiredModules = @('SHiPS')
    }

    View Slide

  53. @glennsarti
    using namespace Microsoft.PowerShell.SHiPS
    # Root
    [SHiPSProvider(UseCache=$true)]
    class Summit2019 : SHiPSDirectory
    {
    Summit2019([string]$name): base($name)
    {
    }
    [object[]] GetChildItem()
    {
    $obj = @()
    return $obj;
    }
    }

    View Slide

  54. @glennsarti
    using namespace Microsoft.PowerShell.SHiPS
    # Root
    [SHiPSProvider(UseCache=$true)]
    class Summit2019 : SHiPSDirectory
    {
    Summit2019([string]$name): base($name)
    {
    }
    [object[]] GetChildItem()
    {
    $obj = @()
    return $obj;
    }
    }

    View Slide

  55. @glennsarti
    using namespace Microsoft.PowerShell.SHiPS
    # Root
    [SHiPSProvider(UseCache=$true)]
    class Summit2019 : SHiPSDirectory
    {
    Summit2019([string]$name): base($name)
    {
    }
    [object[]] GetChildItem()
    {
    $obj = @()
    return $obj;
    }
    }

    View Slide

  56. @glennsarti
    using namespace Microsoft.PowerShell.SHiPS
    # Root
    [SHiPSProvider(UseCache=$true)]
    class Summit2019 : SHiPSDirectory
    {
    Summit2019([string]$name): base($name)
    {
    }
    [object[]] GetChildItem()
    {
    $obj = @()
    return $obj;
    }
    }

    View Slide

  57. @glennsarti
    using namespace Microsoft.PowerShell.SHiPS
    # Root
    [SHiPSProvider(UseCache=$true)]
    class Summit2019 : SHiPSDirectory
    {
    Summit2019([string]$name): base($name)
    {
    }
    [object[]] GetChildItem()
    {
    $obj = @()
    return $obj;
    }
    }

    View Slide

  58. @glennsarti
    using namespace Microsoft.PowerShell.SHiPS
    # Root
    [SHiPSProvider(UseCache=$true)]
    class Summit2019 : SHiPSDirectory
    {
    Summit2019([string]$name): base($name)
    {
    }
    [object[]] GetChildItem()
    {
    $obj = @()
    return $obj;
    }
    }
    ???

    View Slide

  59. @glennsarti
    PS > Import-Module .\PSSummitNA2019.psd1

    View Slide

  60. @glennsarti
    PS > Import-Module .\PSSummitNA2019.psd1
    PS > New-PSDrive -Name Summit2019 `
    -PSProvider SHiPS `
    -Root 'PSSummitNA2019#Summit2019'
    Name Used (GB) Free (GB) Provider Root
    ---- --------- --------- -------- ----
    Summit2019 SHiPS PSSum…

    View Slide

  61. @glennsarti
    PS > Import-Module .\PSSummitNA2019.psd1
    PS > New-PSDrive -Name Summit2019 `
    -PSProvider SHiPS `
    -Root 'PSSummitNA2019#Summit2019'
    Name Used (GB) Free (GB) Provider Root
    ---- --------- --------- -------- ----
    Summit2019 SHiPS PSSum…
    PS > gci Summit2019:\
    PS >

    View Slide

  62. @glennsarti
    Step 2
    Create directories

    View Slide

  63. @glennsarti
    Speakers
    Agenda

    View Slide

  64. @glennsarti
    # Directory Nodes
    # Speakers
    [SHiPSProvider(UseCache=$true)]
    class Speakers : SHiPSDirectory
    {
    Speakers([string]$name): base($name)
    {
    }
    [object[]] GetChildItem()
    {
    return @()
    }
    }
    # Agenda
    [SHiPSProvider(UseCache=$true)]

    View Slide

  65. @glennsarti
    }
    }
    # Agenda
    [SHiPSProvider(UseCache=$true)]
    class Agenda : SHiPSDirectory
    {
    Agenda([string]$name): base($name)
    {
    }
    [object[]] GetChildItem()
    {
    return @()
    }
    }

    View Slide

  66. @glennsarti
    # Root
    [SHiPSProvider(UseCache=$true)]
    class Summit2019 : SHiPSDirectory
    {
    Summit2019([string]$name): base($name)
    {
    }
    [object[]] GetChildItem()
    {
    $obj = @()
    return $obj;
    }
    }
    # Directory Nodes
    # Speakers

    View Slide

  67. @glennsarti
    # Root
    [SHiPSProvider(UseCache=$true)]
    class Summit2019 : SHiPSDirectory
    {
    Summit2019([string]$name): base($name)
    {
    }
    [object[]] GetChildItem()
    {
    $obj = @()
    $obj += [Speakers]::new('Speakers’);
    $obj += [Agenda]::new('Agenda’);
    return $obj;
    }
    }

    View Slide

  68. @glennsarti
    PS > Import-Module .\PSSummitNA2019.psd1
    PS > New-PSDrive -Name Summit2019 `
    -PSProvider SHiPS `
    -Root 'PSSummitNA2019#Summit2019'
    Name Used (GB) Free (GB) Provider Root
    ---- --------- --------- -------- ----
    Summit2019 SHiPS PSSum…

    View Slide

  69. @glennsarti
    PS > gci Summit2019:\
    Directory: Summit2019:
    Mode Name
    ---- ----
    + Speakers
    + Agenda
    PS >

    View Slide

  70. @glennsarti
    Step 3
    Create Speaker leaves

    View Slide

  71. @glennsarti
    Bob
    Alice

    View Slide

  72. @glennsarti
    # Private Functions
    $Script:DataSource = Join-Path -Path $PSScriptRoot -ChildPath 'Data'
    Function Get-SpeakersObject {
    if ($Script:SpeakerObject -eq $null) {
    $Script:SpeakerObject = Get-Content … | ConvertFrom-Json
    }
    Write-Output $Script:SpeakerObject
    }
    Function Remove-HTML($RawString) {
    $result = $RawString
    $result = $result -replace '<[^>]+>',‘’
    Write-Output $result
    }

    View Slide

  73. @glennsarti
    # Leaf Nodes
    # A Speaker
    [SHiPSProvider(UseCache=$true)]
    class Speaker : SHiPSLeaf
    {
    [String]$Name;
    [String]$FirstName;
    [String]$LastName;
    [String]$Bio;
    Speaker ([string]$name, [Object]$data): base($name)
    {
    $this.PopulateFromData($data)
    }
    [void] PopulateFromData([Object]$data) {
    $this.Name = $data.name
    # VERY basic name splitting
    $NameArray = $this.Name -split " ", 2
    $this.FirstName = $NameArray[0]
    $this.LastName = $NameArray[1]
    $this.Bio = Remove-HTML -RawString $data.overview
    }
    }

    View Slide

  74. @glennsarti
    # Leaf Nodes
    # A Speaker
    [SHiPSProvider(UseCache=$true)]
    class Speaker : SHiPSLeaf
    {
    [String]$Name;
    [String]$FirstName;
    [String]$LastName;
    [String]$Bio;
    Speaker ([string]$name, [Object]$data): base($name)
    {
    $this.PopulateFromData($data)
    }
    [void] PopulateFromData([Object]$data) {
    $this.Name = $data.name
    # VERY basic name splitting
    $NameArray = $this.Name -split " ", 2
    $this.FirstName = $NameArray[0]
    $this.LastName = $NameArray[1]
    $this.Bio = Remove-HTML -RawString $data.overview
    }
    }

    View Slide

  75. @glennsarti
    # Leaf Nodes
    # A Speaker
    [SHiPSProvider(UseCache=$true)]
    class Speaker : SHiPSLeaf
    {
    [String]$Name;
    [String]$FirstName;
    [String]$LastName;
    [String]$Bio;
    Speaker ([string]$name, [Object]$data): base($name)
    {
    $this.PopulateFromData($data)
    }
    [void] PopulateFromData([Object]$data) {
    $this.Name = $data.name
    # VERY basic name splitting
    $NameArray = $this.Name -split " ", 2
    $this.FirstName = $NameArray[0]
    $this.LastName = $NameArray[1]
    $this.Bio = Remove-HTML -RawString $data.overview
    }
    }

    View Slide

  76. @glennsarti
    # Leaf Nodes
    # A Speaker
    [SHiPSProvider(UseCache=$true)]
    class Speaker : SHiPSLeaf
    {
    [String]$Name;
    [String]$FirstName;
    [String]$LastName;
    [String]$Bio;
    Speaker ([string]$name, [Object]$data): base($name)
    {
    $this.PopulateFromData($data)
    }
    [void] PopulateFromData([Object]$data) {
    $this.Name = $data.name
    # VERY basic name splitting
    $NameArray = $this.Name -split " ", 2
    $this.FirstName = $NameArray[0]
    $this.LastName = $NameArray[1]
    $this.Bio = Remove-HTML -RawString $data.overview
    }
    }

    View Slide

  77. @glennsarti
    # Speakers
    [SHiPSProvider(UseCache=$true)]
    class Speakers : SHiPSDirectory
    {
    Speakers([string]$name): base($name)
    {
    }
    [object[]] GetChildItem()
    {
    return @()
    }
    }

    View Slide

  78. @glennsarti
    # Speakers
    [SHiPSProvider(UseCache=$true)]
    class Speakers : SHiPSDirectory
    {
    Speakers([string]$name): base($name)
    {
    }
    [object[]] GetChildItem()
    {
    $obj = New-Object System.Collections.ArrayList($null)
    (Get-SpeakersObject).items | ForEach-Object -Process {
    $obj.Add([Speaker]::new($_.name, $_))
    }
    return $obj;
    }

    View Slide

  79. @glennsarti
    PS > gci Summit2019:\Speakers | Select-Object -First 5
    Directory: Summit2019:\Speakers
    Mode Name
    ---- ----
    . James O'Neill
    . Christoph Bergmeister
    . Walter Legowski
    . Paul DeArment Jr
    . Daniel Bohannon
    PS >

    View Slide

  80. @glennsarti
    PS > gci Summit2019:\Speakers | select -First 1 –Property *
    SSItemMode : .
    PSPath : SHiPS\SHiPS::PSSummitNA2019#Summit2019\Speakers\James
    PSParentPath : SHiPS\SHiPS::PSSummitNA2019#Summit2019\Speakers
    PSChildName : James O'Neill
    PSDrive : Summit2019
    PSProvider : SHiPS\SHiPS
    PSIsContainer : False
    Name : James O'Neill
    FirstName : James
    LastName : O'Neill
    Bio : James O'Neill spent the first 10 years of this century
    talking about it to anyone who will listen ever since.
    and DSC to automate processes for one of Britiain's be

    PS >

    View Slide

  81. @glennsarti
    Step 4
    Expand objects

    View Slide

  82. @glennsarti
    Session 1
    Session 2
    Session 3
    Day 1
    All

    View Slide

  83. @glennsarti
    Function Get-SessionsObject {

    }
    Function Get-Sessions($Filter) {

    }
    Function ConvertFrom-EpochTime($Value) {

    }

    View Slide

  84. @glennsarti
    # A Session on the agenda
    [SHiPSProvider(UseCache=$true)]
    class AgendaSession : SHiPSLeaf
    {
    [String]$Id;
    [String]$Name;
    [datetime]$Start;
    [datetime]$End;
    [String]$Location;
    [String]$Description;
    Hidden [Object] $Data
    AgendaSession([string]$id, $data): base($id)
    {
    $this.Id = $Id
    $this.Data = $data

    View Slide

  85. @glennsarti
    # A Session on the agenda
    [SHiPSProvider(UseCache=$true)]
    class AgendaSession : SHiPSLeaf
    {
    [String]$Id;
    [String]$Name;
    [datetime]$Start;
    [datetime]$End;
    [String]$Location;
    [String]$Description;
    Hidden [Object] $Data
    AgendaSession([string]$id, $data): base($id)
    {
    $this.Id = $Id
    $this.Data = $data

    View Slide

  86. @glennsarti
    [String]$Description;
    Hidden [Object] $Data
    AgendaSession([string]$id, $data): base($id)
    {
    $this.Id = $Id
    $this.Data = $data
    $this.Name = $data.name
    $this.Description = Remove-HTML $data.overview
    $this.Start = ConvertFrom-EpochTime -Value $data.start_time
    $this.End = ConvertFrom-EpochTime -Value $data.end_time
    $this.Location = ($data.regions | Select-Object -First 1 |
    ForEach-Object { $_.name })
    }
    }

    View Slide

  87. @glennsarti
    Session 1
    Session 2
    Session 3
    Day 1
    All

    View Slide

  88. @glennsarti
    # AgendaTrackSummary
    [SHiPSProvider(UseCache=$true)]
    class AgendaTrackSummary : SHiPSDirectory
    {
    [String]$Name;
    [Int]$Sessions;
    Hidden [Hashtable]$Filter;
    AgendaTrackSummary([string]$name, [Hashtable]$filter): base($name)
    {
    $this.Name = $name
    $this.Filter = $filter
    $this.Sessions = (Get-Sessions -Filter $this.Filter | Measure-Object).Count;
    }
    [object[]] GetChildItem()
    {
    $obj = New-Object System.Collections.ArrayList($null)

    View Slide

  89. @glennsarti
    # AgendaTrackSummary
    [SHiPSProvider(UseCache=$true)]
    class AgendaTrackSummary : SHiPSDirectory
    {
    [String]$Name;
    [Int]$Sessions;
    Hidden [Hashtable]$Filter;
    AgendaTrackSummary([string]$name, [Hashtable]$filter): base($name)
    {
    $this.Name = $name
    $this.Filter = $filter
    $this.Sessions = (Get-Sessions -Filter $this.Filter | Measure-Object).Count;
    }
    [object[]] GetChildItem()
    {
    $obj = New-Object System.Collections.ArrayList($null)

    View Slide

  90. @glennsarti
    }
    [object[]] GetChildItem()
    {
    $obj = New-Object System.Collections.ArrayList($null)
    Get-Sessions -Filter $this.Filter | ForEach-Object -Process {
    $obj.Add([AgendaSession]::new($_.Id, $_))
    }
    return $obj;
    }
    }

    View Slide

  91. @glennsarti
    [object[]] GetChildItem()
    {
    return @()
    }

    View Slide

  92. @glennsarti
    [object[]] GetChildItem()
    {
    $obj = New-Object System.Collections.ArrayList($null)
    $obj.Add([AgendaTrackSummary]::new('All', @{ }))
    $obj.Add([AgendaTrackSummary]::new('Day 1 - Mon', @{ 'Day' = 29 }))
    $obj.Add([AgendaTrackSummary]::new('Day 2 - Tue', @{ 'Day' = 30 }))
    $obj.Add([AgendaTrackSummary]::new('Day 3 - Wed', @{ 'Day' = 1 }))
    $obj.Add([AgendaTrackSummary]::new('Day 4 - Thu', @{ 'Day' = 2 }))
    $trackList = @{}
    (Get-SessionsObject).sessions | ForEach-Object -Process {
    $_.tracks | ForEach-Object -Process {
    $trackList[$_.name] = $true
    }
    }
    $trackList.GetEnumerator() | ForEach-Object -Process {
    $obj.Add([AgendaTrackSummary]::new($_.Key, @{ 'Track' = $_.Key }))
    }
    return $obj;
    }

    View Slide

  93. @glennsarti
    [object[]] GetChildItem()
    {
    $obj = New-Object System.Collections.ArrayList($null)
    $obj.Add([AgendaTrackSummary]::new('All', @{ }))
    $obj.Add([AgendaTrackSummary]::new('Day 1 - Mon', @{ 'Day' = 29 }))
    $obj.Add([AgendaTrackSummary]::new('Day 2 - Tue', @{ 'Day' = 30 }))
    $obj.Add([AgendaTrackSummary]::new('Day 3 - Wed', @{ 'Day' = 1 }))
    $obj.Add([AgendaTrackSummary]::new('Day 4 - Thu', @{ 'Day' = 2 }))
    $trackList = @{}
    (Get-SessionsObject).sessions | ForEach-Object -Process {
    $_.tracks | ForEach-Object -Process {
    $trackList[$_.name] = $true
    }
    }
    $trackList.GetEnumerator() | ForEach-Object -Process {
    $obj.Add([AgendaTrackSummary]::new($_.Key, @{ 'Track' = $_.Key }))
    }
    return $obj;
    }

    View Slide

  94. @glennsarti
    PS > gci Summit2019:\Agenda | `
    Select-Object -Property Name, Sessions | `
    Format-Table

    View Slide

  95. @glennsarti
    Name Sessions
    ---- --------
    All 80
    Day 1 - Mon 9
    Day 2 - Tue 25
    Day 3 - Wed 26
    Day 4 - Thu 20
    In the Cloud 8
    Meal 9
    Vendor 2
    General 12
    Bits and Bytes 14
    Automate All the Things 17
    OnRamp 3
    PowerShell Language 17

    View Slide

  96. @glennsarti
    PS > gci 'Summit2019:\Agenda\Day 2 – Tue' | `
    Select-Object -First 5

    View Slide

  97. @glennsarti
    Directory: Summit2019:\Agenda\Day 2 - Tue
    Mode Name
    ---- ----
    . 61513
    . 61461
    . 61464
    . 61475
    . 61494
    PS>

    View Slide

  98. @glennsarti
    PS > gci 'Summit2019:\Agenda\Day 2 – Tue' | `
    Select-Object -First 5 –Property *

    View Slide

  99. @glennsarti
    SSItemMode : .
    PSPath : SHiPS\SHiPS::PSSummitNA2019#Summit2019\Agenda\Day 2 - Tu
    PSParentPath : SHiPS\SHiPS::PSSummitNA2019#Summit2019\Agenda\Day 2 - Tu
    PSChildName : 61513
    PSDrive : Summit2019
    PSProvider : SHiPS\SHiPS
    PSIsContainer : False
    Id : 61513
    Name : Breakfast
    Start : 30/04/2019 08:00:00
    End : 30/04/2019 09:00:00
    Location : Center Hall A
    Description : Join us for a hot breakfast. We'll have announcements
    SSItemMode : .
    PSPath : SHiPS\SHiPS::PSSummitNA2019#Summit2019\Agenda\Day 2 - Tu

    View Slide

  100. @glennsarti
    Step 5
    Add content

    View Slide

  101. @glennsarti
    # VERY basic name splitting
    $NameArray = $this.Name -split " ", 2
    $this.FirstName = $NameArray[0]
    $this.LastName = $NameArray[1]
    $this.Bio = Remove-HTML -RawString $data.overview
    }
    [string] GetContent()
    {
    return "# " + $this.Name + "`n`n## Bio`n`n" + $this.Bio
    }
    }

    View Slide

  102. @glennsarti
    $this.Name = $data.name
    $this.Description = Remove-HTML $data.overview
    $this.Start = ConvertFrom-EpochTime -Value $data.start_time
    $this.End = ConvertFrom-EpochTime -Value $data.end_time
    $this.Location = ($data.regions | Select-Object -First 1 | ForEach-
    }
    [string] GetContent()
    {
    return "### " + $this.name + "`n`n" + `
    "Time:" + $this.Start.ToString('hh:mm tt') + `
    " Location:" + $this.Location + "`n`n" + `
    $this.Description + "`n`n"
    }
    }

    View Slide

  103. @glennsarti
    PS > Get-Content 'Summit2019:\Speakers\Glenn Sarti'
    # Glenn Sarti
    ## Bio
    I’m located in Perth (Western Australia) where I now work at
    Puppet as Senior Software Developer on the Windows team. Fro

    PS >

    View Slide

  104. @glennsarti
    PS > Get-Content 'Summit2019:\Agenda\Day 2 - Tue\61451'
    ### Parselmouth - bending the PowerShell language
    Time:11:00 AM Location:406
    I want to introduce the audience to the high-level design of
    the tokenizer, parser and compiler in PowerShell Core, and
    then take it a step …
    PS >

    View Slide

  105. @glennsarti
    Step 6
    Add leaf connections

    View Slide

  106. @glennsarti
    Root
    Alice
    Session 2
    Session 3
    Day 1
    All
    Speakers
    Agenda
    Bob
    Session 1

    View Slide

  107. @glennsarti
    Bob
    Session 1

    View Slide

  108. @glennsarti
    class Speaker : SHiPSLeaf
    {

    [String[]]$SessionIDs;
    [String[]]$Sessions;
    [int]$NumSessions;
    [String[]]$SessionTimes;
    [void] PopulateFromData([Object]$data) {

    $this.Sessions = @()
    $this.SessionIDs = @()
    $this.SessionTimes = @()
    Get-Sessions -Filter @{ 'Speaker' = $data.Id} | ForEach-Object {
    $this.Sessions += $_.name
    $start = (ConvertFrom-EpochTime -Value $_.start_time).ToString("d MMM, hh:mm tt")
    $this.SessionIDs += $_.id
    $this.SessionTimes += $start
    }
    $this.NumSessions = $this.Sessions.Count
    }

    View Slide

  109. @glennsarti
    class Speaker : SHiPSLeaf
    {

    [String[]]$SessionIDs;
    [String[]]$Sessions;
    [int]$NumSessions;
    [String[]]$SessionTimes;
    [void] PopulateFromData([Object]$data) {

    $this.Sessions = @()
    $this.SessionIDs = @()
    $this.SessionTimes = @()
    Get-Sessions -Filter @{ 'Speaker' = $data.Id} | ForEach-Object {
    $this.Sessions += $_.name
    $start = (ConvertFrom-EpochTime -Value $_.start_time).ToString("d MMM, hh:mm tt")
    $this.SessionIDs += $_.id
    $this.SessionTimes += $start
    }
    $this.NumSessions = $this.Sessions.Count
    }

    View Slide

  110. @glennsarti
    class AgendaSession : SHiPSLeaf
    {

    [String[]]$Speakers
    AgendaSession([string]$id, $data): base($id)
    {

    $this.Speakers = $this.Data.item_ids | ForEach-Object -Process {
    $SpeakerID = $_
    Write-Output (Get-SpeakersObject).items | `
    Where-Object { $_.id -eq $SpeakerId } | `
    ForEach-Object { Write-Output $_.name }
    }
    }

    View Slide

  111. @glennsarti
    class AgendaSession : SHiPSLeaf
    {

    [String[]]$Speakers
    AgendaSession([string]$id, $data): base($id)
    {

    $this.Speakers = $this.Data.item_ids | ForEach-Object -Process {
    $SpeakerID = $_
    Write-Output (Get-SpeakersObject).items | `
    Where-Object { $_.id -eq $SpeakerId } | `
    ForEach-Object { Write-Output $_.name }
    }
    }

    View Slide

  112. @glennsarti
    PS > gci 'Summit2019:\Speakers\Glenn Sarti' | Select *
    ...
    Name : Glenn Sarti
    FirstName : Glenn
    LastName : Sarti
    Bio : I’m located in Perth (Western Australia)…
    SessionIDs : {61496, 61497}
    Sessions : {Beyond Pester 102: Acceptance testing w…
    NumSessions : 2
    SessionTimes : {30 Apr, 02:00 PM, 1 May, 11:00 AM}

    View Slide

  113. @glennsarti
    PS > gci 'Summit2019:\Agenda\Day 2 - Tue\61451' | Select *
    ...
    Id : 61451
    Name : Parselmouth - bending the PowerShell lang…
    Start : 30/04/2019 11:00:00
    End : 30/04/2019 11:45:00
    Location : 406
    Description : I want to introduce the audience to the …
    Speakers : Mathias Jessen

    View Slide

  114. @glennsarti
    Step 7
    Make it pretty

    View Slide

  115. @glennsarti
    PS > Get-Help about_Format.ps1xml

    View Slide

  116. @glennsarti
    @{
    RootModule = 'PSSummitNA2019.psm1’
    ModuleVersion = '1.0’
    GUID = '2e946999-7d90-4bfb-88f1-9336e3a01587’
    Author = 'glenn.sarti’
    CompanyName = 'Glenn Sarti’
    Description = 'A PowerShell Provider for the Pow…’
    # Modules that must be imported into the global …
    RequiredModules = @('SHiPS’)
    FormatsToProcess = @('PSSummitNA2019.Format.ps1xml')
    }

    View Slide

  117. @glennsarti
    PS > gci Summit2019:\Speakers | `
    Select-Object –First 3 | Format-Table
    Mode Name
    ---- ----
    . James O'Neill
    . Christoph Bergmeister
    . Walter Legowski

    View Slide

  118. @glennsarti
    Name Session Times Bio
    ---- ------------- ---
    James O'Neill {2 May, 01:00 PM} James O'Neill spe…
    Christoph Bergmeister {30 Apr, 02:00 PM} I am a developer …
    Walter Legowski {1 May, 11:00 AM} ###**Walter Legow…

    View Slide

  119. @glennsarti
    PS > gci 'Summit2019:\Agenda\In the Cloud' | `
    Sort-Object –Property Start | `
    Select-Object –First 3 | Format-List
    Mode : .
    Name : 61464
    Mode : .
    Name : 61501
    Mode : .
    Name : 61466

    View Slide

  120. @glennsarti
    Id : 61464
    Name : Introduction to Serverless Functions
    Start : 30/04/2019 09:00:00
    Location : 405
    Speakers : Kirk Munro
    Description : I've spent a lot of time working with server …
    Id : 61501
    Name : Demystifying Microsoft's Cloud Automation products
    Start : 30/04/2019 11:00:00
    Location : 405
    Speakers : Jaap Brasser
    Description : Azure offers an evergrowing suite of produ …
    Id : 61466
    Name : It’s PowerShell In the Cloud – Welcome to Azure Cloud Shell
    Start : 30/04/2019 13:00:00
    Location : 405
    Speakers : Michael Bender
    Description : As more organizations move towards the cloud and using Microsoft …

    View Slide

  121. @glennsarti
    What did I just see?

    View Slide

  122. @glennsarti
    • Start with some planning
    • Create the root object first
    • … then the Directories
    • … then the Leaves
    • Expand – Test – Iterate
    • Then make it pretty

    View Slide

  123. @glennsarti
    More please …

    View Slide

  124. @glennsarti
    Using an API

    View Slide

  125. @glennsarti

    View Slide

  126. @glennsarti
    Credentials

    View Slide

  127. @glennsarti
    PS> New-PSDrive -Name Github -PSProvider SHiPS `
    -root 'Github#Root’ `
    -Credential (Get-Credential 'glennsarti')
    PowerShell credential request
    Enter your credentials.
    Password for user glennsarti: *********
    The provider does not support the use of credentials. Perform the operation
    At line:1 char:1
    + New-PSDrive -Name Github -PSProvider SHiPS -root 'Github#Root' -Crede ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotImplemented: (:) [], PSNotSupportedException
    + FullyQualifiedErrorId : NotSupported
    PS >

    View Slide

  128. @glennsarti
    • Github Issue #110
    • Environment Variables
    • Files / Registry
    • Anything outside of PS

    View Slide

  129. @glennsarti
    Runspaces

    View Slide

  130. @glennsarti
    Default Runspace
    PS>

    View Slide

  131. @glennsarti
    Default Runspace
    $Global:GithubToken
    Set-GitHubToken
    PS> Import-Module .\Github.psd1

    View Slide

  132. @glennsarti
    Default Runspace
    $Global:GithubToken
    Set-GitHubToken
    PS> Set-GitHubToken abc123
    abc123

    View Slide

  133. @glennsarti
    Default Runspace
    $Global:GithubToken
    Set-GitHubToken
    PS> New-PSDrive –Name Github –Provider SHiPS …
    abc123
    Drive Runspace
    $Global:GithubToken
    Set-GitHubToken

    View Slide

  134. @glennsarti
    Caching

    View Slide

  135. @glennsarti
    … for better navigation user experience … an author can
    decide to set UseCache to true, to cache data returned from
    Get-ChildItem to memory in the current PowerShell session.
    - SHiPS documentation
    [SHiPSProvider(UseCache=$true)]
    class Speaker : SHiPSLeaf
    {

    View Slide

  136. @glennsarti
    Set-Content, Filtering,
    Dynamic Parameters

    View Slide

  137. @glennsarti
    Set-Content 'Summit2019:\Speakers\Glenn Sarti'
    Set-Content

    View Slide

  138. @glennsarti
    PS > gci Summit2019:\Speakers –Filter Glenn*
    PS > gci Summit2019:\Speakers | `
    Where-Object { $_.Name –like 'Glenn*'}
    Filtering

    View Slide

  139. @glennsarti
    PS > gci Summit2019:\Agenda\All | `
    Where-Object { $_.Track –eq 'General' }
    PS > gci Summit2019:\Agenda –Track General
    PS > gci Summit2019:\Agenda –Day 2
    PS > gci Summit2019:\Agenda –Speaker 'Glenn Sarti'
    … or combine them -Track General –Day 2
    Dynamic Parameters

    View Slide

  140. @glennsarti
    See /samples/DynamicParameter for documentation

    View Slide

  141. @glennsarti
    Testing?

    View Slide

  142. @glennsarti
    Making SHiPS better

    View Slide

  143. @glennsarti
    Doesn’t support credentials
    Tiny supported cmdlet list
    Doesn’t support New, Remove, Rename, Move, Copy,
    *ItemProperty
    No Drive information within
    Provider Context

    View Slide

  144. @glennsarti
    SHiPS is still young

    View Slide

  145. @glennsarti
    Wrapping up…

    View Slide

  146. @glennsarti
    SHiPS is a UX module
    Start small and iterate
    Read the docs
    Remember the DAG

    View Slide

  147. THANK YOU!
    Please use the event app or Socio to submit a session rating!
    @glennsarti
    http://glennsarti.github.io/
    https://speakerdeck.com/glennsarti
    https://github.com/glennsarti/PSSummitNA2019-Ships

    View Slide

  148. @glennsarti
    Resources
    Ravikanth Chaganti
    PS Conf EU 2018 - SHiPS: Walk-through a bare-metal system configuration
    https://github.com/psconfeu/2018/tree/master/Ravikanth%20Chaganti/SHiPS
    SHiPS GH Repo
    https://github.com/PowerShell/SHiPS
    SHiPS PS Gallery
    https://www.powershellgallery.com/packages/SHiPS
    https://www.powershellgallery.com/packages?q=ships
    about_format.ps1xml
    https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/
    about_format.ps1xml?view=powershell-5.1

    View Slide

  149. @glennsarti
    Resources
    Writing a PowerShell Formatting File
    https://docs.microsoft.com/en-us/powershell/developer/format/
    writing-a-powershell-formatting-file
    SHiPS Default formatting
    https://github.com/PowerShell/SHiPS/blob/master/src/Modules/SHiPS.formats.ps1xml
    Images
    https://unsplash.com

    View Slide