こんにちは、臼田です。
みなさん、設定をコードで管理してますか?(挨拶
この記事は下記の続編です。
背景や実施内容、事前準備などは上記をご確認ください。
今回は以下のように、開発用テナントのIntuneからデバイスコンプライアンスポリシーをExportして、本番用テナントのIntuneにImportします。
具体的な処理内容としては、deviceManagement/deviceCompliancePolicies
からデバイスコンプライアンスポリシーをすべて取得し、ポリシーを1つずつdeviceManagement/deviceCompliancePolicies
へPOSTします。
やってみた
手順は下記の通りです。
- スクリプト作成
- パイプラインの定義
- デプロイ
スクリプト作成
前回の続きで作成したAzure DevOpsのConfiguration as Code
プロジェクトで、リポジトリにスクリプトを追加していきます。intune-devicecompliance
フォルダを開き「New -> File」からファイルを追加します。
ファイル名をCompliancePolicy_Export_Import.ps1
とし「Create」します。
Export/Importするスクリプトを作成します。元となるコードはCompliancePolicy_Export.ps1とCompliancePolicy_Import_FromJSON.ps1です。動作させる完全なコードは下記の通りです。
<# .COPYRIGHT Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. #> #################################################### # Get Environment Params param( [Parameter(Mandatory = $True)] [string]$appId_DEV, [Parameter(Mandatory = $True)] [string]$appSecret_DEV, [Parameter(Mandatory = $True)] [string]$tenantId_DEV, [Parameter(Mandatory = $True)] [string]$appId_PROD, [Parameter(Mandatory = $True)] [string]$appSecret_PROD, [Parameter(Mandatory = $True)] [string]$tenantId_PROD ) Install-Module Microsoft.Graph -AllowPrerelease -AllowClobber -Force # Install-Module Microsoft.Graph -AllowPrerelease # Add environment variables to be used by Connect-MgGraph. $Env:AZURE_CLIENT_ID = $appId_DEV #application id of the client app $Env:AZURE_TENANT_ID = $tenantId_DEV #Id of your tenant $Env:AZURE_CLIENT_SECRET = $appSecret_DEV #secret of the client app # Tell Connect-MgGraph to use your environment variables. Connect-MgGraph -EnvironmentVariable #################################################### Function Get-DeviceCompliancePolicy() { <# .SYNOPSIS This function is used to get device compliance policies from the Graph API REST interface .DESCRIPTION The function connects to the Graph API Interface and gets any device compliance policies .EXAMPLE Get-DeviceCompliancePolicy Returns any device compliance policies configured in Intune .EXAMPLE Get-DeviceCompliancePolicy -Android Returns any device compliance policies for Android configured in Intune .EXAMPLE Get-DeviceCompliancePolicy -iOS Returns any device compliance policies for iOS configured in Intune .NOTES NAME: Get-DeviceCompliancePolicy #> [cmdletbinding()] param ( [switch]$Android, [switch]$iOS, [switch]$Win10 ) $graphApiVersion = "v1.0" $Resource = "deviceManagement/deviceCompliancePolicies" try { $Count_Params = 0 if ($Android.IsPresent) { $Count_Params++ } if ($iOS.IsPresent) { $Count_Params++ } if ($Win10.IsPresent) { $Count_Params++ } if ($Count_Params -gt 1) { write-host "Multiple parameters set, specify a single parameter -Android -iOS or -Win10 against the function" -f Red } elseif ($Android) { $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)" (Invoke-MgGraphRequest -Uri $uri -Method Get).Value | Where-Object { ($_.'@odata.type').contains("android") } } elseif ($iOS) { $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)" (Invoke-MgGraphRequest -Uri $uri -Method Get).Value | Where-Object { ($_.'@odata.type').contains("ios") } } elseif ($Win10) { $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)" (Invoke-MgGraphRequest -Uri $uri -Method Get).Value | Where-Object { ($_.'@odata.type').contains("windows10CompliancePolicy") } } else { $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)" (Invoke-MgGraphRequest -Uri $uri -Method Get).Value } } catch { $ex = $_.Exception $errorResponse = $ex.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($errorResponse) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $responseBody = $reader.ReadToEnd(); Write-Host "Response content:`n$responseBody" -f Red Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)" write-host break } } #################################################### Function Export-JSONData() { <# .SYNOPSIS This function is used to export JSON data returned from Graph .DESCRIPTION This function is used to export JSON data returned from Graph .EXAMPLE Export-JSONData -JSON $JSON Export the JSON inputted on the function .NOTES NAME: Export-JSONData #> param ( $JSON, $ExportPath ) try { if ($JSON -eq "" -or $JSON -eq $null) { write-host "No JSON specified, please specify valid JSON..." -f Red } elseif (!$ExportPath) { write-host "No export path parameter set, please provide a path to export the file" -f Red } elseif (!(Test-Path $ExportPath)) { write-host "$ExportPath doesn't exist, can't export JSON Data" -f Red } else { $JSON1 = ConvertTo-Json $JSON -Depth 5 $JSON_Convert = $JSON1 | ConvertFrom-Json $displayName = $JSON_Convert.displayName # Updating display name to follow file naming conventions - https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx $DisplayName = $DisplayName -replace '\<|\>|:|"|/|\\|\||\?|\*', "_" $Properties = ($JSON_Convert | Get-Member | ? { $_.MemberType -eq "NoteProperty" }).Name $FileName_CSV = "$DisplayName" + "_" + $(get-date -f dd-MM-yyyy-H-mm-ss) + ".csv" $FileName_JSON = "$DisplayName" + "_" + $(get-date -f dd-MM-yyyy-H-mm-ss) + ".json" $Object = New-Object System.Object foreach ($Property in $Properties) { $Object | Add-Member -MemberType NoteProperty -Name $Property -Value $JSON_Convert.$Property } write-host "Export Path:" "$ExportPath" $Object | Export-Csv -LiteralPath "$ExportPath\$FileName_CSV" -Delimiter "," -NoTypeInformation -Append $JSON1 | Set-Content -LiteralPath "$ExportPath\$FileName_JSON" # write-host "CSV created in $ExportPath\$FileName_CSV..." -f cyan write-host "JSON created in $ExportPath\$FileName_JSON..." -f cyan } } catch { $_.Exception } } #################################################### Function Add-DeviceCompliancePolicy() { <# .SYNOPSIS This function is used to add a device compliance policy using the Graph API REST interface .DESCRIPTION The function connects to the Graph API Interface and adds a device compliance policy .EXAMPLE Add-DeviceCompliancePolicy -JSON $JSON Adds an Android device compliance policy in Intune .NOTES NAME: Add-DeviceCompliancePolicy #> [cmdletbinding()] param ( $JSON ) $graphApiVersion = "v1.0" $Resource = "deviceManagement/deviceCompliancePolicies" try { if ($JSON -eq "" -or $JSON -eq $null) { write-host "No JSON specified, please specify valid JSON for the Android Policy..." -f Red } else { Test-JSON -JSON $JSON $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)" # Invoke-MgGraphRequest -Uri $uri -Method Post -Body $JSON } } catch { Write-Host $ex = $_.Exception $errorResponse = $ex.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($errorResponse) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $responseBody = $reader.ReadToEnd(); Write-Host "Response content:`n$responseBody" -f Red Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)" write-host break } } #################################################### $ExportPath = '.' # If the directory path doesn't exist prompt user to create the directory $ExportPath = $ExportPath.replace('"', '') if (!(Test-Path "$ExportPath")) { Write-Host Write-Host "Path '$ExportPath' doesn't exist" -ForegroundColor Red Write-Host break } Write-Host #################################################### # Export $CPs = Get-DeviceCompliancePolicy #################################################### # Import auth # Add environment variables to be used by Connect-MgGraph. $Env:AZURE_CLIENT_ID = $appId_PROD #application id of the client app $Env:AZURE_TENANT_ID = $tenantId_PROD #Id of your tenant $Env:AZURE_CLIENT_SECRET = $appSecret_PROD #secret of the client app # Tell Connect-MgGraph to use your environment variables. Connect-MgGraph -EnvironmentVariable # Import foreach ($CP in $CPs) { write-host "Device Compliance Policy:"$CP.displayName -f Yellow # Export-JSONData -JSON $CP -ExportPath "$ExportPath" Write-Host # Excluding entries that are not required - id,createdDateTime,lastModifiedDateTime,version $JSON_Convert = $CP | Select-Object -Property * -ExcludeProperty id, createdDateTime, lastModifiedDateTime, version $DisplayName = $JSON_Convert.displayName $JSON_Output = $JSON_Convert | ConvertTo-Json -Depth 5 # Adding Scheduled Actions Rule to JSON $scheduledActionsForRule = '"scheduledActionsForRule":[{"ruleName":"PasswordRequired","scheduledActionConfigurations":[{"actionType":"block","gracePeriodHours":0,"notificationTemplateId":"","notificationMessageCCList":[]}]}]' $JSON_Output = $JSON_Output.trimend("}") $JSON_Output = $JSON_Output.TrimEnd() + "," + "`r`n" # Joining the JSON together $JSON_Output = $JSON_Output + $scheduledActionsForRule + "`r`n" + "}" # write-host # write-host "Compliance Policy '$DisplayName' Found..." -ForegroundColor Yellow # write-host $JSON_Output write-host Write-Host "Adding Compliance Policy '$DisplayName'" -ForegroundColor Yellow Add-DeviceCompliancePolicy -JSON $JSON_Output }
雑に2つのスクリプトをくっつけています。Exportした$CPs
を1つずつ取り出し、Importするための前処理としてSelect-Object
で邪魔なパラメータを除去し、何やらよくわからない値を付け加えてImportを実施しています。
前回の記事で説明しましたが、この処理は冪等な処理ではありません。本来であれば既存環境の設定状況を確認したり、その状態を管理する必要があります。今回はあくまでコンセプトを動かすにとどめます。
コードを入力したら「Commit」していきます。
適当なメッセージを入れて「Commit」します。
パイプラインの定義
続いて新しいパイプラインを作成します。前回と同じ手順で新規のパイプラインを作成していき、パイプラインの定義を入れます。下記の定義です。
variables: - name: vmImageName value: windows-latest - group: configuration-as-code trigger: - none pool: vmImage: $(vmImageName) steps: - task: PowerShell@2 displayName: 'Run CompliancePolicy_Export_Import.ps1' inputs: pwsh: true targetType: filePath filePath: '$(Build.Repository.LocalPath)\\intune-devicecompliance\CompliancePolicy_Export_Import.ps1' arguments: -tenantId_DEV '$(tenantId_DEV)' -appId_DEV '$(appId_DEV)' -appSecret_DEV '$(appSecret_DEV)' -tenantId_PROD '$(tenantId_PROD)' -appId_PROD '$(appId_PROD)' -appSecret_PROD '$(appSecret_PROD)' ignoreLASTEXITCODE: true
パスはintune-devicecompliance/sync-compliancepolicy-pipeline.yml
を指定します。
今回はpwsh: true
を入れていますが、これによりPowerShellのバージョンが7になるようです。前回はこれを指定しなくても通りましたが、元記事の想定ではPowerShell7を利用するとしているので、こちらを設定したほうが良さそうです。今回のスクリプトでは無いと適切に動作しませんでした。
「Save and run」でコミットして進めます。
適当なメッセージを入れて開始します。
デプロイ
「Save and run」するとデプロイが開始されます。
初回は設定した変数グループをパイプラインから利用することを承認するように要求されるので、「Permit」します。今回は1分半ぐらいで完了しました。
それでは結果を確認していきます。本番用のIntuneでデバイスコンプライアンスポリシーが3つとも複製されていることが確認できました。
ちなみに、もう一度実行すると先述の通り、もう3個追加されます。既存で設定が存在するかを確認したり、既存の設定に上書きするなどは各環境に合わせてロジックを検討する必要があるでしょう。
まとめ
Microsoft IntuneをAzure DevOpsでConfiguration as Codeしていくシリーズ2本を書きました。コンセプトとしては面白いですが、実運用に持っていくための作り込みがまだまだ必要そうであることと、宣言的に定義して冪等な設定をしていくには、仕組みが足りていないのが現状です。例えばTerraformでこれを実現するようなIssueも検索するとあったりしましたが、あまりアクティブではないようだったので、そういった仕組みが待ち望まれますね。