MSCommerceFixed.psm1

#Set TLS
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Add-Type "$PSScriptRoot\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
Add-Type "$PSScriptRoot\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"

################################
# Start: Internal use functions
################################

function Get-AccessTokenFromSessionData() {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)]
    [System.Management.Automation.SessionState]
    $SessionState
  )

  $token = Get-MSCommerceConnectionInfo -SessionState $SessionState

  $token
}

function Get-MSCommerceConnectionInfo {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)]
    [System.Management.Automation.SessionState]
    $SessionState
  )

  if ($null -eq $sessionState.PSVariable) {
    throw "unable to access SessionState. PSVariable, Please call Connect-MSCommerce before calling any other Powershell CmdLet for the MSCommerce Module"
  }

  $token = $sessionState.PSVariable.GetValue("token");

  if ($null -eq $token) {
    throw "You must call the Connect-MSCommerce cmdlet before calling any other cmdlets"
  }

  return $token
}

function HandleError() {
  param(
    [Parameter(Mandatory = $true)]
    $ErrorContext,

    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $CustomErrorMessage
  )

  $errorMessage = $ErrorContext.Exception.Message
  $errorDetails = $ErrorContext.ErrorDetails.Message

  if ($_.Exception.Response.StatusCode -eq 401) {
    Write-Error "Your credentials have expired. Please, call Connect-MSCommerce again to regain access to MSCommerce Module."

    return
  }

  write-error "$CustomErrorMessage, ErrorMessage - $errorMessage ErrorDetails - $errorDetails"
}

################################
# End: Internal use functions
################################


################################
# Start: Exported functions
################################

<#
    .SYNOPSIS
    Method to connect to MSCommerce with the credentials specified
#>

function Connect-MSCommerce() {
  [CmdletBinding()]
  param(
    [ValidateNotNull()]
    [System.Management.Automation.PSCredential]
    [System.Management.Automation.Credential()]
    $Credential = [System.Management.Automation.PSCredential]::Empty     
  )

    $ClientID = "3d5cffa9-04da-4657-8cab-c7f074657cad"
    [Uri]$RedirectUri = [uri] "http://localhost/m365/commerce"
    [string]$Resource = "aeb86249-8ea3-49e2-900b-54cc8e308f85"

  IF (($Credential.UserName) -or ($Credential.Password))
  {
    $AuthorityUrl = "https://login.windows.net/common"
    
    $UserPasswordCredential = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.UserPasswordCredential($Credential.UserName, $Credential.Password)
    $AuthenticationContext = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext($AuthorityUrl)
    $Token = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContextIntegratedAuthExtensions]::AcquireTokenAsync($AuthenticationContext, $Resource, $ClientID, $UserPasswordCredential).Result
  }
  ELSE
  {
    $authorityUrl = "https://login.windows.net/common"
    $authCtx = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" $authorityUrl
    $platformParams = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" ([Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Always)
    $token = $authCtx.AcquireTokenAsync($Resource, $ClientId, $RedirectUri, $platformParams).Result
  }

  if ($null -eq $token) {
    Write-Error "Unable to establish connection"

    return
  }

  $sessionState = $PSCmdlet.SessionState

  $sessionState.PSVariable.Set("token", $token)

  Write-Output "Connection established successfully"
}

<#
    .SYNOPSIS
    Method to retrieve configurable policies
#>

function Get-MSCommercePolicies() {
  [CmdletBinding()]
  param()

  $token = Get-AccessTokenFromSessionData -SessionState $PSCmdlet.SessionState
  $correlationId = New-Guid
  $baseUri = "https://licensing.m365.microsoft.com"

  $restPath = "$baseUri/v1.0/policies"

  try {
    $response = Invoke-RestMethod `
      -Method GET `
      -Uri $restPath `
      -Headers @{
        "x-ms-correlation-id" = $correlationId
        "Authorization" = "Bearer $($token.AccessToken)"
      }

    foreach ($policy in $response.items) {
      New-Object PSObject -Property @{
        PolicyId = $policy.id
        Description = $policy.description
        DefaultValue = $policy.defaultValue
      }
    }
  } catch {
    HandleError -ErrorContext $_ -CustomErrorMessage "Failed to retrieve policies"
  }
}

<#
    .SYNOPSIS
    Method to retrieve a description of the specified policy
#>

function Get-MSCommercePolicy() {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $PolicyId
  )

  $token = Get-AccessTokenFromSessionData -SessionState $PSCmdlet.SessionState
  $correlationId = New-Guid
  $baseUri = "https://licensing.m365.microsoft.com"

  $restPath = "$baseUri/v1.0/policies/$PolicyId"

  try {
    $response = Invoke-RestMethod `
      -Method GET `
      -Uri $restPath `
        -Headers @{
        "x-ms-correlation-id" = $correlationId
        "Authorization" = "Bearer $($token.AccessToken)"
      }

    New-Object PSObject -Property @{
      PolicyId = $response.id
      Description = $response.description
      DefaultValue = $response.defaultValue
    }
  } catch {
    HandleError -ErrorContext $_ -CustomErrorMessage "Failed to retrieve policy with PolicyId '$PolicyId'"
  }
}

