This post has been republished via RSS; it originally appeared at: New blog articles in Microsoft Tech Community.
With the recent announcement and sunset scheduling of AIP classic client and the AIP label management portal, the need to migrate to Security and Compliance Center (SCC) is becoming an important task to achieve as soon as possible.
There are 3 main objects in the policy definition that can be migrated to the Security and Compliance portal. Some of the object migrations are automated and some of must be completed manually:
- Label definition – automatically migrated when you click the Activate button.
- Different policies - can be copied automatically, make sure to pay attention to the fact that the copy policy automatically publishes the policies in the Security and Compliance portal.
- Localization settings-manual process (until today).
Today, we are happy to share an internal tool, built by one of our PFEs (Kudos to Friedrich Weinmann) to semi-automate the localization settings migration process.
Instructions to make the Localization settings migration process as easy as possible are provided below.
Note: In case you are using automatic conditions in your policy for automatic labeling, they still need to be reconfigured on the SCC portal.
Disclaimer: This tool is not provided with support or maintenance by Microsoft. This tool is shared with you for use under your consideration only.
1.Connecting
Connect to the Security & Compliance Center using PowerShell.
There are two ways you can login into the SCC using PowerShell:
- Basic authentication using the online documented way.
- Modern authentication with support for MFA
After the connection is established, export the XML and load the command that will perform the import:
2.Exporting the localization XML
From the Azure Information Protection portal, export the languages that you are interested in importing to Security and Compliance label configuration.
3. Loading the command
Paste the following function definition into the console with the established connection from Step . Note that pasting the function does not perform any changes.
--------
Function Import-LegacyLabelXml {
<#
.SYNOPSIS
Imports labels into the Security & Compliance Center from an export-xml of classic AIP labels.
This import only applies to localization components of tags!
.DESCRIPTION
Imports labels into the Security & Compliance Center from an export-xml of classic AIP labels.
This avoids having to manually update labels in multiple-language scenarios.
This import only applies to localization components of tags!
Settings will be merged into existing labels, if those already exist.
Important:
This command assumes you are already connected to the Office365 Security & Compliance Center!
.PARAMETER Path
Path to the XML file(s) to import.
.PARAMETER Replace
By default, keys for existing languages are not overwritten.
Enabling this switch will instead overwrite those with the strings defined in the import xml.
.EXAMPLE
PS C:\> Get-ChildItem C:\export\*.xml | Import-LegacyLabelXml
Imports all the xml files in C:\export, parses them for label DisplayNames and descriptions and uploads them into S&CC.
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('FullName')]
[string[]]
$Path,
[switch]
$Replace
)
begin {
#region Utility Functions
function Write-LabelObject {
[CmdletBinding()]
param (
[string]
$Name,
[AllowNull()]
[AllowEmptyString()]
[string]
$Parent,
[string]
$Identity,
[string]
$Language,
[string]
$Setting,
[string]
$Text,
[string]
$DefaultText,
[Hashtable]
$DataSet,
[Hashtable]
$Labels
)
if (-not $DataSet[$Identity]) {
$DataSet[$Identity] = [PSCustomObject]@{
Name = $Name
Parent = $Parent
Identity = $Identity
ID = $Labels[$Identity].Guid
ParentID = $(if ($Parent) { $Labels[$Parent].Guid } else { $null })
Description = @{ }
DisplayName = @{ }
}
$DataSet[$Identity].$($Setting)['default'] = $DefaultText
}
$DataSet[$Identity].$($Setting)[$Language] = $Text
}
function Update-LocalizedLabel {
[CmdletBinding()]
param (
$DataObject,
[Hashtable]
$Labels,
[bool]
$Replace
)
Write-Verbose "Updating $($DataObject.Identity)"
$identity = $DataObject.ID
if (-not $identity) { $identity = $Labels[$DataObject.Identity].Guid }
$jsonData = $Labels[$DataObject.Identity].LocaleSettings | ConvertTo-Json
$displayNameList = @{ }
foreach ($entry in ($jsonData | Where-Object LocaleKey -eq "displayName").Settings) {
$displayNameList[$entry.Key] = @{
Key = $entry.Key
Value = $entry.Value
}
}
foreach ($languageKey in $DataObject.DisplayName.Keys) {
if (-not $Replace -and $displayNameList[$languageKey]) { continue }
$displayNameList[$languageKey] = @{
Key = $languageKey
Value = $DataObject.DisplayName[$languageKey]
}
}
$displayNameObject = [PSCustomObject]@{
LocaleKey = 'DisplayName'
Settings = @($displayNameList.Values | Write-Output)
}
Set-Label -Identity $identity -LocaleSettings (ConvertTo-Json $displayNameObject -Depth 3 -Compress)
$tooltipList = @{ }
foreach ($entry in ($jsonData | Where-Object LocaleKey -eq "tooltip").Settings) {
$tooltipList[$entry.Key] = @{
Key = $entry.Key
Value = $entry.Value
}
}
foreach ($languageKey in $DataObject.Description.Keys) {
if (-not $Replace -and $tooltipList[$languageKey]) { continue }
$tooltipList[$languageKey] = @{
Key = $languageKey
Value = $DataObject.Description[$languageKey]
}
}
$tooltipObject = [PSCustomObject]@{
LocaleKey = 'Tooltip'
Settings = @($tooltipList.Values | Write-Output)
}
Set-Label -Identity $identity -LocaleSettings (ConvertTo-Json $tooltipObject -Depth 3 -Compress)
}
function New-LocalizedLabel {
[CmdletBinding()]
param (
$DataObject,
[Hashtable]
$Labels
)
Write-Verbose "Creating $($DataObject.Identity)"
$parameters = @{
Name = [guid]::NewGuid()
DisplayName = $DataObject.Name
}
if ($DataObject.ParentID) {
$parameters['ParentID'] = $DataObject.ParentID
}
elseif ($DataObject.Parent) {
$parameters['ParentID'] = $Labels[$DataObject.Parent].Guid
}
$newLabel = New-Label @parameters | Select-Object -Property Name, DisplayName, Guid, ParentID, LocaleSettings, Identity
$newLabel.Identity = $DataObject.Identity
$Labels[$newLabel.Identity] = $newLabel
Update-LocalizedLabel -DataObject $DataObject -Labels $Labels -Replace $true
}
#endregion Utility Functions
$dataSet = @{ }
$labelBase = @{ }
$labels = @{ }
$labelData = Get-Label | Select-Object -Property Name, DisplayName, Guid, ParentID, LocaleSettings, Identity
foreach ($label in $labelData) {
$labelBase[$label.Guid] = $label
}
foreach ($label in $labelData) {
$identity = $label.DisplayName
if ($label.ParentID) { $identity = '{0}/{1}' -f $labelBase[$label.ParentID].DisplayName, $label.DisplayName }
$labels[$identity] = $label
}
}
process {
foreach ($filePath in $Path) {
Write-Verbose "Processing $filePath"
#region Validation
if (-not (Test-Path $filePath)) {
Write-Warning "File does not exist: $filePath !"
throw "File does not exist: $filePath !"
}
try {
[xml]$xmlData = Get-Content -Path $filePath -ErrorAction Stop -Encoding utf8
}
catch {
Write-Warning "Input is not a legal XML file: $filePath !"
throw
}
#endregion Validation
#region Process File
$language = $xmlData.Language.Id
$localizationData = $xmlData.Language.LocItem |
Where-Object ID -match '^labelGroups/Sensitivity/labels/.+/(DisplayName|Description)$' |
Select-Object @{
Name = 'ID'
Expression = {
$_.ID.Replace("labelGroups/Sensitivity/labels/", "")
}
}, defaultText, LocalizedText
foreach ($localDatum in $localizationData) {
$parent = $null
$identity = ($localDatum.ID -split "/")[-2]
if (($localDatum.ID -split "/").Count -ge 4) {
$parent = ($localDatum.ID -split "/")[-4]
$identity = '{0}/{1}' -f $parent, $identity
}
$parameters = @{
Name = ($localDatum.ID -split "/")[-2]
Parent = $parent
Identity = $identity
Language = $language
Setting = ($localDatum.ID -split "/")[-1]
Text = $localDatum.LocalizedText
DefaultText = $localDatum.defaultText
DataSet = $dataSet
Labels = $labels
}
Write-LabelObject @parameters
}
#endregion Process File
}
}
end {
foreach ($labelObject in ($dataSet.Values | Where-Object Parent -eq "")) {
if ($labelObject.ID) { Update-LocalizedLabel -DataObject $labelObject -Labels $labels -Replace $Replace.ToBool() }
else { New-LocalizedLabel -DataObject $labelObject -Labels $labels }
}
foreach ($labelObject in ($dataSet.Values | Where-Object Parent -ne "")) {
if ($labelObject.ID) { Update-LocalizedLabel -DataObject $labelObject -Labels $labels -Replace $Replace.ToBool() }
else { New-LocalizedLabel -DataObject $labelObject -Labels $labels }
}
}
}
-------------
When completed, you are ready to proceed with the import.
4.Importing label localization XML files
When preparations are completed, import the XML files thatcontain the export of the label localization data using the following command:
-------------
Get-ChildItem C:\export\*.xml | Import-LegacyLabelXml
-------------
This sample command assumes the files to import were placed in the folder C:\export (make sure to adjust accordingly, if you chose a different path).
Notes:
- You can import any number of language xml files at the same time.
- You do not need to import all xml files at the same time – you can import additional languages at a later time.
- If localization for a language already exists (manual entry, previous import) this action will not overwrite them, unless you also specify the "-Replace" parameter.
- Importing a localization for a label that does not exist, creates the label.
- This script does not check the validity of the XML files. Make sure you use the correct XML.