Skip to content

Commit eca7676

Browse files
authored
Add files via upload
1 parent bb85338 commit eca7676

File tree

4 files changed

+162
-0
lines changed

4 files changed

+162
-0
lines changed

Deploy-PPE-PBIModel.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//add a value pair for each parameter name and parameter value.
2+
//For values with quotes, use the escape character \ before the quote.
3+
string[,] a = new string[,] {
4+
{"serverName","\"ppeserver.database.windows.net\""}, //set the config for each env
5+
{"databaseName","\"DataHub\""}, //ContosoPPEDb, ContosoProdDb
6+
{"StartDate", "#date(2017, 1, 1)"}, //#datetime(2022, 5, 1, 0, 0, 0), #datetime(2017, 1, 1, 0, 0, 0)
7+
};
8+
9+
//DO NOT CHANGE ANYTHING BELOW THIS LINE
10+
string paramExpression;
11+
int paramMetaLoc;
12+
string oldParameterVal;
13+
string newParameterVal;
14+
string newParamExpression;
15+
int x, y;
16+
foreach(var e in Model.Expressions)
17+
{
18+
paramExpression = Model.Expressions[e.Name].Expression;
19+
paramMetaLoc = paramExpression.IndexOf(" meta [IsParameterQuery=true");
20+
if (paramMetaLoc > 0) { //only do the rest if this is a parameter type query
21+
for (x = 0; x < a.GetLength(0); x++) {
22+
if (e.Name == a[x,0]) { //match up the parameters in the model with those defined above
23+
oldParameterVal = paramExpression.Left(paramMetaLoc);
24+
newParameterVal = a[x,1];
25+
newParamExpression = newParameterVal + paramExpression.Substring(paramMetaLoc);
26+
Model.Expressions[e.Name].Expression = newParamExpression; //update the parameter
27+
}
28+
}
29+
}
30+
}

Deploy-PPE-PBIModel.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Tabular Editor: https://docs.tabulareditor.com/te2/Command-line-Options.html
2+
3+
trigger:
4+
branches:
5+
include:
6+
- main # the name of the branch that should trigger the pipeline
7+
paths:
8+
include:
9+
- DatasetPath/ # the pipeline auto triggers when there is a change commited to branch
10+
11+
variables:
12+
DataSource: powerbi://api.powerbi.com/v1.0/myorg/Workspace Name # the Workspace where you will deploy
13+
DeploymentDB: TestDataset # the name of the dataset to deploy to
14+
UserId: app:xxxxxx-xxxxxx-xxxxxx-xxxxxx-xxxxxxxx@yyyyyy-yyyyyy-yyyyyy-yyyyyy-yyyyyyyy # the UserId that has access to the workspace to deploy and refresh (in the form of app:AppId@TenantId)
15+
AppId: xxxxxx-xxxxxx-xxxxxx-xxxxxx-xxxxxxxx # the Service Principal identifier
16+
TenantId: yyyyyy-yyyyyy-yyyyyy-yyyyyy-yyyyyyyy # the tenant where the service prinicipal is registered
17+
SourceControlPath: DatasetPath # the path in ADO where your Tabular model checked in as code.
18+
ScriptFilePath: Pipelines\Deploy-PPE-PBIModel.cs # the path for the script that Tabular Editor should run before deployment. Sample updates parameters.
19+
20+
jobs:
21+
- template: PBI-Model-Deploy-Template.yml # the name of the yaml template file