<#
    .SYNOPSIS
    Method to retrieve applicable products for the specified policy and their current settings
#>

function Get-MSCommerceProductPolicies() {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $PolicyId
  )

  $token = Get-AccessTokenFromSessionData -SessionState $PSCmdlet.SessionState
  $correlationId = New-Guid
  $baseUri = "https://licensing.m365.microsoft.com"

  $restPath = "$baseUri/v1.0/policies/$PolicyId/products"

  try {
    $response = Invoke-RestMethod `
      -Method GET `
      -Uri $restPath `
      -Headers @{
        "x-ms-correlation-id" = $correlationId
        "Authorization" = "Bearer $($token.AccessToken)"
      }

    foreach ($product in $response.items) {
      New-Object PSObject -Property @{
        PolicyId = $product.policyId
        ProductName = $product.productName
        ProductId = $product.productId
        PolicyValue = $product.policyValue
      }
    }
  } catch {
    HandleError -ErrorContext $_ -CustomErrorMessage "Failed to retrieve product policy with PolicyId '$PolicyId'"
  }
}

<#
    .SYNOPSIS
    Method to retrieve the current setting for the policy for the specified product
#>

function Get-MSCommerceProductPolicy() {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $PolicyId,
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $ProductId
  )

  $token = Get-AccessTokenFromSessionData -SessionState $PSCmdlet.SessionState
  $correlationId = New-Guid
  $baseUri = "https://licensing.m365.microsoft.com"

  $restPath = "$baseUri/v1.0/policies/$PolicyId/products/$ProductId"

  try {
    $response = Invoke-RestMethod `
      -Method GET `
      -Uri $restPath `
      -Headers @{
        "x-ms-correlation-id" = $correlationId
        "Authorization" = "Bearer $($token.AccessToken)"
      }

    New-Object PSObject -Property @{
      PolicyId = $response.policyId
      ProductName = $response.productName
      ProductId = $response.productId
      PolicyValue = $response.policyValue
    }
  } catch {
    HandleError -ErrorContext $_ -CustomErrorMessage "Failed to retrieve product policy with PolicyId '$PolicyId' ProductId '$ProductId'"
  }
}

<#
    .SYNOPSIS
    Method to modify the current setting for the policy for the specified product
#>

