-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
3236 lines (2666 loc) · 181 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<title>Zim's Notes</title>
<meta name="author" content="Your Name">
<meta name="description" content="I have tested new Visual Studio Code Remote Development feature with Azure CLI. Now you can start developing Azure CLI in a few simple steps: Install …">
<!-- http://t.co/dKP3o1e -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="canonical" href="http://zikalino.github.io">
<link href="/favicon.png" rel="icon">
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
<link href="/atom.xml" rel="alternate" title="Zim's Notes" type="application/atom+xml">
<script src="/javascripts/modernizr-2.0.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>!window.jQuery && document.write(unescape('%3Cscript src="/javascripts/libs/jquery.min.js"%3E%3C/script%3E'))</script>
<script src="/javascripts/octopress.js" type="text/javascript"></script>
<!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
<link href="//fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<link href="//fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css">
<link href="//fonts.googleapis.com/css?family=Fjalla+One" rel="stylesheet" type="text/css">
<!--- MathJax Configuration -->
<script type="text/javascript" src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</head>
<body class="collapse-sidebar sidebar-footer" >
<header role="banner"><hgroup>
<h1><a href="/">Zim's Notes</a></h1>
<h2>Just work related notes.</h2>
</hgroup>
</header>
<nav role="navigation"><ul class="subscribe" data-subscription="rss">
<li><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS" target="_blank"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="25" height="25" viewbox="0 0 100 100"><path class="social" d="M 13.310204,73.332654 C 5.967347,73.332654 0,79.322448 0,86.621428 c 0,7.338776 5.967347,13.262246 13.310204,13.262246 7.370408,0 13.328572,-5.92245 13.328572,-13.262246 0,-7.29898 -5.958164,-13.288774 -13.328572,-13.288774 z M 0.01530612,33.978572 V 53.143878 C 12.493878,53.143878 24.229592,58.02347 33.068368,66.865306 41.894898,75.685714 46.767346,87.47449 46.767346,100 h 19.25 C 66.017346,63.592858 36.4,33.979592 0.01530612,33.978572 l 0,0 z M 0.03877552,0 V 19.17449 C 44.54796,19.17551 80.77551,55.437756 80.77551,100 H 100 C 100,44.87653 55.15102,0 0.03877552,0 z"></path></svg></a></li>
</ul>
<form action="https://www.google.com/search" method="get">
<fieldset role="search">
<input type="hidden" name="sitesearch" value="zikalino.github.io" />
<input class="search" type="text" name="q" results="0" placeholder="Search"/>
</fieldset>
</form>
<ul class="main-navigation">
<li><a href="/">Blog</a></li>
<li><a href="/blog/archives">Archives</a></li>
</ul>
</nav>
<div id="main">
<div id="content">
<div class="blog-index">
<article>
<header>
<h1 class="entry-title"><a href="/blog/2019/06/10/setting-azure-cli-development-environment-using-visual-studio-code-remote-development-with-containers/">Setting Azure CLI Development Environment Using Visual Studio Code Remote Development With Containers</a></h1>
<p class="meta">
<time class='entry-date' datetime='2019-06-10T19:22:11+08:00'><span class='date'><span class='date-month'>Jun</span> <span class='date-day'>10</span><span class='date-suffix'>th</span>, <span class='date-year'>2019</span></span> <span class='time'>7:22 pm</span></time>
</p>
</header>
<div class="entry-content"><p>I have tested new Visual Studio Code Remote Development feature with Azure CLI. Now you can start developing Azure CLI in a few simple steps:</p>
<ul>
<li>Install Visual Studio Code Insiders Edition</li>
<li>Clone Azure CLI Repository</li>
<li>Launch Visual Studio Code and wait for a few minutes</li>
<li>Set a breakpoint and press F5</li>
</ul>
<p>To replicate this you need do download Visual Studio Code from here:</p>
<p><a href="https://code.visualstudio.com/insiders/">https://code.visualstudio.com/insiders/</a></p>
<p>Clone my fork of Azure CLI repository and switch to <strong>adding-devcontainer-setup</strong> branch.</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>git clone https://github.com/zikalino/azure-cli.git
</span><span class='line'><span class="nb">cd </span>azure-cli
</span><span class='line'>git checkout adding-devcontainer-setup
</span></code></pre></td></tr></table></div></figure>
<p>Then run Visual Code from cloned repository:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>code-insiders .
</span></code></pre></td></tr></table></div></figure>
<p>Visual Studio Code should detect that <strong>.devcontainer</strong> folder is present, and will ask whether you want to use container.</p>
<p><img src="/images/devcontainer-reopen.png" alt="Reopen in Container" /></p>
<p>When you choose <strong>Reopen in Container</strong>, VSC will build the image from docker file, that may take a few minutes, but it will be done only once (for current folder).</p>
<p><img src="/images/devcontainer-build.png" alt="Build Container" /></p>
<p>When container is ready, just set a breakpoint, for instance in <strong>azure-cli/src/azure-cli/<strong>main</strong>.py</strong> and press <strong>F5</strong>.</p>
<p><img src="/images/devcontainer-run.png" alt="Run Container" /></p>
<h2>More Details on the Container</h2>
<p>My Dockerfile is pretty simple.
As a base I used Ubuntu 16.04, and:</p>
<ul>
<li>Installed Python 3.6</li>
<li>Cloned Azure CLI repo (just to install dependencies)</li>
<li>Created virtual environment located at <strong>/env</strong> (outside Azure CLI repo)</li>
<li>Installed <strong>azdev</strong> using <strong>pip</strong></li>
<li>Installed Azure CLI dependencies using <strong>azdev setup</strong></li>
<li>Made sure that <strong>/env</strong> is activated every time <strong>bash</strong> is started by Visual Studio Code.</li>
</ul>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="c"># start from Ubuntu 16.04</span>
</span><span class='line'>FROM ubuntu:xenial
</span><span class='line'>
</span><span class='line'><span class="c"># install python 3.6, pip, venv and git</span>
</span><span class='line'>RUN apt-get update
</span><span class='line'>RUN apt-get -y install software-properties-common
</span><span class='line'>RUN add-apt-repository ppa:deadsnakes/ppa
</span><span class='line'>RUN apt-get update
</span><span class='line'>RUN apt-get -y install python3.6 python3-pip python3-venv git
</span><span class='line'>
</span><span class='line'><span class="c"># clone azure cli into /workspaces/azure-cli</span>
</span><span class='line'>RUN mkdir /workspaces<span class="p">;</span> <span class="nb">cd</span> /workspaces<span class="p">;</span> git clone https://github.com/Azure/azure-cli.git
</span><span class='line'>
</span><span class='line'><span class="c"># create environment in /env and set up </span>
</span><span class='line'>RUN python3 -m venv env
</span><span class='line'>RUN /bin/bash -c <span class="s2">"source /env/bin/activate; pip3 install azdev; azdev setup --cli /workspaces/azure-cli"</span>
</span><span class='line'>
</span><span class='line'><span class="c"># remove azure-cli as it will be used </span>
</span><span class='line'>RUN rm -rf ./workspaces/azure-cli
</span><span class='line'>
</span><span class='line'><span class="c"># add environment activation to .bashrc</span>
</span><span class='line'>RUN <span class="nb">echo</span> <span class="s2">"source /env/bin/activate"</span> >> ~/.bashrc
</span></code></pre></td></tr></table></div></figure>
<h2>And Configuration File</h2>
<p>Configuration file <strong>devcontainer.json</strong> is pretty simple. It specifies <strong>Dockerfile</strong> and a path to Python interpreter inside <strong>/env</strong>.</p>
<p>Please note that instead of specifying <strong>dockerFile</strong> it’s possible to specify existing <strong>image</strong>.</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="o">{</span>
</span><span class='line'> <span class="s2">"name"</span>: <span class="s2">"Azure CLI Dev"</span>,
</span><span class='line'> <span class="s2">"dockerFile"</span>: <span class="s2">"Dockerfile"</span>,
</span><span class='line'> <span class="s2">"context"</span>: <span class="s2">"."</span>,
</span><span class='line'> <span class="s2">"extensions"</span>: <span class="o">[</span>
</span><span class='line'> <span class="s2">"ms-python.python"</span>
</span><span class='line'> <span class="o">]</span>,
</span><span class='line'> <span class="s2">"settings"</span>: <span class="o">{</span>
</span><span class='line'> <span class="s2">"python.pythonPath"</span>: <span class="s2">"/env/bin/python3"</span>
</span><span class='line'> <span class="o">}</span>,
</span><span class='line'> <span class="s2">"postCreateCommand"</span>: <span class="s2">"bash"</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>
<h2>Other Considerations</h2>
<p>(1) It’s possible to specify existing image instead of dockerfile. If we have existing development image that would make setup even faster.</p>
<p>(2) Perhaps it would be a good idea to clone all <strong>azure-cli</strong>, <strong>azure-cli-dev-tools</strong>, <strong>azure-cli-extensions</strong> under workspaces. Then it should be possible to map all three of them when starting the container.</p>
<p>(3) Perhaps leaving <strong>azure-cli</strong> (I currently delete it) would be a good idea, this way the container could be used as a standalone dev environment with source code cloned inside.</p>
<p>(4) Instead of cloning <strong>azure-cli</strong> it’s possible to map external repository. I am not doing it right now, as there’s <strong>.dockerignore</strong> file that would need to be modified in order to make this solution to work, and that could affect</p>
<h2>Documents</h2>
<p>Development environment setup instructions:</p>
<p><a href="https://github.com/Azure/azure-cli-dev-tools">https://github.com/Azure/azure-cli-dev-tools</a></p>
<p>About VSC Remote Development</p>
<p><a href="https://code.visualstudio.com/docs/remote/remote-overview">https://code.visualstudio.com/docs/remote/remote-overview</a></p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2019/05/27/idea-of-using-azure-cli-extension-to-test-validate-fix-and-create-azure-swagger-examples/">Idea of Using Azure CLI Extension to Test, Validate, Fix and Create Azure Swagger Examples</a></h1>
<p class="meta">
<time class='entry-date' datetime='2019-05-27T11:01:54+08:00'><span class='date'><span class='date-month'>May</span> <span class='date-day'>27</span><span class='date-suffix'>th</span>, <span class='date-year'>2019</span></span> <span class='time'>11:01 am</span></time>
</p>
</header>
<div class="entry-content"><h2>Overview</h2>
<p>This idea occured to me for several reasons:</p>
<ul>
<li>there’s no single tool used to create, test and validate Azure Swagger examples</li>
<li>process of validating and updating Azure REST API examples is quite awkward and time consuming. Updating even single input value reauires a lot of steps, for instance responses should be updated as well</li>
<li>there are no single naming conventions</li>
<li>Azure CLI is the most commonly used tool to manage Azure resources</li>
<li>it would be very easy to create an extension based on <strong>az resource</strong> which already allows raw REST API calls meaning it provides entire framework</li>
</ul>
<h2>Advantages</h2>
<ul>
<li>easy, fast and consistent process of creating new and updating existing examples</li>
<li>validation and dependency checking</li>
<li>enforcing consistent naming conventions, that would lead to more consistent documentation and compatibility between examples (for instance all the examples would use single resource group <strong>myResourceGroup</strong>)</li>
<li>simple way to check examples without using any additional tool</li>
</ul>
<h2>Running / Validating Existing Examples</h2>
<h3>Overview</h3>
<p>The tool should allow to:</p>
<ul>
<li>run existing examples directly from Azure REST API specs (local or GitHub repo)</li>
<li>run examples in their raw form</li>
<li>automatically fix problems / adjust naming conventions to match common naming convention</li>
<li>allow applying custom parameter fixes</li>
<li>generate final adjusted / updated example source code</li>
<li>optionally copy example to original source tree or create a pull request in original GitHub repo (fork of Azure REST API specification)</li>
</ul>
<h3>Command</h3>
<p>To run existing example:</p>
<p><strong>az swagger run –example {location} [–raw] [–override {field}={value}] [–dependencies] [–update]</strong></p>
<p>where:</p>
<p><strong>–example</strong></p>
<p>{location} - either URL of example in GitHub or local path to example in cloned GitHub repo</p>
<p><strong>–raw</strong></p>
<p>If specified all field values from the example files will be used as is.</p>
<p>By default extension would override existing values using default resource naming conventions.</p>
<p><strong>–override</strong></p>
<p>{field} - field name or path to a field in</p>
<p><strong>–dependencies</strong></p>
<p>The tool will attempt to create all the dependencies if they don’t exist, e.g. resource group or parent resource (using appropriate examples from swagger).</p>
<p>If this option is note specified, the tool will display appropriate warnings.</p>
<p><strong>–update</strong></p>
<p>If this option is specified extension will attempt to update existing sample in the local filesystem.
In the future it could create a branch and pull requests directly to the GitHub Repo.</p>
<p>The attempt will be made if:</p>
<ul>
<li>example was run successfully</li>
<li>there are differences between existing example input parameters and overrided input parameters</li>
<li>there is difference in return value specified and value actually returned</li>
</ul>
<p>Sample input could look like that:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>az swagger run --example https://github.com/Azure/azure-rest-api-specs/blob/master/specification/botservice/resource-manager/Microsoft.BotService/preview/2018-07-12/examples/CreateBot.json</span></code></pre></td></tr></table></div></figure>
<p>Sample output could look like this:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
<span class='line-number'>94</span>
<span class='line-number'>95</span>
<span class='line-number'>96</span>
<span class='line-number'>97</span>
<span class='line-number'>98</span>
<span class='line-number'>99</span>
<span class='line-number'>100</span>
<span class='line-number'>101</span>
<span class='line-number'>102</span>
<span class='line-number'>103</span>
<span class='line-number'>104</span>
<span class='line-number'>105</span>
<span class='line-number'>106</span>
<span class='line-number'>107</span>
<span class='line-number'>108</span>
<span class='line-number'>109</span>
<span class='line-number'>110</span>
<span class='line-number'>111</span>
<span class='line-number'>112</span>
<span class='line-number'>113</span>
<span class='line-number'>114</span>
<span class='line-number'>115</span>
<span class='line-number'>116</span>
<span class='line-number'>117</span>
<span class='line-number'>118</span>
<span class='line-number'>119</span>
<span class='line-number'>120</span>
<span class='line-number'>121</span>
<span class='line-number'>122</span>
<span class='line-number'>123</span>
<span class='line-number'>124</span>
<span class='line-number'>125</span>
<span class='line-number'>126</span>
<span class='line-number'>127</span>
<span class='line-number'>128</span>
<span class='line-number'>129</span>
<span class='line-number'>130</span>
<span class='line-number'>131</span>
<span class='line-number'>132</span>
<span class='line-number'>133</span>
<span class='line-number'>134</span>
<span class='line-number'>135</span>
<span class='line-number'>136</span>
<span class='line-number'>137</span>
<span class='line-number'>138</span>
<span class='line-number'>139</span>
<span class='line-number'>140</span>
<span class='line-number'>141</span>
<span class='line-number'>142</span>
<span class='line-number'>143</span>
<span class='line-number'>144</span>
<span class='line-number'>145</span>
<span class='line-number'>146</span>
<span class='line-number'>147</span>
<span class='line-number'>148</span>
<span class='line-number'>149</span>
<span class='line-number'>150</span>
<span class='line-number'>151</span>
<span class='line-number'>152</span>
<span class='line-number'>153</span>
<span class='line-number'>154</span>
<span class='line-number'>155</span>
<span class='line-number'>156</span>
<span class='line-number'>157</span>
<span class='line-number'>158</span>
<span class='line-number'>159</span>
<span class='line-number'>160</span>
<span class='line-number'>161</span>
<span class='line-number'>162</span>
<span class='line-number'>163</span>
<span class='line-number'>164</span>
<span class='line-number'>165</span>
<span class='line-number'>166</span>
<span class='line-number'>167</span>
<span class='line-number'>168</span>
<span class='line-number'>169</span>
<span class='line-number'>170</span>
<span class='line-number'>171</span>
<span class='line-number'>172</span>
<span class='line-number'>173</span>
<span class='line-number'>174</span>
<span class='line-number'>175</span>
<span class='line-number'>176</span>
<span class='line-number'>177</span>
<span class='line-number'>178</span>
<span class='line-number'>179</span>
<span class='line-number'>180</span>
<span class='line-number'>181</span>
<span class='line-number'>182</span>
<span class='line-number'>183</span>
<span class='line-number'>184</span>
<span class='line-number'>185</span>
<span class='line-number'>186</span>
<span class='line-number'>187</span>
<span class='line-number'>188</span>
<span class='line-number'>189</span>
<span class='line-number'>190</span>
<span class='line-number'>191</span>
<span class='line-number'>192</span>
<span class='line-number'>193</span>
<span class='line-number'>194</span>
<span class='line-number'>195</span>
<span class='line-number'>196</span>
<span class='line-number'>197</span>
<span class='line-number'>198</span>
<span class='line-number'>199</span>
<span class='line-number'>200</span>
<span class='line-number'>201</span>
<span class='line-number'>202</span>
<span class='line-number'>203</span>
<span class='line-number'>204</span>
<span class='line-number'>205</span>
<span class='line-number'>206</span>
<span class='line-number'>207</span>
<span class='line-number'>208</span>
<span class='line-number'>209</span>
<span class='line-number'>210</span>
<span class='line-number'>211</span>
<span class='line-number'>212</span>
<span class='line-number'>213</span>
<span class='line-number'>214</span>
<span class='line-number'>215</span>
<span class='line-number'>216</span>
<span class='line-number'>217</span>
<span class='line-number'>218</span>
<span class='line-number'>219</span>
<span class='line-number'>220</span>
<span class='line-number'>221</span>
<span class='line-number'>222</span>
<span class='line-number'>223</span>
<span class='line-number'>224</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>-------- WARNINGS
</span><span class='line'>- resourceGroupName "OneResourceGroup" was replaced by "myResourceGroup"
</span><span class='line'>- resourceName "sampleBot" was replaced by "myBot"
</span><span class='line'>-------- ORIGINAL EXAMPLE
</span><span class='line'>{
</span><span class='line'> "parameters": {
</span><span class='line'> "subscriptionId": "subscription-id",
</span><span class='line'> "resourceGroupName": "OneResourceGroupName",
</span><span class='line'> "api-version": "2017-01-01",
</span><span class='line'> "resourceName": "samplebotname",
</span><span class='line'> "parameters": {
</span><span class='line'> "location": "West US",
</span><span class='line'> "sku": {
</span><span class='line'> "name": "S1"
</span><span class='line'> },
</span><span class='line'> "etag": "etag1",
</span><span class='line'> "tags": {
</span><span class='line'> "tag1": "value1",
</span><span class='line'> "tag2": "value2"
</span><span class='line'> },
</span><span class='line'> "name": "samplename",
</span><span class='line'> "type": "sampletype",
</span><span class='line'> "id": "someid",
</span><span class='line'> "kind": "sdk",
</span><span class='line'> "properties": {
</span><span class='line'> "description": "The description of the bot",
</span><span class='line'> "developerAppInsightKey": "appinsightskey",
</span><span class='line'> "developerAppInsightsApiKey": "appinsightsapikey",
</span><span class='line'> "developerAppInsightsApplicationId": "appinsightsappid",
</span><span class='line'> "displayName": "The Name of the bot",
</span><span class='line'> "endpoint": "http://mybot.coffee",
</span><span class='line'> "iconUrl": "http://myicon",
</span><span class='line'> "luisAppIds": [
</span><span class='line'> "luisappid1",
</span><span class='line'> "luisappid2"
</span><span class='line'> ],
</span><span class='line'> "luisKey": "luiskey",
</span><span class='line'> "msaAppId": "exampleappid"
</span><span class='line'> }
</span><span class='line'> }
</span><span class='line'> },
</span><span class='line'> "responses": {
</span><span class='line'> "200": {
</span><span class='line'> "body": {
</span><span class='line'> "location": "West US",
</span><span class='line'> "tags": {
</span><span class='line'> "tag1": "value1",
</span><span class='line'> "tag2": "value2"
</span><span class='line'> },
</span><span class='line'> "name": "samplename",
</span><span class='line'> "type": "sampletype",
</span><span class='line'> "id": "someid",
</span><span class='line'> "kind": "sdk",
</span><span class='line'> "etag": "etag1",
</span><span class='line'> "properties": {
</span><span class='line'> "description": "The description of the bot",
</span><span class='line'> "developerAppInsightKey": "appinsightskey",
</span><span class='line'> "developerAppInsightsApplicationId": "appinsightsappid",
</span><span class='line'> "displayName": "The Name of the bot",
</span><span class='line'> "endpoint": "http://mybot.coffee",
</span><span class='line'> "endpointVersion": "version",
</span><span class='line'> "iconUrl": "http://myicon",
</span><span class='line'> "luisAppIds": [
</span><span class='line'> "luisappid1",
</span><span class='line'> "luisappid2"
</span><span class='line'> ],
</span><span class='line'> "msaAppId": "msaappid",
</span><span class='line'> "configuredChannels": [
</span><span class='line'> "facebook",
</span><span class='line'> "groupme"
</span><span class='line'> ],
</span><span class='line'> "enabledChannels": [
</span><span class='line'> "facebook"
</span><span class='line'> ]
</span><span class='line'> }
</span><span class='line'> }
</span><span class='line'> },
</span><span class='line'> "201": {
</span><span class='line'> "body": {
</span><span class='line'> "location": "West US",
</span><span class='line'> "tags": {
</span><span class='line'> "tag1": "value1",
</span><span class='line'> "tag2": "value2"
</span><span class='line'> },
</span><span class='line'> "name": "samplename",
</span><span class='line'> "type": "sampletype",
</span><span class='line'> "id": "someid",
</span><span class='line'> "kind": "sdk",
</span><span class='line'> "properties": {
</span><span class='line'> "description": "The description of the bot",
</span><span class='line'> "developerAppInsightsApplicationId": "appinsightsappid",
</span><span class='line'> "displayName": "The Name of the bot",
</span><span class='line'> "endpoint": "http://mybot.coffee",
</span><span class='line'> "endpointVersion": "version",
</span><span class='line'> "iconUrl": "http://myicon",
</span><span class='line'> "luisAppIds": [
</span><span class='line'> "luisappid1",
</span><span class='line'> "luisappid2"
</span><span class='line'> ],
</span><span class='line'> "msaAppId": "msaappid",
</span><span class='line'> "configuredChannels": [
</span><span class='line'> "facebook",
</span><span class='line'> "groupme"
</span><span class='line'> ],
</span><span class='line'> "enabledChannels": [
</span><span class='line'> "facebook"
</span><span class='line'> ]
</span><span class='line'> }
</span><span class='line'> }
</span><span class='line'> }
</span><span class='line'> }
</span><span class='line'>}
</span><span class='line'>-------- UPDATED EXAMPLE (createbot.json)
</span><span class='line'>{
</span><span class='line'> "parameters": {
</span><span class='line'> "subscriptionId": "subscription-id",
</span><span class='line'> "resourceGroupName": "myResourceGroup",
</span><span class='line'> "api-version": "2017-01-01",
</span><span class='line'> "resourceName": "myBot",
</span><span class='line'> "parameters": {
</span><span class='line'> "location": "West US",
</span><span class='line'> "sku": {
</span><span class='line'> "name": "S1"
</span><span class='line'> },
</span><span class='line'> "etag": "etag1",
</span><span class='line'> "tags": {
</span><span class='line'> "tag1": "value1",
</span><span class='line'> "tag2": "value2"
</span><span class='line'> },
</span><span class='line'> "name": "samplename",
</span><span class='line'> "type": "sampletype",
</span><span class='line'> "id": "someid",
</span><span class='line'> "kind": "sdk",
</span><span class='line'> "properties": {
</span><span class='line'> "description": "The description of the bot",
</span><span class='line'> "developerAppInsightKey": "appinsightskey",
</span><span class='line'> "developerAppInsightsApiKey": "appinsightsapikey",
</span><span class='line'> "developerAppInsightsApplicationId": "appinsightsappid",
</span><span class='line'> "displayName": "The Name of the bot",
</span><span class='line'> "endpoint": "http://mybot.coffee",
</span><span class='line'> "iconUrl": "http://myicon",
</span><span class='line'> "luisAppIds": [
</span><span class='line'> "luisappid1",
</span><span class='line'> "luisappid2"
</span><span class='line'> ],
</span><span class='line'> "luisKey": "luiskey",
</span><span class='line'> "msaAppId": "exampleappid"
</span><span class='line'> }
</span><span class='line'> }
</span><span class='line'> },
</span><span class='line'> "responses": {
</span><span class='line'> "200": {
</span><span class='line'> "body": {
</span><span class='line'> "location": "West US",
</span><span class='line'> "tags": {
</span><span class='line'> "tag1": "value1",
</span><span class='line'> "tag2": "value2"
</span><span class='line'> },
</span><span class='line'> "name": "samplename",
</span><span class='line'> "type": "sampletype",
</span><span class='line'> "id": "someid",
</span><span class='line'> "kind": "sdk",
</span><span class='line'> "etag": "etag1",
</span><span class='line'> "properties": {
</span><span class='line'> "description": "The description of the bot",
</span><span class='line'> "developerAppInsightKey": "appinsightskey",
</span><span class='line'> "developerAppInsightsApplicationId": "appinsightsappid",
</span><span class='line'> "displayName": "The Name of the bot",
</span><span class='line'> "endpoint": "http://mybot.coffee",
</span><span class='line'> "endpointVersion": "version",
</span><span class='line'> "iconUrl": "http://myicon",
</span><span class='line'> "luisAppIds": [
</span><span class='line'> "luisappid1",
</span><span class='line'> "luisappid2"
</span><span class='line'> ],
</span><span class='line'> "msaAppId": "msaappid",
</span><span class='line'> "configuredChannels": [
</span><span class='line'> "facebook",
</span><span class='line'> "groupme"
</span><span class='line'> ],
</span><span class='line'> "enabledChannels": [
</span><span class='line'> "facebook"
</span><span class='line'> ]
</span><span class='line'> }
</span><span class='line'> }
</span><span class='line'> },
</span><span class='line'> "201": {
</span><span class='line'> "body": {
</span><span class='line'> "location": "West US",
</span><span class='line'> "tags": {
</span><span class='line'> "tag1": "value1",
</span><span class='line'> "tag2": "value2"
</span><span class='line'> },
</span><span class='line'> "name": "samplename",
</span><span class='line'> "type": "sampletype",
</span><span class='line'> "id": "someid",
</span><span class='line'> "kind": "sdk",
</span><span class='line'> "properties": {
</span><span class='line'> "description": "The description of the bot",
</span><span class='line'> "developerAppInsightsApplicationId": "appinsightsappid",
</span><span class='line'> "displayName": "The Name of the bot",
</span><span class='line'> "endpoint": "http://mybot.coffee",
</span><span class='line'> "endpointVersion": "version",
</span><span class='line'> "iconUrl": "http://myicon",
</span><span class='line'> "luisAppIds": [
</span><span class='line'> "luisappid1",
</span><span class='line'> "luisappid2"
</span><span class='line'> ],
</span><span class='line'> "msaAppId": "msaappid",
</span><span class='line'> "configuredChannels": [
</span><span class='line'> "facebook",
</span><span class='line'> "groupme"
</span><span class='line'> ],
</span><span class='line'> "enabledChannels": [
</span><span class='line'> "facebook"
</span><span class='line'> ]
</span><span class='line'> }
</span><span class='line'> }
</span><span class='line'> }
</span><span class='line'> }
</span><span class='line'>}
</span><span class='line'>-------- RESULT
</span><span class='line'>UPDATED: YES
</span><span class='line'>RUN: SUCCESS</span></code></pre></td></tr></table></div></figure>
<h2>Scaffolding Examples</h2>
<h3>Overview</h3>
<p>If example doesn’t exist, the tool will allow creating a template based on REST API specs.
In the next step user should update the template and rerun the tool to validate example until it runs correctly.</p>
<h3>Command</h3>
<p><strong>az swagger scaffold –swagger {location} –uri {resource-uri} –method {method} [–update]</strong></p>
<p><strong>–example</strong></p>
<p>{location} - either URL of specific swagger json file or path in local</p>
<p><strong>–uri</strong></p>
<p>Resource URI as appears in swagger specification / documentation.</p>
<p><strong>–method</strong></p>
<p>Method for which scaffolding should be done, e.g. <strong>put</strong>, <strong>get</strong>, etc…</p>
<p><strong>–update</strong></p>
<p>If this option is specified extension will attempt to update existing sample in the local filesystem.
In the future it could create a branch and pull requests directly to the GitHub Repo.</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2019/05/22/ansible-and-azure-cli/">Ansible and Azure CLI - Closer Than I Thought</a></h1>
<p class="meta">
<time class='entry-date' datetime='2019-05-22T11:25:08+08:00'><span class='date'><span class='date-month'>May</span> <span class='date-day'>22</span><span class='date-suffix'>nd</span>, <span class='date-year'>2019</span></span> <span class='time'>11:25 am</span></time>
</p>
</header>
<div class="entry-content"><h3>What if…</h3>
<p>… Ansible support for Azure was based on <strong>Azure CLI</strong>?</p>
<h2>Similarities and Differences</h2>
<table>
<thead>
<tr>
<th>Item</th>
<th>Azure CLI</th>
<th>Ansible</th>
</tr>
</thead>
<tbody>
<tr>
<td>Implementation</td>
<td>Python/Python SDK</td>
<td>Python/Python SDK</td>
</tr>
<tr>
<td>UX</td>
<td>Azure CLI Options are flattened, simplified and well designed</td>
<td>Ansible options are flattened and we strive to simplify the structure. The level of simplification of Azure CLI is our ultimate goal.</td>
</tr>
<tr>
<td>Error Handling</td>
<td>Excellent. Azure CLI has a lot of checks and gracefully handles critical situations. User can easily create any new resource by reading documentation and reading error responses when something goes wrong.</td>
<td>Poor, if user specifies wrong parameters in most cases module propagates error received from Azure, which in most cases is meaningless</td>
</tr>
<tr>
<td>Idempotence</td>
<td>Not a major goal of Azure CLI, however the idea is that Azure CLI commands should be idempotent</td>
<td>Idempotence is a major feature of Ansible, however we have several implementation problems, for instance not handling non-updatable fiels correctly, not detecting changes in parameters corectly. A lot of inconsistencies in the implementation</td>
</tr>
<tr>
<td>Documentation</td>
<td>Excellent</td>
<td>Good, but sometimes not complete or directly copied from REST API Specification</td>
</tr>
<tr>
<td>Check Mode</td>
<td>NO</td>
<td>YES (see notes on idempotence)</td>
</tr>
<tr>
<td>Coverage</td>
<td>Around 60% of Azure API</td>
<td>Around 12% of Azure API</td>
</tr>
</tbody>
</table>
<h2>How it Could Be Done?</h2>
<p>Implementation could be extremely simple</p>
<p>We could have entire implementation in a single base <strong>az.py</strong> Ansible module to encapsulate <strong>Azure CLI</strong>.</p>
<p>Instead of implementing hundreds of modules for every single resource, we could just have symbolic links to the base module:</p>
<ul>
<li><strong>az_vm.py</strong></li>
<li><strong>az_acr.py</strong></li>
<li><strong>az_vmss.py</strong></li>
</ul>
<p>Base module implementation knows via which link it was loaded, so based on that it can dynamically create:</p>
<ul>
<li>appropriate documentation</li>
<li>appropriate argument spec</li>
<li>handle arguments correctly and call correct commands</li>
</ul>
<p>On the top of that module can:
- recognise which options are updatable by comparing <strong>create</strong> and <strong>update</strong> parameters (using distilled knowledge from Azure CLI)</p>
<h3>How module would work?</h3>
<p>(1) call <strong>az xxx get</strong> to check whether resource exists, get current state
(2) call <strong>az xxx create</strong> or <strong>az xxx update</strong> if resource exists
(3) compare output of initial <strong>get</strong> with output of <strong>update</strong>, to check whether anything has changed</p>
<h2>What’s missing?</h2>
<h3>Check Mode</h3>
<p>Check mode is probably the only missing feature that Ansible has (but we are struggling to implement it correctly) and Azure CLI doesn’t.</p>
<p>As far as I know Azure CLI is striving to implement idempotency. I need to collect more details on that.</p>
<p>Anyway, a new option called <strong>–check-mode</strong> would be nice to have.
It could work as follows:</p>
<p>User calls:</p>
<p><strong>az xxxx create ….. params …..</strong></p>
<p>Azure CLI calls <strong>get</strong> method on the resource to be created.
Azure CLI prepares the structure to call Python SDK API.
Acure CLI compares structure returned by <strong>get</strong> and structure prepared for <strong>create</strong> call.
If the structures match, return <strong>no change required</strong>, if structures don’t match return list of changes detected.</p>
<p>This method would probably work for around 90% of Azure resources, it would fail with</p>
<p>This module would just handle all the interaction with unerlying <strong>Azure CLI</strong>, including:
- handling currently selected subscription if necessary
- handling authentication</p>
<p>On the top of that we could just create symbolic links to that module for every Azure resource, for instance:</p>