22using PnP . PowerShell . Commands . Base ;
33using PnP . PowerShell . Commands . Base . PipeBinds ;
44using PnP . PowerShell . Commands . Utilities ;
5- using PnP . PowerShell . Commands . Utilities . REST ;
6- using System ;
7- using System . IO ;
85using System . Management . Automation ;
9- using System . Net . Http ;
10- using System . Text . Json ;
11- using System . Text . Json . Nodes ;
6+
127
138namespace PnP . PowerShell . Commands . PowerPlatform . PowerAutomate
149{
@@ -35,32 +30,21 @@ protected override void ExecuteCmdlet()
3530 {
3631 var environmentName = GetEnvironmentName ( ) ;
3732 string baseUrl = PowerPlatformUtility . GetBapEndpoint ( Connection . AzureEnvironment ) ;
38-
39- //Get the SAS URL for the blob storage
40- var sasUrl = GenerateSasUrl ( baseUrl , environmentName ) ;
41- var blobUri = BuildBlobUri ( sasUrl , PackagePath ) ;
42- // Step 1: Upload the package to the blob storage using the SAS URL
43- UploadPackageToBlob ( blobUri ) ;
44- //Step 2: this will list the import parameters
45- var importParametersResponse = GetImportParameters ( baseUrl , environmentName , blobUri ) ;
46- // Step 3: Get the list of import operations data
47- var importOperationsData = GetImportOperations ( importParametersResponse . Location . ToString ( ) ) ;
48- var propertiesElement = GetPropertiesElement ( importOperationsData ) ;
49-
50- ValidateProperties ( propertiesElement ) ;
51- var resourcesObject = ParseResources ( propertiesElement ) ;
52- // Step 4: Transform the resources object
53- var resource = TransformResources ( resourcesObject ) ;
54-
55- var validatePackagePayload = CreateImportObject ( propertiesElement , resourcesObject ) ;
56- //Step 5: Validate the import package
57- var validateResponseData = ValidateImportPackage ( baseUrl , environmentName , validatePackagePayload ) ;
58-
59- var importPackagePayload = CreateImportObject ( validateResponseData ) ;
60- //Step 6: import package
61- var importResult = ImportPackage ( baseUrl , environmentName , importPackagePayload ) ;
62- //Step 7: Wait for the import to complete
63- var importStatus = WaitForImportCompletion ( importResult . Location . ToString ( ) ) ;
33+ var sasUrl = ImportFlowUtility . GenerateSasUrl ( Connection . HttpClient , AccessToken , baseUrl , environmentName ) ;
34+ var blobUri = ImportFlowUtility . BuildBlobUri ( sasUrl , PackagePath ) ;
35+ ImportFlowUtility . UploadPackageToBlob ( blobUri , PackagePath ) ;
36+ var importParametersResponse = ImportFlowUtility . GetImportParameters ( Connection . HttpClient , AccessToken , baseUrl , environmentName , blobUri ) ;
37+ var importOperationsData = ImportFlowUtility . GetImportOperations ( Connection . HttpClient , AccessToken , importParametersResponse . Location . ToString ( ) ) ;
38+ var propertiesElement = ImportFlowUtility . GetPropertiesElement ( importOperationsData ) ;
39+
40+ ImportFlowUtility . ValidateProperties ( propertiesElement ) ;
41+ var resourcesObject = ImportFlowUtility . ParseResources ( propertiesElement ) ;
42+ var resource = ImportFlowUtility . TransformResources ( resourcesObject , Name ) ;
43+ var validatePackagePayload = ImportFlowUtility . CreateImportObject ( propertiesElement , resourcesObject ) ;
44+ var validateResponseData = ImportFlowUtility . ValidateImportPackage ( Connection . HttpClient , AccessToken , baseUrl , environmentName , validatePackagePayload ) ;
45+ var importPackagePayload = ImportFlowUtility . CreateImportObject ( validateResponseData ) ;
46+ var importResult = ImportFlowUtility . ImportPackage ( Connection . HttpClient , AccessToken , baseUrl , environmentName , importPackagePayload ) ;
47+ var importStatus = ImportFlowUtility . WaitForImportCompletion ( Connection . HttpClient , AccessToken , importResult . Location . ToString ( ) ) ;
6448
6549 WriteObject ( $ "Import { importStatus } ") ;
6650 }
@@ -71,257 +55,5 @@ private string GetEnvironmentName()
7155 ? Environment . GetName ( )
7256 : PowerPlatformUtility . GetDefaultEnvironment ( ArmRequestHelper , Connection . AzureEnvironment ) ? . Name ;
7357 }
74-
75- private string GenerateSasUrl ( string baseUrl , string environmentName )
76- {
77- var response = RestHelper . Post ( Connection . HttpClient , $ "{ baseUrl } /providers/Microsoft.BusinessAppPlatform/environments/{ environmentName } /generateResourceStorage?api-version=2016-11-01", AccessToken ) ;
78- WriteVerbose ( $ "Storage resource URL generated: { response } ") ;
79- var data = JsonSerializer . Deserialize < JsonElement > ( response ) ;
80- return data . GetProperty ( "sharedAccessSignature" ) . GetString ( ) ;
81- }
82-
83- private UriBuilder BuildBlobUri ( string sasUrl , string packagePath )
84- {
85- var fileName = Path . GetFileName ( packagePath ) ;
86- var blobUri = new UriBuilder ( sasUrl ) ;
87- blobUri . Path += $ "/{ fileName } ";
88- return blobUri ;
89- }
90-
91- private void UploadPackageToBlob ( UriBuilder blobUri )
92- {
93- // Step 2: Upload the package to the blob storage using the SAS URL
94-
95- // Upload using clean HttpClient
96- using ( var blobClient = new HttpClient ( ) )
97- using ( var packageFileStream = new FileStream ( PackagePath , FileMode . Open , FileAccess . Read ) )
98- {
99- var packageContent = new StreamContent ( packageFileStream ) ;
100- packageContent . Headers . ContentType = new System . Net . Http . Headers . MediaTypeHeaderValue ( "application/octet-stream" ) ;
101-
102- var request = new HttpRequestMessage ( HttpMethod . Put , blobUri . Uri )
103- {
104- Content = packageContent
105- } ;
106-
107- request . Headers . Add ( "x-ms-blob-type" , "BlockBlob" ) ;
108-
109- var uploadResponse = blobClient . SendAsync ( request ) . GetAwaiter ( ) . GetResult ( ) ;
110-
111- if ( ! uploadResponse . IsSuccessStatusCode )
112- {
113- var errorContent = uploadResponse . Content . ReadAsStringAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
114- throw new Exception ( $ "Upload failed: { uploadResponse . StatusCode } - { errorContent } ") ;
115- }
116- }
117- }
118-
119- private System . Net . Http . Headers . HttpResponseHeaders GetImportParameters ( string baseUrl , string environmentName , UriBuilder blobUri )
120- {
121- var importPayload = new
122- {
123- packageLink = new
124- {
125- value = blobUri . Uri . ToString ( )
126- }
127- } ;
128- var response = RestHelper . PostGetResponseHeader < JsonElement > (
129- Connection . HttpClient ,
130- $ "{ baseUrl } /providers/Microsoft.BusinessAppPlatform/environments/{ environmentName } /listImportParameters?api-version=2016-11-01",
131- AccessToken ,
132- payload : importPayload ,
133- accept : "application/json"
134- ) ;
135- WriteVerbose ( "Import parameters retrieved" ) ;
136- System . Threading . Thread . Sleep ( 2500 ) ;
137- return response ;
138- }
139-
140- private JsonElement GetImportOperations ( string importOperationsUrl )
141- {
142- var listImportOperations = RestHelper . Get (
143- Connection . HttpClient ,
144- importOperationsUrl ,
145- AccessToken ,
146- accept : "application/json"
147- ) ;
148- WriteVerbose ( "Import operations retrieved" ) ;
149- return JsonSerializer . Deserialize < JsonElement > ( listImportOperations ) ;
150- }
151-
152- private JsonElement GetPropertiesElement ( JsonElement importOperationsData )
153- {
154- if ( ! importOperationsData . TryGetProperty ( "properties" , out JsonElement propertiesElement ) )
155- {
156- WriteObject ( "Import failed: 'properties' section missing." ) ;
157- throw new Exception ( "Import failed: 'properties' section missing." ) ;
158- }
159- return propertiesElement ;
160- }
161-
162- private void ValidateProperties ( JsonElement propertiesElement )
163- {
164- bool hasStatus = propertiesElement . TryGetProperty ( "status" , out _ ) ;
165- bool hasPackageLink = propertiesElement . TryGetProperty ( "packageLink" , out _ ) ;
166- bool hasDetails = propertiesElement . TryGetProperty ( "details" , out _ ) ;
167- bool hasResources = propertiesElement . TryGetProperty ( "resources" , out _ ) ;
168-
169- if ( ! ( hasStatus && hasPackageLink && hasDetails && hasResources ) )
170- {
171- WriteObject ( "Import failed: One or more required fields are missing in 'properties'." ) ;
172- throw new Exception ( "Import failed: One or more required fields are missing in 'properties'." ) ;
173- }
174- if ( ! propertiesElement . TryGetProperty ( "resources" , out JsonElement resourcesElement ) )
175- {
176- WriteObject ( "Import failed: 'resources' section missing in 'properties'." ) ;
177- return ;
178- }
179- }
180-
181- private JsonObject ParseResources ( JsonElement propertiesElement )
182- {
183- if ( ! propertiesElement . TryGetProperty ( "resources" , out JsonElement resourcesElement ) )
184- {
185- WriteObject ( "Import failed: 'resources' section missing in 'properties'." ) ;
186- throw new Exception ( "Import failed: 'resources' section missing in 'properties'." ) ;
187- }
188- return JsonNode . Parse ( resourcesElement . GetRawText ( ) ) as JsonObject ;
189- }
190-
191- private JsonElement ValidateImportPackage ( string baseUrl , string environmentName , JsonObject validatePackagePayload )
192- {
193- var validateResponse = RestHelper . Post ( Connection . HttpClient , $ "{ baseUrl } /providers/Microsoft.BusinessAppPlatform/environments/{ environmentName } /validateImportPackage?api-version=2016-11-01", AccessToken , payload : validatePackagePayload ) ;
194- return JsonSerializer . Deserialize < JsonElement > ( validateResponse ) ;
195- }
196-
197- private JsonObject TransformResources ( JsonObject resourcesObject )
198- {
199- foreach ( var property in resourcesObject )
200- {
201- string resourceKey = property . Key ;
202- var resource = property . Value as JsonObject ;
203-
204- if ( resource != null && resource . TryGetPropertyValue ( "type" , out JsonNode typeNode ) )
205- {
206- string resourceType = typeNode ? . ToString ( ) ;
207-
208- if ( resourceType == "Microsoft.Flow/flows" )
209- {
210- resource [ "selectedCreationType" ] = "New" ;
211- if ( ParameterSpecified ( nameof ( Name ) ) )
212- {
213- if ( resource . TryGetPropertyValue ( "details" , out JsonNode detailsNode ) && detailsNode is JsonObject detailsObject )
214- {
215- detailsObject [ "displayName" ] = Name ;
216- }
217- }
218- }
219- else if ( resourceType == "Microsoft.PowerApps/apis/connections" )
220- {
221- resource [ "selectedCreationType" ] = "Existing" ;
222-
223- // Only set the id if suggestedId exists
224- if ( resource . TryGetPropertyValue ( "suggestedId" , out JsonNode suggestedIdNode ) && suggestedIdNode != null )
225- {
226- resource [ "id" ] = JsonValue . Create ( suggestedIdNode . ToString ( ) ) ;
227- }
228- }
229- }
230- }
231- return resourcesObject ;
232- }
233-
234- private JsonObject CreateImportObject ( JsonElement importData , JsonObject resourceObject = null )
235- {
236- JsonObject resourcesObject = new JsonObject
237- {
238- [ "details" ] = JsonNode . Parse ( importData . GetProperty ( "details" ) . GetRawText ( ) ) ,
239- [ "packageLink" ] = JsonNode . Parse ( importData . GetProperty ( "packageLink" ) . GetRawText ( ) ) ,
240- [ "status" ] = JsonNode . Parse ( importData . GetProperty ( "status" ) . GetRawText ( ) ) ,
241- [ "resources" ] = resourceObject ?? JsonNode . Parse ( importData . GetProperty ( "resources" ) . GetRawText ( ) )
242- } ;
243- return resourcesObject ;
244- }
245-
246- private System . Net . Http . Headers . HttpResponseHeaders ImportPackage ( string baseUrl , string environmentName , JsonObject importPackagePayload )
247- {
248- var importResult = RestHelper . PostGetResponseHeader < JsonElement > (
249- Connection . HttpClient ,
250- $ "{ baseUrl } /providers/Microsoft.BusinessAppPlatform/environments/{ environmentName } /importPackage?api-version=2016-11-01",
251- AccessToken ,
252- payload : importPackagePayload ,
253- accept : "application/json"
254- ) ;
255- WriteVerbose ( "Import package initiated" ) ;
256- return importResult ;
257- }
258-
259- private string WaitForImportCompletion ( string importPackageResponseUrl )
260- {
261- string status ;
262- int retryCount = 0 ;
263-
264- do
265- {
266- System . Threading . Thread . Sleep ( 2500 ) ;
267- var importResultData = RestHelper . Get ( Connection . HttpClient , importPackageResponseUrl , AccessToken , accept : "application/json" ) ;
268- var importResultDataElement = JsonSerializer . Deserialize < JsonElement > ( importResultData ) ;
269-
270- if ( importResultDataElement . TryGetProperty ( "properties" , out JsonElement importResultPropertiesElement ) &&
271- importResultPropertiesElement . TryGetProperty ( "status" , out JsonElement statusElement ) )
272- {
273- status = statusElement . GetString ( ) ;
274- }
275- else
276- {
277- WriteWarning ( "Failed to retrieve the status from the response." ) ;
278- throw new Exception ( "Import status could not be determined." ) ;
279- }
280-
281-
282- if ( status == "Running" )
283- {
284- WriteVerbose ( "Import is still running. Waiting for completion..." ) ;
285- retryCount ++ ;
286- }
287- else if ( status == "Failed" )
288- {
289- ThrowImportError ( importResultData ) ;
290- }
291- } while ( status == "Running" && retryCount < 5 ) ;
292-
293- if ( status == "Running" )
294- {
295- throw new Exception ( "Import failed to complete after 5 attempts." ) ;
296- }
297-
298- return status ;
299- }
300-
301- private void ThrowImportError ( string importResultData )
302- {
303- var importErrorResultData = JsonSerializer . Deserialize < JsonElement > ( importResultData ) ;
304- if ( importErrorResultData . TryGetProperty ( "properties" , out JsonElement importErrorResultPropertiesElement ) &&
305- importErrorResultPropertiesElement . TryGetProperty ( "resources" , out JsonElement resourcesElement ) )
306- {
307- foreach ( var resource in resourcesElement . EnumerateObject ( ) )
308- {
309- if ( resource . Value . TryGetProperty ( "error" , out JsonElement errorElement ) )
310- {
311- string errorMessage = errorElement . TryGetProperty ( "message" , out JsonElement messageElement )
312- ? messageElement . GetString ( )
313- : errorElement . TryGetProperty ( "code" , out JsonElement codeElement )
314- ? codeElement . GetString ( )
315- : "Unknown error" ;
316- throw new Exception ( $ "Import failed: { errorMessage } ") ;
317- }
318- }
319- throw new Exception ( "Import failed: No error details found in resources." ) ;
320- }
321- else
322- {
323- throw new Exception ( "Import failed: Unknown error." ) ;
324- }
325- }
32658 }
32759}
0 commit comments