MSCommerce.psm1

################################
# 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(
    [string]
    $ClientId = "3d5cffa9-04da-4657-8cab-c7f074657cad",

    [Uri]
    $RedirectUri = [uri] "http://localhost/m365/commerce",

    [string]
    $Resource = "aeb86249-8ea3-49e2-900b-54cc8e308f85"   #LicenseManager App Id
  )

  $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
# MIIQeQYJKoZIhvcNAQcCoIIQajCCEGYCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUFVFSo1GvfPMk0MFeMAVCNbZd
# fRWggg2BMIIF/zCCA+egAwIBAgITMwAAAVGejY9AcaMOQQAAAAABUTANBgkqhkiG
# 9w0BAQsFADB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgw
# JgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMB4XDTE5MDUw
# MjIxMzc0NloXDTIwMDUwMjIxMzc0NlowdDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEeMBwGA1UEAxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMIIB
# IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlVrGhmlHHTQe8VXDZnX2YlQY
# WBRnJ/CjKsYDSLzmVjR/SWEC7oR4ZieUViEstaHst807sai25BwHZm3lPDRTKOPT
# 7+9TICEvSBvxLasDh+7qWp/pSKujTnMOXzujrPtdkdENvDMxp/t8uxdpig56KVbt
# LBLn8uOd4Mc9ejPGwMOPkF7r+/n0fVs0SdgqVOtsECmIhUDH3sOlYeX7j6F5aDd5
# OvkJc+84HE+GEZsc8e4zFaT/7ryurGXAcUN1oAf1pMlx4MWmNfSAMy6tkIj4l9mK
# 8okeRLGAat+QT+1NeQ5WbaUrNsCGE5JAwcYTySAyYKMGbuRsoR3aq7Ldzo5EkQID
# AQABo4IBfjCCAXowHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYD
# VR0OBBYEFFeCGq5Kue3rGoLuhVAW9u9GYo3PMFAGA1UdEQRJMEekRTBDMSkwJwYD
# VQQLEyBNaWNyb3NvZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEWMBQGA1UEBRMN
# MjMwMDEyKzQ1NDEzNTAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzcitW2oynUClTBU
# BgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# L2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEGCCsGAQUFBwEB
# BFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w
# cy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0MAwGA1UdEwEB
# /wQCMAAwDQYJKoZIhvcNAQELBQADggIBAFoPgK0uAJ6uyq6ILJSEJ6DB0l1D6/0a
# jiISV/t9jm7mNzBb3ZURJW0rEica0cvzggmXUrHvn9gKkPhf9mmMAQ5lEG3jAfJ9
# KWC2Zzj4n6nu/EQR9n1WbL18cn4s2x7m1lqFHzVvxSZWZS18CZhdwaC/BNqdPSt4
# WoMM/6LXCrUNfkOPg2jmF1pXqiayVLJx7PVIi3K1RdJXi/0NVeW7IaG9jk5WatKs
# 0nazAy1nYcq1DsZ2fqI2e1HU2OFZwrqIG2fWbPbMiW4O2VEvUlYGJKMEbFr+Y1eJ
# W6kw/rRuBc4TsHEUMHW+gPM6djZ3frO1XQrmBbBoCONbnA/KMVX3ADIxfWF+TdrO
# JpbZKp1Ht/4bVa58SigwMEmJYmrsdi+4CsPmw9ZBGf+aMy0Zyd44w5KJk6z3LaJG
# Pymmdarm9DVJ5jih/t8VCv6ZViSvATkGLlO4CmB+2MvVkijZT+6So+ouNe/tzWv3
# 6yJ45wZCkQif3mE7wR/rOYLns6X0FrVOGzaP4/EMNr6U0PcO35YR+/EZsHd9ffmE
# 5ob+03MQ21pcrXmo+EStsJN6WNaEs9iTxOTzbjZnfRpn+qHj2YMuyIvSSy6vEp1C
# 1e2/iD9FF0WPavhnUYzNgBF+prGb7zwiKBtGmB0LvcGbChCto6r1ovP8XXnnkiPR
# TeitcOKFUDODMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsF
# ADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UE
# AxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcN
# MTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQGEwJVUzETMBEG
# A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj
# cm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWdu
# aW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6
# chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZ
# BrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpg
# GgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0RrrgOGSsbmQ1eKag
# Yw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy4BI6t0le2O3t
# Q5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9sbKvkjh+0p2A
# LPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAhdCVfGCi2zCco
# OCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPj
# cF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/
# RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmnEyimp31ngOaK
# Ynhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90lfdu+HggWCwT
# XWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsG
# AQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2oynUClTAZBgkr
# BgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw
# AwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBR
# ME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0
# cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIw
# UDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0
# cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQw
# gZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3Nv
# ZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsGAQUFBwICMDQe
# MiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABlAG0AZQBuAHQA
# LiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKbC5YR4WOSmUKW
# fdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11lhJB9i0ZQVdgM
# knzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6I/MTfaaQdION
# 9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0wI/zRive/DvQ
# vTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560STkKxgrCxq2u
# 5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQamASooPoI/E01m
# C8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1j
# QeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ahXJbYANahRr1Z
# 85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi
# 63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33VtY5E90Z1WTk+/
# gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJ
# UnMTDXpQzTGCAmIwggJeAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX
# YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg
# Q29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENB
# IDIwMTECEzMAAAFRno2PQHGjDkEAAAAAAVEwCQYFKw4DAhoFAKCBojAZBgkqhkiG
# 9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIB
# FTAjBgkqhkiG9w0BCQQxFgQUK5OHRh6HsUr+OdUsjwSWYxZIo9YwQgYKKwYBBAGC
# NwIBDDE0MDKgFIASAE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbTANBgkqhkiG9w0BAQEFAASCAQA4VSxfK2MTskictQb0jyCEIvWl
# svLj/M3tLr/T2UWPFgQHtH893bCNB1MT1vHSZvgFe9VHN5f8LIPMRMRxu97pDkTn
# OMMiAZXfZVrsauWETBwHszJ1FCs4i/xJNJdrxbhb2wLn34c0mz3JFVjV2INSU53p
# 30hrNFTNVVMAiC1ZKnazRIkLIZhiuqmwYxKMJjByP/9Vufq6go8I7AxI0gBsFSRq
# gfSGCrnyjwqQXv0KKUbacPF1ApKNdN6gb378L/hSEreWhBDRIk5TGtb1kwbPX2Jz
# LNPfnl+4FyswWX44in7tmOhwBXJ+tSbTu01nJQKZJ5J6T1wv27v0kUFNWKUt
# SIG # End signature block