function Update-MSCommerceProductPolicy() {
  [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium')]
  param(
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $PolicyId,
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $ProductId,
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $Enabled
  )

  if ("True" -ne $Enabled -and "False" -ne $Enabled) {
    Write-Error "Value of `$Enabled must be one of the following: `$True, `$true, `$False, `$false"
    return
  }

  $token = Get-AccessTokenFromSessionData -SessionState $PSCmdlet.SessionState
  $correlationId = New-Guid
  $baseUri = "https://licensing.m365.microsoft.com"

  $restPath = "$baseUri/v1.0/policies/$PolicyId/products/$ProductId"
  $enabledStr = if ("True" -eq $Enabled -or "true" -eq $Enabled) {"Enabled"} else {"Disabled"}
  $body = @{
    policyValue = $enabledStr
  }

  if ($False -eq $PSCmdlet.ShouldProcess("ShouldProcess?")) {
    Write-Output "Updating product policy aborted"

    return
  }

  try {
    $response = Invoke-RestMethod `
      -Method PUT `
      -Uri $restPath `
      -Body ($body | ConvertTo-Json)`
      -ContentType 'application/json' `
      -Headers @{
        "x-ms-correlation-id" = $correlationId
        "Authorization" = "Bearer $($token.AccessToken)"
      }

    write-output "Update policy product success"
    New-Object PSObject -Property @{
      PolicyId = $response.policyId
      ProductName = $response.productName
      ProductId = $response.productId
      PolicyValue = $response.policyValue
    }
  }
  catch {
    HandleError -ErrorContext $_ -CustomErrorMessage "Failed to update product policy"
  }
}

################################
# End: Exported functions
################################

Write-Output "MSCommerce module loaded"

# SIG # Begin signature block
# MIIN+wYJKoZIhvcNAQcCoIIN7DCCDegCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUkVh5zuVgfc2Erpc9dtlRd35R
# PEWgggsxMIIFSTCCBDGgAwIBAgIRAPMKxE4HZQs+EipTj3vsWEUwDQYJKoZIhvcN
# AQELBQAwfTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3Rl
# cjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQx
# IzAhBgNVBAMTGkNPTU9ETyBSU0EgQ29kZSBTaWduaW5nIENBMB4XDTE4MTExNjAw
# MDAwMFoXDTIyMTExNjIzNTk1OVowgY4xCzAJBgNVBAYTAk5PMQ0wCwYDVQQRDAQ2
# NDU2MRgwFgYDVQQIDA9Nb3JlIG9nIFJvbXNkYWwxDzANBgNVBAcMBlNrYWFsYTEX
# MBUGA1UECQwOS3Zlcm5odXNsaWEgMTgxFTATBgNVBAoMDExhcnMgU3ZpbmdlbjEV
# MBMGA1UEAwwMTGFycyBTdmluZ2VuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
# CgKCAQEA3lYNFOUtf0eMeUVVQlCLQTVFJLW5H/U54jYPdAJYtADo/FiZlDtvqcnr
# XfFXg7Y1d6yYItV+QsW3eaZGRRbqe7LNpYD7xqxfD1fLWXXXVGr3Bohr0tt+JZ6i
# u9+VjBKoypbk81pclZbp1BeXD1LYEfBE0g8avFAfiwT7JYg8qYgCQaDikq69Xijr
# qw8hug7fJ1aEzUG+g2o9hjZcaDMmAa8ohVYCmkk0tyybizdUA2P2iiOzcYGTwLih
# VuvF7A1sNZ/jZ4ffHlhqLT68oP3dWwIzmkPxSV7vKE3yMITLEQmSP7/uJvp5qg0d
# CLv2mz20fMUN2ad2i5QgJ+wnJ/FjuQIDAQABo4IBsDCCAawwHwYDVR0jBBgwFoAU
# KZFg/4pN+uv5pmq4z/nmS71JzhIwHQYDVR0OBBYEFKx5HPnt0GiU4dXHDSGe7tCj
# 0u5xMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG
# AQUFBwMDMBEGCWCGSAGG+EIBAQQEAwIEEDBGBgNVHSAEPzA9MDsGDCsGAQQBsjEB
# AgEDAjArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5jb21vZG8ubmV0L0NQ
# UzBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01P
# RE9SU0FDb2RlU2lnbmluZ0NBLmNybDB0BggrBgEFBQcBAQRoMGYwPgYIKwYBBQUH
# MAKGMmh0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9ET1JTQUNvZGVTaWduaW5n
# Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wIQYD
# VR0RBBowGIEWbGFycy5zdmluZ2VuQGdtYWlsLmNvbTANBgkqhkiG9w0BAQsFAAOC
# AQEAVVafexhPkBmVX66iUEO9cka2KDhnoa67LS3+vS0+Q+jjny9bVTYvZXbSaDmk
# WgQKZZk3iK0AMum3eCg+Hkm4bkfar2zBUeABX45IdAV1I698GHZkmsIWAjematn7
# 5qQnX3t0Xjz6RBkvfTdItY6sFzZUAkDUGtiy3BCfANgkQKhqTvRljK6S653tynqt
# yVvb65E+K7rbe6S00L6ichAUCYM2EUV9PpvZMViUrz74pAlOl2oopOBc8SGPWC+U
# Ja/IdQocXch1+ePnkktgZBDqVMkoP846Bh0CrPOxBPAAgMy/407jXXfjqJL8TZdD
# MvT9GeBtv8Spl9aQSl+lYRzA+zCCBeAwggPIoAMCAQICEC58h8wOk0pS/pT9HLfN
# NK8wDQYJKoZIhvcNAQEMBQAwgYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVh
# dGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9E
# TyBDQSBMaW1pdGVkMSswKQYDVQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24g
# QXV0aG9yaXR5MB4XDTEzMDUwOTAwMDAwMFoXDTI4MDUwODIzNTk1OVowfTELMAkG
# A1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMH
# U2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxIzAhBgNVBAMTGkNP
# TU9ETyBSU0EgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
# MIIBCgKCAQEAppiQY3eRNH+K0d3pZzER68we/TEds7liVz+TvFvjnx4kMhEna7xR
# kafPnp4ls1+BqBgPHR4gMA77YXuGCbPj/aJonRwsnb9y4+R1oOU1I47Jiu4aDGTH
# 2EKhe7VSA0s6sI4jS0tj4CKUN3vVeZAKFBhRLOb+wRLwHD9hYQqMotz2wzCqzSgY
# dUjBeVoIzbuMVYz31HaQOjNGUHOYXPSFSmsPgN1e1r39qS/AJfX5eNeNXxDCRFU8
# kDwxRstwrgepCuOvwQFvkBoj4l8428YIXUezg0HwLgA3FLkSqnmSUs2HD3vYYimk
# fjC9G7WMcrRI8uPoIfleTGJ5iwIGn3/VCwIDAQABo4IBUTCCAU0wHwYDVR0jBBgw
# FoAUu69+Aj36pvE8hI6t7jiY7NkyMtQwHQYDVR0OBBYEFCmRYP+KTfrr+aZquM/5
# 5ku9Sc4SMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMBMGA1Ud
# JQQMMAoGCCsGAQUFBwMDMBEGA1UdIAQKMAgwBgYEVR0gADBMBgNVHR8ERTBDMEGg
# P6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9SU0FDZXJ0aWZpY2F0
# aW9uQXV0aG9yaXR5LmNybDBxBggrBgEFBQcBAQRlMGMwOwYIKwYBBQUHMAKGL2h0
# dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9ET1JTQUFkZFRydXN0Q0EuY3J0MCQG
# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQEM
# BQADggIBAAI/AjnD7vjKO4neDG1NsfFOkk+vwjgsBMzFYxGrCWOvq6LXAj/MbxnD
# PdYaCJT/JdipiKcrEBrgm7EHIhpRHDrU4ekJv+YkdK8eexYxbiPvVFEtUgLidQgF
# TPG3UeFRAMaH9mzuEER2V2rx31hrIapJ1Hw3Tr3/tnVUQBg2V2cRzU8C5P7z2vx1
# F9vst/dlCSNJH0NXg+p+IHdhyE3yu2VNqPeFRQevemknZZApQIvfezpROYyoH3B5
# rW1CIKLPDGwDjEzNcweU51qOOgS6oqF8H8tjOhWn1BUbp1JHMqn0v2RH0aofU04y
# MHPCb7d4gp1c/0a7ayIdiAv4G6o0pvyM9d1/ZYyMMVcx0DbsR6HPy4uo7xwYWMUG
# d8pLm1GvTAhKeo/io1Lijo7MJuSy2OU4wqjtxoGcNWupWGFKCpe0S0K2VZ2+medw
# bVn4bSoMfxlgXwyaiGwwrFIJkBYb/yud29AgyonqKH4yjhnfe0gzHtdl+K7J+IMU
# k3Z9ZNCOzr41ff9yMU2fnr0ebC+ojwwGUPuMJ7N2yfTm18M04oyHIYZh/r9VdOEh
# dwMKaGy75Mmp5s9ZJet87EUOeWZo6CLNuO+YhU2WETwJitB/vCgoE/tqylSNklzN
# wmWYBp7OSFvUtTeTRkF8B93P+kPvumdh/31J4LswfVyA4+YWOUunMYICNDCCAjAC
# AQEwgZIwfTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3Rl
# cjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQx
# IzAhBgNVBAMTGkNPTU9ETyBSU0EgQ29kZSBTaWduaW5nIENBAhEA8wrETgdlCz4S
# KlOPe+xYRTAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZ
# BgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYB
# BAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUYgHQHlh4Zq/2+Esg6xWNSljGI7QwDQYJ
# KoZIhvcNAQEBBQAEggEAdbqZSwaXvQfXKyL2e76BUXBGofDhBsAfsiyiHJfZdgmz
# qCZ/HdFodb4X7rbOzRbj8WKGjGWfsB8/6+dnyR0kapVM4s4v8t4ByeN1B41iIDIX
# Mdb25+9EcKfHcPYoQMI59cmA4HASCZ9ABAPc0ZHa7ctTNLKTDSGTRI2ENUoP7R2u
# xfZi7B8vF3pqIguX8VP7ITfzkK0uvWodjctzYg7DVd+yZsySBunCoQbzyNeTWkc2
# 6s0CJIWdgKX9DEnhhnaTM0v1+wkrWtzihfT7uGwwydMPMJqckRnADCXiSSHVLfOG
# 7rXL8JNezSmraIEALimoKIAhmQ9C0cXuVYNudxwLGg==
# SIG # End signature block