ProvisioningAPI_utils.ps1
# This script contains utility functions for provisioning API at https://provisioning.microsoftonline.com # Office 365 / Azure AD v1, a.k.a. MSOnline module uses this API # Azure AD Roles $AADRoles=@{ "Helpdesk Administrator"= "729827e3-9c14-49f7-bb1b-9608f156bbb8" "Service Support Administrator"= "f023fd81-a637-4b56-95fd-791ac0226033" "Billing Administrator"= "b0f54661-2d74-4c50-afa3-1ec803f12efe" "Partner Tier1 Support"= "4ba39ca4-527c-499a-b93d-d9b492c50246" "Partner Tier2 Support"= "e00e864a-17c5-4a4b-9c06-f5b95a8d5bd8" "Directory Readers"= "88d8e3e3-8f55-4a1e-953a-9b9898b8876b" "Exchange Service Administrator"= "29232cdf-9323-42fd-ade2-1d097af3e4de" "Lync Service Administrator"= "75941009-915a-4869-abe7-691bff18279e" "User Account Administrator"= "fe930be7-5e62-47db-91af-98c3a49a38b1" "Directory Writers"= "9360feb5-f418-4baa-8175-e2a00bac4301" "Company Administrator"= "62e90394-69f5-4237-9190-012177145e10" "SharePoint Service Administrator"= "f28a1f50-f6e7-4571-818b-6a12f2af6b6c" "Device Users"= "d405c6df-0af8-4e3b-95e4-4d06e542189e" "Device Administrators"= "9f06204d-73c1-4d4c-880a-6edb90606fd8" "Device Join"= "9c094953-4995-41c8-84c8-3ebb9b32c93f" "Workplace Device Join"= "c34f683f-4d5a-4403-affd-6615e00e3a7f" "Compliance Administrator"= "17315797-102d-40b4-93e0-432062caca18" "Directory Synchronization Accounts"= "d29b2b05-8046-44ba-8758-1e26182fcf32" "Device Managers"= "2b499bcd-da44-4968-8aec-78e1674fa64d" "Application Administrator"= "9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3" "Application Developer"= "cf1c38e5-3621-4004-a7cb-879624dced7c" "Security Reader"= "5d6b6bb7-de71-4623-b4af-96380a352509" "Security Administrator"= "194ae4cb-b126-40b2-bd5b-6091b380977d" "Privileged Role Administrator"= "e8611ab8-c189-46e8-94e1-60213ab1f814" "Intune Service Administrator"= "3a2c62db-5318-420d-8d74-23affee5d9d5" "Cloud Application Administrator"= "158c047a-c907-4556-b7ef-446551a6b5f7" "Customer LockBox Access Approver"= "5c4f9dcd-47dc-4cf7-8c9a-9e4207cbfc91" "CRM Service Administrator"= "44367163-eba1-44c3-98af-f5787879f96a" "Power BI Service Administrator"= "a9ea8996-122f-4c74-9520-8edcd192826c" "Guest Inviter"= "95e79109-95c0-4d8e-aee3-d01accf2d47b" "Conditional Access Administrator"= "b1be1c3e-b65d-4f19-8427-f6fa0d97feb9" "Reports Reader"= "4a5d8f65-41da-4de4-8968-e035b65339cf" "Message Center Reader"= "790c1fb9-7f7d-4f88-86a1-ef1f95c05c1b" "Information Protection Administrator"= "7495fdc4-34c4-4d15-a289-98788ce399fd" "License Administrator"= "4d6ac14f-3453-41d0-bef9-a3e0c569773a" "Cloud Device Administrator"= "7698a772-787b-4ac8-901f-60d6b08affd2" "Teams Communications Administrator"= "baf37b3a-610e-45da-9e62-d9d1e5e8914b" "Teams Communications Support Engineer"= "f70938a0-fc10-4177-9e90-2178f8765737" "Teams Communications Support Specialist"= "fcf91098-03e3-41a9-b5ba-6f0ec8188a12" "Teams Service Administrator"= "69091246-20e8-4a56-aa4d-066075b2a7a8" "Guest User"= "10dae51f-b6af-4016-8d66-8c2a99b929b3" } # Boolean to string function b2s { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [bool]$Bool ) Process { $Bool.ToString().ToLower() } } # Create SOAP envelope function Create-Envelope { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [String]$AccessToken, [Parameter(Mandatory=$True)] [String]$Command, [Parameter(Mandatory=$True)] [String]$RequestElements, [Parameter(Mandatory=$False)] [String]$TenantId ) Process { # Create the envelope $message_id=(New-Guid).ToString() $envelope=@" <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"> <s:Header> <a:Action s:mustUnderstand="1">http://provisioning.microsoftonline.com/IProvisioningWebService/$Command</a:Action> <a:MessageID>urn:uuid:$message_id</a:MessageID> <a:ReplyTo> <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address> </a:ReplyTo> <UserIdentityHeader xmlns="http://provisioning.microsoftonline.com/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <BearerToken xmlns="http://schemas.datacontract.org/2004/07/Microsoft.Online.Administration.WebService">Bearer $AccessToken</BearerToken> <LiveToken i:nil="true" xmlns="http://schemas.datacontract.org/2004/07/Microsoft.Online.Administration.WebService"/> </UserIdentityHeader> <ClientVersionHeader xmlns="http://provisioning.microsoftonline.com/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <ClientId xmlns="http://schemas.datacontract.org/2004/07/Microsoft.Online.Administration.WebService">50afce61-c917-435b-8c6d-60aa5a8b8aa7</ClientId> <Version xmlns="http://schemas.datacontract.org/2004/07/Microsoft.Online.Administration.WebService">1.2.183.81</Version> </ClientVersionHeader> <ContractVersionHeader xmlns="http://becwebservice.microsoftonline.com/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <BecVersion xmlns="http://schemas.datacontract.org/2004/07/Microsoft.Online.Administration.WebService">Version47</BecVersion> </ContractVersionHeader> <a:To s:mustUnderstand="1">https://provisioningapi.microsoftonline.com/provisioningwebservice.svc</a:To> </s:Header> <s:Body> <$Command xmlns="http://provisioning.microsoftonline.com/"> <request xmlns:b="http://schemas.datacontract.org/2004/07/Microsoft.Online.Administration.WebService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <b:BecVersion>Version16</b:BecVersion> $(Add-BElement -Parameter "TenantId" -Value $TenantId) <b:VerifiedDomain i:nil="true"/> $RequestElements </request> </$Command> </s:Body> </s:Envelope> "@ # Debug Write-Debug "ENVELOPE ($Command): $envelope" # Return return $envelope } } # Calls the provisioning SOAP API function Call-ProvisioningAPI { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [String]$Envelope ) Process { $Headers=@{ "User-Agent" = Get-UserAgent } # Call the API Invoke-RestMethod -UseBasicParsing -Uri "https://provisioningapi.microsoftonline.com/provisioningwebservice.svc" -ContentType "application/soap+xml" -Method POST -Body $envelope -Headers $Headers } } # Parses the response object(s) from SOAP message function Parse-SOAPResponse { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] $Response ) Process { # Check if empty if(![string]::IsNullOrEmpty($Response)) { # All good, try to parse the response object $results=(Select-Xml -Xml $response -XPath "//*[local-name()='$($Command+"Result")']").Node # Check if we got response if([string]::IsNullOrEmpty($results)) { # Got error throw $Response.Envelope.Body.Fault.Reason.Text.'#text' } # Sometimes response message is empty if($results.ReturnValue -ne $null) { (Remove-XMLNameSpace $results.ReturnValue).ReturnValue } else { return "" } } else { # Empty, so throw an exception throw "Null or empty Response" } } } # Remove namespace from xml doc function Remove-XMLNameSpace { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] $xmlDoc ) Process { $xml=[System.Xml.Linq.XDocument]::Parse($xmlDoc.OuterXml) $remove=@() foreach ($XE in $xml.Descendants()) { if($XE.Name.LocalName.Length -eq 1) { # Remove "c" etc. tags $remove+=$XE } else { # Strip the namespace and attributes $XE.Name = $XE.Name.LocalName $XE.RemoveAttributes() } } foreach($XE in $remove) { $XE.Remove() } return [xml]$xml.ToString() } } # Parses the given ServiceInformation object from Get-CompanyInformation and returns as hashtable # Aug 11th 2018 function Parse-ServiceInformation { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [System.Xml.XmlElement]$ServiceInformation ) Process { # Set the return hastable $retVal=@{} # Loop through service information elements. There might be even more than these here. foreach($service in $ServiceInformation.ServiceInformation) { $settings=$null $instance = $service.ServiceInstance $instance_name=$instance.Split("/")[0] if($instance_name -ceq "sharepoint") { $settings=$service.ServiceElements.XElement.XElement.ServiceExtension.ServiceParameters.ServiceParameter } elseif($instance_name -ceq "SharePoint") { $settings=$service.ServiceElements.XElement.ServiceExtension.ServiceParameters.ServiceParameter #$service.ServiceElements.XElement.ServiceExtension.DNSRecords.DNSRecord } elseif($instance_name -eq "RMSOnline") { $settings=$service.ServiceElements.XElement.RmsCompanyServiceInfo.ServiceLocations.ServiceLocation } elseif($instance_name -eq "SCO") { $settings=$service.ServiceElements.XElement.WindowsIntuneServiceInfo.ServiceParameters.ServiceParameter $name = $service.ServiceElements.XElement.WindowsIntuneServiceInfo.ServiceParameters.ServiceParameter.Name $value = $service.ServiceElements.XElement.WindowsIntuneServiceInfo.ServiceParameters.ServiceParameter.Value if($name -ne $null) { $settings=@{$name = $value} } } elseif($instance_name -ieq "YammerEnterprise") { $settings=$service.ServiceElements.XElement.ServiceExtension.ServiceParameters.ServiceParameter } elseif($instance_name -ieq "ProjectWorkManagement") { $settings=$service.ServiceElements.XElement.Topology } elseif($instance_name -ieq "Netbreeze") { $settings=$service.ServiceElements.XElement.ServiceExtension.ServiceParameters.ServiceParameter } elseif($instance_name -ieq "DynamicsMarketing") { $settings=$service.ServiceElements.XElement.ServiceExtension.ServiceParameters.ServiceParameter } elseif($instance_name -ieq "CRM") { $settings=$service.ServiceElements.XElement.ServiceExtension.ServiceParameters.ServiceParameter } $retVal[$instance]=$settings } $retval } } # Get Sku and service name from SKU array # Aug 12th 2018 function Get-SkuAndServiceName { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [psobject[]]$SKUs, [Parameter(Mandatory=$True)] [string]$ServicePlanId ) Process { $attributes=@{} foreach($sku in $SKUs) { $attributes["SkuName"]=$sku.SkuPartNumber foreach($service in $sku.ServiceStatus) { if($service.ServicePlanId -eq $ServicePlanId) { $attributes["ServiceName"]=$service.ServiceName $attributes["ServiceType"]=$service.ServiceType $attributes["ProvisioningStatus"]=$service.ProvisioningStatus return New-Object psobject -Property $attributes } } } # No matching SKU found so return $null $null } } # Creates a <namespace:parameter> -element function Add-Element { [cmdletbinding()] Param( [Parameter(Mandatory=$False)] [String]$NameSpace, [Parameter(Mandatory=$True)] [String]$Parameter, [Parameter(Mandatory=$False)] $Value ) Process { if(![string]::IsNullOrEmpty($NameSpace)) { $Parameter="$NameSpace`:$Parameter" } if([string]::IsNullOrEmpty($Value)) { $element="<$Parameter i:nil=`"true`"/>" } else { if($Value -is [Boolean]) { $Value=b2s -Bool $Value } $element="<$Parameter>$Value</$Parameter>" } $element } } # Creates a <b:parameter> -element function Add-BElement { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [String]$Parameter, [Parameter(Mandatory=$False)] $Value ) Process { Add-Element -NameSpace "b" -Parameter $Parameter -Value $Value } } # Creates a <c:parameter> -element function Add-CElement { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [String]$Parameter, [Parameter(Mandatory=$False)] $Value ) Process { Add-Element -NameSpace "c" -Parameter $Parameter -Value $Value } } # Creates a <d:parameter> -element function Add-DElement { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [String]$Parameter, [Parameter(Mandatory=$False)] $Value ) Process { Add-Element -NameSpace "d" -Parameter $Parameter -Value $Value } } # Converts xml to PSObject function ConvertXmlTo-PSObject { [cmdletbinding()] Param( [Parameter(Mandatory=$True)] [System.Xml.XmlLinkedNode]$xml ) Begin { $XMLProperties=@( "InnerText" "InnerXml" "OuterXml" "BaseURI" "Prefix" "NamespaceURI" "Name" "LocalName" "Value" "IsEmpty" "HasAttributes" "HasChildNodes" "IsReadOnly" "ChildNodes" ) } Process { $attributes=[ordered]@{} foreach($property in $xml.PSObject.Properties) { if(!$XMLProperties.Contains($property.Name)) { switch($property.TypeNameOfValue) { "System.String" { $attributes[$property.Name] = $property.Value break } "System.Boolean" { $attributes[$property.Name] = $property.Value break } "System.Object[]" { $values=@() foreach($value in $property.Value) { $values += $value } $attributes[$property.Name] = $values break } "System.Xml.XmlElement" { $values = ConvertXmlTo-PSObject -xml $property.Value $attributes[$property.Name] = $values.PSObject.Properties.Value break } "System.Xml.XmlNodeList" { $attributes[$property.Name] = $property.Value break } } } } return New-Object psobject -Property $attributes } } |