PBI-Model-Deploy-Template.yml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
jobs:
2+
- job: AgentJob
3+
displayName: Deploy Tabular Model
4+
pool:
5+
vmImage: 'windows-latest'
6+
steps:
7+
- checkout: self
8+
9+
- task: PowerShell@2
10+
displayName: 1. Download Tabular Editor
11+
inputs:
12+
targetType: inline
13+
script: "# Download URL for Tabular Editor portable: \n\n$TabularEditorUrl = \"https://github.com/otykier/TabularEditor/releases/download/2.16.5/TabularEditor.Portable.zip\"\n\n# Download destination (root of PowerShell script execution path): \n$DownloadDestination = join-path (get-location) \"TabularEditor.zip\" \necho($DownloadDestination)\n\n# Download from GitHub: \nInvoke-WebRequest -Uri $TabularEditorUrl -OutFile $DownloadDestination \n\n \n# Unzip Tabular Editor portable, and then delete the zip file:\n \nExpand-Archive -Path $DownloadDestination -DestinationPath (get-location).Path \n#Remove-Item $DownloadDestination "
14+
15+
- task: CmdLine@2
16+
displayName: 2. Best Practice Check
17+
inputs:
18+
script: TabularEditor.exe "$(SourceControlPath)\database.json" -A -T testbpa.trx -V
19+
20+
- task: CopyFiles@2
21+
displayName: 3. Copy Source Files
22+
inputs:
23+
SourceFolder: $(SourceControlPath)
24+
Contents: database.json
25+
TargetFolder: $(Build.ArtifactStagingDirectory)/PBIModel/Scripts
26+
27+
- task: CmdLine@2
28+
displayName: 4. Schema Check
29+
inputs:
30+
script: TabularEditor.exe "$(Build.ArtifactStagingDirectory)\PBIModel\Scripts\database.json" -S "Pipelines\SetCredentialsForValidation.csx" -SC -T testsc.trx -V
31+
32+
- task: AzureKeyVault@2
33+
displayName: 5. Read Key vault
34+
inputs:
35+
connectedServiceName: 'MyServiceConnectionName' # The name of the Service Connection to access the key vault
36+
KeyVaultName: 'MyKeyVauleName' # The name of the key vault
37+
SecretsFilter: 'mysecret' # This step copies the password to this variable, so in subseqent steps the name should match. If you change it here, do a find and replace to change it everywhere in this file.
38+
RunAsPreJob: true
39+
40+
- task: CmdLine@2
41+
displayName: 6. Deployment Check
42+
inputs:
43+
script: TabularEditor.exe "$(Build.ArtifactStagingDirectory)\PBIModel\Scripts\database.json" -D "Provider=MSOLAP;Data Source=$(DataSource);User ID=$(UserId);password=$(mysecret)" "$(DeploymentDB)" -O -C -E -W -T testdep.trx -V
44+
45+
- task: CmdLine@2
46+
displayName: 7. Create BIM File
47+
inputs:
48+
script: TabularEditor.exe "$(Build.SourcesDirectory)\$(SourceControlPath)" -BIM "$(Build.ArtifactStagingDirectory)\Model.bim"
49+
50+
- task: PublishBuildArtifacts@1
51+
displayName: 8. Publish Artifact DBArtifacts
52+
inputs:
53+
PathtoPublish: $(build.ArtifactStagingDirectory)
54+
ArtifactName: DBArtifacts
55+
56+
- task: CmdLine@2
57+
displayName: 9. Deploy BIM File
58+
inputs:
59+
script: TabularEditor.exe "$(Build.ArtifactStagingDirectory)\Model.bim" -S "$(ScriptFilePath)" -D "Provider=MSOLAP;Data Source=$(DataSource);User ID=$(UserId);password=$(mysecret)" "$(DeploymentDB)" -O -P -Y
60+
61+
- task: PowerShell@2
62+
displayName: '10. Refresh Power BI dataset'
63+
inputs:
64+
targetType: 'inline'
65+
script: |
66+
Install-Module -Name MicrosoftPowerBIMgmt -Scope CurrentUser -Force
67+
$SecurePassword = ConvertTo-SecureString “$(mysecret)” -AsPlainText -Force
68+
$credential = [System.Management.Automation.PSCredential]::new("$(AppId)", $SecurePassword)
69+
Connect-PowerBIServiceAccount -ServicePrincipal -Tenant $(TenantId) -Credential $credential
70+
$WorkspaceName = "$(DataSource)".split("/")[-1]
71+
$Workspace = Get-PowerBIWorkspace -Name $WorkspaceName
72+
$groupId = $Workspace[0].Id
73+
$Dataset = Get-PowerBIDataset -WorkspaceId $groupId | Where-Object {$_.name -eq "$(DeploymentDB)"}
74+
$datasetId = $Dataset[0].Id
75+
76+
Invoke-PowerBIRestMethod -Url "https://api.powerbi.com/v1.0/myorg/groups/$groupId/datasets/$datasetId/refreshes" -Method POST -WarningAction Ignore
77+

SetCredentialsForValidation.csx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// This script replaces all Power Query partitions on this model with a
2+
// The script assumes that all Power Query partitions load data from the same SQL Server-based data source.
3+
4+
// This function will extract all quoted values from the M expression, returning a list of strings
5+
// with the values extracted (in order), but ignoring any quoted values where a hashtag (#) precedes
6+
// the quotation mark:
7+
var split = new Func<string, List<string>>(m => {
8+
var result = new List<string>();
9+
var i = 0;
10+
foreach (var s in m.Split('"'))
11+
{
12+
if (s.EndsWith("#") && i % 2 == 0) i = -2;
13+
if (i >= 0 && i % 2 == 1) result.Add(s);
14+
i++;
15+
}
16+
return result;
17+
});
18+
var GetServer = new Func<string, string>(m => split(m)[0]); // Server name is usually the 1st encountered string
19+
var GetDatabase = new Func<string, string>(m => split(m)[1]); // Database name is usually the 2nd encountered string
20+
var GetSchema = new Func<string, string>(m => split(m)[2]); // Schema name is usually the 3rd encountered string
21+
var GetTable = new Func<string, string>(m => split(m)[3]); // Table name is usually the 4th encountered string
22+
23+
// Remove Power Query partitions from all tables and replace them with a single Legacy partition:
24+
foreach (var t in Model.Tables.Where(t => t.Partitions.OfType<MPartition>().Any()))
25+
{
26+
var mPartitions = t.Partitions.OfType<MPartition>();
27+
if (!mPartitions.Any()) continue;
28+
29+
var schema = GetSchema(mPartitions.First().Expression);
30+
var table = GetTable(mPartitions.First().Expression);
31+
32+
t.AddPartition(t.Name, string.Format("SELECT * FROM [{0}].[{1}]", schema, table));
33+
foreach (var p in mPartitions.ToList()) p.Delete();
34+
}

0 commit comments

Comments
 (0)